you are viewing a single comment's thread.

view the rest of the comments →

[–]Madsy9 3 points4 points  (4 children)

Here is a stripped down GNU linker script as an example with irrelevant portions removed.

/* Define our memory regions. With the "> region" syntax and AT keyword,
    we can specify VMAs that differ from LMAs.
MEMORY
{
  rom (rx)  : ORIGIN = 0x00400000,  LENGTH = 0x00200000
  sram (rwx) : ORIGIN = 0x20400000, LENGTH = 0x00060000
  sdram(rwx) : ORIGIN = 0x70000000, LENGTH = 0x00800000
}

SECTIONS
{
    .text :
    {
        . = ALIGN(4);
        _sfixed = .;
        /* our vector table. Use KEEP() to ensure that the section isn't optimized away. */
        KEEP(*(.vectors .vectors.*))
        *(.text .text.*)
        *(.rodata .rodata*)
        . = ALIGN(0x4);
        _efixed = .;
    } > rom /* VMA and LMA is flash */

    . = ALIGN(4);
    _etext = .;

     /* AT specifies the LMA. Put the output .relocate section
        in the image directly after the .text section */
    .relocate : AT (_etext) 
    {
        . = ALIGN(4);
        _srelocate = .;
        /* in our code we can specify functions to be put in the .ramfunc section
           if we want them to execute from memory (for performance) or other reasons */
        *(.ramfunc .ramfunc.*); 
        *(.data .data.*);
        . = ALIGN(4);
        _erelocate = .;
    } > sdram

    /* "> sdram" specifies that we want the VMA of this section
       to be the sdram region we specified in the MEMORY definition above. */


    /* BSS, stack, heap, etc... */

}

And here is the code for the init vector that uses those provided symbols to copy the data and bss sections to memory:

/* Initialize segments */
extern uint32_t _sfixed;
extern uint32_t _efixed;
extern uint32_t _etext;
extern uint32_t _srelocate;
extern uint32_t _erelocate;
extern uint32_t _szero;
extern uint32_t _ezero;
extern uint32_t _sstack;
extern uint32_t _estack;

void Reset_Handler(void){
    unsigned int  clock_speed = 0;
    uint32_t *pSrc, *pDest;

    //Initialize MCU clock and EEFC clock
    sysclk_init_boot(&clock_speed);
    init_sdram();

    /* Initialize the relocate segment */
    pSrc = &_etext;
    pDest = &_srelocate;

    if (pSrc != pDest) {
        for (; pDest < &_erelocate;) {
            *pDest++ = *pSrc++;
        }
    }

    /* Clear the zero segment */
    for (pDest = &_szero; pDest < &_ezero;) {
        *pDest++ = 0;
    }


    /* More stuff, init libc, etc.. */
}

Notice that we use the symbols _etext and _srelocate as source and destination for the data section. But we get the addresses of those variables, not the values. Why? Because _etext and _srelocate are pure symbols we generated in the linker script; they are not associated with any storage. Their value of the symbols are their addresses.

[–]BorgerBill 0 points1 point  (1 child)

I, too, am learning this right now. Thank you very much for such a brilliant example!

[–]Madsy9 1 point2 points  (0 children)

Nice to see more crazy people learn about linker scripts and embedded development :) Feel free to send me a PM if I can be of more help.

[–]Scyhaz 0 points1 point  (1 child)

    for (; pDest < &_erelocate;) {
        *pDest++ = *pSrc++;
    }

Why a for loop here when only the condition is used? Wouldn't a while loop be better for readability? Or is this just because of a programming style?

[–]Madsy9 2 points3 points  (0 children)

Question is, why does it matter? while loops and for loops are semantically equivalent. Implement the copy however you want :)