如何在链接脚本中从已有ELF二进制导入符号,避免硬编码地址?
实现方案:无需硬编码地址的Bootloader与应用程序链接流程
可以通过自动提取Bootloader符号并生成动态链接脚本片段的方式实现需求,完全避免硬编码地址,具体步骤如下:
1. 在Bootloader中定义关键符号
首先在Bootloader的链接脚本里,显式定义Bootloader的结束地址(以及其他需要被应用引用的符号),确保这些符号能被工具提取:
/* bootloader.ld 片段 */ MEMORY { FLASH (rx) : ORIGIN = 0x10000000, LENGTH = 0x4A000 /* RAM 定义... */ } SECTIONS { /* ...其他段定义... */ /* 所有Bootloader段完成后,添加结束标记 */ .end_of_bootloader : { PROVIDE(__bootloader_end = .); } > FLASH /* 如需让应用引用其他符号(如配置区地址),同样用PROVIDE声明 */ PROVIDE(__bootloader_config = 0x10048000); }
也可以在Bootloader代码中通过属性定义全局符号,效果一致:
// bootloader.c 片段 __attribute__((section(".end_of_bootloader"))) const uint32_t __bootloader_end = 0;
2. 自动生成Bootloader符号链接脚本
在构建流程(如Makefile或Shell脚本)中,编译完bootloader.elf后,用nm工具提取关键符号的地址,生成供应用链接脚本引用的片段文件bootloader_symbols.ld:
# 提取__bootloader_end地址并写入符号文件 nm -n bootloader.elf | grep __bootloader_end | awk '{print "__bootloader_end = 0x" $1 ";"}' > bootloader_symbols.ld # 提取其他需要的符号(比如__bootloader_config) nm -n bootloader.elf | grep __bootloader_config | awk '{print "__bootloader_config = 0x" $1 ";"}' >> bootloader_symbols.ld
这个步骤会自动同步Bootloader的地址变化,无需手动修改应用配置。
3. 在应用链接脚本中引用动态符号
修改application.ld,通过INCLUDE命令引入自动生成的符号文件,用动态符号替代硬编码地址:
/* application.ld 片段 */ # 引入Bootloader的动态符号 INCLUDE bootloader_symbols.ld MEMORY { /* Flash起始地址直接使用__bootloader_end,长度自动计算 */ FLASH (rx) : ORIGIN = __bootloader_end, LENGTH = 0x10080000 - __bootloader_end RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x40000 } SECTIONS { /* 应用程序段从__bootloader_end开始布局 */ .text : { *(.text) } > FLASH /* ...其他段定义... */ }
4. 在应用代码中引用Bootloader符号
在应用代码中,直接声明需要引用的Bootloader符号为extern即可,链接时会自动从bootloader_symbols.ld获取地址:
// application.c 片段 extern uint32_t __bootloader_end; extern uint32_t __bootloader_config; void app_init() { // 访问Bootloader的配置区 uint32_t boot_cfg = *(uint32_t*)__bootloader_config; }
构建流程自动化
把上述步骤整合到Makefile中,确保构建顺序为:
- 编译并链接生成
bootloader.elf - 生成
bootloader_symbols.ld - 编译并链接生成
application.elf
这样每次修改Bootloader后,应用会自动使用新的起始地址和符号,完全无需手动同步地址。
内容的提问来源于stack exchange,提问作者felix
相关产品推荐
相关产品推荐

