BLE application with nRF51822 – The linker script

linker script c code
In embedded C langage development, you have to convert the humain readable file into a binary file that could be executed on microcontroller. The compiling step allows to convert this C code into a machine understandable code. But that’s not enough to get this code running on a microcontroller because the memory mapping is still not done. That’s why it’s mandatory to write the linker script in order to help you to place functions and variables at the right place in memory.

  • A quick look of the nRF51822′ memory mapping:

nrf51 memory mapping

Regarding the memory mapping above, code start in flash at 0x00000000 and end at start + 256ko (nRF51 flash size memory), this means functions could be placed between addresses 0x00000000 and 0x00040000 (hex values). The RAM memory is place at 0x20000000 and end at 0x20000000 + 16ko (nRF51 RAM size memory), this means variables could be placed between addresses 0x20000000 and 0x20002000.

Basically with GCC there are three sections. The first one is called .text, and contains functions. The second one is called .data and contains global and static variables initialized by application, and values are placed at compilation in flash. This means that during boot these variables should init from FLASH to RAM. The third one is called .bss and contains non initialized global and static variables. This means that during boot these variables should be explicitly set to zero. So now let’s see how we can specifies these addresses to linker script.

  • First we are gonna create the linker script file:
        $ cd nrf51</code>
        $ mkdir linker_script</code>
        $ cd linker_script</code>
        $ touch nrf51_ld_script.ld</code>
  • Once the file is created, open it and add the followingnRF51822′ memory layout description:
             RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 16k
             FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256k

The first memory region is the RAM with orgin 0x20000000 and length 16ko, x parameter means this region can execute function, r and w parameters mean that we can read and write in it. The second region is the FLASH with origin 0x00000000 and length 256ko, r parameter means that we can read in it and x means that we can execute function in it.

We also have to set manually the stack and heap sizes. To be short, the stack in the RAM area dedicated for allocated memory during function called (local variables), while heap RAM area is dedicated for dynamic memory allocation.

        _end_of_stack = (0x20000000 + 16k);/*RAM start address+16k*/
        _stack_size = 2k;
        _heap_size = 2k;
  • Once we specified the memory region we have to detail section regions:

Between these brackets we can declare all sections needed by application. The first section we have to declare is called .isr_vector and should be placed at the beginning of flash memory since the first instruction executed by ARM Cortex M0 is the Reset handler.

        .isr_vector : <
            KEEP(*(.isr_vector)) /* vector table */
            . = ALIGN(4);
        } > FLASH</code>

KEEP command allow to tell ldscript that this section should not be eliminated (by garbage collection for example). ALIGN(4) command is used to tell to ldscript that this section is 4 bytes aligned. The >FLASH symbol means the section have to be placed in Flash memory.

  • After interrupt vectors we can directly place functions and constants located in flash:
        .text :
        { /*code and constants*/
            . = ALIGN(4);
            *(.text) /*.text sections (code)*/
            *(.text*) /*.text sections (code) */
            *(.rodata) /*.rodata sections (constants, strings, etc.)*/
            *(.rodata*) /*.rodata sections (constants, strings, etc.)*/
            . = ALIGN(4);
            _etext = .;/*Used by .data, to place initialized variable RAM values*/
        } > FLASH
  • We can now defined the section used to store uninitialized data in RAM:
        .bss :
        { /* code and constants */
            . = ALIGN(4);
            _sbss = .;/*Used by the startup code to initialize the .bss section*/
            . = ALIGN(4);
            _ebss = .;/*Used by the startup code to initialize the .bss section*/
        } > RAM
  • The .data section contains initialized data. This section is located in RAM but initialized values are placed in FLASH so at startup, the processor has to relocated these values in RAM.
        .data : AT ( _etext )
            . = ALIGN(4);
            _sdata = .;/* Used by the startup in order to initialize the .data */
            . = ALIGN(4);
            _edata = .;/* Used by the startup in order to initialize the .data */
        } > RAM
  • We can also add a DISCARD section in order to avoid some informations generated by standard librairies:
        /DISCARD/ :
            libc.a ( * )
            libm.a ( * )
            libgcc.a ( * )

After this post you should have a basic and functional linker script. This one works fine with ARM cortex M0 core and especially with the nRF51822 processor. Just keep in mind that with slight modifications this script can work with pretty much all ARM Cortex M family since the boot process is the same. If you need more informations about linker script I recommend you the following web site:

If you want to learn more about the Cortex M0 boot up process:

You can retrieve linker script file directly on my github repo:


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s