cgcclinkerarmembedded

GCC Linker, bss section symbols equal zero length


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

Solution

  • 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.