[英]x86 Assembly, getting segmentation fault
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
当我运行此代码时,我得到:
你好!
分段故障(核心已转储)
代码有什么问题?
printf期望将一个指针推入堆栈中作为参数,但是根据C调用约定,这是从堆栈中删除该参数的任务。
您省略了此操作,因此指令popa
在所有GPRS中都 popa
了错误的值,而指令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,在序言和尾声之间推送的所有其他项都消失了 。 解决方案2可以省去很多add esp, 4
条指令(当例程变长时)。
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
ING。 任何使用堆栈的子例程都应保存并恢复堆栈指针。
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
时,执行将跳转到错误的返回地址,从而导致段错误。 阅读有关汇编中的调用约定和函数的更多信息。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.