Fork me on GitHub

7. Linker Script File

As mentioned in the previous section, section merging and placement is done by the linker. The programmer can control how the sections are merged, and at what locations they are placed in memory through a linker script file. A very simple linker script file, is shown below.

Listing 6. Basic linker script

SECTIONS { ❶
        . = 0x00000000; ❷
        .text : { ❸
                abc.o (.text);
                def.o (.text);
        } ❹
}

The SECTIONS command is the most important linker command, it specifies how the sections are to be merged and at what location they are to be placed.

Within the block following the SECTIONS command, the . (period) represents the location counter. The location is always initialised to 0x0. It can be modified by assigning a new value to it. Setting the value to 0x0 at the beginning is superfluous.

This part of the script specifies that, the .text section from the input files abc.o and def.o should go to the .text section of the output file.

The linker script can be further simplified and generalised by using the wild card character * instead of individually specifying the file names.

Listing 7. Wildcard in linker scripts

SECTIONS {
        . = 0x00000000;
        .text : { * (.text); }
}

If the program contains both .text and .data sections, the .data section merging and location can be specified as shown below.

Listing 8. Multiple sections in linker scripts

SECTIONS {
         . = 0x00000000;
         .text : { * (.text); }

         . = 0x00000400;
         .data : { * (.data); }
}

Here, the .text section is located at 0x0 and .data is located at 0x400. Note that, if the location counter is not assigned a different value, the .text and .data sections will be located at adjacent memory locations.

7.1. Linker Script Example

To demonstrate the use of linker scripts, we will use the linker script shown in Listing 8, “Multiple sections in linker scripts” to control the placement of a program’s .text and .data section. We will use a slightly modified version of the sum of array program for this purpose. The code is shown below.

        .data
arr:    .byte 10, 20, 25        @ Read-only array of bytes
eoa:                            @ Address of end of array + 1

        .text
start:
        ldr   r0, =eoa          @ r0 = &eoa
        ldr   r1, =arr          @ r1 = &arr
        mov   r3, #0            @ r3 = 0
loop:   ldrb  r2, [r1], #1      @ r2 = *r1++
        add   r3, r2, r3        @ r3 += r2
        cmp   r1, r0            @ if (r1 != r2)
        bne   loop              @    goto loop
stop:   b stop

The only change here is that the array is now in the .data section. Also note that the nasty branch instruction to skip over the data is also not required, since the linker script will place the .text section and .data section appropriately. As a result, statements can be placed in the program, in any convenient way, and the linker script will take care of placing the sections correctly in memory.

When the program is linked, the linker script is passed as an input to the linker, as shown in the following command.

$ arm-none-eabi-as -o sum-data.o sum-data.s
$ arm-none-eabi-ld -T sum-data.lds -o sum-data.elf sum-data.o

The option -T sum-data.lds specifies that sum-data.lds is to be used as the linker script. Dumping the symbol table, will provide an insight into how the sections are placed in memory.

$ arm-none-eabi-nm -n sum-data.elf
00000000 t start
0000000c t loop
0000001c t stop
00000400 d arr
00000403 d eoa

From the symbol table it is obvious that the .text is placed starting from address 0x0 and .data section is placed starting from address 0x400.