Zettelkasten
Wonko's collection of notes

Posted on , updated on , in Programming, tagged with ,

Explicitly Place All Sections When Linking With GNU LD

The Problem

If you place your sections manually, you sometimes forget one and the gnu linker places it automatically. Having data or code linked in the wrong place often works for a while or when not doing in depth tests, but can cause your system to have very odd behavior that is hard to pinpoint. If you are writing a linker script, it is best to place everything explicitly and get notified about every unplaced section.

The Solution

A way to produce such a warning, is to define a catch all section and using ASSERT() to test, that it is empty:

1memories {...}
2sections {
3
4 .text : {
5 *(.text)
6 } > rom
7 .data : {
8 *(.data)
9 } > ram
10
11 .debug_macro : { *(.debug_macro) }
12 .debug_line : { *(.debug_line) }
13 .debug_str : { *(.debug_str) }
14 .debug_frame : { *(.debug_frame) }
15 .debug_info : { *(.debug_info) }
16 .debug_abbrev : { *(.debug_abbrev) }
17 .debug_aranges : { *(.debug_aranges) }
18 .debug_ranges : { *(.debug_ranges) }
19 .debug_loc : { *(.debug_loc) }
20 .comment : { *(.comment) }
21 .version_info : { *(.version_info) }
22
23 .unplaced : {
24 __unplaced_start = . ;
25 *(*)
26 __unplaced_end = . ;
27
28 ASSERT(__unplaced_start == __unplaced_end, "ASSERT(.unplaced empty) failed");
29 /*
30 * If the assert failed, check the .unplaced section in map file and
31 * manually place anything that was placed there.
32 */
33 }
34}

This part of the linker script places .data in ram, a bunch of debug stuff at its default location and anything else will end up in .unplaced and trigger an error like this:

ld: error: ASSERT(.unplaced empty) failed

If you look at the map file:

.unplaced       memory region -> /DISCARD/
                0x00000000    0x1234
                0x00000000                __unplaced_start = .
 *(*)
 .text.hello_world
                0x00000000        0x32 ./lib/lib.a(test.o)
                0x00000034                __unplaced_end = .
                0x00000000                ASSERT ((__unplaced_start == __unplaced_end), ...)

This tells you, that there is a section .text.hello_world which the compiler created when compiling test.o which is part of lib/lib.a and you did not place it explicitly. The reason is, that we captured .text but not .text.*. The issue was introduced when adding -ffunction-sections to the compiler options in order to reduce ROM usage (by also passing -gc-sections to the linker). This changes the section where code is placed, so it ended up in the wrong place.

Depending on your compilers settings, a bunch of different debug sections will have to be added to the script. I have not found a more compact way which still allows debugging. Please write to me if you know the way!