You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助: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之后,不会执行,可删除

额外说明

  1. 寄存器使用规则:严格遵循ABI规范是避免此类隐蔽错误的关键——调用者保存寄存器(如rax、rdi、rsi、rdx等)可以随意修改,无需保存;被调用者保存寄存器必须先保存再修改,退出前恢复。
  2. 栈对齐:x86-64 ABI要求call指令执行后栈必须保持16字节对齐,修复后的add函数栈操作已满足对齐要求(push rbp后栈为8字节对齐,push rbx后再sub rsp,8,栈仍保持16字节对齐)。

内容的提问来源于stack exchange,提问作者gillo04

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 05:22:45