Linux Toolchains
Short notes about native compilers, cross compilers, libc, dynamic linking, and static linking.
Terms
| term | meaning |
|---|---|
| Compiler | Translate source code to object files or a binary. |
| Linker | Connect object files with libraries and create the final executable. |
| Host compiler | Compiler that builds a binary for the same machine you are using. |
| Cross compiler | Compiler that runs on one machine but builds a binary for another target. |
| libc | The C runtime library used by Linux programs, for example glibc or musl. |
| Dynamic binary | Loads shared libraries at runtime. |
| Static binary | Copies the needed libraries into the executable at link time. |
Compile and link
Compile only:
Link:
Compile and link in one command:
g++ calls the compiler and then the linker. For C++ it also links the C++ standard library.
Why linker is used when the binary runs
For a dynamic binary, linking is not fully finished when you build the program.
The binary contains:
- the program code
- the shared libraries it needs
- the runtime loader path, for example
/lib64/ld-linux-x86-64.so.2
When you run the binary, Linux starts the loader first. The loader finds the shared libraries, maps them into memory, resolves symbols, and then starts main().
Check it:
For a static binary, there is no runtime loader dependency for normal libc libraries:
glibc vs musl
| libc | common use |
|---|---|
| glibc | Default libc on most Debian, Ubuntu, Fedora, and many desktop/server Linux systems. |
| musl | Small libc used by Alpine Linux and useful for static embedded deployment. |
glibc is common and very compatible with normal Linux distributions.
musl is designed to be small, simple, and friendly to static linking. It is useful when you want one binary that is easy to copy to an embedded target.
Static compiler
"Static compiler" usually means a compiler toolchain or build command that creates a statically linked binary.
It is not a different C++ language. The main difference is the link step:
Install glibc host tools
Install native build tools on Ubuntu/Debian:
Build with the host glibc compiler:
Install glibc cross compiler
For AArch64 target:
Verify:
Build AArch64 binaries with glibc:
Useful package names for other targets:
| target | packages |
|---|---|
| AArch64 | gcc-aarch64-linux-gnu g++-aarch64-linux-gnu |
| ARM hard-float | gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf |
| ARM soft-float | gcc-arm-linux-gnueabi g++-arm-linux-gnueabi |
| x86_64 | gcc-x86-64-linux-gnu g++-x86-64-linux-gnu |
Install musl cross compiler from musl.cc
musl.cc provides prebuilt musl toolchains. It is a community source, not the official musl project.
Choose the archive by the target CPU, not by the host CPU.
For an x86_64 Ubuntu host building for AArch64 Linux, use:
Do not use this unless the target is x86_64 Linux with musl:
Download and install in /opt:
Add it to the shell path:
Verify:
Build with musl cross compiler
Dynamic musl build:
Static musl build:
Static musl binaries are useful for embedded targets because they do not require the target root filesystem to provide matching shared libc files.
Test AArch64 binary on x86_64 host
Install QEMU user emulation:
Run the static binary:
Expected output: