求助:使用GAS编写的even函数打印偶数时触发Segmentation fault错误
修复GAS汇编even函数的段错误问题
错误根源分析
你的汇编代码存在多个违反x86-64 System V ABI规范和逻辑错误的地方,直接导致了段错误:
printf参数传递完全颠倒
x86-64下printf的调用约定是:第一个参数(格式字符串)必须放%rdi,第二个参数(要打印的整数)放%rsi。你代码里把格式串丢进%esi、把打印值丢进%edi,完全搞反了参数顺序,导致printf读取非法内存地址触发段错误。丢失了传入的参数n
你在循环里把%ebx的值mov到%edi,直接覆盖了初始传入的n(原本存在%edi里),导致后续循环比较时用的不是原始的n,逻辑彻底混乱。栈操作错误且冗余
x86-64前6个函数参数用寄存器传递,不需要手动压栈;同时调用函数前栈必须16字节对齐,你手动subq/addq的操作既多余又没保证对齐要求。未保护被调用者保存寄存器
%rbx属于被调用者保存寄存器,你的函数修改了它但没保存恢复,会破坏调用者(main函数)的寄存器状态,引发潜在错误。
修正后的代码
.extern printf .section .data format: .asciz "%d\n" .section .text .global even even: # 保存栈帧和被调用者保存寄存器,保证栈对齐 pushq %rbp movq %rsp, %rbp pushq %rbx movl %edi, %ebx # 把传入的n保存到rbx,避免被printf破坏 cmpl $2, %ebx # 对比n和2 jl less_than_two # n<2时跳转至分支 movl $2, %esi # 从2开始打印,esi作为printf的第二个参数 loop: # 按ABI传递printf参数:格式串放rdi,打印值放rsi movq $format, %rdi call printf # 调用printf,此时栈是16字节对齐的 addl $2, %esi # 生成下一个偶数 cmpl %ebx, %esi # 对比当前数和n jg end # 当前数>n时退出循环 jmp loop less_than_two: # 按原逻辑打印0 movq $format, %rdi movl $0, %esi call printf end: # 恢复寄存器和栈帧 popq %rbx popq %rbp ret
关键修改说明
- 参数传递规范:严格遵循x86-64 System V ABI,用
%rdi传格式串、%rsi传打印值,不再手动压栈传递参数。 - 保存原始参数:把传入的n存入
%rbx(被调用者保存寄存器),避免被printf修改,保证循环比较时用的是原始n值。 - 栈对齐与寄存器保护:通过
pushq保存%rbp和%rbx,既保证调用printf时栈是16字节对齐,又符合ABI对寄存器保护的要求。 - 简化栈操作:移除了冗余的栈空间调整,依赖寄存器传递参数,减少错误风险。
编译运行示例
假设main.c内容如下:
#include <stdio.h> void even(int n); int main() { even(10); return 0; }
编译命令:
gcc -c even.s -o even.o gcc main.c even.o -o even_test
运行./even_test会输出2、4、6、8、10,无段错误。
内容的提问来源于stack exchange,提问作者G Nguyễn
相关产品推荐
相关产品推荐

