Cross-GCC
Purpose
Guide agents through setting up and using cross-compilation GCC toolchains: triplets, sysroots, pkg-config, QEMU-based testing, and common failure modes.
Triggers
"How do I compile for ARM on my x86 machine?"
"I'm getting 'wrong ELF class' or 'cannot execute binary file'"
"How do I set up a sysroot for cross-compilation?"
"pkg-config returns host libraries in my cross build"
"How do I debug a cross-compiled binary with QEMU + GDB?"
Workflow
1. Understand the triplet
A GNU triplet has the form
Debian/Ubuntu
sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu
For bare-metal ARM (Cortex-M)
sudo apt install gcc-arm-none-eabi binutils-arm-none-eabi
Verify
aarch64-linux-gnu-gcc --version 3. Basic cross-compilation
C
aarch64-linux-gnu-gcc -O2 -o hello hello.c
C++
aarch64-linux-gnu-g++ -O2 -std = c++17 -o hello hello.cpp
Bare-metal (no stdlib, no OS)
arm-none-eabi-gcc -mcpu = cortex-m4 -mthumb -mfloat-abi = hard -mfpu = fpv4-sp-d16 \ -ffreestanding -nostdlib -T linker.ld -o firmware.elf startup.s main.c 4. Sysroot A sysroot is a directory containing the target's headers and libraries. Required when your code links against target-specific libraries.
Use a sysroot
aarch64-linux-gnu-gcc --sysroot = /path/to/aarch64-sysroot -O2 -o prog main.c
Common sysroot sources:
- Raspberry Pi: download from raspbian/raspios
- Debian multiarch: debootstrap --arch arm64 bullseye /tmp/sysroot
- Yocto/Buildroot: generated automatically in build output
Verify the sysroot is correct: aarch64-linux-gnu-gcc --sysroot = /path/to/sysroot -v -E - < /dev/null 2
&1 | grep sysroot 5. pkg-config for cross builds pkg-config will return host library paths by default. Override: export PKG_CONFIG_SYSROOT_DIR = /path/to/sysroot export PKG_CONFIG_LIBDIR = ${PKG_CONFIG_SYSROOT_DIR} /usr/lib/aarch64-linux-gnu/pkgconfig: ${PKG_CONFIG_SYSROOT_DIR} /usr/share/pkgconfig export PKG_CONFIG_PATH =
clear host path
pkg-config --libs libssl
now returns target paths
- CMake cross-compilation Create a toolchain file aarch64.cmake : set ( CMAKE_SYSTEM_NAME Linux ) set ( CMAKE_SYSTEM_PROCESSOR aarch64 ) set ( CMAKE_C_COMPILER aarch64-linux-gnu-gcc ) set ( CMAKE_CXX_COMPILER aarch64-linux-gnu-g++ ) set ( CMAKE_SYSROOT /path/to/aarch64-sysroot ) set ( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) set ( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) set ( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE = aarch64.cmake cmake --build build
- Test with QEMU
User-mode emulation (Linux binaries, no full OS)
sudo apt install qemu-user-static qemu-aarch64-static ./hello
Or set binfmt_misc for transparent execution:
Then just: ./hello
GDB remote debug via QEMU
qemu-aarch64-static
-g
1234
./hello
&
aarch64-linux-gnu-gdb
-ex
"target remote :1234"
./hello
8. Common errors
Error
Cause
Fix
cannot execute binary file: Exec format error
Running target binary on host without QEMU
Use
qemu-
Tell build systems to use cross-compiler
export CC = aarch64-linux-gnu-gcc export CXX = aarch64-linux-gnu-g++ export AR = aarch64-linux-gnu-ar export STRIP = aarch64-linux-gnu-strip export OBJDUMP = aarch64-linux-gnu-objdump
For autoconf projects
./configure --host = aarch64-linux-gnu --prefix = /usr For a reference on ARM-specific GCC flags, see references/arm-flags.md .