为何等量push/pop操作会导致x86-64 NASM程序崩溃?
用push/pop传递NASM函数参数触发Signal 11错误的原因
在WSL Ubuntu环境下编写x86-64 NASM程序,尝试通过push/pop传递函数参数时,运行后触发报错:
Command terminated by signal 11
移除所有push和pop操作后程序可正常运行,相关代码如下:
section .text global _start _start: mov edx, len mov ecx, msg push ecx push edx call println mov eax, 1 int 0x80 println: pop edx pop ecx mov ebx, 1 mov eax, 4 int 0x80 ret section .data msg db "Hello world!",0xa len equ $ - msg
错误原因
Signal 11是段错误,本质是你破坏了call指令的工作逻辑:
- 执行
call println时,CPU会自动把当前指令的下一条地址(也就是mov eax, 1的地址)压入栈,这个地址是函数执行完后ret要跳转回去的返回地址。 - 你的
println函数开头直接执行pop edx和pop ecx,这两个操作把栈顶的返回地址弹走了:
按你的push顺序,栈内结构从栈顶到栈底是「返回地址 → edx的值 → ecx的值」,pop edx实际拿到的是返回地址,pop ecx拿到的是原本的edx值。 - 当执行
ret时,CPU会把当前栈顶的ecx值当成返回地址跳转,这个地址不是合法的代码段地址,直接触发段错误。
修正方案
有几种常见的修复方式:
- 临时保存返回地址:先把返回地址弹到寄存器保存,用完参数后重新压栈
println: pop ebx ; 保存返回地址到ebx pop ecx ; 取出真正的ecx参数 pop edx ; 取出真正的edx参数 mov eax,4 mov ebx,1 int 0x80 push ebx ; 把返回地址重新压栈 ret
- 使用栈帧访问参数(更规范):通过ebp寄存器定位栈内参数
println: push ebp mov ebp, esp mov ecx, [ebp+8] ; 跳过ebp和返回地址,取第一个参数 mov edx, [ebp+12] ; 取第二个参数 mov eax,4 mov ebx,1 int 0x80 pop ebp ret 8 ; ret 8 直接平衡栈,替代调用者的add esp,8
- 调用者手动平衡栈:函数内直接通过esp偏移访问参数,调用后清理栈
_start: mov edx, len mov ecx, msg push ecx push edx call println add esp, 8 ; 清理栈上的两个参数 mov eax,1 int 0x80 println: mov ecx, [esp+4] ; 跳过返回地址,取第一个参数 mov edx, [esp+8] ; 取第二个参数 mov eax,4 mov ebx,1 int 0x80 ret
内容的提问来源于stack exchange,提问作者Amukh1
相关产品推荐
相关产品推荐

