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)…

Jul 01

While_one project on the STM32F4-Discovery with a GNU ARM Eclipse template

In our previous post, we reported how easy it was to produce and run a blinking program on the STM32F4-Discovery with Eclipse IDE for C/C++ Developers, GNU ARM Eclipse, GNU Tools for ARM Embedded Processors and OpenOCD. It did however, leave me an impression that the project and executable were quite large. Let’s check how it really is.
Who says that “Hello world” is a simple program? It certainly isn’t in bare metal programming. Even blinking a LED is too advanced for our purpose which is to study in details the structure of the STM32F4xx C/C++ project template in GNU ARM Eclipse:

  • Source code
  • Makefile
  • Map file
  • In a lesser extent or just for fun, processor instruction level

To reach that purpose, we need no more no less than the while_one program:

In the Eclipse setup described in our previous post, we create a C project called while_one. It will be an STM32F4xx C/C++ Project, with the Cross ARM GCC toolchain.
Under “Target processor settings”, we choose an STM32F407xx with a Flash size of 1024 KB. The content is “empty”, we use no POSIX system calls and no trace output. We check “some” and “most” warnings, and leave the other settings as they are. We leave the standard folders as they are. We select the Debug and the Release configurations. We use the tool chain “GNU Tools for ARM Embedded Processors (arm-none-eabi-gcc)” and set the correct path to the bin folder (where arm_none_eabi_gcc is located).
The generated code is just what we need:

I have however a number of issues with the resulting project:

  • It does not run on target! More precisely, when trying to step over from the first row in main(), the OpenOCD console ends in a "Info : halted: PC: 0x08000cb4" forever loop. This is in contrast to the blinky program, that just runs as expected. Since the main() function is trivial, the problem must be related to the initialization that happens before main() is invoked.
  • The project includes 10 files from the STM32CubeF4 HAL. I have a hard time believing that while_one needs some much hardware support.
  • The project includes some files, for example _initialize_hardware.c, that are part of “the µOS++ III distribution”. Firstly, I find a bit strange to have some files included from a project that I did not intend to use (at least not right now). Secondly, just to take one example, __initialize_hardware() only enables the FPU, which is also done by SystemInit() in system_stm32f4xx.c, provided by STM32CubeF4 as specified in CMSIS. In other words, the template provides code that is redundant with what ST provides, that also is included in the project.

The observations above are pretty much enough for me to avoid using the STM32F4xx C/C++ Project template from GNU ARM Eclipse. The rest of it, GNU ARM C/C++ Cross Compiler Support and GNU ARM OpenOCD Debugging support still seems interesting, however. My next step will probably be to keep these two plugins, to remove GNU ARM C/C++ STM32Fx Project Templates from Eclipse, and to rebuild the while_one project from STM32CubeF4 instead.