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

为何这段简单的x86-64汇编代码会出现段错误?

问题分析与修复方案

段错误的核心原因

你遇到的段错误本质是printf的参数传递完全不符合调用约定,加上入口函数选择的潜在问题:

  1. 最初代码中,你把整数12345直接放到%rdi,但printf要求第一个参数是格式字符串的内存地址,12345作为地址属于无效内存区域,访问自然触发段错误。
  2. 后来改成字符串后仍出错,大概率是因为用_start作为入口时,libc的初始化流程没有执行,printf这类标准库函数无法正常工作;另外你也没搞清楚printf的参数逻辑——如果要打印整数,需要格式控制符配合参数传递,不是直接传数字字符串。

修复后的代码(两种方案)

方案一:用main作为入口(推荐,自动处理libc初始化)

这是符合GCC链接规范的写法,无需手动处理库初始化:

.extern printf
.data
fmt: .string "%d\n"  # 格式字符串,%d对应整数类型,\n实现换行
n:   .long 12345     # 要打印的目标整数
.text
.global main

main:
    push %rbp        # 标准栈帧设置(可选,但符合函数调用规范)
    mov %rsp, %rbp

    xorq %rax, %rax  # printf是可变参数函数,无浮点参数时%rax置0
    movq $fmt, %rdi  # 第一个参数:格式字符串的内存地址
    movl n, %esi     # 第二个参数:要打印的整数(32位mov会自动零扩展到64位%rsi)
    call printf

    movl $0, %eax    # main函数返回0表示正常退出
    leave
    ret

编译链接命令:gcc -o print_int print_int.s

方案二:用_start作为入口(需手动初始化libc)

如果坚持用_start,需要先调用libc的初始化函数,写法复杂且不推荐,示例仅作参考:

.extern printf
.extern __libc_start_main
.data
fmt: .string "%d\n"
n:   .long 12345
.text
.global _start

_start:
    # 调用__libc_start_main初始化libc并执行主逻辑
    movq $main, %rdi
    movq %rsp, %rsi
    movq %rsp, %rdx
    call __libc_start_main

main:
    xorq %rax, %rax
    movq $fmt, %rdi
    movl n, %esi
    call printf

    movq $60, %rax
    xor %rdi, %rdi
    syscall

关于%rsi寄存器的疑问

  • %rsi绝不是仅用于系统调用:在x86-64 System V调用约定(GCC、GAS默认遵循的规范)中,用户级函数的第二个参数就是通过%rsi传递的(第一个参数%rdi,第三个%rdx,以此类推)。比如你要给printf传整数参数,就必须把值放到%rsi。
  • 系统调用的寄存器规则是另一套标准:系统调用的第一个参数是%rdi,第二个是%rsi,和用户级函数调用有部分重叠,但属于完全独立的两套规范,不能混淆。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 04:46:07