Search
Close this search box.

How to Survive Embedded Linux: How to Compile

Share:

How to Survive Embedded Linux: How to Compile

One of the first challenges in designing a Linux BSP and associated software is just how to compile the thing in the first place.

In this part of the series on How to Survive Embedded Linux, we’ll guide you through some best practice ideas and techniques to utilise when learning how to compile your Linux BSP.

How to Compile

Cross-compilation: an introduction
A “cross-compiler” is a compiler that creates executable code for a different architecture. In the case of developing your Linux BSP, you’ll be compiling on your PC but the code you make is targeting your custom board.

Things that need to be compiled include, but are not limited to: the bootloader, the kernel, shared libraries, your application code. We’ll look at a few ways of getting this done

Finding the right cross-compiler
To get your cross-compiler you will need to find it from somewhere. There are several places where you might find what you need, but for a newbie some of the options and terminology might be a bit confusing.

If you’re using Ubuntu and you’re looking for an ARM cross-compiler based on GCC, you might find the following packages that may suit your needs: –

gcc-arm-linux-androideabi gcc-arm-linux-gnueabi gcc-arm-linux-gnueabihf gcc-arm-none-eabi gcc-arm-none-eabi-source

In this scenario you’ll want to pick out the one that includes linux-gnueabi. The ABI, or application binary interface, is different if you’re compiling for Android (linux-androideabi) or GNU/Linux (linux-gnueabi). The gcc-arm-none-eabi is for bare-metal programming, when you want to avoid an operating system altogether.

The reason for the difference is due to the multiple C implementations that exist for different operating systems, as we are compiling for Linux and using the GNU C Library (generally what is understood by a ‘Linux BSP’) then we must use the gcc-arm-linux-gnueabi cross-compiler.

One last point, there is a gcc-arm-linux-gnueabihf compiler there to, so what is the difference there? In this case the ‘hf’ stands for hard float, in other words, you should choose this compiler if your target includes a hardware floating-point unit, this will considerably speed up operations that include floating point calculations.

Remember that, if you do decide to use your package manager, there may be updates to your compiler over time as your package manager does periodic updates. This will stop your builds being reproducible over time. In a production setting, you’re more likely to want to pick a specific compiler and stick with it as long as you can.

In most cases the CPU vendor will provide, or point to, a toolchain that is suitable and has been well tested. Use this unless you have reason to move to another one, different versions of GCC can have different compile bugs, or even different behaviour; when making a switch, make sure to understand the differences and carry out a good set of tests.

Using ‘sysroots’

A sysroot is a scaled down version of your target’s filesystem, it need only contain the libraries and headers which you will compile/link against. There are many ways to set up a sysroot, one is to copy the /usr and /lib directories from your target to somewhere on your host filesystem. You could also NFS (network file system) mount the target filesystem somewhere locally.

The GCC family of compilers accept a “ — sysroot=dir” argument, which is used to specify the path to your sysroot. Check the documentation for your cross-compiler to how to set your compiler correctly, you may need to take extra steps for using the sysroot when passing include/library directories.

If you compiler does not support using a sysroot then you can always just tell the compiler to look in your sysroot manually, if you’re using a makefile then it’s not extra effort once everything is setup. Just try to keep your sysroot unchanged during development.

Cross compilation with autotools


Most well-known Linux software uses autotools, it’s likely you’ve used its famous two step build process (./configure, make) when compiling a package from source. Moving your project to use autotools can take a great amount of effort, but its benefits include very straight-forward cross compilation.
To cross-compile with autotools you need only change the flags passed to configure. In the example that you are building for your local machine (i.e.: not cross-compiling), you run configure without any flags. When cross-compiling, there are four flags you will want to use:-

– -build= this is the machine you are building on (e.g.: x86_64-linux)
– -target= the machine you are building for (e.g.: arm-linux-gnueabihf)
– -with-sysroot= the sysroot as described in the previous section

After the configure script is done, you can run ‘make’ as normal and the cross-compiler will be automatically selected and passed the correct flags. If you encounter any errors in the configure stage of the process, just go through one by one and try to fill any missing gaps.

Cross compilation with Yocto

If you’ve been using Yocto to build your Linux BSP, then consider building a Yocto SDK to make your life much easier going forward.

To build an SDK run the following bitbake command:-

bitbake -c do_populate_sdk

This will create an installer in your “build/tmp/deploy/sdk” directory. Refer to the Yocto manual for more details. Install your SDK somewhere on your filesystem.

Once it is installed there is a script that begins with “environment-setup-” followed by the target machine that the SDK builds for. This script will export a series of very useful variables into your environment, allowing you to cross-compile using tools actually generated by Yocto itself.

A few example variables are given below for an ARM Cortex A9 processor target:-

export SDKTARGETSYSROOT=/home/villeb/sdk/sysroots/cortexa9hf-vfp-neon-poky-linux-gnueabi

export CFLAGS=” -O2 -pipe -g -feliminate-unused-debug-types”

export CC=”arm-poky-linux-gnueabi-gcc -march=armv7-a -mfloat-abi=hard -mfpu=neon -mtune=cortex-a9 — sysroot=$SDKTARGETSYSROOT”

export CXX=”arm-poky-linux-gnueabi-g++ -march=armv7-a -mfloat-abi=hard -mfpu=neon -mtune=cortex-a9 — sysroot=$SDKTARGETSYSROOT”

export CONFIGURE_FLAGS=” — target=arm-poky-linux-gnueabi — host=arm-poky-linux-gnueabi — build=i686-linux — with-libtool-sysroot=$SDKTARGETSYSROOT”

As you can see, it will give you sensible compiler defaults and flags to use for cross-compiling. It will also give you a full set of flags to pass to an autotools configure script, so all you have to do is run ./configure ${CONFIGURE_FLAGS} and you’re cross-compiling with a Yocto built binary.

Conclusion

Here we have covered the most straightforward methods with the most straightforward terminology. There are other methods, for example “chroot cross-compilation”, which we have not gone into as I believe these contain unneeded complexity or only address specific target/host combinations. Someone might quiz you on the meaning of “Canadian-cross-compilation”, this term only applies to cross-compiling a cross-compiler, and you won’t need it for 99.9% percent of your daily work.

Also published on Medium

How can ByteSnap help you today?
From start-ups to blue chips, ByteSnap’s embedded systems developers are enabling companies to stay a step ahead by providing them with bespoke solutions. Maintain your competitive edge – contact us today and find out how we can optimise your product development!

Share:

Related Posts