You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

ARM64汇编/目标文件中链接器解析机制及跳转指令修复疑问

38: 00000007 .word 0x00000007
3c: 00000000 .word 0x00000000

我大致理解`8:   58000141        ldr     x1, 30 <again+0x2c>`对应PC相对寻址到字面池,但对`20:   54ffff21        b.ne    4 <again>`对应的`b.ne #0xffffffffffffffe8`感到困惑,想请教:
1. 我的指令理解是否有误?
2. 链接器如何修复这类指令?
3. 多目标文件链接时是否会调整机器码?

---

## 解答

### 1. 你的指令理解没有问题
ARM64的分支指令(如`b.ne`)采用**PC相对寻址**,偏移量是相对于当前指令执行时的PC值计算的——注意ARM64流水线特性,当前PC指向的是**当前指令的下一条指令地址**(即当前指令地址+4)。

以你反汇编里的`b.ne`为例:
- 指令位于地址`0x20`,执行时PC值为`0x20 + 4 = 0x24`
- 目标地址是`0x4`(`again`标签的位置)
- 计算相对偏移:`0x4 - 0x24 = -0x20`,转换成64位补码就是`0xffffffffffffffe8`,这就是你看到的偏移值。

这个偏移逻辑是正确的,你的理解没问题。

### 2. 链接器对指令的修复逻辑
链接器的核心工作之一是**重定位**,针对不同类型的指令处理方式不同:

#### (1)同文件内的分支指令(如你的`b.ne again`)
这类指令的目标符号在同一个目标文件内,汇编器已经能计算出准确的相对偏移,生成的机器码本身就是正确的,链接时不需要修改——因为不管代码段最终加载到哪个虚拟地址,当前PC和目标地址的相对距离始终不变。

#### (2)跨文件的分支/调用指令(如`bl other_func`,`other_func`在另一个.o文件)
汇编器无法知道`other_func`的最终地址,会在目标文件中留下一个**重定位条目**,记录需要修改的指令位置和符号信息。链接时:
1. 链接器确定所有符号的最终虚拟地址
2. 计算当前指令PC与目标符号地址的相对偏移
3. 将偏移值除以4(ARM64指令是4字节对齐,偏移量以"指令条数"为单位),填充到分支指令的偏移字段中,完成机器码修改。

#### (3)全局变量/符号的加载指令(如你的`ldr x1, =msg`)
`ldr x1, =msg`是汇编伪指令,汇编器会将其转换为:
- 在代码段末尾的**字面池**中预留一个8字节位置,用来存储`msg`的地址
- 生成一条PC相对的`ldr`指令,从字面池加载地址

汇编时字面池里的内容是占位符,链接器会将`msg`的最终虚拟地址写入这个占位符位置,完成重定位。

### 3. 多目标文件链接时会调整机器码
会的,主要在以下场景修改机器码:
- **跨文件符号引用**:当指令引用其他目标文件中的函数或变量时,链接器会计算最终的相对偏移或绝对地址,修改指令本身或字面池中的数据。
- **段地址合并调整**:链接器会将多个目标文件的代码段、数据段合并到连续的虚拟地址空间,对于依赖绝对地址的指令(如直接加载全局变量地址),会更新对应的地址值。
- **动态链接相关重定位**:如果生成的是动态链接可执行文件,链接器会生成PLT(过程链接表)和GOT(全局偏移表),并修改相关指令指向这些表项,让程序在运行时动态绑定符号地址。

---

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

火山引擎 最新活动