关于RT-Thread的__bss_end - __bss_start的问题解析

q1. elf bss == (bss_end - .stack)?
q1. 编译完成后,elf解析的bss数值3372,并不等于bss_end - bss_start,而是等于bss_end - sstack。
step 1. 在rt-thread studio中创建一个基于4.0.5和stm32l431rctx的工程。
编译完成后,得到的输出结果是
arm-none-eabi-size --format=berkeley rtthread.elf
text data bss dec hex filename
53632 1816 3372 58820 e5c4 rtthread.elf
used size(b) used size(kb)
flash: 55448 b 54.15 kb
ram: 5188 b 5.07 kb
flash大小:55448 = text + data,因为data段的初始值存放在flash中。
ram大小:5188 = data + bss,因为r/w变量存放在ram中。
step 2. 贴上rt-thread studio生成的部分map文件
.stack 0x20000718 0x400 load address 0x0800d898
0x20000718 . = align (0x4)
0x20000718 _sstack = .
0x20000b18 . = (. + _system_stack_size)
fill 0x20000718 0x400
0x20000b18 . = align (0x4)
0x20000b18 _estack = .
0x20000b18 __bss_start = .
.bss 0x20000b18 0x92c load address 0x0800dc98
0x20000b18 . = align (0x4)
0x20000b18 _sbss = .
*(.bss)
.bss 0x20000b18 0x1c c:/rt-threadstudio/repo/extract/toolchain_support_packages/arm/gnu_tools_for_arm_embedded_processors/5.4.1/bin/../lib/gcc/arm-none-eabi/5.4.1/armv7e-m/fpu/crtbegin.o
(.bss. )
.bss.rt_tick 0x20000b34 0x4 ./rt-thread/src/clock.o
.bss.idle 0x20000b38 0x80 ./rt-thread/src/idle.o
.bss.rt_thread_stack
......
.bss.uart_obj 0x2000123c 0xdc ./drivers/drv_usart.o
*(common)
common 0x20001318 0x4 ./rt-thread/src/kservice.o
0x20001318 rt_assert_hook
......
common 0x20001440 0x4 ./libraries/stm32l4xx_hal_driver/src/stm32l4xx_hal.o
0x20001440 uwtick
0x20001444 . = align (0x4)
0x20001444 _ebss = .
*(.bss.init)
0x20001444 __bss_end = .
0x20001444 _end = .
使用__bss_end - __bss_start = 0x20001444 - 0x20000b18 = 0x92c = 2348d
使用__bss_end - _sstack = 0x20001444 - 0x20000718 = 0xd2c = 3372d
关注rt-thread studio给出的信息,只包含了text,data,bss三个信息,其中,data和bss都是存放在ram中的。
因此,从分析中可知,studio编译完成后:
elf分析工具给出的bss数据,包括了*.lds文件中的.stack段, .bss段和.coommon段。
data数据对应.data段。
.bss + .common + .stack == elf bss。
q2. which kind of variables are placed in .common?
q2. .bss和.common部分的关系?
step 1. 在main.c中声明新全局变量global_uint32_a
rt_uint32_t global_uint32_a;
因为仅仅声明了变量,而没有使用该变量,因此编译器优化了这个变量,没有为它实际分配地址,编译后,elf bss没有变化,依然是3372。
step 2. 在main函数中添加对变量的使用,防止编译器优化
在main函数中增加一条使用lod_d打印global_uint32_a的语句,发现bss数据从3372变化成3376。
打开map文件,查看global_uint32_a。此时,发现global_uint32_a被放入到了common段中。
common 0x20001440 0x4 ./libraries/stm32l4xx_hal_driver/src/stm32l4xx_hal.o
0x20001440 uwtick
common 0x20001444 0x4 ./applications/main.o
0x20001444 global_uint32_a
0x20001448 . = align (0x4)
0x20001448 _ebss = .
*(.bss.init)
0x20001448 __bss_end = .
0x20001448 _end = .
step 3. 修改global_uint32_a的声明,在声明前加入static
static rt_uint32_t global_uint32_a;
编译后,将gloabal_a放入了.bss段中
.bss.global_uint32_a 0x20001318 0x4 ./applications/main.o
不妨查找map中的common段的变量,rt_object_put_hook, rt_object_take_hook等,它们均是全局变量,在某个*.c文件中被声明,在其他文件中使用extern方式声明,在多个文件中均有使用。
rt-thread代码中的.bss的变量,均使用了static声明,该变量的作用域仅限于本文件。若外部文件需要使用,则可以以函数调用形式,返回该变量的地址即可。使用这种方式,防止了全局变量满天飞,在模块间的解耦较好。
step 4. 在main.c声明新变量rt_uint8_t类型的新变量global_uint8_b
再次声明一个rt_uint8_t类型的新变量global_uint8_b,不使用static描述,且打印该变量防止编译器优化该变量。
rt_uint8_t global_uint8_b;
log_d(%d, %d,global_uint32_a, global_uint8_b);
编译后,elf bss变化成3380,而不是预期的从3376变化成3377。原因是ram 4字节对齐,在lds文件中使用align(4)的方式进行了约束。
从生成的map文件中可以看到,global_uint8_b被放置在common中,且下方还有align(4)和fill(填充)的描述。
common 0x20001448 0x1 ./applications/main.o
0x20001448 global_uint8_b
0x2000144c . = align (0x4)
fill 0x2000144d 0x3
step 5. 在board.c中声明同名、同类型的global_uint8_b
在board.c中声明同名、同类型的global_uint8_b,且在rt_hw_board_init函数中对global_uint8_b变量赋值为2,防止编译器优化。
rt_uint8_t global_uint8_b;
rt_weak void rt_hw_board_init()
{
global_uint8_b = 2;
.......
}
未在board.c中声明global_uint8_b之前的编译结果和map文件节选如下所示,global_uint8_b在map文件中出现了3次。
/ board.c中声明变量之前 /
arm-none-eabi-size --format=berkeley rtthread.elf
text data bss dec hex filename
53680 1816 3380 58876 e5fc rtthread.elf
/ map文件节选,省略部分内容 /
allocating common symbols
common symbol size file
global_uint8_b 0x1 ./applications/main.o
.....
.bss.global_uint32_a
0x20001318 0x4 ./applications/main.o
*(common)
common 0x2000131c 0x4 ./rt-thread/src/kservice.o
0x2000131c rt_assert_hook
.....
common 0x20001448 0x1 ./applications/main.o
0x20001448 global_uint8_b
0x2000144c . = align (0x4)
fill 0x20001449 0x3
.....
global_uint8_b ./applications/main.o
在board.c中声明global_uint8_b之后的编译结果和map文件节选如下所示。global_uint8_b在map文件中出现了3次,但是同时main.o和board.o均引用了该变量。
/ board.c中声明变量之后 /
arm-none-eabi-size --format=berkeley rtthread.elf
text data bss dec hex filename
53692 1816 3380 58888 e608 rtthread.elf
/ map文件节选,省略部分内容 /
allocating common symbols
common symbol size file
global_uint8_b 0x1 ./drivers/board.o
.....
.bss.global_uint32_a
0x20001318 0x4 ./applications/main.o
*(common)
common 0x2000131c 0x4 ./rt-thread/src/kservice.o
0x2000131c rt_assert_hook
.....
common 0x20001448 0x1 ./drivers/board.o
0x20001448 global_uint8_b
0x2000144c . = align (0x4)
fill 0x20001449 0x3
.....
global_uint8_b ./applications/main.o
./drivers/board.o
对比在board.c中声明变量global_uint8_b的前后,发现elf bss均没有变化。从map文件中可以看到,在链接过程中,main.o文件中实际用到的是board.o的值。实际整个工程中只有一个global_uint8_b变量,属于弱符号。
因为board.c中的rt_hw_board_init函数先于main.c中的main函数运行,所以将程序下载到开发板中,会发现main函数中打印出来的global_uint8_b的值为2。
step 6. 将main.c中的global_uint8_b限制为static
保持上述在board.c中的修改,将main.c中的global_uint8_b变量用static描述。
编译之后elf bss增加4个字节,变化成3384,整个工程中会有两个global_uint8_b变量,且位于不同的地址。
main.c中的global_uint8_b位于.bss,是个强符号
board.c中的global_uint8_b位于common,是个弱符号
下方内容是将main.c中的global_uint8_b用static修饰之后的map文件节选。global_uint8_b在map文件中出现了4次,且main.o和board.o的变量位于不同地址。
/ main.c中的变量用static限制 /
arm-none-eabi-size --format=berkeley rtthread.elf
text data bss dec hex filename
53692 1816 3384 58892 e60c rtthread.elf
/ map文件节选,省略部分内容 /
allocating common symbols
common symbol size file
global_uint8_b 0x1 ./drivers/board.o
.....
.bss.global_uint32_a
0x20001318 0x4 ./applications/main.o
.bss.global_uint8_b
0x2000131c 0x1 ./applications/main.o
*(common)
fill 0x2000131d 0x3
common 0x20001320 0x4 ./rt-thread/src/kservice.o
0x20001320 rt_assert_hook
.....
common 0x2000144c 0x1 ./drivers/board.o
0x2000144c global_uint8_b
0x20001450 . = align (0x4)
fill 0x2000144d 0x3
.....
global_uint8_b ./drivers/board.o
虽然board.c中的rt_hw_board_init函数先于main.c中的main函数运行,且在board.c中global_uint8_b的值修改成2,但是将程序下载到开发板中,打印出来的global_uint8_b的值为0。因为两者实际处于不同地址。
小结
综上所述,形成如下小结:
elf bss包括了:.stack, .bss, .common,它们的地址均位于ram。
.bss段中的是强符号,.common段中的是弱符号。
rt-thread的启动代码中_bss_start和_bss_end部分的差值,是强符号的.bss部分和弱符号的.common部分之和,启动代码对这部分进行清零操作。
多个文件中有同名的变量不可怕,要有强、弱之分。
加了static修饰的全局变量在.bss;未加static修饰的全局变量在.common。
多字节对齐,方便cpu对数据进行快速访问。

5G时代,光网络将走向何方?
棒棒团线上学习有妙招,它是如何达到98%就业率的
IDT 为 LG 最新款的旗舰智能手机 G6 提供高效的无线电源解决方案
马良神笔成现实!国外超火的3D打印笔,不会画画的人也可以用它作画
拍人像应该买哪款相机
关于RT-Thread的__bss_end - __bss_start的问题解析
Guru声称研制毫米波无线充电系统,可为手机和物联网设备进行充电
电芯起火问题,欧宝召回近1万辆Ampera-e电动车
新iPhone大家比较关心的问题总结,新到底iPhone怎么买划算?
AD9866BCPZ 现货
串口设备框架serial_v2源码分析-阻塞模式
新思科技携手AWS加速软件定义汽车的验证
知名互联网网易创始人丁磊是如何成功的
华为mate10即将上市!全面屏+麒麟970,性能强劲,外观直逼iPhone8
国芯思辰|基本半导体碳化硅肖特基二极管B2D06065E在主体为BOOST电路的PFC电路应用方案
确定,诺基亚8售价3188!骁龙821+双面玻璃+蔡司镜头!
科沃斯空气净化机器人引领潮流,在高端智能家居领域获得认可
如何在智能座舱及智能驾驶场景中助力用户提升用车体验及实现智慧出行
什么是自组装单层,自组装单层涂层简介
PCIe4.0固态硬盘上手实测