如何在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对应值:0x2000000010000bc0对应值:0x0000000010000bc4对应值:0x2000000010000bc8对应值:0x0000000010000bcc对应值:0x2000000010000bd0对应值: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
相关产品推荐
相关产品推荐

