汇编代码中printf调用触发段错误问题求助
问题原因分析
- libc堆管理冲突:你手动调用
brk修改堆边界,但printf等libc函数依赖自身的堆管理机制(比如malloc的内部结构)。当你将堆末端缩小回HEAP_START时,可能覆盖了libc已分配的堆内存(例如printf的内部缓冲区),导致第三次调用printf时访问无效内存触发段错误。 - 可变参数函数调用规范缺失:x86-64 System V ABI要求调用可变参数函数(如
printf)时,必须将%al寄存器设置为通过XMM寄存器传递的浮点参数数量。你的代码每次调用printf前都未设置%al为0(此处无浮点参数),可能导致printf内部行为异常。
修复方案
方案1:替换printf为自定义打印函数(避免libc堆依赖)
直接通过系统调用实现地址打印,不依赖libc的stdio函数,彻底避免堆管理冲突:
.section .data newline: .byte 0x0a .section .text .globl main # 打印64位地址为十六进制字符串 print_addr: pushq %rbp movq %rsp, %rbp subq $18, %rsp # 16个十六进制字符 + 换行 + 对齐 movq %rdi, -8(%rbp) # 保存要打印的地址 movq $16, -16(%rbp) # 字符计数器 leaq -17(%rbp), %rsi # 字符存储起始位置 print_loop: movq -8(%rbp), %rax andq $0xf, %rax # 取低4位 cmpq $9, %rax jle to_digit addq $7, %rax # 0xA-F转成ASCII字母 to_digit: addq $48, %rax # 转成ASCII数字 movb %al, (%rsi) movq -8(%rbp), %rax shrq $4, %rax # 地址右移4位 movq %rax, -8(%rbp) incq %rsi subq $1, -16(%rbp) jnz print_loop movb $0x0a, (%rsi) # 添加换行符 # 调用write系统输出 movq $1, %rax movq $1, %rdi leaq -17(%rbp), %rsi movq $17, %rdx syscall leave ret startAlocator: pushq %rbp movq %rsp, %rbp movq $12, %rax movq $0, %rdi syscall movq %rax, HEAP_START movq %rax, HEAP_END pop %rbp ret main: pushq %rbp movq %rsp, %rbp subq $16, %rsp movq $0, -16(%rbp) call startAlocator # 打印HEAP_START movq HEAP_START, %rdi call print_addr # 分配2字节 movq $12, %rax movq HEAP_END, %rdi addq $2, %rdi syscall movq %rax, HEAP_END # 打印HEAP_END movq HEAP_END, %rdi call print_addr # 恢复堆起始位置 movq $12, %rax movq HEAP_START, %rdi syscall movq %rax, HEAP_END # 打印HEAP_END movq HEAP_END, %rdi call print_addr # 结束程序 movq $60, %rax syscall .section .bss HEAP_START: .quad 0 HEAP_END: .quad 0
方案2:保留printf但修复调用规范
如果你坚持使用printf,需补充ABI要求的寄存器设置,但仍可能存在libc堆冲突风险:
.section .data str3: .string "%p\n" HEAP_START: .quad 0 HEAP_END: .quad 0 .section .text .globl main startAlocator: pushq %rbp movq %rsp, %rbp movq $12, %rax movq $0, %rdi syscall movq %rax, HEAP_START movq %rax, HEAP_END pop %rbp ret main: pushq %rbp movq %rsp, %rbp subq $16, %rsp movq $0, -16(%rbp) call startAlocator # 打印HEAP_START movq HEAP_START, %rsi mov $str3, %rdi movb $0, %al # 必须设置:浮点参数数量为0 call printf # 分配2字节 movq $12, %rax movq HEAP_END, %rdi addq $2, %rdi syscall movq %rax, HEAP_END # 打印HEAP_END movq HEAP_END, %rsi mov $str3, %rdi movb $0, %al call printf # 恢复堆起始位置 movq $12, %rax movq HEAP_START, %rdi syscall movq %rax, HEAP_END # 打印HEAP_END movq HEAP_END, %rsi mov $str3, %rdi movb $0, %al call printf # 结束程序 movq $60, %rax syscall
额外说明
x86-64下brk系统调用(编号12):
- 当
%rdi为0时,返回当前堆末端地址; - 当
%rdi不为0时,尝试将堆末端设置为该地址,成功返回新末端,失败返回-1。
手动管理堆时,需避免与libc的堆操作混合使用,否则极易破坏libc的内部内存结构,引发不可预测的错误。
内容的提问来源于stack exchange,提问作者muriki
相关产品推荐
相关产品推荐

