If you have ever stared at a compiler name like arm-buildroot-linux-gnueabihf-gcc and wondered why it looks nothing like plain old gcc, this lecture is for you. Every serious free embedded linux course eventually has to explain the GNU target triplet, because it is the single string that tells you which CPU, which vendor, which kernel, and which ABI a toolchain was built for. Get this wrong and your cross-compiled binary either refuses to link or crashes the moment it touches a floating-point instruction on target hardware.
In this free embedded linux course lecture we break the triplet apart field by field, walk through the three ARM ABI generations (OABI, EABI, EABIHF), and show you how to inspect any toolchain on your machine with a single command.
cross compiler prefix
arm eabi
eabihf
dumpmachine
free embedded linux course
What You Will Learn
- Why cross-compilers are prefixed instead of just being called
gcc - The four fields of a GNU target triplet and what each one really means
- The difference between OABI, EABI, and EABIHF on ARM, and why it matters for performance
- How to query any installed toolchain for its exact triplet
- A hands-on script that detects and reports the ABI of a cross toolchain automatically
Prerequisites
You should already be comfortable with basic Linux command-line usage and know what a cross-compiler is at a high level (covered in the earlier lectures of this free embedded linux course series). No prior ABI knowledge is assumed.
Why the Prefix Exists
A native compiler on your development machine only ever needs to target one CPU architecture and one ABI, so it is safe to call it just gcc. A cross toolchain is different: your build server might hold a dozen toolchains side by side, one for a Cortex-A embedded gateway, one for a MIPS router, one for a RISC-V sensor node. If they were all named gcc, you would have chaos. GNU solves this by baking the target description directly into the binary’s file name as a prefix, so arm-linux-gnueabihf-gcc and mips-linux-gnu-gcc can happily coexist in the same PATH.
<CPU> <VENDOR> <KERNEL> <OS/ABI>CPU : architecture family (arm, mips, x86_64, riscv64…)
VENDOR : toolchain provider, often omitted or “unknown”
KERNEL : always “linux” for our purposes
OS/ABI : userspace + ABI, e.g. gnu, gnueabi, gnueabihf, musl
The Four Fields, One at a Time
CPU Architecture
The first field names the processor family: arm, aarch64, mips, riscv64, x86_64. On architectures that support both byte orders, an endianness suffix is appended to the CPU field itself: mipsel for little-endian MIPS, armeb for big-endian ARM. If you don’t see a suffix, the architecture only has one natural endianness on that build, or the vendor chose not to encode it.
Vendor
This field is purely informational — it names whoever assembled the toolchain, for example buildroot, poky, or a board vendor’s own tag. Many toolchains simply drop it or set it to unknown. Never write build logic that depends on this field being present.
Kernel
For every toolchain you will build against in this course, this field is always linux. Bare-metal toolchains (no OS at all) instead use none here, which is a useful way to spot a bare-metal build at a glance.
Operating System and ABI
The final field names the C library and, on ARM, the calling convention. gnu means glibc with the default ABI for that architecture. On ARM specifically, you will see gnueabi or gnueabihf, and on musl-based toolchains you will see plain musl or musleabihf. This is the field that most directly affects binary compatibility, which brings us to ARM’s ABI history.
ARM’s ABI Evolution: OABI, EABI, EABIHF
Most architectures keep one stable ABI across their whole processor family. ARM is the exception, and understanding why saves you real debugging time.
| ABI | Status | Floating Point Handling |
|---|---|---|
| OABI (old ABI) | Obsolete, historical only | Pre-standardization, incompatible with modern toolchains |
| EABI (soft-float) | Still used on FPU-less cores | Float args passed in general-purpose integer registers |
| EABIHF (hard-float) | Default on modern ARM Linux | Float args passed directly in FPU registers |
EABIHF exists because copying floating-point values between integer and FPU registers on every function call is wasted work when the CPU has a hardware FPU. Skipping that copy makes floating-point-heavy code measurably faster. The catch: an EABIHF binary will not run at all on a core with no FPU, and you cannot link EABI and EABIHF object files together — the choice has to be made once, at toolchain-selection time, and applied consistently across your whole build.
float arg –> copy into integer register –> function call –> copy back to FPUEABIHF (hard-float)
float arg –> already in FPU register –> function call directly, no copy
Inspecting a Toolchain: -dumpmachine
Every GCC build, native or cross, understands -dumpmachine. This is the fastest way to confirm exactly what you are pointing at before you kick off a long build.
# Native compiler on an x86_64 desktop
$ gcc -dumpmachine
x86_64-linux-gnu
# A hard-float ARM cross compiler
$ arm-linux-gnueabihf-gcc -dumpmachine
arm-linux-gnueabihf
# A soft-float ARM cross compiler
$ arm-linux-gnueabi-gcc -dumpmachine
arm-linux-gnueabi
# A little-endian MIPS cross compiler with an explicit vendor
$ mipsel-unknown-linux-gnu-gcc -dumpmachine
mipsel-unknown-linux-gnu
Note that when a native compiler is installed, distributions usually also create an unprefixed gcc symlink pointing at the same binary purely for convenience — the underlying triplet identity doesn’t change.
Original Example: An ABI-Detecting Build Script
Instead of hardcoding a toolchain prefix in a Makefile and hoping for the best, here is a small original shell helper, ep_abi_check.sh, that inspects whatever cross compiler you point it at and warns you if it’s soft-float when you expected hard-float, or vice versa.
#!/usr/bin/env bash
# ep_abi_check.sh - report the ABI of a given cross-compiler prefix
set -euo pipefail
CC="${1:-gcc}"
if ! command -v "$CC" &>/dev/null; then
echo "error: '$CC' not found in PATH" >&2
exit 1
fi
TRIPLET=$("$CC" -dumpmachine)
echo "Toolchain : $CC"
echo "Triplet : $TRIPLET"
case "$TRIPLET" in
*gnueabihf*|*musleabihf*)
echo "ABI : hard-float (EABIHF) - FPU register args"
;;
*gnueabi*|*musleabi*)
echo "ABI : soft-float (EABI) - integer register args"
;;
*gnu*)
echo "ABI : standard glibc ABI (non-ARM or default)"
;;
*)
echo "ABI : unrecognized, inspect manually"
;;
esac
Run it against any toolchain on your PATH:
$ chmod +x ep_abi_check.sh
$ ./ep_abi_check.sh arm-linux-gnueabihf-gcc
Toolchain : arm-linux-gnueabihf-gcc
Triplet : arm-linux-gnueabihf
ABI : hard-float (EABIHF) - FPU register args
Real-World Use Case
Imagine you inherit a Yocto-built root filesystem for a Cortex-A7 board and you need to cross-compile a standalone diagnostic utility to drop onto that filesystem. If you grab a generic arm-linux-gnueabi-gcc (soft-float) off the internet while the rest of the image was built hard-float, your utility will run, but any call into a shared library built hard-float will silently break with corrupted floating-point results, or the dynamic linker will refuse to load it entirely. Checking the triplet with -dumpmachine before you build is a five-second habit that avoids hours of confused debugging.
Common Mistakes and Troubleshooting
- Mixing EABI and EABIHF objects — the linker will usually error out with an ABI mismatch message; if it doesn’t, runtime float corruption is the likely symptom.
- Assuming the vendor field is meaningful — never branch build logic on it; it’s advisory only.
- Forgetting the target has no FPU — an EABIHF binary on an FPU-less core fails at load time, not compile time, so test on real hardware early.
- Confusing OABI with EABI — OABI is dead; if you see it referenced anywhere, treat the source as very old.
Best Practices
- Always run
-dumpmachineon a new toolchain before your first build, and record the output in your project’s build documentation. - Standardize your whole project — kernel, libraries, applications — on one ABI and never mix.
- Prefer hard-float on any modern ARM core with an FPU; the performance gain is essentially free once toolchain and rootfs agree.
Summary and Key Takeaways
- The GNU triplet has four fields: CPU, vendor, kernel, OS/ABI — vendor is the only optional/unreliable one.
- ARM has three ABI generations; only EABI and EABIHF are relevant today, and they are not link-compatible.
-dumpmachineis the single fastest way to confirm what any compiler was built for.
Conclusion
The GNU target triplet looks intimidating the first time you see it, but it’s really just a compact, four-field label describing exactly what a compiler produces. Once you can read arm-buildroot-linux-gnueabihf-gcc at a glance — ARM CPU, buildroot-provided, targeting Linux, hard-float ABI — you’ll never again wonder why a binary refuses to run on a particular board. That single skill, taught early in this free embedded linux course, will save you from some of the most confusing failure modes in embedded Linux development.
FAQ
What does the “hf” in gnueabihf mean?
It stands for hard-float — floating-point arguments are passed in dedicated FPU registers instead of general-purpose integer registers.
Can I mix EABI and EABIHF object files in one binary?
No. They use incompatible calling conventions for floating-point arguments and cannot be linked together.
Is OABI still used anywhere?
No, OABI is obsolete. You may still encounter references to it in very old documentation, but no current toolchain targets it.
How do I find the triplet of a toolchain I already have installed?
Run the compiler with the -dumpmachine flag, for example arm-linux-gnueabihf-gcc -dumpmachine.
Why is the vendor field sometimes missing entirely?
It is purely informational and many toolchain builders omit it or set it to “unknown” since it has no effect on compilation.
Does x86_64 have multiple ABIs like ARM does?
Not in the same way — x86_64 has one dominant Linux ABI, which is why ARM’s EABI/EABIHF split often confuses people coming from x86 backgrounds.
What happens if I run an EABIHF binary on a core without an FPU?
It will typically fail to load or crash on the first floating-point instruction, since there is no hardware to execute the FPU register operations.
Ready to Pick Your C Library Next?
Continue this free embedded linux course with the next lecture on choosing between glibc, musl, and uClibc.
