As a point of trying to understand the basics, I wrote [or extracted I guess] the following c code and linker script. The resulting binary works and the led blinks without issue. However, while debuging, I found that the bss_start and bss_end symbols both hold a value of 0x20000000. The bss zero fuction is basically skipped. When doing an objdump, I can see
20000000 g O .bss 00000004 timer_delayCount
So the section is 4 bytes long and located at 0x20000000 correclty. And at least bss_start is pointed to the correct memory address. However, bss_end should be pointed at 0x20000004 [I think], not 0x20000000.
I would like to now whey the bss_end symbol is not incremeting after the *(.bss), which I think contains four bytes, is placed.
The micro controller is stm32f103rb, a cortex-m3 chip. I am compling with ARM GNU GCC
My main.c file:
#define _stackInit 0x20005000U
volatile unsigned int * const RCC_APB2ENR = (unsigned int *)0x40021018;
volatile unsigned int * const GPIOA_CRL = (unsigned int *)0x40010800;
volatile unsigned int * const GPIOA_BSR = (unsigned int *)0x40010810;
volatile unsigned int * const GPIOA_BRR = (unsigned int *)0x40010814;
volatile unsigned int * const STK_CTRL = (unsigned int *)0xE000E010;
volatile unsigned int * const STK_LOAD = (unsigned int *)0xE000E014;
volatile unsigned int * const STK_VAL = (unsigned int *)0xE000E018;
volatile unsigned int timer_delayCount;
int main() {
// enable GIOA clock and set PB5 to output
*RCC_APB2ENR |= (unsigned int)0x00000004;
*GPIOA_CRL = (unsigned int)0x44244444;
// COnfigure Systick Timer for 1 millisecond interrupts
*STK_VAL = 0x00;
*STK_LOAD = 7999U; //tick every 1 ms
*STK_CTRL = 0x07;
while (1){
int c, d;
timer_delayCount = 500u;
while(timer_delayCount != 0u);
*GPIOA_BSR = 0x20;
timer_delayCount = 500u;
while(timer_delayCount != 0u);
*GPIOA_BRR = 0x20;
}
}
// Begin and End addresses for the .bss section. Symbols defined in linker script
extern unsigned int __bss_start__;
extern unsigned int __bss_end__;
void __attribute__ ((section(".after_vectors"))) Reset_Handler (void)
{
// Initialize bss section by iterating and clearing word by word.
// It is assumed that the pointers are word aligned.
unsigned int *p = &__bss_start__;
while (p < &__bss_end__) {
*p++ = 0;
}
main();
}
void SysTick_Handler() {
// Decrement to coutner to zero.
if (timer_delayCount != 0u)
{
--timer_delayCount;
}
}
void __attribute__ ((section(".after_vectors"))) Default_Handler(void)
{
while(1);
}
void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) NMI_Handler(void);
void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) HardFault_Handler(void);
void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) MemManage_Handler(void);
void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) BusFault_Handler(void);
void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) UsageFault_Handler(void);
void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) SVC_Handler(void);
void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) DebugMon_Handler(void);
void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) PendSV_Handler(void);
typedef void(* const pHandler)(void);
// The vector table.
// The linker script to place at correct location in memory.
__attribute__ ((section(".isr_vector"),used)) pHandler __isr_vectors[] =
{
//core exceptions
(pHandler)_stackInit, // Inital Stack Pointer
Reset_Handler, // reset handler
NMI_Handler, // NMI handler
HardFault_Handler, // hard fault handler
MemManage_Handler, // MPU fault handler
BusFault_Handler, // bus fault handler
UsageFault_Handler, // usage fault handler
0x00, // reserved
0x00, // reserved
0x00, // reserved
0x00, // reserved
SVC_Handler, // SVCall handler
DebugMon_Handler, // debug monitor handler
0x00, // reserved
PendSV_Handler, // PendSV handler
SysTick_Handler, // systick handler
};
My linker file:
ENTRY(Reset_Handler)
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K
}
SECTIONS
{
.text : {
*(.isr_vector)
*(.after_vectors)
*(.text)
} > FLASH
.bss : {
__bss_start__ = .; /* symbol for c code to initialize bss section */
*(.bss)
__bss_end__ = .; /* symbol for c code to initialize bss section */
} > RAM
}
Compiler commands:
/opt/gcc-arm-none-eabi-7-2017-q4-major/bin/arm-none-eabi-gcc -c -mcpu=cortex-m3 -mthumb -g blinky-interrupt.c -o blinky-interrupt.o
/opt/gcc-arm-none-eabi-7-2017-q4-major/bin/arm-none-eabi-ld -T blinky-interrupt.ld blinky-interrupt.o -o blinky-interrupt.elf
That's because an unitialized variable goes into COMMON instead of .bss. Had you initialized it with 0, then it'd go into .bss.
Look at the linker map file (if you don't have it, let the linker generate it with -Map), you should see something like this
.bss 0x0000000020000000 0x4 load address 0x00000000080000e0
0x0000000020000000 . = ALIGN (0x4)
0x0000000020000000 __bss_start__ = .
*(.bss)
0x0000000020000000 . = ALIGN (0x4)
0x0000000020000000 __bss_end__ = .
COMMON 0x0000000020000000 0x4 ./src/app/main.o
0x0000000020000000 timer_delayCount
When COMMON is not specified in the linker script, the linker puts it somewhere else, probably dumps it at the end.
A more complete linker script has the following .bss section
.bss :
{
. = ALIGN(4);
__bss_start__ = .;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} >RAM
Then the COMMON section would go between __bss_start__ and __bss_end__.
FYI, *(.bss*) is there to cover the -fdata-sections option, when each variable gets its own segment, so that the linker can drop unreferenced ones.
User contributions licensed under CC BY-SA 3.0