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
当我运行此代码时,我得到:
您好!
分段错误(核心转储)
代码有什么问题?
答案 0 :(得分:1)
printf 期望在堆栈上推送一个参数作为参数,但在C调用约定下,从堆栈中删除此参数是您的任务。
您省略了这一点,因此指令popa
将错误值放入所有GPRS ,指令ret
使用EAX的原始值作为目标地址,从而触发分段故障。
print_string:
pusha
push msg
call printf ;should print "Hello"
add esp, 4 ; <-- Clean-up
popa
ret ;return back to main
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.