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  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
gcc.ld define the same memory regions, which I needed to adapt the the STM32F4-Discovery board. The contents of
/* Linker script to configure memory regions.
* Need modifying for a specific board.
* FLASH.ORIGIN: starting address of flash
* FLASH.LENGTH: length of flash
* RAM.ORIGIN: starting address of RAM bank 0
* RAM.LENGTH: length of RAM bank 0
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 0x00100000 /* 1M */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x20000 /* 128K */
This corresponds to the board’s physical memory map, as specified in  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:
/* Linker script to place sections and symbol values. Should be used together
* with other linker script that defines memory regions FLASH and RAM.
* It references following symbols, which must be defined in code:
* Reset_Handler : Entry of reset handler
* It defines following symbols, which code can use without definition:
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 , “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 , “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:
/* .ctors */
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
/* .dtors */
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
} > FLASH
.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 .
The first of these is:
If we start by ignoring
KEEP we see, according to , 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.
KEEP, according to : “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  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)…