简体   繁体   English

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

[英]How pass a file pointer from c to a call in asm

I'm messing around with nasm, and after doing a hello world with no problem, I though I'd try to do some c integration.我在搞 nasm,在没有问题的情况下做了一个 hello world 之后,我虽然我会尝试做一些 c 集成。

I'm using c to open a file, and I then want to use the pointer returned for the open file to process the text.我正在使用 c 打开一个文件,然后我想使用为打开文件返回的指针来处理文本。 However, when I call fgetc with the pointer in rdi, I get a "no such file or directory", followed by a segfault.但是,当我用 rdi 中的指针调用 fgetc 时,我得到一个“没有这样的文件或目录”,然后是段错误。

What am I doing wrong?我究竟做错了什么?

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

The first instruction of asmFunc isn't a call either, but I removed some setup stuff for later operations to make it easier to read. asmFunc 的第一条指令也不是调用,但我删除了一些设置内容以供以后操作以使其更易于阅读。

Well that just defeats the entire purpose of an MCVE.好吧,这违背了 MCVE 的全部目的。 You need to re-run your test after simplifying to make sure it still shows the same problem as your full version.您需要简化重新运行您的测试以确保它仍然显示与您的完整版本相同的问题。 But for this answer, I'll assume your setup didn't clobber the fptr arg in RDI or modify RSP.但是对于这个答案,我假设您的设置没有破坏 RDI 中的fptr arg 或修改 RSP。


asmFunc:
  call fgetc  ; segfault occurs here.

fptr will still be in RDI, where your caller passed it, so that's correct for int fgetc(FILE *fp) . fptr仍将在 RDI 中,您的调用者通过它,所以这对于int fgetc(FILE *fp)是正确的。

So presumably fgetc is segfaulting because you called it with a misaligned stack.所以大概fgetc是段错误,因为你用一个未对齐的堆栈调用它。 (It was 16-byte aligned before the call that jumped to asmFunc , but you don't have an odd number of pushes or any sub rsp, 8*n ). (在跳转到asmFunccall之前它是 16 字节对齐的,但是您没有奇数次推送或任何sub rsp, 8*n )。 Modern builds of glibc actually do depend on 16-byte alignment for scanf ( glibc scanf Segmentation faults when called from a function that doesn't align RSP ) so it's easy to imagine that fgetc includes code that also compiles to include a movaps of something on the stack. glibc 的现代版本实际上确实依赖于 scanf 的 16 字节对齐( 从不对齐 RSP 的函数调用时 glibc scanf 分段错误),因此很容易想象 fgetc 包含的代码也可以编译为包含某些内容的movaps堆栈。


Once you fix this bug, you will have the problem that call fgetc destroys your char *ret arg, because your caller passes it in RSI.一旦你修复了这个错误,你就会遇到call fgetc会破坏你的char *ret arg 的问题,因为你的调用者在 RSI 中传递了它。 Arg-passing registers are call-clobbered .传递参数的寄存器是 call-clobbered What registers are preserved through a linux x86-64 function call 通过 linux x86-64 函数调用保留了哪些寄存器

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

AC compiler would normally save/restore RBX and use mov to save ret there. AC 编译器通常会保存/恢复 RBX 并使用movret保存在那里。

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

  pop    rbx        ; restore rbx
  ret

However, when I call fgetc with the pointer in rdi, I get a "no such file or directory", followed by a segfault.但是,当我用 rdi 中的指针调用 fgetc 时,我得到一个“没有这样的文件或目录”,然后是段错误。

No idea how you're getting "no such file or directory".不知道你是如何得到“没有这样的文件或目录”的。 Is that from your debugger looking source for glibc functions?那是从您的调试器中寻找 glibc 函数的源代码吗? If it's part of what your program itself prints, that makes near zero sense, because you do exit(1) correctly when fptr == NULL .如果它是您的程序本身打印内容的一部分,那么这几乎为零,因为您在fptr == NULL时正确执行exit(1) And you don't use perror() or anything else that looks up errno codes to generate standard error strings.并且您不使用perror()或任何其他查找 errno 代码的方法来生成标准错误字符串。

You need to learn and follow the calling conventions documented in Linux x86-64 ABI specification, in particular its §3.2.3 Parameter passing section .您需要学习并遵循Linux x86-64 ABI规范中记录的调用约定,特别是其§3.2.3 参数传递部分 So the pointer value fptr is in %rdi , and the pointer value ret is in %rsi and you probably should push a call frame for your asmFunc所以指针值fptr%rdi ,指针值ret%rsi ,你可能应该为你的asmFunc推送一个调用帧

Read also the x86 calling conventions wikipage.另请阅读x86 调用约定wikipage。

If you are able to code the equivalent (even a simplified one) of asmFunc in C in some example.c file, I recommend compiling it with gcc -O -fverbose-asm -Wall -S example.c and looking into the emitted example.s assembler file for inspiration.如果您能够在某些example.c文件asmFunc C 编写等效的(甚至是简化的) asmFunc ,我建议使用gcc -O -fverbose-asm -Wall -S example.c编译它并查看发出的example.s汇编程序文件以获得灵感。 Most of the time, the first machine instruction of such a function is not a call (but something, called the function prologue , changing the stack pointer %esp and allocating some call frame on the call stack )大多数情况下,此类函数的第一条机器指令不是call (而是某种称为函数序言、更改堆栈指针%esp并在调用堆栈上分配一些调用帧的东西)

For example, on my Linux/Debian/x86-64 with gcc-8例如,在我的 Linux/Debian/x86-64 上使用 gcc-8

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

is compiled into:编译成:

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

Notice however that in some cases, GCC is capable (eg with -O2 ) of tail-call optimizations and might call some leaf-functions specially.但是请注意,在某些情况下,GCC 能够(例如使用-O2 )进行尾调用优化,并且可能会专门调用一些叶函数

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM