RISC-V 64gc LD出现R_RISCV_PCREL_HI20截断重定位错误如何解决
错误原因
这个错误的核心原因是RISC-V默认的medlow代码模型不支持你当前使用的地址范围:
- RISC-V 64位的默认
medlow代码模型要求所有符号地址必须是可符号扩展到64位的32位有符号数,即地址的第31位到第63位必须完全相同,仅支持0x0000000000000000 ~ 0x000000007FFFFFFF(低2GiB)或者0xFFFFFFFF80000000 ~ 0xFFFFFFFFFFFFFFFF(高2GiB)两个地址区间。 - 你在链接脚本中指定的加载基址是
0x0000000080000000,刚好落在上述两个区间的间隙中,第31位为1、第32~63位全为0,不符合medlow模型的地址要求,导致重定位时无法用32位偏移表示地址,触发截断错误。
额外存在一个容易引发后续运行时错误的问题:你在C代码中将链接脚本定义的globalPointer声明为指针类型是错误的。链接脚本中PROVIDE(globalPointer = .)定义的符号本身的值就是目标地址,不是存储地址的指针变量,声明为指针会导致编译器去读取该地址存储的内容作为指针,而非直接使用该地址值。
解决方案
你可以根据自己的需求选择以下任意一种方案解决链接错误,建议同时修正C代码的符号声明避免后续运行时问题:
- 修改代码模型为
medany(最通用的解决方案)medany代码模型支持任意地址的PC相对寻址,只要符号和当前指令的偏移在±2GiB范围内即可,完全覆盖你的使用场景。你需要在编译汇编和C代码时都加上-mcmodel=medany参数:
# 汇编编译命令添加参数 riscv64-elf-as -march=rv64gc -mcmodel=medany source/entry.s -o build/entry.o # C代码编译命令也需要添加同样参数 riscv64-elf-gcc -march=rv64gc -mcmodel=medany -c source/kernel.c -o build/kernel.o
- 调整链接基址符合
medlow模型要求
如果你不想修改代码模型,可以将链接脚本中的基址修改为高2GiB的对应地址,即可匹配medlow的地址要求:
. = 0xFFFFFFFF80000000;
- 修正C代码中的符号声明
将C代码中的globalPointer声明修改为数组类型,即可直接获取符号对应的地址值:
#include <stdint.h> extern volatile char globalPointer[]; int kernalMain(void){ *(volatile uint64_t*)globalPointer = 0; return 0; }
内容的提问来源于stack exchange,提问作者gudenau
相关产品推荐
相关产品推荐

