解决 GCC Linker 的 relocation truncated to fit 错误

最近在编译一个 C 项目时,链接阶段链接器报了如下的错误:

/.../build/am-riscv64-nemu.a(start.o): in function `.L0 ':
(entry+0xc): relocation truncated to fit: R_RISCV_JAL against symbol `_trm_init' defined in .text._trm_init section in .../build/am-riscv64-nemu.a(trm.o)
make: *** [/.../Makefile:141: /.../build/alutest-riscv64-nemu.elf] Error 1

查了一遍之后,似乎该错误主要由以下几个原因触发:

链接时,一条 跳转(不一定是JAL指令) 指令:

  1. 跳转的目标地址没有对齐[1],如:
jal some_label + 0x6 # 跳转目标是非对齐的地址
  1. 跳转的目标在编译过程中被写死了[1]:
bne ra, ra, 0x80000002 # 写死的地址
  1. 目标地址与跳转指令的距离过大,指令无法直接跳转到目标地址[2],如:
0x80000000: jal far_label # far_label 在链接时被确定为 0x90000000
# 它们之间的距离为 0x10000000 字节,远大于 jal 指令所能够表示的寻址范围 (+-1MiB)

第一种情况显然属于写汇编写错了,更正即可。第二种情况则不要使用写死的地址进行跳转,而是使用标签来表示需要跳转到的位置。对于第三种情况,应当使用寄存器间接跳转的方式,本次链接不通过即为第三种情况。来自于以下一段代码:

_start:
  mv s0, zero
  la sp, _stack_pointer
  jal _trm_init

其中 _trm_init 超过了 jal 指令能够寻址的范围,因此提示了 relocation truncated to fit: R_RISCV_JAL 错误。

修改方式为使用寄存器间接跳转:

_start:
  mv s0, zero
  la sp, _stack_pointer
  la ra, _trm_init # 先将 _trm_init 地址加载到 ra 寄存器中
  jalr ra, 0(ra) # 然后使用间接寻址的方式跳转

参考资料

  1. https://stackoverflow.com/questions/63169631/risc-v-ld-error-text0xc4-relocation-truncated-to-fit-r-riscv-jal-against
  2. https://forums.sifive.com/t/link-error-relocation-truncated-to-fit-r-riscv-jal-against-symbol/4163/4

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据