The cross-compiler
The cross-compiler distributed with a toolchain is in fact a variant of GCC, with the backend configured to build object files that contain machine code for a specific architecture. The output of the compilation are object files containing symbols that can only be interpreted by the specific target. arm-none-eabi-gcc is able to compile C code into machine instructions and CPU optimizations for several different targets.
The GCC backend for the ARM architecture supports a number of machine-specific options, to select the correct instruction set for the CPU, and the machine-specific optimization parameters.
The following table lists some of the ARM-specific machine options available on the GCC backend as -m flags:
Option | Description |
-marm / -mthumb | Selects ARM or Thumb instruction set |
-march=name | Selects the architecture name within the family (for example, armv7) |
-mtune=name | Selects the CPU name for which GCC is optimized (for example, cortex-m3) |
-mcpu=name | Selects the CPU name for optimizations and architecture (can be used instead of -march and -mtune) |
To compile code that is compatible with a generic ARM Cortex M4, the -mthumb and -mcpu=cortex-m4 options must be specified every time the compiler is invoked:
$ arm-none-eabi-gcc -c test.c -mthumb -mcpu=cortex-m4
The test.o file that is the result of this compile step is very different from the one that can be compiled, from the same source, using the host gcc. The difference can be better appreciated if instead of the two object files, the intermediate assembly code is compared. The compiler is in fact capable of creating intermediate assembly code files, instead of compiled and assembled objects, when it is invoked with the -S option.
Similarly to the host GCC compiler, there are different levels of possible optimization. In some cases, it makes sense to activate the size optimization to generate smaller object files. It is preferable, though, that the non-optimized image can fit the flash during the development, in order to facilitate the debugging procedures, as optimized code flow is more difficult to follow when the compiler may change the order of the execution of the code and hide away the content of some variables. The optimization parameter can be provided at the command line to select the desired optimization level:
Option | Effect |
-O0 | Do not optimize—turn off optimizations |
-O1 | Optimize for performance |
-O2 | Optimize even more |
-O3 | Maximum level of performance optimization |
-Os | Optimize for size |
Another generic GCC command-line option that is often used while debugging and prototyping is the -g flag, which instructs the compiler to keep the debugging-related data in the final object, in order to facilitate the access to functions' and variables' readable handles while running within the debugger.
To inform the compiler that we are running a bare-metal application, the -ffreestanding command-line option is used. In GCC jargon, a freestanding environment is defined by the possible lack of a standard library in the linking step, and most importantly, this option alerts the compiler that it should not expect to use the main function as the entry point of the program or provide any preamble code before the beginning of the execution. This option is required when compiling code for the embedded platforms, as it enables the boot mechanism described in Chapter 4, The Boot-up Procedure.
The GCC program supports many more command-line options than those quickly introduced here. For a more complete overview of the functionalities offered, please refer to the GNU GCC manual, available at https://gcc.gnu.org/onlinedocs/.
To integrate the cross-compiling toolchain in the automated build using Make, a few changes are required in the makefile.
Assuming that the toolchain is correctly installed on the development host and reachable in its executing path, it is sufficient to change the default compiler command using the CC Make variable in the makefile:
CC=arm-none-eabi-gcc
The custom command-line options required to run the compile options may be exported through the CFLAGS variable:
CFLAGS=-mthumb -mcpu=cortex-m4 -ffreestanding
Using default makefile variables, such as CC and CFLAGS, enables implicit makefile rules, building object files from C sources with the same name and a custom compiler configuration.