RV64环境下GCC链接器R_RISCV_HI20重定位错误及32位差异咨询
RISC-V链接重定位错误原因解析
问题现象
将DCCM内存地址设为0x80000000时,编译触发如下错误:
Main.c:(.text+0x8): relocation truncated to fit: R_RISCV_HI20 against `G_var'
但地址设为0x40000000时编译正常,且32位RISC-V编译无此错误。
相关代码与编译命令
C代码(Main.c)
static volatile unsigned int G_var; void main() { G_var |= 1; }
链接脚本(link.lds)
/* Linker script */ OUTPUT_ARCH( "riscv" ) ENTRY( main ) MEMORY { bram (wxa!ri) : ORIGIN = 0x00000000, LENGTH = 64K dccm (wa!ri) : ORIGIN = 0x80000000, LENGTH = 64K } PHDRS { ram_load PT_LOAD; dccm_load PT_LOAD; } /*----------------------------------------------------------------------*/ /* Sections */ /*----------------------------------------------------------------------*/ SECTIONS { .text : { *(.text.unlikely .text.unlikely.*) *(.text.startup .text.startup.*) . = ALIGN(8); } > bram : ram_load .bss : { *(.sbss .sbss.* .gnu.linkonce.sb.*) *(.scommon) *(.bss .bss.*) . = ALIGN(8); } >dccm }
编译命令
../toolchains/gcc/bin/riscv32-swerv-elf-gcc -march=rv64imac -mabi=lp64 -nostartfiles -nostdlib Main.c -o main.elf -T link.lds
原因分析
1. 64位小地址模型的寻址限制
当前使用RV64架构+LP64 ABI,编译器默认启用小地址模型,访问全局变量时优先用auipc + lw/sw的PC相对寻址组合。其中auipc指令的HI20立即数是有符号20位,仅能覆盖相对于当前PC的-512KB到+512KB范围。一旦全局变量与代码段的地址差超出这个区间,就会触发重定位截断错误。
2. 两种地址的差异
- 当DCCM地址为
0x80000000时,G_var所在的.bss段与.text段(0x0起始)的地址差为0x80000000,远大于±512KB,auipc无法生成合法偏移量,直接报错。 - 当DCCM地址为
0x40000000时,地址差的高20位是0x40000,这个值在有符号20位的正区间内(最大正偏移为0x7FFFF),auipc能正确生成偏移,因此编译通过。
3. 32位编译无错的原因
32位RISC-V编译时,编译器默认采用lui + lw/sw的绝对寻址方式。lui指令加载无符号20位高位地址,只要变量地址在32位空间内合法,就能通过lui加载高20位,再配合12位偏移访问,不存在PC相对寻址的范围限制,因此不会触发错误。
解决办法
- 切换到中等地址模型:编译时添加
-mcmodel=medany参数,RV64的中等地址模型允许数据分布在整个64位地址空间,编译器会自动改用lui + lw/sw的绝对寻址方式,避开PC相对寻址的范围限制。 - 调整内存布局:将代码段和数据段的地址差控制在±512KB以内。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

