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

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

; Build using these commands: ; nasm -f elf64 -g -F stabs .asm ; gcc .o -o  ; 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 

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

 rdi, rsi, rdx, rcx, r8, r9 

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

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

  • 整数参数在rdi,rsi,rdx,rcx,r8和r9中传递
  • 浮点数在xmm0到xmm7中传递
  • 对于varargs函数,使用的SSE寄存器的数量放在rax中

所以对于电话

 printf ("Hello World\n"); 

你有

 .section .rodata format db "Hello World", 10, 0 .section .text my_function: ; with RSP 16-byte aligned 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 

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

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