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

求助:使用GAS编写的even函数打印偶数时触发Segmentation fault错误

修复GAS汇编even函数的段错误问题

错误根源分析

你的汇编代码存在多个违反x86-64 System V ABI规范和逻辑错误的地方,直接导致了段错误:

  1. printf参数传递完全颠倒
    x86-64下printf的调用约定是:第一个参数(格式字符串)必须放%rdi,第二个参数(要打印的整数)放%rsi。你代码里把格式串丢进%esi、把打印值丢进%edi,完全搞反了参数顺序,导致printf读取非法内存地址触发段错误。

  2. 丢失了传入的参数n
    你在循环里把%ebx的值mov到%edi,直接覆盖了初始传入的n(原本存在%edi里),导致后续循环比较时用的不是原始的n,逻辑彻底混乱。

  3. 栈操作错误且冗余
    x86-64前6个函数参数用寄存器传递,不需要手动压栈;同时调用函数前栈必须16字节对齐,你手动subq/addq的操作既多余又没保证对齐要求。

  4. 未保护被调用者保存寄存器
    %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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 19:20:13