GCC Linker中__bss_start__与__bss_end__符号长度为0的问题
解决__bss_start__/__bss_end__符号异常导致BSS清零跳过的问题
最近我在钻研嵌入式裸机程序的底层运行逻辑时,自己写了配套的C代码和链接脚本,生成的二进制能正常驱动LED闪烁,但调试时碰到了一个头疼的问题:__bss_start__和__bss_end__两个符号的值居然都是0x20000000,导致我的BSS段清零函数因为判断start >= end直接跳过执行了。
我用objdump工具分析目标文件,得到了这样的输出:
20000000 g O .bss 00000004 timer_delayCount
这说明.bss段其实是正常的——长度4字节,起始地址正确落在0x20000000,但为什么链接脚本里定义的__bss_start__和__bss_end__没有正确计算出段的结束地址呢?
结合我的排查经验,给你几个可能的解决方向:
- 检查链接脚本的符号定义逻辑:这是最常见的出错点。很多人会把
__bss_end__的位置写错,正确的写法应该是在.bss段的定义块里,先标记起始,再放段内容,最后标记结束,示例如下:
要是把.bss : { __bss_start__ = .; /* 标记BSS段起始地址 */ *(.bss) /* 包含所有目标文件的.bss段 */ *(COMMON) /* 包含公共符号 */ __bss_end__ = .; /* 标记BSS段结束地址 */ } > RAM /* 指定加载到RAM区域 */__bss_end__写在了.bss段内容之前,那它的值自然会和起始地址一样。 - 确认RAM内存区域的定义:链接脚本里的
RAM区域如果起始地址正确但长度设置过小,或者定义有误,链接器可能无法正确扩展.bss段的结束地址,导致__bss_end__和起始地址重合。 - 核对符号名的拼写:不同编译器/链接器对BSS段符号的命名可能有差异,比如有些是
__bss_start(无末尾下划线)、__end_bss等。如果你的链接脚本里的符号名和C代码里引用的不匹配,或者不符合工具链的约定,就会出现符号值异常。 - 检查C代码里的符号引用方式:在C代码里声明这些符号时,要用正确的类型,比如:
避免用指针类型或者错误的类型转换,防止地址计算出现偏差。extern unsigned char __bss_start__; extern unsigned char __bss_end__;
如果以上方法都没解决问题,建议编译链接时加上-Wl,-Map=output.map参数生成链接映射文件,查看__bss_start__和__bss_end__在映射文件里的实际值,能更直观地看到链接器的分配逻辑,帮你快速定位问题。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

