繁体   English   中英

x86组装,出现分段错误

[英]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的原始值作为目标地址,从而触发了分段错误。

解决方案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,在序言和尾声之间推送的所有其他项都消失了 解决方案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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM