为何这段简单的x86-64汇编代码会出现段错误?
问题分析与修复方案
段错误的核心原因
你遇到的段错误本质是printf的参数传递完全不符合调用约定,加上入口函数选择的潜在问题:
- 最初代码中,你把整数
12345直接放到%rdi,但printf要求第一个参数是格式字符串的内存地址,12345作为地址属于无效内存区域,访问自然触发段错误。 - 后来改成字符串后仍出错,大概率是因为用
_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
相关产品推荐
相关产品推荐

