2. 链接脚本定制内存布局

任务说明

为 RISC-V 裸机程序编写自定义链接脚本,把 .text、.data、.bss 分别放到三个互不重叠的固定地址,再用 objdump 反汇编确认各段起始地址与脚本一致。

本案例用最小可复现的裸机程序验证链接脚本,不依赖前序章节代码。目标内存布局:

  • .text 起始 0x80000000(代码段)
  • .data 起始 0x80010000(已初始化数据)
  • .bss 起始 0x80020000(未初始化数据)

环境与依赖

  • RISC-V 工具链:riscv64-unknown-elf-gcc、riscv64-unknown-elf-objdump(或 riscv32-*,本案例用 64 位)。
  • 无需真实硬件,验证只依赖反汇编输出。

关键文件

link.ld 定义内存布局,start.S 提供入口并引用各段符号,main.c 提供 .data 与 .bss 变量。

/* link.ld */
ENTRY(_start)

MEMORY
{
    TEXT (rx)  : ORIGIN = 0x80000000, LENGTH = 64K
    DATA (rw)  : ORIGIN = 0x80010000, LENGTH = 64K
    BSS  (rw)  : ORIGIN = 0x80020000, LENGTH = 64K
}

SECTIONS
{
    .text : {
        *(.text._start)
        *(.text*)
    } > TEXT

    .data : {
        *(.data*)
    } > DATA

    .bss : {
        *(.bss*)
        *(COMMON)
    } > BSS
}
/* start.S */
    .section .text._start
    .globl _start
_start:
    la   t0, _data_start
    la   t1, _bss_start
    la   t2, _bss_end
1:  bgeu t1, t2, 2f
    sd   zero, 0(t1)
    addi t1, t1, 8
    j    1b
2:  j    2b
/* main.c */
int  g_init = 0x1234;   /* 放入 .data */
int  g_zero;            /* 放入 .bss  */
const int g_const = 7;  /* 放入 .text(只读) */

void _start(void);

构建与验证

riscv64-unknown-elf-gcc -nostdlib -T link.ld \
    -o demo.elf start.S main.c

riscv64-unknown-elf-objdump -h demo.elf

在 objdump -h 输出中检查各段 VMA:.text 应为 0x80000000,.data 应为 0x80010000,.bss 应为 0x80020000。若三者地址与脚本一致且互不重叠,即判定通过。

段布局示意

.text @ 0x80000000 .data @ 0x80010000 .bss @ 0x80020000 代码段(只读) 已初始化数据 未初始化数据

常见问题

  • 若 .data 未出现在预期地址,检查是否被 *(.data*) 之外的规则抢先匹配。
  • 若 .bss 为空,确认 g_zero 未被优化掉(本案例未开优化,默认保留)。
  • 链接报 region overflow 时,说明段大小超过 LENGTH,按需调大。