Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
RT-Thread
记录一次学生关于RT-Thread的__bss_end - __bss_start的有趣问题与派生问题
发布于 2023-04-20 23:11:47 浏览:820
订阅该版
[tocm] 在课堂上给学生讲解了RT-Thread Studio对CSP工程编译之后的MAP文件阅读要点,并布置了一次作业,要求学生绘制出编译之后的STM32内存布局。 有学生提出一个有意思的问题,即编译后给出的ELF bss数据,实际大于_bss_end - bss_start的值。在交流过程中,发现学生对于.COMMON部分也有些疑惑。 > 老师,打扰到您!在写嵌入式第三次作业第一题时,遇到了一点点小问题,根据地址,我用_bss_end减去_bss_start得到的值却不等于控制台给出的bss 3376,而用_bss_end减去_sstack却得到了对应的值"3376",请问这是怎么一回事? 本文对学生提出的问题进行回复,也简单讨论了一下.bss和.COMMON。内容相对简单,但希望能对刚入门的朋友有些许帮助,不足之处,请批评指正。 ## 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的工程。 编译完成后,得到的输出结果是 ```c 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文件 ```c .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 ```c 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段中。 ```c 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 ```c static rt_uint32_t global_uint32_a; ``` 编译后,将gloabal_a放入了.bss段中 ```c .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描述,且打印该变量防止编译器优化该变量。 ```c 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(填充)的描述。 ```c 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,防止编译器优化。 ```c 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次。` ```c /*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均引用了该变量。` ```c /*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的变量位于不同地址。` ```c /*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。因为两者实际处于不同地址。` ## 小结 综上所述,形成如下小结: 1. ELF bss包括了:.stack, .bss, .COMMON,它们的地址均位于RAM。 2. .bss段中的是强符号,.COMMON段中的是弱符号。 3. RT-Thread的启动代码中_bss_start和_bss_end部分的差值,是强符号的.bss部分和弱符号的.COMMON部分之和,启动代码对这部分进行清零操作。 4. 多个文件中有同名的变量不可怕,要有强、弱之分。 5. 加了static修饰的全局变量在.bss;未加static修饰的全局变量在.COMMON。 6. 多字节对齐,方便CPU对数据进行快速访问。
6
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
lchnu
Witness, Understand, Skill
文章
10
回答
229
被采纳
88
关注TA
发私信
相关文章
1
RT-THREAD在STM32H747平台上移植lwip
2
正点原子miniSTM32开发板读写sdcard
3
反馈rtt串口驱动对低功耗串口lpuart1不兼容的问题
4
Keil MDK 移植 RT-Thread Nano
5
RT1061/1052 带 RTT + LWIP和LPSPI,有什么坑要注意吗?
6
RT thread HID 如何收发数据
7
求一份基于RTT系统封装好的STM32F1系列的FLASH操作程序
8
RT-Thread修改项目名称之后不能下载
9
rt-studio编译c++
10
有木有移植rt-thread(nano)到riscv 32位MCU上
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
编译报错
Debug
SFUD
msh
rt_mq_消息队列_msg_queue
keil_MDK
ulog
MicroPython
C++_cpp
本月问答贡献
出出啊
1517
个答案
342
次被采纳
小小李sunny
1443
个答案
289
次被采纳
张世争
805
个答案
174
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
148
次被采纳
本月文章贡献
出出啊
1
篇文章
4
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
1
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部