嵌入式C中如何通过链接器将结构体精确定位到指定内存地址?
问题根源与修复方案
核心问题
- const变量的段归属错误:你定义的
const conf_t gv_confReg是只读常量,GCC等嵌入式编译器会默认将全局const变量放入.rodata段(只读数据段),而非你链接脚本中配置的.data段,因此链接器不会将其映射到0x40000000地址。 - 链接脚本配置不匹配:你的链接脚本仅对
.data段做了地址映射,未处理.rodata段;且原脚本中.data段的ADDR属性写法冗余(> PROM已指定内存区域,无需额外ADDR)。 - 未初始化结构体:仅声明
const conf_t gv_confReg但未初始化,编译器可能将其标记为未使用符号或放入未初始化段,导致地址不可控。
修复步骤
1. 显式指定变量的段并初始化结构体
修改config.c,通过编译器属性将变量放入自定义段,同时必须初始化结构体(避免编译器优化):
const conf_t gv_confReg __attribute__((section(".conf_section"))) = { .val1 = 0x12345678, .val2 = 0x87654321 };
2. 修改链接器脚本,映射自定义段到指定地址
更新linker.ld,将自定义的.conf_section段放在0x40000000起始位置,同时保留其他必要段的配置:
MEMORY { PROM (r) : ORIGIN = 0x40000000, LENGTH = 256k } SECTIONS { . = ORIGIN(PROM); // 优先放置配置结构体,确保起始地址精确为0x40000000 .conf_section : { KEEP(*(.conf_section)) // 防止链接器丢弃该段 } > PROM // 处理其他只读数据段 .rodata : { *(.rodata) *(.rodata.*) } > PROM // 处理读写数据段 .data : { *(.data) *(.data.*) } > PROM .init_array : { PROVIDE(__init_array_start = .); KEEP(*(.init_array)) PROVIDE(__init_array_end = .); } > PROM }
如果不想用自定义段,也可以直接将.rodata段放在起始位置,但自定义段更灵活,不会影响其他只读数据的布局。
3. 验证地址正确性
编译完成后,通过以下方式验证:
- 查看生成的
out.map文件,搜索gv_confReg确认地址; - 使用
objdump工具反编译二进制文件:
正常情况下会显示地址为objdump -t your_output_binary | grep gv_confReg0x40000000。
额外注意事项
- 若开启了
-fdata-sections或-ffunction-sections编译选项,必须用KEEP()确保目标段不被链接器丢弃; - const全局变量必须初始化,否则编译器可能不为其分配实际内存;
- 链接脚本中
> PROM已指定运行地址(VMA),若PROM是运行时直接访问的存储介质,无需额外设置ADDR属性(该属性用于指定加载地址LMA,通常用于需要拷贝数据到RAM的场景)。
内容的提问来源于stack exchange,提问作者user23554556
相关产品推荐
相关产品推荐

