代码testlinker.c
cpp
int a = 5;
int foo(void){
return a;
}
int main(void){
foo();
}
运行命令,生成中间文件
bash
riscv64-linux-gnu-gcc testlinker.c -o testlinker --save-temps -mno-relax
--save-temps:保留所有中间文件。
-mno-relax:关闭链接器松弛优化

-
.c:高级语言文件
-
.i:预处理文件。
-
.s:汇编文件
-
.o:可重定位文件
-
无后缀:可执行文件
查看汇编后还没链接的重定位文件
riscv64-linux-gnu-objdump -d -r testlinker.o
以下这几行就是需要链接器重定位的地方,可以看到此时全局变量 a 的地址是"未知"的,只能留一个占位符(通常是0x0),需要在重定位中填入修正值

(如a: R_RISCV_PCREL_LO12_I .L0:表示在地址偏移 a处有一个 重定位项(relocation entry) ,类型是 R_RISCV_PCREL_LO12_I,其目标符号是 .L0)
重定位修正值(relocation addend / relocation offset) :
在链接阶段,链接器需要写入到目标指令或数据中的具体数值,用于修正该位置的占位值(通常是 0),使其在最终可执行文件中能正确引用符号(如变量、函数)的实际地址。
修正值的计算公式取决于类型:
如a是R_RISCV_PCREL_HI20类型的,修正值=S+A-P,其中:
- S:链接地址,被引用符号(如
a)在最终可执行文件的链接地址。 - A:重定位项中携带的附加常量(addend),在某些情况下要考虑对齐用于调整对齐
- P:当前重定位发生处(即
auipc指令)的 运行时地址(运行到重定位发生处该指令PC的值)。
先通过查看程序反汇编查询P,可以看到auipc指令时P=0x602(PS:这是基于人的视角从可执行文件倒推,实际链接器在连接的时候会直接根据S、A、P计算出修正值,填入重定位值)
riscv64-linux-gnu-objdump -d testlinker

查看符号表,查看全局变量a在哪被定义
riscv64-linux-gnu-readelf -s testlinker

可以看到链接地址S为2008,不用对齐,A=0
所以offset = S + A - P = 0x1A06,所以链接器要填入的重定位的值就是0x1A06。