我正在忙于nasm,并且在毫无问题地进行了问候之后,尽管我会尝试进行一些c集成。
我正在使用c打开文件,然后我想使用为打开的文件返回的指针来处理文本。但是,当我使用rdi中的指针调用fgetc时,会得到“没有这样的文件或目录”,后跟一个segfault。
我在做什么错了?
int64_t asmFunc(FILE* a, char* b);
int main()
{
int num;
FILE *fptr;
size_t line_buf_size = 0;
char *ret = malloc(100);
fptr = fopen("./test.txt","r");
if(fptr == NULL)
{
printf("Error!");
exit(1);
}
printf("%ld", asmFunc(fptr, ret));
return 0;
}
global asmFunc
section .text
extern fgetc
asmFunc:
call fgetc ; segfault occurs here.
(...)
ret
答案 0 :(得分:3)
asmFunc的第一条指令也不是调用,但是我删除了一些安装程序,以便以后进行操作,以便于阅读。
这恰恰打败了MCVE的全部目的。您需要简化后重新运行测试 以确保它仍然显示与完整版本相同的问题。但是对于这个答案,我假设您的设置没有破坏RDI中的fptr
arg或修改RSP。
asmFunc:
call fgetc ; segfault occurs here.
fptr
仍将在您的呼叫者通过它的RDI中使用,因此对int fgetc(FILE *fp)
来说是正确的。
因此,fgetc
可能是段错误的,因为您使用未对齐的堆栈对其进行了调用。(call
之前跳转到asmFunc
的位置是16字节对齐,但您没有多少次推送或任何sub rsp, 8*n
)。实际上,现代的glibc构建确实依赖于scanf(glibc scanf Segmentation faults when called from a function that doesn't align RSP)的16字节对齐,因此很容易想象fgetc包含的代码也可以编译为在堆栈中包含movaps
的内容。
一旦修复了 this 错误,您将遇到call fgetc
破坏您的char *ret
arg的问题,因为调用者在RSI中传递了它。 传递arg的寄存器被调用。 What registers are preserved through a linux x86-64 function call
asmFunc: ; (FILE *fptr, char *ret)
push rsi ; save ret
call fgetc
pop rsi
mov [rsi], al
ret
C编译器通常会保存/还原RBX,然后使用mov
在此处保存ret
。
asmFunc: ; (FILE *fptr, char *ret)
push rbx
mov rbx, rsi ; save ret
call fgetc
mov [rbx], al
pop rbx ; restore rbx
ret
但是,当我使用rdi中的指针调用fgetc时,得到的是“没有这样的文件或目录”,然后是segfault。
不知道您如何获得“没有这样的文件或目录”。是从调试器中查找glibc函数的源代码吗?如果它是程序本身打印的内容的一部分,那么这几乎是零意义,因为您在exit(1)
时正确地执行了fptr == NULL
。而且,您不会使用perror()
或其他会查找errno代码的东西来生成标准错误字符串。
答案 1 :(得分:1)
您需要学习并遵循calling conventions规范中记录的Linux x86-64 ABI,尤其是其§3.2.3参数传递部分。因此,指针值fptr
在%rdi
中,指针值ret
在%rsi
中,您可能应该为asmFunc
推送呼叫帧>
也请阅读x86 calling conventions维基页面。
如果您可以在某些asmFunc
文件中用C编码example.c
的等效语言(甚至是简化的代码),我建议使用gcc -O -fverbose-asm -Wall -S example.c
对其进行编译,并查看发出的{ {1}}汇编文件以获取灵感。在大多数情况下,此类函数的第一条机器指令不是example.s
(而是称为function prologue的东西,它会更改堆栈指针call
并在{ {3}})
例如,在使用gcc-8的Linux / Debian / x86-64上
%esp
被编译为:
void asmfunc(FILE* fil, char*s) {
fputc ('\t', fil);
fputs (s, fil);
fputc ('\n', fil);
fflush (fil);
}
但是请注意,在某些情况下,GCC能够(例如使用 .text
.globl asmfunc
.type asmfunc, @function
asmfunc:
.LFB11:
.cfi_startproc
pushq %rbp #
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
pushq %rbx #
.cfi_def_cfa_offset 24
.cfi_offset 3, -24
subq $8, %rsp #,
.cfi_def_cfa_offset 32
movq %rdi, %rbx # fil, fil
movq %rsi, %rbp # s, s
# /tmp/example.c:4: fputc ('\t', fil);
movq %rdi, %rsi # fil,
movl $9, %edi #,
call fputc@PLT #
# /tmp/example.c:5: fputs (s, fil);
movq %rbx, %rsi # fil,
movq %rbp, %rdi # s,
call fputs@PLT #
# /tmp/example.c:6: fputc ('\n', fil);
movq %rbx, %rsi # fil,
movl $10, %edi #,
call fputc@PLT #
# /tmp/example.c:7: fflush (fil);
movq %rbx, %rdi # fil,
call fflush@PLT #
# /tmp/example.c:8: }
addq $8, %rsp #,
.cfi_def_cfa_offset 24
popq %rbx #
.cfi_def_cfa_offset 16
popq %rbp #
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE11:
.size asmfunc, .-asmfunc
.ident "GCC: (Debian 8.3.0-6) 8.3.0"
)进行call stack优化,并且可能会专门调用某些tail-call。