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

如何在已剥离的ARM R5 ELF文件中获取关键符号地址且不占用RAM

问题描述

使用gcc 11.2.0和ld 2.37.20210721编译面向小内存ARM R5 CPU的程序,需求如下:

  • 程序被剥离(stripped)后,仍能获取某个关键符号的地址
  • 禁止在RAM中存储该地址或常量,避免内存浪费
  • 剥离后的ELF文件需保留可标识该符号地址的信息,理想状态是类似.comment段:readelf不报告其Addr,且无W/X权限
  • 原本希望在链接脚本中使用ADDR(symbol)语法,但该功能不可用

尝试过的方案及问题

用__attribute__指定自定义段,结合链接脚本设置NOLOAD并将段放置在闲置地址空间(如不可用的处理器寄存器/代码地址空间),但objdump显示段内容丢失;若移除NOLOAD,内容虽保留但会占用RAM。

示例程序

__attribute__((unused)) unsigned int GENERAL_FAULT = 0;
volatile unsigned int critical = 5;
__attribute__((section(".criticaladdr"),unused)) const void *critical_addr = (void *)&critical;

void r5_main(void)
{
  while (1)
  {
    if (critical != 5)
      critical = 5;
  }
}

未添加链接脚本时的段信息

.criticaladdr段为可写且占用RAM,objdump -s -j .criticaladdr能显示正确地址,段信息如下:

[ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 010000 0000c0 00  AX  0   0  4
  [ 2] .note.gnu.bu[...] NOTE            000000c0 0100c0 000024 00   A  0   0  4
  [ 3] .data             PROGBITS        000000e4 0100e4 000004 00  WA  0   0  4
  [ 4] .criticaladdr     PROGBITS        000000e8 0100e8 000004 00  WA  0   0  4
[...]
  [ 9] .comment          PROGBITS        00000000 01011b 000012 01  MS  0   0  1

添加链接脚本后的问题

添加以下链接脚本后,.criticaladdr段仍为可写,但objdump显示内容为0x0:

…
MEMORY
{
…
  OCM_RAM_ATF (r) : ORIGIN = 0xFFFD0000, LENGTH = 0x20000
}
…
SECTIONS
{
…
.criticaladdr (NOLOAD) : { KEEP(*(".criticaladdr")) } > OCM_RAM_ATF
}
解决方案

方法1:只读非加载段+链接脚本属性配置

核心是创建只读、非加载、不分配内存的段,让链接器将符号地址写入段的文件内容,但不映射到RAM。

  1. 修改代码,给自定义段添加只读属性:
__attribute__((section(".criticaladdr"), unused, const)) const void *critical_addr = (void *)&critical;

添加const让gcc将该变量放入只读段,避免默认的可写属性。

  1. 修改链接脚本,定义段为非加载、无内存分配:
SECTIONS
{
  …
  .criticaladdr (NOLOAD) : {
    KEEP(*(".criticaladdr"))
  } :none
}
  • :none 指定段不分配到任何内存区域,readelf将不会显示Addr字段
  • KEEP() 确保链接器不会因无代码引用而优化掉该段

编译链接后,即使stripped,用objdump -s -j .criticaladdr仍能读取符号地址,且该段不占用RAM。

方法2:自定义NOTE段

利用ELF的NOTE段特性,该段本身不占用内存,stripped后仍会保留:

  1. 编写汇编代码创建NOTE段并写入符号地址:
.section .note.criticaladdr, "a"
.long 4,4,0
.string "CRITICAL_ADDR"
.long critical
  1. 在链接脚本中保留该段:
SECTIONS
{
  …
  KEEP(*(.note.criticaladdr))
}

stripped后,可通过readelf -n查看NOTE段内容,从中提取critical的地址,且该段不占用RAM。

方法3:链接脚本直接生成地址段

利用链接器指令直接在链接脚本中生成包含符号地址的段:

SECTIONS
{
  …
  .criticaladdr (NOLOAD) : {
    LONG(critical)
  } :none
}

链接器会直接将critical的地址写入.criticaladdr段的文件内容中,该段为非加载状态,不占用RAM,stripped后可通过objdump -s读取地址。

关键注意事项

  • 必须使用KEEP()指令,防止链接器优化掉目标段
  • 段属性需设置为只读(R),避免W权限;通过NOLOAD或:none确保不分配内存
  • 用readelf -S检查段属性,确认Addr字段为0,权限仅含R(无W/X)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 02:53:14