Linux-based gcc ARM cross compilers for bare metal targets

While looking for a gcc cross compiler to use for an EmbedOps project, I took a look at my options and found a few choices, each with advantages and disadvantages.

I wanted to set up an EmbedOps Continuous Integration (CI) environment for an embedded ARM processor project. The project doesn’t use an operating system, so it’s just a “bare metal” project. In case you’re not familiar with this concept, the compiler and its libraries need to know about your underlying system. If the system is capable of providing certain services, such as memory management, the requests from the program are passed down. Otherwise, it’s up to the compiler and libraries to provide those services.

The gcc development tools are built for a specific combination of: host (aka, your laptop) OS, host processor, target OS, target processor. There are many supported target operating systems and processors. For the EmbedOps build environment, I’m using an x86_64-based Debian Linux server. So, the setup is an ARM-based platform with no OS, and it will build on an x86_64-based Debian Linux server.

The usual suspects

For each Ubuntu/Debian version, there is a corresponding gcc version associated with it. If you use the apt-get package installation tool, you will get the associated version. See the table below for a few of these combinations.

The gcc-arm-none-eabi Packages on Debian Releases

Debian Release Debian version Debian Release date gcc-arm-none-eabi package version gcc version Release Date
Jessie 8 2015-04 4.8.4-1+11-1 4.8 2014-12
Stretch 9 (previous) 2017-06 15:5.4.1+svn241155-1 5.4 2016-06
Buster 10 (latest stable) 2019-07 15:7-2018-q2-6 7.3 2018-01
Bullseye 11 (In test) 2021 15:8-2019-q3-1 8.3 2019-02
9.3 2020-03
10.2 2020-06

The advantage here is that this is probably the easiest, standard, and most straight forward way to install the gcc compiler and tools. The downside is that you may wind up with a version of the gcc compiler and tools that is several major versions behind. In my case, due to compatibility issues with other components in the project, I had to stick to Buster, which gave me gcc version 7.3. Even though updates to the Buster distribution regularly appear, they do not include updates to gcc with a different major version number.

A second option is from the ARM web site. They provide an official release of the complete set of gnu tools. Several flavors of this are available for multiple host OS platforms, including Linux, Windows and MacOS. This is a good option because it’s the ARM official release, but the drawback here is that the releases are only updated a couple of times per year. This is probably fine for most applications. As a side note, ARM also offers a proprietary compiler/toolset that’s licensed for a fee. I didn’t consider that as an option for this project.

A Couple of Alternatives

While looking around, I came across the arm-gcc-xpack distribution. This distribution is based on the same gnu code base as the official ARM releases and the Debian releases. The difference here is that the xpack distribution is updated very frequently. It’s not tied to any distribution version like Debian is. And with its very frequent updates, it’s more likely to include the latest features and bug fixes than the official ARM releases. The only down-side of this distribution is that the installation is a bit non-standard. It installs into a folder local to the user who runs the installation script. This results in a non-standard location with an obscure path, which required two things:(a) The build scripts and path variables must be updated to point to the non-standard installation path. The executables will wind up in ~/xpacks/repo/@pack-dev-tools/... instead of the typical /usr/local/... (b) In Debian, the default user is root. If the installation is being performed as root, the installation directory will wind up being located in the user root‘s home directory: /root. For security reasons, the folder /root does not provide read access to the non-root users. That prevents non-root users from being able to access the installation directory and therefore using the tools. I addressed this by creating the container and installing most of the necessary components as root, then created the non-root user and switched to that before performing the tools installation. For security and sanity reasons, I set up CI environments to perform all the CI work as a non-root user.

This shows the non-standard installation path of the xpack compiler and associated tools

 

Yet another option is to pull in the gcc source code and build your compiler, libraries, and tools from scratch. The gcc tools have become a very mature project with many features, and this means there’s an enormous amount of code to be built. The compressed source code is about 118 MB! And that is just the compiler, not the libraries or the tools. This can be time-consuming even on a fast system. Considering that you can get a recent, pre-built copy, this doesn’t provide much benefit. The value here would be if a custom configuration was needed, but for my project that wasn’t the case. If you need the absolutely latest version (10.2 at the time of this writing), this is your only option.

Comparisons

I experimented with each one of these solutions. At the time of my experiment, both the xpack and the official ARM releases were both at version 9.3. I discovered that by using the current xpack compiler, the binary image was about 5% smaller than the one created by the older Debian distribution with the same settings. There were space savings in all three areas (.text, .data, and .bss). The Debian distribution contains version 7.3. The details are in the table below.

In each case, the same compiler settings were used:

  • -mcpu=cortex-m0 (Generate code for an ARM Cortex-M0)
  • -mthumb (Generate code that can call between ARM andThumb instructions)
  • -Os (Optimize for size)
  • -fsigned-char (Treat ‘char’ as ‘signed char’)
  • -ffunction-sections (Place each function into its own section in the output file)
  • -fdata-sections (Place each data item into its own section in the output file)
  • -g3 (Include extra debugging information)
  • -mfloat-abi=soft (Floating point math is performed in software)
  • -march=armv6s-m (Indicates what instructions are available for the processor)

Conclusion

For older ARM architectures, any of these solutions could suffice. The Debian solution will lag a year or two behind, whereas the official ARM and xpack solutions will be less than a year old. You also have the option to build the latest 10.2 compiler from the source code. For this project, I chose to go with the xpack solution, to take advantage of the latest fixes and updates without waiting for the next official ARM release.

If you want all of the benefits listed here and none of the hassle of setting them up or maintaining them, consider reaching out! At DojoFive, we have talented engineers on hand ready to help you with all aspects of your EmbedOps journey. We are always happy to help with interesting problems that need solving. You can reach out at any time on LinkedIn or through email!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.