0%
BlogEmbedded Linux

(Embedded Linux Part 1) How to Build an ARM Cross-Compiling Toolchain with Crosstool-NG

April 12, 2026 15 min read 33 views

This is Part 1 of the Embedded Linux series. This post sets up the cross-compiling toolchain that every subsequent lab depends on.

Time: ~2 hours (mostly waiting for the build) Target: AArch64 (ARM 64-bit), musl C library, GCC 14.3.0


What you will learn

  • What a cross-compiling toolchain is and why you need one
  • How to install and configure Crosstool-NG
  • How to build a custom AArch64 toolchain from scratch
  • How to test your toolchain with QEMU user emulation

Prerequisites

  • Ubuntu 24.04 (or any recent Debian-based distro)
  • At least 4 GB of RAM
  • At least 15 GB of free disk space
  • Basic familiarity with the Linux command line

Background: what is a cross-compiling toolchain?

When you develop software on your laptop, you compile and run code on the same machine. Both the compiler and the binary target the same architecture — x86_64.

In embedded Linux development, this changes. You write code on your x86_64 laptop, but the code runs on an ARM64 processor — a Raspberry Pi 4, an STM32MP2, a custom board. Your native gcc has no idea how to produce AArch64 binaries. You need a cross-compiler.

A cross-compiling toolchain is a set of tools that runs on your host (x86_64) but produces binaries for a different target (AArch64). It includes:

  • GCC — the cross-compiler (aarch64-linux-gcc)
  • binutils — linker, assembler, objcopy (aarch64-linux-ld, aarch64-linux-as)
  • C library — musl, glibc, or uClibc-ng (compiled for AArch64)
  • Kernel headers — the interface between userspace and the Linux kernel

Building this from scratch by hand is notoriously painful. You have to compile each component in the right order, with the right version combinations. Crosstool-NG automates the entire process.


Part A — Installing dependencies

A.1 System packages

sudo apt install build-essential git autoconf bison flex texinfo \
  help2man gawk libtool-bin libncurses5-dev unzip gettext python3 rsync

Part B — Downloading and building Crosstool-NG

B.1 Getting the sources

Clone the repository and check out a stable release:

git clone https://github.com/crosstool-ng/crosstool-ng
cd crosstool-ng/
git checkout crosstool-ng-1.28.0

B.2 Building Crosstool-NG

Since we are building from git rather than a release archive, the build system files need to be generated first:

./bootstrap

Configure it for local use — this installs Crosstool-NG inside the current directory instead of system-wide:

./configure --enable-local
make

Verify the installation:

./ct-ng help

Part C — Configuring the toolchain

C.1 Loading the AArch64 sample

Crosstool-NG ships with ready-made configuration templates called samples. List them with:

./ct-ng list-samples

Load the AArch64 sample as a starting point:

./ct-ng aarch64-unknown-linux-gnu

Open the configuration menu:

./ct-ng menuconfig

C.2 Path and misc options

Enable Try features marked as EXPERIMENTAL.

C.3 Toolchain options

  • Set Tuple's vendor string (TARGET_VENDOR) to training
  • Set Tuple's alias (TARGET_ALIAS) to aarch64-linux

This allows calling the compiler as aarch64-linux-gcc instead of the full tuple name aarch64-training-linux-musl-gcc.

C.4 Operating System

Set Version of linux to the closest version older than or equal to 6.1.

This is important. The kernel headers used in the toolchain must not be more recent than the kernel running on the target board. If they are, binaries may reference syscalls or data structures that the running kernel does not know about.

C.5 C library

  • Set C library to musl
  • Keep the default version

musl is a lightweight alternative to glibc, commonly used in embedded systems. It produces smaller binaries and has a simpler, more auditable codebase.

C.6 C compiler

  • Set Version of gcc to 14.3.0
  • Make sure C++ (CC_LANG_CXX) is enabled

C.7 Debug facilities

Remove all options.

Save and exit the menu.


Part D — Building the toolchain

D.1 Running the build

./ct-ng build

This will take between 20 and 60 minutes depending on your machine. Crosstool-NG will download, configure, and compile all components automatically.

The toolchain will be installed in:

$HOME/x-tools/aarch64-training-linux-musl/

D.2 Adding the toolchain to your PATH

export PATH=$HOME/x-tools/aarch64-training-linux-musl/bin:$PATH

Add this line to your ~/.bashrc to make it permanent.

Verify the compiler is accessible:

aarch64-linux-gcc --version

Expected output:

aarch64-linux-gcc (crosstool-NG 1.28.0) 14.3.0

Part E — Testing the toolchain

E.1 Compiling a test program

Create a simple hello.c:

#include <stdio.h>

int main(void) {
    printf("Hello from AArch64!\n");
    return 0;
}

Cross-compile it:

aarch64-linux-gcc -o hello hello.c

Verify the binary targets AArch64:

file hello

Expected output:

hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked,
interpreter /lib/ld-musl-aarch64.so.1, not stripped

E.2 Running the binary with QEMU user emulation

You do not need physical ARM hardware to test the binary. QEMU's user emulation mode lets you run ARM binaries directly on your x86 machine.

Install QEMU:

sudo apt install qemu-user

Try running the binary:

qemu-aarch64 hello

You will get an error:

qemu-aarch64: Could not open '/lib/ld-musl-aarch64.so.1': No such file or directory

This is expected. The binary is dynamically linked and needs ld-musl-aarch64.so.1 — the AArch64 shared library loader — which does not exist on the x86 system.

Find it inside the toolchain sysroot:

find ~/x-tools -name "ld-musl-aarch64.so.1"

Output:

/home/user/x-tools/aarch64-training-linux-musl/aarch64-training-linux-musl/sysroot/lib/ld-musl-aarch64.so.1

Tell QEMU where to find AArch64 shared libraries using the -L flag:

qemu-aarch64 -L ~/x-tools/aarch64-training-linux-musl/aarch64-training-linux-musl/sysroot hello

Output:

Hello from AArch64!

The cross-compiled AArch64 binary is running on the x86 machine.


Understanding the toolchain tuple

The toolchain is named aarch64-training-linux-musl. Each part has a meaning:

Part Meaning
aarch64 Target CPU architecture (ARM 64-bit)
training Vendor string (set in menuconfig)
linux Target OS
musl C library

Cleaning up (optional)

If disk space is tight, remove the downloaded source archives and build artifacts. This saves around 9 GB but means re-downloading everything on a rebuild:

./ct-ng clean

Only do this once you are satisfied with the toolchain configuration.


Key takeaways

  • A cross-compiling toolchain runs on x86 but produces binaries for AArch64
  • Crosstool-NG automates the complex process of building one from source
  • Always use kernel headers older than or equal to the kernel on the target board
  • musl is a lightweight alternative to glibc, well-suited for embedded systems
  • QEMU user emulation lets you test AArch64 binaries without physical hardware

Credits

This article is based on Bootlin's Embedded Linux training materials, authored by Bootlin engineers and available under the Creative Commons Attribution-ShareAlike 3.0 license.

This article is also published under the Creative Commons Attribution-ShareAlike 3.0 license.

Comments

Loading comments…

Leave a comment

Your email will not be published.

Enjoy this article?

Get firmware tips, project breakdowns, and embedded news, straight to your inbox.

Share: Twitter/X LinkedIn