簡體   English   中英

如何將文件指針從 c 傳遞給 asm 中的調用

[英]How pass a file pointer from c to a call in 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

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 的 16 字節對齊( 從不對齊 RSP 的函數調用時 glibc scanf 分段錯誤),因此很容易想象 fgetc 包含的代碼也可以編譯為包含某些內容的movaps堆棧。


一旦你修復了這個錯誤,你就會遇到call fgetc會破壞你的char *ret arg 的問題,因為你的調用者在 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 並使用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 代碼的方法來生成標准錯誤字符串。

您需要學習並遵循Linux x86-64 ABI規范中記錄的調用約定,特別是其§3.2.3 參數傳遞部分 所以指針值fptr%rdi ,指針值ret%rsi ,你可能應該為你的asmFunc推送一個調用幀

另請閱讀x86 調用約定wikipage。

如果您能夠在某些example.c文件asmFunc C 編寫等效的(甚至是簡化的) asmFunc ,我建議使用gcc -O -fverbose-asm -Wall -S example.c編譯它並查看發出的example.s匯編程序文件以獲得靈感。 大多數情況下,此類函數的第一條機器指令不是call (而是某種稱為函數序言、更改堆棧指針%esp並在調用堆棧上分配一些調用幀的東西)

例如,在我的 Linux/Debian/x86-64 上使用 gcc-8

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 )進行尾調用優化,並且可能會專門調用一些葉函數

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM