2013-03-22 89 views
0

我在linux上使用NASM编写基本汇编程序,该程序从C库(printf)调用函数。不幸的是,我在这样做时发生了分段错误。注释掉对printf的调用可以使程序无误地运行。从组件调用C函数时发生Seg故障

; 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 
+0

库函数的调用约定又是什么?我的猜测是你使用了错误的。 – 2013-03-22 16:46:31

+0

在我正在阅读的书中(涵盖32位程序集,而不是64位),它只是说推动字符串地址,调用函数,清理堆栈指针。我认为文本将是唯一需要的参数,因为printf搜索空字节终止。 – user2177208 2013-03-22 16:56:10

+1

文本字符串必须以0字节结尾!如果没有,'printf()'知道它会在哪里结束? – 2013-03-22 17:00:30

回答

3

x86_64不使用堆栈的前6个参数。您需要将它们加载到正确的寄存器中。这些都是:

rdi, rsi, rdx, rcx, r8, r9 

我使用记得前两个诀窍是想象中的功能memcpy实现为rep movsb

+0

谢谢。我的书是在32位组件上,它将参数放在堆栈上。 – user2177208 2013-03-22 17:15:59

+2

另外,由于'printf'是一个可变参数函数,所以你需要在这里设置'%al'为用于参数的XMM寄存器的数量。 – 2013-03-22 17:57:56

0

你需要熟悉与使用中的调用约定。 AMD64上的Linux使用System V AMD64 ABI。从这个文件中,我们得知:

  • 整型参数RDI,RSI,RDX,通过RCX,R8和R9
  • 花车传入XMM0到XMM7
  • 的可变参数的函数的SSE寄存器的数目使用放在RAX

所以呼叫

printf ("Hello World\n"); 

你有

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

.section .text 
mov rdi, format 
mov rax, 0 
call printf