GCC链接器:如何从自定义输入节提取.data与.bss子节
方案可行:在链接脚本中拆分自定义节的初始化/未初始化变量
完全可以从.my_section中拆分出对应.data和.bss的子节,确保变量只存放在ECC保护RAM中,不进入常规的.data/.bss。以下是具体实现步骤:
1. 理解编译器对自定义节的处理
当你用__attribute__((section(".my_section")))声明变量时:
- 已初始化变量(如
var2 = 2)会被编译器放入.my_section的初始化子节,默认标记为需要从Flash加载到RAM - 未初始化变量(如
var1)会被编译器放入.my_section.bss子节(或直接标记为NOLOAD属性),不需要加载,仅需在启动时清零
2. 修改链接脚本
第一步:定义ECC保护RAM的内存区域
先在链接脚本的MEMORY块中声明ECC保护RAM的地址和大小:
MEMORY { ECC_RAM (rw) : ORIGIN = 0x20000000, LENGTH = 0x1000 /* 替换为你的硬件实际地址和长度 */ FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 0x40000 /* 常规Flash区域 */ RAM (rw) : ORIGIN = 0x20010000, LENGTH = 0x2000 /* 常规RAM区域 */ }
第二步:拆分自定义节的初始化/未初始化部分
在SECTIONS块中,单独处理.my_section的初始化和未初始化内容,同时确保常规.data/.bss不包含这些变量:
SECTIONS { /* 常规代码节(保持原有定义) */ .text : { *(.text) *(.text.*) } > FLASH /* 处理.my_section的初始化变量(等效于自定义.data节) */ .my_data : { PROVIDE(__my_data_start = .); *(.my_section) /* 捕获所有.my_section的初始化内容 */ *(.my_section.*) /* 捕获编译器生成的.my_section子节 */ PROVIDE(__my_data_end = .); } > ECC_RAM AT> FLASH /* 加载地址在Flash,运行地址在ECC_RAM */ /* 处理.my_section的未初始化变量(等效于自定义.bss节) */ .my_bss (NOLOAD) : { PROVIDE(__my_bss_start = .); *(.my_section.bss) /* 捕获未初始化变量的子节 */ *(.my_section.*.bss) PROVIDE(__my_bss_end = .); } > ECC_RAM /* 常规.data节(排除.my_section内容) */ .data : { PROVIDE(__data_start = .); *(.data) *(.data.*) PROVIDE(__data_end = .); } > RAM AT> FLASH /* 常规.bss节(排除.my_section内容) */ .bss : { PROVIDE(__bss_start = .); *(.bss) *(.bss.*) *(COMMON) PROVIDE(__bss_end = .); } > RAM /* 其他节定义(如.rodata等,保持原有逻辑) */ }
3. 调整启动代码逻辑
在启动代码(汇编或C语言初始化代码)中,增加对自定义节的处理:
- 复制初始化变量:将
.my_data从Flash的加载地址复制到ECC_RAM的运行地址,逻辑和常规.data复制一致:
extern char __my_data_start[]; extern char __my_data_end[]; extern char __my_data_load_start[]; /* 链接器自动生成,对应LOADADDR(.my_data) */ void copy_my_data(void) { char *src = __my_data_load_start; char *dst = __my_data_start; while (dst < __my_data_end) { *dst++ = *src++; } }
- 清零未初始化变量:对
.my_bss区域执行清零操作,逻辑和常规.bss清零一致:
extern char __my_bss_start[]; extern char __my_bss_end[]; void clear_my_bss(void) { char *dst = __my_bss_start; while (dst < __my_bss_end) { *dst++ = 0; } }
记得在进入main前调用这两个函数。
4. 可选优化:代码中显式区分子节
如果编译器版本未自动拆分.my_section.bss,可以在代码中直接指定子节,让链接脚本处理更明确:
int var1 __attribute__((section(".my_section.bss"))); /* 显式放入未初始化子节 */ int var2 __attribute__((section(".my_section"))) = 2; /* 初始化变量放入主节 */
验证方法
用objdump工具查看生成的可执行文件,确认变量地址:
arm-none-eabi-objdump -t your_program.elf | grep -E "var1|var2"
输出应显示var1在.my_bss区域(ECC_RAM地址),var2在.my_data区域(ECC_RAM地址),且不在常规.data/.bss中。
内容的提问来源于stack exchange,提问作者edgyhedgehog
相关产品推荐
相关产品推荐

