You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RISC-V64下直接编译.c与预编译目标文件链接的重定位错误问题

重定位错误R_RISCV_HI20的原因与解决方法

问题场景

为RISC-V64架构编写的Hello World程序,直接编译.c文件可正常构建并在QEMU运行,但分步编译目标文件再链接时,出现R_RISCV_HI20重定位截断错误。

错误信息

main.o: in function `main': 
main.c:(.text+0x8): relocation truncated to fit: R_RISCV_HI20 against `hello'
test.o: in function `test': 
test.c:(.text+0x8): relocation truncated to fit: R_RISCV_HI20 against `hello' 
collect2.exe: error: ld returned 1 exit status

成功编译命令

gcc -march=rv64gc -mabi=lp64d -nostartfiles --machine-cmodel=medany -T linker.ld crt0.o main.c putchar.a test.c -o main.elf

出错的分步编译命令

第一步:编译目标文件

gcc -march=rv64gc -mabi=lp64d -nostartfiles --machine-cmodel=medany -T linker.ld -c main.c test.c

第二步:链接

gcc -march=rv64gc -mabi=lp64d -nostartfiles --machine-cmodel=medany -T linker.ld crt0.o main.o putchar.a test.o -o main.elf

相关代码文件

main.c

#include "putchar.h"
// #include "test.h"
static const char *hello = "Hello, World!\n";

void main(){
    // test();
    putchars(hello);
    putchars("Hello, World!\n");
    while(1);
}   

test.c

#include "putchar.h"

static const char *hello = "Hello, World!\n";

void test(){
    putchars(hello);
}

putchar.c

volatile char * const UART = (char*)0x10000000;

void putchars(char *str){
    for(char *i = str; *i != 0; i++) *UART = *i;
}

linker.ld

/* Template linker definition script for riscv64 virtIO machine */

OUTPUT_FORMAT("elf64-littleriscv", "elf64-littleriscv", "elf64-littleriscv")
OUTPUT_ARCH(riscv)

/* Define the entry point */
ENTRY(_start)

/* Define the memory regions */
MEMORY
{
  /* The flash memory region */
  FLASH (rwx) : ORIGIN = 0x20000000, LENGTH = 0x2000000
  /* The RAM memory region */
  RAM (rw) : ORIGIN = 0x80000000, LENGTH = 0x1000000
}

/* Define the output sections */
SECTIONS
{
  /* The text section contains the executable code */
  .text :
  {
    /* Align to page boundary */
    . = ALIGN(0x1000);
    /* Record the start address of this section */
    _stext = .;
    /* Include all input files with .text sections */
    *(.text)
    /* Record the end address of this section */
    _etext = .;
    /* Align to page boundary */
    . = ALIGN(0x1000);
  } > FLASH

  /* The rodata section contains read-only data */
  .rodata :
  {
    /* Align to page boundary */
    . = ALIGN(0x1000);
    /* Record the start address of this section */
    _srodata = .;
    /* Include all input files with .rodata sections */
    *(.rodata)
    *(.srodata)
    /* Record the end address of this section */
    _erodata = .;
    /* Align to page boundary */
    . = ALIGN(0x1000);
  } > FLASH

  /* The data section contains initialized data */
  .data :
  {
    /* Align to page boundary */
    . = ALIGN(0x1000);
    /* Record the start address of this section */
    _sdata = .;
    /* Include all input files with .data sections */
    *(.data)
    *(.sdata)
    /* Record the end address of this section */
    _edata = .;
    /* Align to page boundary */
    . = ALIGN(0x1000);
  } > RAM AT > FLASH

  .fill :
  {
    FILL(0xFFFF);
    . = ORIGIN(FLASH) + LENGTH(FLASH) - 1;
    BYTE(0xAA);
  } > FLASH

  _sdata_load = LOADADDR(.data);

  /* The bss section contains uninitialized data */
  .bss (NOLOAD):
  {
    /* Align to page boundary */
    . = ALIGN(0x1000);
    /* Record the start address of this section */
    _sbss = .;
    /* Include all input files with .bss sections */
    *(.bss)
    /* Record the end address of this section */
    _ebss = .;
    /* Align to page boundary */
    . = ALIGN(0x1000);
  } > RAM

}

Makefile相关片段

CC = riscv64-unknown-elf-gcc
AR = riscv64-unknown-elf-ar

*.o:
    $(CC) -march=rv64gc -mabi=lp64d -nostartfiles --machine-cmodel=medany -misa-spec=20191213 -T linker.ld -c %.c 

crt0.o: crt0.s
    $(CC) -march=rv64gc -mabi=lp64d -nostartfiles --machine-cmodel=medany -misa-spec=20191213 -T linker.ld -c crt0.s

putchar.a: putchar.o
    $(AR) r putchar.a putchar.o

main.elf: putchar.a main.o crt0.o test.o
    $(CC) -march=rv64gc -mabi=lp64d -nostartfiles --machine-cmodel=medany -misa-spec=20191213 -T linker.ld crt0.o main.o putchar.a test.o -o main.elf

错误原因

RISC-V的R_RISCV_HI20重定位用于获取符号地址的高20位,配合R_RISCV_LO12_I/R_RISCV_LO12_S获取低12位组合成完整地址,要求符号地址与当前代码段的偏移在±2GB范围内(RV64架构下)。

分步编译时,错误地在编译目标文件阶段传入了链接专用的-T linker.ld选项。该选项会让编译器提前根据链接脚本中的内存布局(FLASH起始0x20000000)计算符号预期位置,但此时还未完成链接,编译器无法确定符号最终地址,导致生成的重定位信息不符合后续链接时的实际地址范围,最终触发截断错误。

而直接编译时,GCC合并了编译与链接阶段,-T linker.ld在正确的链接阶段生效,编译器能根据链接脚本生成适配的重定位信息,因此不会报错。

解决方法

1. 修正分步编译命令

编译目标文件时移除-T linker.ld选项,该选项仅在链接阶段使用:

第一步:编译目标文件

gcc -march=rv64gc -mabi=lp64d --machine-cmodel=medany -c main.c test.c

第二步:链接(保持原命令不变)

gcc -march=rv64gc -mabi=lp64d -nostartfiles --machine-cmodel=medany -T linker.ld crt0.o main.o putchar.a test.o -o main.elf

2. 修正Makefile规则

将通用.o目标的编译命令中的-T linker.ld移除,同时修正通配符规则为正确的模式规则:

CC = riscv64-unknown-elf-gcc
AR = riscv64-unknown-elf-ar

%.o: %.c
    $(CC) -march=rv64gc -mabi=lp64d --machine-cmodel=medany -misa-spec=20191213 -c $< -o $@

crt0.o: crt0.s
    $(CC) -march=rv64gc -mabi=lp64d --machine-cmodel=medany -misa-spec=20191213 -c crt0.s -o crt0.o

putchar.a: putchar.o
    $(AR) r putchar.a putchar.o

main.elf: putchar.a main.o crt0.o test.o
    $(CC) -march=rv64gc -mabi=lp64d -nostartfiles --machine-cmodel=medany -misa-spec=20191213 -T linker.ld crt0.o main.o putchar.a test.o -o main.elf

说明:

  • 编译阶段无需-nostartfiles,该选项是链接阶段用于跳过标准启动文件的
  • 模式规则%.o: %.c确保每个.c文件正确生成对应的.o文件

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 08:14:53