0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

冬至子 来源:lchnu 作者:lchnu 2023-09-15 17:40 次阅读

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对数据进行快速访问。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • RAM
    RAM
    +关注

    关注

    7

    文章

    1322

    浏览量

    113706
  • RT-Thread
    +关注

    关注

    31

    文章

    1149

    浏览量

    38894
  • Flash存储
    +关注

    关注

    0

    文章

    38

    浏览量

    8235
  • STM32L4
    +关注

    关注

    1

    文章

    42

    浏览量

    9261
收藏 人收藏

    评论

    相关推荐

    RT-Thread记录(二、RT-Thread内核启动流程)

    在前面我们RT-Thread Studio工程基础之上讲一讲RT-Thread内核启动流程.
    的头像 发表于 06-20 00:30 4420次阅读
    <b class='flag-5'>RT-Thread</b>记录(二、<b class='flag-5'>RT-Thread</b>内核启动流程)

    【OK210试用体验】u-boot篇 -- u-boot内存分布

    。 SDRAM_BASE 和 TEXT_BASE 的定义位于 include/configs/smdkv210.h中,可以根据自己单板的要求来修改地址。 _end和__bss_start为链接脚本文件中最后定义的bss
    发表于 09-07 16:48

    请问__bss_start是直接表示bss起始地址的值吗?

    __bss_start.globl _bss_end_bss_end:.word __bss_end/* * 清BSS段 */clear_bss
    发表于 07-19 05:42

    为什么重定位的时候BSS段不拷贝过去?

    拷贝过去,为什么存储在SDRAM上u-boot代码总的大小是:__bss_end__ - _start 呢?代码重定向拷贝代码不是只拷贝了除BSS段以外代码段和数据段的大小么?ldr r2
    发表于 07-29 23:19

    RT-ThreadBSS段清零后就初始化C++构造有什么作用呢

    大家好,我现在正在学习RT-Thread,但是在学习启动代码的时候有一些疑问:1、在BSS段清零后就初始化C++构造有什么作用呢?是必须初始化呢还是这个组件可选?2、在初始化C++构造后就直接跳到C
    发表于 03-21 09:26

    小白求助RT-Thread操作系统的执行流程是怎样的

    ;), (void*)STM32_SRAM_END);#else /* init memory system */ rt_system_heap_init((void*)&__bss_end
    发表于 04-08 10:43

    RT-THREAD标准版移植使用

    前言:无意在微信看到了GD做活动,想到了第一时间体验一下,搭配RT-THREAD,也很方便使用。主要:本文只使用keil进行测试验证,gcc和iar需自行修改硬件准备:GD32F427 start
    发表于 11-21 16:43

    rt thread studio如何把变量定义到backup sram中 ?

    for initial. */  。 = ALIGN(4);  __rt_init_start = 。;  KEEP(*(SORT(.rti_fn*)))  __rt_init_end
    发表于 02-14 11:25

    BSS信令大全

    BSS信令大全:1. BSS系统中的信令应用 12. BSS系统的信令模型 23. 各层信令在BSS系统中的作用 64. 移动主叫流程 175.
    发表于 05-21 23:02 22次下载

    RT-Thread编程指南

    RT-Thread编程指南——RT-Thread开发组(2015-03-31)。RT-Thread做为国内有较大影响力的开源实时操作系统,本文是RT-Thread实时操作系统的编程指南
    发表于 11-26 16:06 0次下载

    RT-Thread开发,如何有效学习RT-Thread的五个步骤

    RT-Thread推出RT-Thread Inside战略开放RT-Thread开发平台授权合作,与硬件十万个为什么合作首次推出第一款RT-Inside的开发板——iBox物联网开发套
    的头像 发表于 09-25 09:55 3.4w次阅读
    <b class='flag-5'>RT-Thread</b>开发,如何有效学习<b class='flag-5'>RT-Thread</b>的五个步骤

    RT-Thread开源作品秀】基于RT-Thread的星务平台研究

    本作品为了验证星务软件在RT-Thread系统运行的可行性,底层是否能够驱动星务软件,同时扩展RT-Thread应用范围。ART-Pi作为卫星下位机,...
    发表于 01-25 18:26 5次下载
    【<b class='flag-5'>RT-Thread</b>开源作品秀】基于<b class='flag-5'>RT-Thread</b>的星务平台研究

    RT-Thread学习笔记 RT-Thread的架构概述

    RT-Thread 简介 作为一名 RTOS 的初学者,也许你对 RT-Thread 还比较陌生。然而,随着你的深入接触,你会逐渐发现 RT-Thread 的魅力和它相较于其他同类型 RTOS
    的头像 发表于 07-09 11:27 4001次阅读
    <b class='flag-5'>RT-Thread</b>学习笔记 <b class='flag-5'>RT-Thread</b>的架构概述

    RT-Thread文档_RT-Thread 简介

    RT-Thread文档_RT-Thread 简介
    发表于 02-22 18:22 5次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> 简介

    RT-Thread文档_RT-Thread SMP 介绍与移植

    RT-Thread文档_RT-Thread SMP 介绍与移植
    发表于 02-22 18:31 7次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> SMP 介绍与移植