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

如何使GCC/LD对BSS与.data段采用指令相对寻址生成PIC

解决.data/.bss段硬编码地址问题,实现全PC相对寻址的位置无关Blob

要让.bss和.data段也使用PC相对寻址,核心思路是通过链接脚本记录各段相对于.text的固定偏移,在代码中基于当前PC计算出这些段的实际加载地址,避免直接使用链接时的绝对地址。以下是具体实现步骤:

1. 修改链接脚本,导出段偏移符号

在链接脚本中定义各段的起始地址和相对于.text起始的偏移量,让代码可以引用这些符号计算实际地址:

SECTIONS {
    .text : {
        __text_base = .;  /* 记录.text段的链接起始地址 */
        *(.text);
    }
    .data : { 
        __data_base = .;  /* 记录.data段的链接起始地址 */
        *(.bss);          /* 将BSS包含进镜像,初始化为0 */
        *(.data); 
    } = 0
    .rodata : { 
        __rodata_base = .; /* 记录.rodata段的链接起始地址 */
        *(.rodata); 
    }

    /* 计算各段相对于.text的偏移(链接时的固定值) */
    __data_offset = __data_base - __text_base;
    __rodata_offset = __rodata_base - __text_base;
}

2. 修改代码,用PC相对方式访问可写数据

代码中需要先获取当前PC的实际地址,再结合链接脚本导出的偏移量,计算出.data/.bss的实际加载地址。注意不同CPU架构的PC获取方式不同,以下是多架构兼容的实现:

#include <stdint.h>

/* 声明链接脚本中导出的符号 */
extern uintptr_t __text_base, __data_offset, __data_base;

/* 链接时的占位变量(实际地址通过计算得到) */
static int some_bss_values_link[8];
static int some_data_values_link[] = {1,2,3,4,5,6,7,8};
const int some_rodata_values[] = {9,10,11,12,13,14,15,16};

/* 获取当前PC的实际地址(架构相关实现) */
static inline uintptr_t get_current_pc(void) {
#ifdef __arm__
    uintptr_t pc;
    // ARM的PC寄存器指向当前指令+8,所以需要调整
    __asm__ volatile ("mov %0, pc" : "=r"(pc));
    return pc - 8;
#elif defined(__x86_64__)
    uintptr_t pc;
    __asm__ volatile ("lea 0(%%rip), %0" : "=r"(pc));
    return pc;
#elif defined(__i386__)
    uintptr_t pc;
    // 通过call指令将当前PC压栈,再弹出获取
    __asm__ volatile ("call 1f; 1: pop %0" : "=r"(pc));
    return pc;
#endif
}

/* 计算.data段的实际加载基地址 */
static inline uintptr_t get_data_load_base(void) {
    // 当前PC的链接地址与__text_base的差值,等于实际PC与TEXT加载基地址的差值
    uintptr_t text_load_base = get_current_pc() - ((uintptr_t)get_current_pc - (uintptr_t)&__text_base);
    // TEXT加载基地址加上data偏移,得到data实际加载基地址
    return text_load_base + (uintptr_t)&__data_offset;
}

/* 定义宏,将占位变量转换为实际加载地址的指针 */
#define some_bss_values ((int *)(get_data_load_base() + ((uintptr_t)&some_bss_values_link - (uintptr_t)&__data_base)))
#define some_data_values ((int *)(get_data_load_base() + ((uintptr_t)&some_data_values_link - (uintptr_t)&__data_base)))

int some_function(int x) {
   some_bss_values[x] = some_rodata_values[x];
   return some_data_values[x]++;
}

3. 调整编译命令

保持原有编译命令不变,确保-fPIC和-nostdlib参数正确:

gcc -Wl,-esome_function -o t.elf -fPIC -nostdlib -T my_linker_script.ld -g -O0 my_code.cpp
objcopy -Obinary -j.text -j.data -j.rodata t.elf t.bin

原理说明

  • .rodata能自动使用PC相对寻址,是因为-fPIC下GCC对只读数据默认生成PC相对访问指令,且.rodata不需要写入,无需依赖GOT表。
  • .data/.bss默认使用绝对地址,是因为PIC模式下可写数据通常依赖动态链接器维护的GOT表,但嵌入式无动态链接器,因此需要手动通过PC偏移计算实际地址。
  • 链接脚本中定义的偏移量是链接时的固定值,Blob加载后各段的相对位置不变,因此通过PC计算出的实际地址始终正确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 08:04:52