x86汇编,出现分段错误

时间:2015-07-29 15:45:29

标签: assembly x86 nasm

section .data
msg: db "hello!", 10, 0 ;my message

section .text
extern printf ;C printf function
global main
main:
    push ebp
    mov ebp, esp
    call print_string
    mov esp, ebp
    pop ebp
    ret ;end of program

print_string:
    pusha
    push msg
    call printf ;should print "Hello"
    popa
    ret ;return back to main

当我运行此代码时,我得到:
您好!
分段错误(核心转储)

代码有什么问题?

2 个答案:

答案 0 :(得分:1)

printf 期望在堆栈上推送一个参数作为参数,但在C调用约定下,从堆栈中删除此参数是您的任务。

您省略了这一点,因此指令popa错误值放入所有GPRS ,指令ret使用EAX的原始值作为目标地址,从而触发分段故障。

解决方案1手动清理

print_string:
 pusha
 push  msg
 call  printf   ;should print "Hello"
 add   esp, 4   ; <-- Clean-up
 popa
 ret            ;return back to main

解决方案2在 print_string

上使用prolog / epilog
print_string:
 push  ebp
 mov   ebp, esp
 push  msg
 call  printf   ;should print "Hello"
 mov   esp, ebp ; <-- Clean-up
 pop   ebp
 ret            ;return back to main

解决方案2是唯一可行的,因为 printf 是一个保存EBP寄存器的良好函数。通过将EBP移动到ESP中,在prolog和epilog 之间推送的每个额外项目都会消失。解决方案2可以为您节省大量add esp, 4指令(当例程变得更长时)。

答案 1 :(得分:-1)

print_string子例程正在修改堆栈指针而不进行恢复。 子例程main具有以下布局:

push ebp    ;save the stack frame of the caller
mov ebp, esp    ;save the stack pointer
<code for subroutine>
mov esp, ebp    ;restore the stack pointer
pop ebp    ;restore the caller's stack frame
ret ;return to address pushed onto the stack by call

类似地,print_string子例程应具有相同的布局,保存堆栈指针,然后在ret之前恢复它。任何使用堆栈的子程序都应保存并恢复堆栈指针。

push ebp
mov ebp, esp
pusha
push msg
call printf ;should print "Hello"
add esp, 4
popa
mov esp, ebp
pop ebp
ret

不保存堆栈指针并恢复它,被调用的子程序修改堆栈指针,其中call指令保存了返回地址。因此,当遇到ret时,执行会跳转到错误的返回地址,因此会出现segfaulting。阅读calling conventions and functions in assembly.

的更多信息