【问题标题】:How pass a file pointer from c to a call in asm如何将文件指针从 c 传递给 asm 中的调用
【发布时间】:2019-04-27 15:47:36
【问题描述】:

我在搞乱 nasm,在没有问题地打了个招呼之后,我想尝试做一些 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

【问题讨论】:

标签: c linux assembly x86-64 nasm


【解决方案1】:

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

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


asmFunc:
  call fgetc  ; segfault occurs here.

fptr 仍将在 RDI 中,您的调用者将其传递给它,因此 int fgetc(FILE *fp) 是正确的。

所以大概fgetc 存在段错误,因为您使用未对齐的堆栈调用它。(在跳转到 asmFunccall 之前它是 16 字节对齐的,但您没有有奇数个推送或任何sub rsp, 8*n)。 glibc 的现代构建实​​际上确实依赖于 scanf (glibc scanf Segmentation faults when called from a function that doesn't align RSP) 的 16 字节对齐,因此很容易想象 fgetc 包含的代码也可以编译为在堆栈中包含 movaps


一旦你修复了这个错误,你就会遇到call fgetc破坏你的char *ret参数的问题,因为你的调用者在RSI中传递了它。 传递参数的寄存器被调用破坏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 并使用movret 保存在那里。

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 函数的源代码吗?如果它是您的程序本身打印的内容的一部分,那几乎是零意义,因为您在fptr == NULL 时正确地执行exit(1)。而且您不使用perror() 或任何其他查找errno 代码来生成标准错误字符串的东西。

【讨论】:

  • 我已经测试了简化版。我得到的错误是:程序收到信号 SIGSEGV,分段错误。 _IO_getc (fp=0x3) at getc.c:37 37 getc.c: 没有这样的文件或目录。
  • @hellomatey27: getc.c: No such file or directory 显然是由您的调试器打印的,就像我说的那样。无论如何,如果您使用调试器查看出错的指令,您可能会看到应该对齐但没有对齐的堆栈地址的movapsmovdqa。 (因为您违反了 ABI 中的堆栈对齐要求)。
【解决方案2】:

您需要学习并遵循Linux x86-64 ABI 规范中记录的calling conventions,尤其是其§3.2.3 参数传递部分。所以指针值fptr%rdi 中,而指针值ret%rsi 中,您可能应该为您的asmFunc 推送一个调用帧

另请阅读 x86 calling conventions 维基页面。

如果您能够在某些 example.c 文件中用 C 语言编写与 asmFunc 等效的代码(甚至是简化的代码),我建议使用 gcc -O -fverbose-asm -Wall -S example.c 编译它并查看发出的 example.s 汇编程序文件以获取灵感.大多数时候,此类函数的第一条机器指令不是call(而是一些东西,称为function prologue,更改堆栈指针%esp并在call stack上分配一些调用帧)

例如,在我的带有 gcc-8 的 Linux/Debian/x86-64 上

void asmfunc(FILE* fil, char*s) {
   fputc ('\t', fil);
   fputs (s, fil);
   fputc ('\n', fil);
   fflush (fil);
}

编译成:

    .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"

但请注意,在某些情况下,GCC 能够(例如使用-O2)进行tail-call 优化,并且可能会专门调用一些leaf-functions

【讨论】:

  • 我的阅读方式,这显然是错误的,第一个参数需要在rdi中,这是c函数在调用我的函数时放置的位置,对吧? asmFunc 的第一条指令也不是调用,但我删除了一些设置内容以供以后操作以使其更易于阅读。
猜你喜欢
  • 2021-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-01
  • 1970-01-01
  • 2017-08-30
  • 2022-01-03
  • 1970-01-01
相关资源
最近更新 更多