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

汇编代码中printf调用触发段错误问题求助

问题原因分析
  1. libc堆管理冲突:你手动调用brk修改堆边界,但printf等libc函数依赖自身的堆管理机制(比如malloc的内部结构)。当你将堆末端缩小回HEAP_START时,可能覆盖了libc已分配的堆内存(例如printf的内部缓冲区),导致第三次调用printf时访问无效内存触发段错误。
  2. 可变参数函数调用规范缺失: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 09:27:41