Jan 17

Studying while_one linker script on the STM32F4-Discovery

In my previous article, I presented a quite detailed analysis of the binary produced by compiling the “minimal” code sample from GNU Tools for ARM Embedded Processors.
I concluded that in order to have a complete interpretation, one needed to analyze the source code, more precisely the linker script and the start-up code (in assembly). Note that the “payload”-code for the while_one program is, as its name implies, trivial. The linker script and the start-up code are, on the other hand, not trivial, and I will be analyzing the linker script in the rest of this article.
In order to do that, we need to consult [3] ld documentation (part of GNU Binutils documentation).
I might as well start by commenting on the linker script’s name: nokeep.ld. I could not find a clear comment about that in the code, but comparing the file with gcc.ld, which is used for most samples, shows that the LD command KEEP is used far many more times in gcc.ld that in nokeep.ld. We will come back to that command later on.

The linker script starts by including another linker script. This is actually a change that I made, since nokeep.ld and gcc.ld define the same memory regions, which I needed to adapt the the STM32F4-Discovery board. The contents of mem.ld are:

This corresponds to the board’s physical memory map, as specified in [2] STM32F407VG data-sheet.
We will see later on how these memory areas are referred to in the rest of the script.
Next, the linker script includes some introductory comments worth reading:

The “other linker script that defines memory regions FLASH and RAM” is the one included above.
We can see that the rest of the code is supposed to define the symbol Reset_Handler. We will see in the next article that the start-up code (in assembly) does that.
The next row in the linker script is:

According to [3], “The first instruction to execute in a program is called the entry point. You can use the ENTRY linker script command to set the entry point. The argument is a symbol name”. As described in my previous article, Reset_Handler is effectively the start of the first instructions that get executed by the processor.
The rest of the linker script is a single high level block:

According to [3], “The SECTIONS command tells the linker how to map input sections into output sections, and how to place the output sections in memory”. The first output section is:

.text is the name of the output section. As a software engineer, I expect the text section to hold some executable code. We see that it is placed in the FLASH memory region, which seems logical.
Within the curly brackets come some output section commands according to [3].
The first of these is:

If we start by ignoring KEEP we see, according to [3], a fairly typical input section specification that tells the linker to output the .isr_vector sections from all object files (to the .text output section in flash). As we will see in my next article, there is only one such section, defined in the start-up code (in assembly). It is as discussed in my previous article, the vector table.
As for KEEP, according to [3]: “When link-time garbage collection is in use (`–gc-sections’), it is often useful to mark sections that should not be eliminated. This is accomplished by surrounding an input section’s wildcard entry with KEEP(), as in KEEP(*(.init)) or KEEP(SORT_BY_NAME(*)(.ctors))”.
A quick look at makefile.conf and our Makefile will confirm that we do indeed make use of --gc-sections (to reduce code size). The presence of the vector table is required for Cortex-M4 (see [1] Cortex-M4 Devices Generic User Guide), but the linker does not know that. KEEP is how we force the linker to output that input section anyway.
Too be continued (maybe)…