首页 > 解决方案 > 如何将文件指针从 c 传递给 asm 中的调用

问题描述

我在搞乱 nasm,在没有问题的情况下做了一个 hello world 之后,我虽然会尝试做一些 c 集成。

我正在使用 c 打开一个文件,然后我想使用为打开的文件返回的指针来处理文本。但是,当我用 rdi 中的指针调用 fgetc 时,我得到一个“没有这样的文件或目录”,然后是一个段错误。

我究竟做错了什么?

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

标签: clinuxassemblyx86-64nasm

解决方案


asmFunc 的第一条指令也不是调用,但我删除了一些设置内容以供以后操作以使其更易于阅读。

好吧,这违背了 MCVE 的全部目的。您需要在简化后重新运行测试,以确保它仍然显示与完整版本相同的问题。但是对于这个答案,我假设您的设置没有破坏fptrRDI 中的 arg 或修改 RSP。


asmFunc:
  call fgetc  ; segfault occurs here.

fptr仍将在 RDI 中,您的调用者将其传递给它,因此对于int fgetc(FILE *fp).

所以大概fgetc是段错误,因为你用未对齐的堆栈调用它。call(在跳转到 之前它是 16 字节对齐的asmFunc,但是你没有奇数的 push 或任何sub rsp, 8*n)。glibc 的现代构建实​​际上确实依赖于 scanf 的 16 字节对齐(从不对齐 RSP 的函数调用时 glibc scanf 分段错误),因此很容易想象 fgetc 包含的代码也可以编译为movaps在堆。


一旦你修复了这个错误,你就会遇到call fgetc破坏你的char *retarg 的问题,因为你的调用者在 RSI 中传递了它。 传递参数的寄存器是 call-clobbered通过 linux x86-64 函数调用保留了哪些寄存器

asmFunc:            ; (FILE *fptr,  char *ret)
  push   rsi        ; save ret
  call   fgetc
  pop    rsi
  mov    [rsi], al
  ret

AC 编译器通常会保存/恢复 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 时,我得到一个“没有这样的文件或目录”,然后是一个段错误。

不知道你是如何得到“没有这样的文件或目录”的。那是从您的调试器中寻找 glibc 函数的源代码吗?如果它是您的程序本身打印的内容的一部分,那几乎是零意义,因为您exit(1)fptr == NULL. 而且您不使用perror()或任何其他查找 errno 代码来生成标准错误字符串的东西。


推荐阅读