Search code examples
gcclinkerarmgnulinker-scripts

Bare Metal ARM linker script: How to relocate initialized data from ROM to RAM?


I would like to put initial values of global variables in ROM section, and then, relocate their addresses and copy their values to RAM (so it can be edited by the program).

My linker script SECTIONS:

/* code */
.text :
{
    __text_start = .;
    *(.text)
    __text_end = .;
} > ROM



/* Initialized global and static variables */
.data : AT ( __text_end )
{ 
    __data_start = . ; 
    *(.data); 
    __data_end = . ;  
} > RAM

My Boot ROM code:

int global_var = 0xAAA;

void main()
{
    global_var = 0xBBB;
}

The problem: The initial value 0xAAA is located at RAM address in the compiled bin file instead of ROM

How can I define initial .data values in ROM, and then relocate their address to RAM?

Thank you for your help


Solution

  • int global_var = 0xAAA;
    int global_too;
    void main()
    {
        global_var = 0xBBB;
    }
    
    MEMORY
    {
        bob : ORIGIN = 0x08000000, LENGTH = 0x100
        ted : ORIGIN = 0x20000000, LENGTH = 0x100
    }
    SECTIONS
    {
        .text   : { *(.text*)   } > bob
        .rodata : { *(.rodata*) } > bob
        .data   : { *(.data*)   } > ted AT > bob
        .bss    : { *(.bss*)    } > ted AT > bob
    }
    
    Disassembly of section .text:
    
    08000000 <reset-0x8>:
     8000000:   20000400
     8000004:   08000009
    
    08000008 <reset>:
     8000008:   f000 f802   bl  8000010 <main>
     800000c:   e7fe        b.n 800000c <reset+0x4>
        ...
    
    08000010 <main>:
     8000010:   4b01        ldr r3, [pc, #4]    ; (8000018 <main+0x8>)
     8000012:   4a02        ldr r2, [pc, #8]    ; (800001c <main+0xc>)
     8000014:   601a        str r2, [r3, #0]
     8000016:   4770        bx  lr
     8000018:   20000000
     800001c:   00000bbb
    
    Disassembly of section .data:
    
    20000000 <global_var>:
    20000000:   00000aaa
    
    Disassembly of section .bss:
    
    20000004 <global_too>:
    20000004:   00000000
    

    and if you really want/need .data or .bss then continue and add something like this

    MEMORY
    {
        bob : ORIGIN = 0x08000000, LENGTH = 0x100
        ted : ORIGIN = 0x20000000, LENGTH = 0x100
    }
    SECTIONS
    {
        .text   : { *(.text*)   } > bob
        .rodata : { *(.rodata*) } > bob
        __data_rom_start__ = .;
        .data   : 
            { 
                __data_start__ = .;
                *(.data*)   
                __data_end__ = .;
            } > ted AT > bob
        .bss    : 
            { 
                __bss_start__ = .;
                *(.bss*)    
                __bss_end__ = .;
            } > ted AT > bob
    }
    

    In bootstrap to double check

    .align
    .word __data_rom_start__
    .word __data_start__
    .word __data_end__
    .word __bss_start__
    .word __bss_end__
    
    Disassembly of section .text:
    
    08000000 <reset-0x8>:
     8000000:   20000400
     8000004:   08000009
    
    08000008 <reset>:
     8000008:   f000 f80c   bl  8000024 <main>
     800000c:   e7fe        b.n 800000c <reset+0x4>
     800000e:   46c0        nop         ; (mov r8, r8)
     8000010:   08000034
     8000014:   20000000
     8000018:   20000004
     800001c:   20000004
     8000020:   20000008
    
    08000024 <main>:
     8000024:   4b01        ldr r3, [pc, #4]    ; (800002c <main+0x8>)
     8000026:   4a02        ldr r2, [pc, #8]    ; (8000030 <main+0xc>)
     8000028:   601a        str r2, [r3, #0]
     800002a:   4770        bx  lr
     800002c:   20000000
     8000030:   00000bbb
    
    Disassembly of section .data:
    
    20000000 <global_var>:
    20000000:   00000aaa
    
    Disassembly of section .bss:
    
    20000004 <global_too>:
    20000004:   00000000
    
     8000010:   08000034
     8000014:   20000000
     8000018:   20000004
     800001c:   20000004
     8000020:   20000008
    
    arm-none-eabi-nm so.elf 
    20000008 B __bss_end__
    20000004 B __bss_start__
    20000004 D __data_end__
    08000034 T __data_rom_start__
    20000000 D __data_start__
    20000004 B global_too
    20000000 D global_var
    08000024 T main
    08000008 t reset
    
    hexdump -C so.bin
    00000000  00 04 00 20 09 00 00 08  00 f0 0c f8 fe e7 c0 46  |... ...........F|
    00000010  34 00 00 08 00 00 00 20  04 00 00 20 04 00 00 20  |4...... ... ... |
    00000020  08 00 00 20 01 4b 02 4a  1a 60 70 47 00 00 00 20  |... .K.J.`pG... |
    00000030  bb 0b 00 00 aa 0a 00 00                           |........|
    00000038
    

    And you can see the 0x00000aaa at offset 0x34 in the file.

    Then fill in the bootstrap to copy .data from flash to ram and to zero .bss using these tool generated addresses.

    I have no use for .data and make no assumptions about .bss so mine looks like this

    reset:
        bl main
        b .
    MEMORY
    {
        bob : ORIGIN = 0x08000000, LENGTH = 0x100
        ted : ORIGIN = 0x20000000, LENGTH = 0x100
    }
    SECTIONS
    {
        .text   : { *(.text*)   } > bob
        .rodata : { *(.rodata*) } > bob
        .bss    : { *(.bss*)    } > ted
    }
    

    YMMV

    Fortunately or unfortunately gnu ld provides more than one way to solve this problem as you have started to demonstrate with your linker script. At the same time gnu ld makes it a PITA to figure this out, very sensitive to where/how to place the something = .; You will see various different examples yet they all seem work.

    And also note these names do not matter either:

    MEMORY
    {
        bob : ORIGIN = 0x08000000, LENGTH = 0x100
        ted : ORIGIN = 0x20000000, LENGTH = 0x100
    }
    SECTIONS
    {
        .hello : { *(.text*)    } > bob
        .world : { *(.data*)    } > ted AT > bob
    }
    
    
    Disassembly of section .hello:
    
    08000000 <reset-0x8>:
     8000000:   20000400
     8000004:   08000009
    
    08000008 <reset>:
     8000008:   f000 f802   bl  8000010 <main>
     800000c:   e7fe        b.n 800000c <reset+0x4>
        ...
    
    08000010 <main>:
     8000010:   4b01        ldr r3, [pc, #4]    ; (8000018 <main+0x8>)
     8000012:   4a02        ldr r2, [pc, #8]    ; (800001c <main+0xc>)
     8000014:   601a        str r2, [r3, #0]
     8000016:   4770        bx  lr
     8000018:   20000000
     800001c:   00000bbb
    
    Disassembly of section .world:
    
    20000000 <global_var>:
    20000000:   00000aaa
    
    
    hexdump -C so.bin
    00000000  00 04 00 20 09 00 00 08  00 f0 02 f8 fe e7 00 00  |... ............|
    00000010  01 4b 02 4a 1a 60 70 47  00 00 00 20 bb 0b 00 00  |.K.J.`pG... ....|
    00000020  aa 0a 00 00                                       |....|
    00000024
    

    Still produces the desired binary.

    short answer:

    Try this

    } > RAM AT > ROM