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

如何在C语言中读取外部ELF符号的结构体数据?

问题描述

我在汇编文件中定义了ELF符号__memory_info,它所在的.memory_info段反汇编内容如下:

Disassembly of section .memory_info:

10000bb8 <__memory_info>:
10000bb8:   10000bd4    ldrdne  r0, [r0], -r4
10000bbc:   20000000    andcs   r0, r0, r0
10000bc0:   00000000    andeq   r0, r0, r0
10000bc4:   20000000    andcs   r0, r0, r0
10000bc8:   00000000    andeq   r0, r0, r0
10000bcc:   20000000    andcs   r0, r0, r0
10000bd0:   000000a8    andeq   r0, r0, r8, lsr #1

我尝试在C语言中将该符号作为结构体读取,实现代码如下:

typedef struct {
    void *flash_end;
    void *ram_text_begin;
    void *ram_text_size;
    void *ram_data_begin;
    void *ram_data_size;
    void *ram_bss_begin;
    void *ram_bss_size;
} memory_info_t;

int main() {
    extern memory_info_t __memory_info;
    uint32_t *a = __memory_info.flash_end;
    
    asm("bkpt #0");
}

但通过GDB调试发现,断点处变量a的实际值不符合预期:

(gdb) print a
$1 = (uint32_t *) 0xffffffff

请问如何正确实现读取该ELF符号的结构体数据?

解决方案

从反汇编内容和调试结果来看,问题核心在于结构体对齐、类型匹配、段属性配置这三个方面,以下是具体修正步骤:

1. 解析汇编段的原始数据

反汇编输出是把二进制数据当成ARM指令解析的,实际我们需要提取每个4字节地址对应的原始32位数值:

  • 地址10000bb8对应值:0x10000bd4
  • 10000bbc对应值:0x20000000
  • 10000bc0对应值:0x00000000
  • 10000bc4对应值:0x20000000
  • 10000bc8对应值:0x00000000
  • 10000bcc对应值:0x20000000
  • 10000bd0对应值:0x000000a8

这些值是连续存储的4字节数据,无额外填充。

2. 修正C语言结构体定义

(1)禁用自动对齐并匹配数据类型

编译器默认会给结构体成员添加对齐填充,导致结构体布局和汇编段的连续数据不匹配。需要用packed属性强制紧凑对齐,同时将成员类型从void*改为uint32_t(内存地址本质是32位数值,避免指针类型带来的转换或寻址问题):

typedef struct __attribute__((packed, aligned(4))) {
    uint32_t flash_end;
    uint32_t ram_text_begin;
    uint32_t ram_text_size;
    uint32_t ram_data_begin;
    uint32_t ram_data_size;
    uint32_t ram_bss_begin;
    uint32_t ram_bss_size;
} memory_info_t;

(2)正确引用符号并关联段属性

在C代码中,明确指定符号所属的段,避免编译器解析错误:

extern memory_info_t __memory_info __attribute__((section(".memory_info")));

3. 确保汇编段属性正确

如果是GNU汇编,需要将.memory_info段标记为可分配的只读数据段,保证数据在程序加载时被正确映射到内存:

.section .memory_info, "a"
.global __memory_info
__memory_info:
    .word 0x10000bd4
    .word 0x20000000
    .word 0x00000000
    .word 0x20000000
    .word 0x00000000
    .word 0x20000000
    .word 0x000000a8

4. 调试验证

修改后重新编译,在GDB中查看变量值:

(gdb) print __memory_info.flash_end
$1 = 0x10000bd4
(gdb) print __memory_info.ram_bss_size
$2 = 0xa8

此时就能得到符合预期的数值。

关键注意点

  • 不要被反汇编的指令格式误导,直接提取每个4字节地址的原始32位值
  • 必须用packed属性保证结构体布局和汇编数据完全对齐
  • 使用uint32_t代替void*更稳妥,避免指针类型的潜在问题
  • 汇编段的属性配置是关键,确保数据能被正确加载到内存

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:35:01