从Assembly调用C函数(printf)时的Segfault

时间:2013-03-22 16:43:56

标签: c linux assembly x86-64 calling-convention

我在linux上使用NASM编写一个基本的汇编程序,它从C库(printf)调用一个函数。不幸的是,我这样做会导致分段错误。注释掉对printf的调用允许程序无错误地运行。

; Build using these commands:
;   nasm -f elf64 -g -F stabs <filename>.asm 
;   gcc <filename>.o -o <filename>
;

SECTION .bss    ; Section containing uninitialized data

SECTION .data   ; Section containing initialized data

  text db "hello world",10 ; 

SECTION .text   ; Section containing code


global main

extern printf

;-------------
;MAIN PROGRAM BEGINS HERE
;-------------

main:



      push rbp

      mov rbp,rsp

      push rbx

      push rsi

      push rdi ;preserve registers

      ****************


      ;code i wish to execute

      push text ;pushing address of text on to the stack
      ;x86-64 uses registers for first 6 args, thus should have been:
      ;mov rdi,text (place address of text in rdi)
      ;mov rax,0 (place a terminating byte at end of rdi)

      call printf ;calling printf from c-libraries

      add rsp,8 ;reseting the stack to pre "push text"

      **************  

      pop rdi ;preserve registers

      pop rsi

      pop rbx

      mov rsp,rbp

      pop rbp

      ret

3 个答案:

答案 0 :(得分:3)

x86_64不会将堆栈用于前6个args。您需要在适当的寄存器中加载它们。那些是:

rdi, rsi, rdx, rcx, r8, r9

我用来记住前两个的技巧是想象函数是memcpy实现为rep movsb

答案 1 :(得分:1)

您需要熟悉正在使用的调用约定。 AMD64上的Linux使用System V AMD64 ABI。从该文件中我们了解到:

  • 整数参数在rdi,rsi,rdx,rcx,r8和r9中传递
  • 浮点数以xmm0传递到xmm7
  • 对于varargs函数,使用的SSE寄存器的数量放在rax
  • RSP必须在 a call之前以16字节对齐,因此在函数条目RSP-8上对齐(在call本身推送返回地址之后。)

所以对于电话

printf ("Hello World\n");

你有

.section .rodata
format db "Hello World", 10, 0

.section .text
my_function:
    sub   rsp, 8

    ; with RSP 16-byte aligned, e.g. from sub rsp,8  or an odd number of pushes
    mov   edi, format             ; or in PIC code: lea rdi, [rel format]
                      ; mov rsi, 2nd arg  if any
    xor   eax, eax                ; no FP args
    call  printf

    add   rsp, 8
    ret

请注意字符串上的0终结符:C字符串是隐式长度,以0字节终止。

答案 2 :(得分:0)

你正在调用一个varargs函数 - printf需要一个可变数量的参数,你必须在参数堆栈中考虑它。见这里:http://www.csee.umbc.edu/portal/help/nasm/sample.shtml#printf1