求助:main函数中pop rbp指令触发Segmentation Fault的问题排查
汇编代码执行
pop rbp时触发Segmentation Fault的原因及修复 首先给出问题中的汇编代码:
.intel_syntax noprefix .global main .text add: push rbp mov rbp, rsp sub rsp, 4 mov dword [rbp - 4], edi sub rsp, 4 mov dword [rbp - 8], esi mov eax, dword [rbp - 4] mov ebx, dword [rbp - 8] add eax, ebx mov rsp, rbp pop rbp ret main: push rbp mov rbp, rsp mov eax, 60 mov edi, eax mov eax, 9 mov esi, eax call add mov rsp, rbp pop rbp ret add rsp, 4
错误核心原因
问题出在add函数中使用了被调用者保存寄存器rbx(32位对应ebx)但未做保存恢复。在x86-64的System V应用二进制接口(ABI)规范中,rbx、rbp、r12-r15属于被调用者保存寄存器:被调用函数如果要修改这些寄存器的值,必须先将原值压入栈中保存,函数退出前再恢复,否则会破坏调用者(或上层调用链)依赖的寄存器状态,进而引发栈异常或崩溃。
你的add函数直接通过mov ebx, dword [rbp - 8]修改了rbx寄存器,但没有保存和恢复其初始值,导致main函数返回时,上层调用环境(如C运行时的_start函数)的rbx状态被破坏,最终表现为main执行pop rbp时触发段错误(实际是寄存器异常引发的栈访问错误)。
另外,add函数的栈分配存在冗余(两次sub rsp,4),虽然不会直接引发错误,但可以优化为单次sub rsp,8来分配8字节栈空间,同时更符合栈对齐规范。
修复后的代码
.intel_syntax noprefix .global main .text add: push rbp mov rbp, rsp push rbx ; 保存被调用者保存寄存器rbx sub rsp, 8 ; 一次性分配8字节栈空间,存放edi和esi mov dword [rbp - 4], edi mov dword [rbp - 8], esi mov eax, dword [rbp - 4] mov ebx, dword [rbp - 8] add eax, ebx add rsp, 8 ; 释放栈空间 pop rbx ; 恢复rbx寄存器原值 mov rsp, rbp pop rbp ret main: push rbp mov rbp, rsp mov edi, 60 ; 优化冗余指令,直接赋值edi mov esi, 9 ; 直接赋值esi call add mov rsp, rbp pop rbp ret ; 原add rsp,4在ret之后,不会执行,可删除
额外说明
- 寄存器使用规则:严格遵循ABI规范是避免此类隐蔽错误的关键——调用者保存寄存器(如
rax、rdi、rsi、rdx等)可以随意修改,无需保存;被调用者保存寄存器必须先保存再修改,退出前恢复。 - 栈对齐:x86-64 ABI要求
call指令执行后栈必须保持16字节对齐,修复后的add函数栈操作已满足对齐要求(push rbp后栈为8字节对齐,push rbx后再sub rsp,8,栈仍保持16字节对齐)。
内容的提问来源于stack exchange,提问作者gillo04
相关产品推荐
相关产品推荐

