arm-none-eabi global initialized variable incorrect value

382 views Asked by At

I try to make some example for stm32f334 (just led blink). I had a problem with the linker when I want to constrain using .data section (by use initialized global variable) I got a problem. Global variable got incorrect value!

This is my code:

startup.s:

    .global _start
    .thumb_func
    _start:
    .word 0x20003000
    .word reset
    .word hang
    .word hang
    .word hang
    .word hang
    .word hang
    .word hang
    .word hang
    .word hang
    .word hang
    .word hang
    .word hang
    .word hang
    .word hang
    .word hang

    .thumb_func
    reset:
        bl main
        b hang
    .thumb_func
    hang:   b .

blink.c:

    #define RCCBASE 0x40021000
    #define GPIOBBASE 0x48000400

    static int wymuszenie_bss;
    int wymuszenie_data = GPIOBBASE;

    int main ( void )
    {
        unsigned int* ptr;

        wymuszenie_bss = 0x40021000;

        ptr = (unsigned int*)(wymuszenie_bss+0x14);
        *ptr |= 1<<18; //enable port B;
        //moder
        ptr = (unsigned int*)(wymuszenie_data+0x00);
        *ptr &= ~(3<<24); //PB12
        *ptr |= 1<<24; //PB12
        //OTYPER
        ptr = (unsigned int*)(wymuszenie_data+0x04);
        *ptr &= ~(1<<6); //PB12
        //ospeedr
        ptr = (unsigned int*)(GPIOBBASE+0x08);
        *ptr |= ~(3<<24); //PB12
        //pupdr
        ptr = (unsigned int*)(GPIOBBASE+0x0C);
        *ptr &= ~(3<<24); //PB12

        while(1)
        {
            ptr = (unsigned int*)(GPIOBBASE+0x18);
            *ptr = (1<<12)<<0;
            for(int ra=0;ra<400000;ra++) asm("NOP");;
            ptr = (unsigned int*)(GPIOBBASE+0x18);
            *ptr = (1<<12)<<16;
            for(int ra=0;ra<400000;ra++) asm("NOP");;
        }
        return(0);
    }

linker script:

    MEMORY
    {
        flash : ORIGIN = 0x08000000, LENGTH = 0x1000
        SRAM  : ORIGIN = 0x20000000, LENGTH = 12K
    }

    SECTIONS
    {
        .text : 
        {
            __text_start__ = .;
            *(.text) 
            startup.o (.text);
            blink.o (.text);
            __text_end__ = .;
        } > flash

        .data :
        {
            __data_start__ = .;
            KEEP (*(.data))
            KEEP (*(.data*))
            __data_end__ = .;
        } > SRAM AT > flash 

        .bss : 
        {
             __bss_start__ = .;
            *(.bss)
            __bss_end__ = .;
        } > SRAM
    }

makefile:

    ARMGNU = arm-none-eabi

    gcc : blink.bin 

    all : gcc

    clean:
        rm -f *.bin
        rm -f *.o
        rm -f *.elf
        rm -f *.list
        rm -f *.bc
        rm -f *.opt.s
        rm -f *.norm.s
        rm -f *.nm

    startup.o : startup.s
        $(ARMGNU)-as --warn --fatal-warnings -mcpu=cortex-m4 startup.s -o startup.o

    blink.o : blink.c
        $(ARMGNU)-gcc -Wall -Wint-to-pointer-cast -g -c -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 -c blink.c -o blink.o

    blink.bin : startup.o blink.o
        $(ARMGNU)-ld -o blink.elf -T startup.ld startup.o blink.o
        $(ARMGNU)-objdump -D blink.elf > blink.list
        $(ARMGNU)-nm blink.elf > blink.nm
        $(ARMGNU)-objcopy blink.elf blink.bin -O binary

on the .list I see correct value and adress:

Disassembly of section .data:

20000000 <wymuszenie_data>:
20000000:   48000400    stmdami r0, {sl}

Disassembly of section .bss:

20000004 <__bss_start__>:
20000004:   00000000    andeq   r0, r0, r0

but when I debug code value of variable "wymuszenie_data" is corrupted (0x2e006816).

I really don't know why there is incorrect global variable value.

Best regards, Marcin

1

There are 1 answers

1
followed Monica to Codidact On BEST ANSWER

This line

} > SRAM AT > flash

tells the linker that sections in the preceding block should be linked as if they were placed in RAM, but actually put to a different address in flash. The code looks for the data in RAM, but it's not there yet. You should arrange it to be copied before calling main(). As it is a bare metal embedded platform, there is no operating system loader to perform this task.

First, create a symbol with the destination address in the linker script.

__data_destination__ = LOADADDR(.data);

then copy (&__data_end__ - &__data_start__) bytes from &__data_start__ to &__data_destination__ before calling main().

You should as well clear .bss, i.e. fill it with zeroes to avoid the next surprise.

If you are linking with a C library, you can use memcpy() and memset(), otherwise you must write you own code in C or assembly.