自制编译器生成x86汇编函数调用后触发Segmentation fault求助
问题排查结果
1. 核心崩溃原因:局部变量栈偏移错误,覆盖栈上保存的rbp值
你在函数中为局部变量/参数分配栈空间后,使用了完全错误的内存地址写入:
function_main中通过sub rsp,4分配了4字节栈空间存储变量f,正确的访问地址应为[rbp-4],你当前使用的[rbp-0](等价于[rbp])指向的是入栈时保存的旧rbp值,写入10直接覆盖了该值。function_func中存在完全相同的问题,分配4字节存储参数a,却写入[rbp-0]覆盖旧rbp值。
当函数执行到pop rbp时,取出的rbp是被篡改的错误值,后续ret指令会将非法地址作为返回地址跳转,直接触发段错误,这就是第一次调用func返回后崩溃的根本原因。
2. 违反x86-64 System V ABI寄存器规则
rbx属于被调用者保存寄存器,在function_main中直接修改rbx前没有提前入栈保存,使用后也没有恢复,会导致上层调用方的寄存器上下文被破坏,引发不可预期的错误。如果仅需要临时存储常量2,建议改用rcx这类调用者保存寄存器,无需额外保存操作。
3. 64位除法未初始化rdx寄存器
你使用idiv rbx执行64位有符号除法时,被除数默认是rdx:rax寄存器对,你没有提前对eax做符号扩展填充rdx,rdx中的垃圾值会导致除法结果错误,甚至触发除法溢出异常。如果只需要做32位除法,可改为使用idiv ebx,同时提前执行cdq指令符号扩展eax到edx即可。
4. 链接命令参数错误
你当前使用的链接命令ld -g ./test9.lv.asm.o a.out是错误的,该命令会把已存在的a.out作为输入文件参与链接,正确的命令应该指定输出参数:ld -g ./test9.lv.asm.o -o a.out。
修复后可正常运行的汇编代码示例
section .text global _start _start: call function_main mov rdi, rax mov rax, 60 syscall global function_main function_main: push rbp mov rbp, rsp sub rsp, 4 mov rax, 10 mov DWORD[rbp-4], eax ; 修正偏移 mov eax, DWORD[rbp-4] mov rdi, rax call function_func push rax mov eax, DWORD[rbp-4] cdq ; 符号扩展eax到edx mov ecx, 2 ; 改用调用者保存寄存器rcx,无需额外保存 idiv ecx mov rdi, rax call function_func mov rcx, rax pop rax sub rax, rcx mov rsp, rbp pop rbp ret global function_func function_func: push rbp mov rbp, rsp sub rsp, 4 mov DWORD[rbp-4], edi ; 修正偏移 mov eax, DWORD[rbp-4] mov rsp, rbp pop rbp ret
内容的提问来源于stack exchange,提问作者Lucraft
相关产品推荐
相关产品推荐

