5.3.10 · D5Build Systems & Toolchain
Question bank — Cross-compilation — toolchains, sysroot
The vocabulary you need before the traps
The traps below lean on three ideas. So that this page stands on its own — even if you skipped the parent note — here they are in plain words.


True or false — justify
A cross-compiler produces a binary that runs on the machine the compiler runs on.
False — the whole point is the opposite: the compiler runs on the host but emits code for a different target ISA; running the output on the host is exactly what fails.
If two triplets share the same arch field (e.g. both arm), their binaries are interchangeable.
False — the
abi/libc field still matters; arm-linux-gnueabi (soft-float) and arm-linux-gnueabihf (hard-float) share the arch but pass floats differently, so they link yet crash. See ABI and calling conventions.Setting --sysroot changes what CPU instructions the compiler emits.
False — the sysroot only relocates where headers and libraries are found; the target ISA comes from the compiler's triplet/
-march, a completely separate decision.A binary is "just a file", so copying a host-built executable to the target is a valid deployment step.
False — the file contains host machine instructions the target CPU cannot decode; portability of the file is not portability of the code inside it.
--host=arm-linux-gnueabihf alone tells autotools everything it needs to cross-compile.
False —
configure compares --host against --build; without a differing --build (and a matching CC), it may still try to run test binaries, which won't execute on your host. See Build systems CMake and autotools.A sysroot must contain a full working copy of the target OS.
False — it only needs the development pieces:
usr/include (headers) and lib/usr/lib (libraries and startup objects); no kernel, shell, or userland needed to compile.Static linking removes the need for a sysroot.
False — you still need the target's
libc.a and headers to compile and link against; the sysroot supplies them. Static linking only changes whether that code is baked in vs resolved at runtime. See Static vs dynamic linking.A bare-metal triplet like aarch64-none-elf still links the standard C library by default.
False — the
os field is none, so there is no hosted libc; you compile with -nostdlib/-ffreestanding and provide any runtime yourself.The vendor field of a triplet must be filled in for the toolchain to work.
False — vendor is largely cosmetic and is usually
unknown or dropped; the arch, os, and abi/libc fields carry the real meaning.Two toolchains with the same prefix always target exactly one identical ABI.
False — the prefix (= the triplet) fixes arch/os/abi, but a single GCC can still be multilib: flags like
-m32/-mabi= or -march select a different ABI variant from the same prefix, so the prefix alone doesn't pin the exact ABI actually used.Spot the error
What is wrong with arm-linux-gnueabihf-gcc -I/usr/include -o hello hello.c?
The
-I/usr/include points at the host's headers; you'll pull x86 struct sizes and macros into an ARM build, causing silent ABI corruption. Reference only paths inside the sysroot.What is wrong with gcc --target=arm hello.c expecting an ARM binary?
Plain
gcc is a native compiler with no ARM backend or ARM sysroot; you need an actual cross toolchain (arm-linux-gnueabihf-gcc), not a flag on the host compiler. (Clang differs — it is multi-target — but even then needs a target sysroot.)Someone runs ./configure --host=arm-linux-gnueabihf but the build still tests by executing binaries and hangs. What did they forget?
They omitted
--build=x86_64-linux-gnu; without an inequality between host and build, configure doesn't realise it's a cross build and keeps trying run-time feature probes.A student links a hard-float library into a soft-float program; it links cleanly but crashes on the first float. Why did the linker not catch it?
The linker matches symbol names, not calling conventions; float-passing register vs stack mismatch is an ABI-level fact invisible at link time, so it only manifests at runtime.
file firmware.elf reports "ELF 64-bit, x86-64" after a supposed aarch64 build. What is the likely error?
The wrong compiler was invoked — the native
gcc shadowed the cross one in $PATH, or the prefix was mistyped; the ISA in the output reveals which backend actually ran.Someone sets --sysroot but still gets fatal error: stdio.h: No such file. What's the most likely cause?
The sysroot path is wrong or lacks
usr/include (empty/partial sysroot); --sysroot relocates the search root but cannot invent headers that aren't there.Why questions
Why does the compiler need the target's stdio.h and not just any stdio.h?
Struct layouts, type sizes, and macro definitions can differ across platforms; using host headers bakes host assumptions (e.g.
long size, struct padding) into a target binary, breaking the ABI.Why is the sysroot conceptually the same trick as chroot?
Both relocate the meaning of
/: chroot does it for the running kernel's filesystem view, --sysroot does it for the compiler's default search paths, rewriting each absolute default p to S+p. See Chroot and namespaces.Why does the toolchain prefix exist at all?
So a native
gcc and a cross arm-...-gcc coexist in $PATH without colliding; the prefix (the triplet) names which target a tool serves, making the toolchain self-documenting.Why must the assembler, linker, and libc all share one triplet?
Each stage encodes target-specific assumptions (instruction encoding, relocation types, ABI); mixing triplets means one tool emits or expects data another can't interpret, producing corrupt or non-running output.
Why does --host matter for autotools even though you also pass CC?
CC sets which compiler to invoke, but --host (vs --build) is what flips configure into cross mode so it stops trying to run compiled probes it cannot execute on the host.Why can -nostdlib be required for a bare-metal target?
With
none as the os field there is no hosted C library or startup file (crt0); -nostdlib stops the driver from auto-linking libc and startup objects that simply don't exist for that target.Why doesn't picking the right ISA guarantee a working binary?
The ISA (the
arch field) is only one axis; the ABI (float convention, calling convention, libc variant) must also match end-to-end, or you get code that decodes fine but behaves wrongly at runtime.Edge cases
What is a "Canadian cross", and why is it a distinct case?
When build, host, and target are all different (e.g. build on x86, produce a compiler that runs on arm and emits mips); it needs a chain of toolchains and is the reason the three-machine vocabulary exists.
What happens if build == host but host != target?
That is an ordinary cross-compile: the compiler runs on the same machine it was built on, but emits code for a different target — the everyday firmware/mobile case.
For a native compile, what are build, host, and target?
All three are equal (e.g. x86/x86/x86); this is the degenerate case where cross-compilation vocabulary collapses to a single machine.
Can you cross-compile for a target that has no compiler in existence yet (a brand-new chip)?
Yes — that is the bootstrapping case: you build the first toolchain on an existing host targeting the new chip, since the chip cannot yet build anything itself.
If the sysroot exists but is for the wrong ABI (soft-float when you need hard-float), what symptom appears?
Compilation and linking succeed because names resolve, but the program crashes or misbehaves at runtime — the classic silent ABI mismatch that only surfaces on execution.
What is special about a triplet whose os field is none and that ends in elf?
It signals raw ELF output with no OS or libc assumptions; the compiler won't link
crt0/libc, and you typically supply a linker script to place code at physical addresses.Does dynamic linking on the target require anything the sysroot doesn't need at build time?
Yes — at build time the sysroot's
libc.so satisfies the linker, but at runtime the target device must also have the matching shared library present, or loading fails. See Static vs dynamic linking.