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
+關注
關注
8文章
1369瀏覽量
114817 -
RT-Thread
+關注
關注
31文章
1300瀏覽量
40264 -
Flash存儲
+關注
關注
0文章
38瀏覽量
8329 -
STM32L4
+關注
關注
1文章
42瀏覽量
9420
發布評論請先 登錄
相關推薦
評論