MakerLab创客实验室 — Let's Make something.
现在注册
已注册用户请 登入
Andy large avatar
MakerLab创客实验室  ›  C/C++

【学习笔记】gcc中全局变量初始化到.bss或.data -fno-zero-initialized-in-bss

By Andy at 2 年前 , 1520 次浏览

最近尝试将Marlin固件移植到32位ARM上,但遇到了一个头疼的问题,全局变量(或某些局部变量)初始化为零时无效。
后面的程序调用该变量时,数值都乱了,并不是想要的初始值。
举例(Arduino代码,经过测试,简单代码并没有出现初始值随机的情况,但在Marlin这么大的项目的情况下,全局变量初始化为0,确实会变为随机值。所以这里列出Marlin的一部分,做分析)

Marlin_main.cpp

static int16_t bufindr = 0;//已经初始化为0
//...省略n行
void setup()
{
setup_killpin();
setup_powerhold();
MYSERIAL.begin(BAUDRATE);
SERIAL_PROTOCOLLNPGM("start");
// init those values again.
MYSERIAL.println(bufindr);//实际并不是0

bufindr = 0;//需要再次初始化为0
}
//...省略n行
void loop()
{}

最开始没有发现问题在哪,也找不到好的解决办法,只能通过在setup里面重新初始化之前已经初始化的变量,但Marlin中的变量太多,所有的变量这样重新初始化不现实。由于同样的代码在avr cpu上一切正常,但在arm cpu上出现这个问题,所以想到应该是gcc的问题,google后了解到,可以通过arm-none-eabi-nm这个命令来查看变量:

>arm-none-eabi-nm -S Marlin.cpp.elf
....
2000147c b _ZL7bufindr
...

可以看到bufindr这个变量的标记为b,b的含义是
"B", "b" The symbol is in the uninitialized data section (known as BSS).标识没有初始化的数据,保存到.BSS区域。

初始化为0的变量,结果变成了uninitialized data。实在是搞不清楚。

几番查找后,发现原来arm-none-eabi-gcc 有一个编译选项,可以控制初始化为0的变量的存放位置,默认放在了BSS区域。而我们需要它放在.data区域(也就是标记为"D","d" The symbol is in the initialized data section.)

这个gcc或g++选项是:-fno-zero-initialized-in-bss,不加任何选项的默认值是-fzero-initialized-in-bss
-fno-zero-initialized-in-bss选项加入到gcc选项后得到elf文件,重新查看:

>arm-none-eabi-nm -S  Marlin.cpp.elf | grep bufindr
2000006e 00000002 d _ZL7bufindr

会发现bufindr变量的标记由b变为了d,也就是变为了初始化的值了。将之前setup中的初始化代码删除后测试后发现,正常。

困扰几天的问题解决了,并且还学到了这么多知识。

参考:
http://www.jollen.org/blog/2007/01/no-zero-initialized-in-bss.html
http://stackoverflow.com/a/8721627
http://manpages.ubuntu.com/manpages/utopic/man1/arm-none-eabi-nm.1.html
https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

1 回复