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

x86-64 AT&T汇编代码输出异常问题求助

问题分析与解答

核心问题:栈操作与返回地址的误用

你的代码逻辑存在根本性错误:你误以为movq %rsp, %rsi指向的是你压入栈的$66,但实际上call print指令会自动将返回地址(即end标签处的指令地址)压入栈顶,导致%rsi指向的是返回地址的内存位置,而非你预期的字符数据。

疑问解答

  1. 现象原理
    你调用write系统调用时,%rsi是返回地址的指针,%rdx被设为1,所以实际输出的是返回地址的最低1字节。每添加一条3字节的冗余指令(比如xor %rcx, %rcx),print函数的总长度就增加3字节,导致_start和end标签的地址整体后移3字节,返回地址的最低字节值也随之增加3,最终输出的ASCII码就加3。

  2. 为何影响%rsi寄存器
    %rsi在print函数开头就被赋值为%rsp,而此时%rsp指向的是call指令压入的返回地址,后续的pop操作只是修改了%rsp,但不会改变已经赋值的%rsi。所以无论你加多少冗余指令,%rsi始终指向返回地址,输出的自然是返回地址的低字节,而返回地址会随print的长度变化。

  3. 初始值为何是33
    33是无冗余指令时,end标签地址的最低字节的十进制值,对应ASCII字符!。这是由编译器链接后的内存布局决定的,属于巧合。

  4. 修复方案
    你需要让%rsi指向真正的字符数据$66,同时修正栈操作的逻辑,避免破坏call指令的返回地址。以下是两种规范的修复方式:

方式一:修正栈指针指向字符数据

.section .text
.globl _start
print:
  movq $1, %rax
  movq $1, %rdi
  # 跳过call压入的返回地址和$1,指向$66的位置
  leaq 16(%rsp), %rsi  
  movq $1, %rdx        # 输出长度设为1
  syscall
  ret                  # 正常返回,无需jmp end

_start:
  pushq $66
  pushq $1
  call print
end:
  movq $60, %rax
  xor %rdi, %rdi
  syscall

解释:leaq 16(%rsp), %rsi是因为call压入8字节返回地址,加上你压入的$1(8字节),所以rsp+16就是$66的位置。

方式二:使用寄存器传递参数(更符合x86-64规范)

.section .text
.globl _start
print:
  # %rdi: 输出字符地址,%rsi: 输出长度
  movq $1, %rax
  movq $1, %rdi        # 标准输出fd
  syscall
  ret

_start:
  # 将字符'B'暂存到栈上
  pushq $66
  movq %rsp, %rdi      # 传递字符地址
  movq $1, %rsi        # 传递输出长度
  call print
  addq $8, %rsp        # 清理栈上的临时数据
end:
  movq $60, %rax
  xor %rdi, %rdi
  syscall

解释:采用x86-64系统V调用约定的思路,用寄存器传递参数,避免栈操作的混乱。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 08:40:36