如何将文件指针从c传递给asm中的调用

时间:2019-04-27 15:47:36

标签: c linux assembly x86-64 nasm

我正在忙于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

2 个答案:

答案 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