When working with executables and libraries for ARM, sooner or later the question arises: Is it compiled with hard-float (ARMHF) or with soft-float (ARMEL)? The difference is significant, as it affects binary compatibility and performance in floating-point operations. Fortunately, the ELF binary itself contains the necessary clues to reliably determine this.
In this article we review how to inspect an ARM ELF using the utility Reader To distinguish between the two variants, we'll cover which labels to look for, what output differences you might find depending on the tool version, and how to interpret details like Thumb or NEON. We'll also discuss why the ARMEL vs. ARMHF decision is made during compilation. not in the runtime systemand we clarify common doubts about multiarch environments where arm-linux-gnueabihf and arm-linux-gnueabi coexist.
What do ARMHF (hard-float) and ARMEL (soft-float) mean?
This behavior is defined by the AAPCS (ARM Architecture Procedure Call Standard), which describes how parameters are passed and values are returned. The AAPCS has variantsAnd the one that interests us to differentiate ARMHF is the one associated with VFP, an indicator that we will see reflected in the attributes of the binary.
ELF and its header: why it gives us all the information
The ELF (Executable and Linkable Format) is the de facto standard in Linux and stores data in its header and attribute sections. information about the target architecture and ABI conventions used to compile the executable or library. If you're interested in exhaustive detail, simply search the ELF header specification to see all the fields that exist.
To inspect an ELF, the tool Reader It's the direct route. Option -a overturns practically everything, and option -A allows focus on architecture-dependent information (ARM in our case), showing attributes such as the floating point type, whether Thumb is enabled, NEON support, and other relevant flags.
Prepare an environment with ARMEL and ARMHF
If you want to compare real-world output, a practical way is to install the gcc/g++ toolchains for both variants on an Ubuntu machine. By doing so, You will get system libraries for armel and armhf installed side by side thanks to multiarch.
After installation, typical library routes are: /usr/arm-linux-gnueabihf/lib for ARMHF y /usr/arm-linux-gnueabi/lib for ARMELHaving both allows you to compare, for example, the mathematical library libm.so.6 and observe how the attributes change depending on the variant.
The definitive clue: Tag_ABI_VFP_args with readelf
The practical test involves analyzing the ELF with readelf and looking for references to FP in the attributes. A very straightforward tactic is to filter the output by the string FP to focus on the floating-point numbers. since the most revealing indicator is the Tag_ABI_VFP_args attribute.
In the libm.so.6 library compiled as ARMEL (soft-float), you will see the typical ARM attributes, but The line Tag_ABI_VFP_args will not appearIn contrast, in the ARMHF (hard-float) libm.so.6 file, an additional line with that attribute will be displayed, confirming the use of the VFP calling convention.
readelf -A /usr/arm-linux-gnueabi/lib/libm.so.6 | grep FP
In ARMEL, filtering by FP will show information related to general floating-point capabilities, but without the attribute that declares VFP argumentsWhen repeating it over ARMHF:
readelf -A /usr/arm-linux-gnueabihf/lib/libm.so.6 | grep FP
You will find an additional entry of the type Tag_ABI_VFP_args which is the unmistakable sign of a hard float. This detail is what, in practice, This allows us to reliably state that the binary follows the ABI hard-float.
Output variations depending on the readelf version
Depending on the implementation and version of readelf, the exact form of the text may vary. For example, with readelf compiled from elftoolchain-0.6.1The additional line not only indicates the attribute, but can be described as: Tag_ABI_VFP_args: AAPCS (VFP variant).
In that same family of outputs, there are two other values that may appear for Tag_ABI_VFP_args: “AAPCS (base variant)” and “toolchain-specific”The available literature mentions these alternatives, although it is not always clear under what exact conditions each one is returned. The important thing is to recognize that the mention of the VFP variant It is associated with the hard-float case.
Beyond vocational training: -A also teaches architecture, Thumb, and NEON
Although here we focus on distinguishing between ARMHF and ARMEL, it is worth remembering that readelf -A It offers a broader view of the binary. Its output will show, in addition to floating-point attributes, the exact target architecture, if the binary It is built with Thumb and if support has been enabled NEON, among other relevant capabilities for optimization and compatibility.
All of this helps validate that the binary not only corresponds to the FP variant you expect, but also It is aligned with the instruction set and extensions that you have available on the device where it will run.
Example of a recommended workflow
A typical sequence for comparing both variants of libm.so.6 could be as simple as this: List routes, run readelf, and filter by FPThis will give you the key difference between armel and armhf at a glance.
# ARMEL (soft-float)
ls -l /usr/arm-linux-gnueabi/lib/libm.so.6
readelf -A /usr/arm-linux-gnueabi/lib/libm.so.6 | grep FP
# ARMHF (hard-float)
ls -l /usr/arm-linux-gnueabihf/lib/libm.so.6
readelf -A /usr/arm-linux-gnueabihf/lib/libm.so.6 | grep FP
If in ARMHF you observe the line with Tag_ABI_VFP_args And since it doesn't appear in ARMEL, you can consider the difference verified. And if you prefer to see everything, you can always use readelf -a for a complete dump of headers, sections, attributes, and symbols.
Compilation, not system: who decides ARMEL or ARMHF
It is important to emphasize one concept: The ARMEL/ARMHF variant is determined by the binary code.It's the compilation process that determines the ELF file's structure, not the abstract "system." If you compile your executable with hard-float support, you'll get an ARMHF ELF; otherwise, it will be ARMEL. This distinction stems from the build process and is hardcoded in the ELF's attributes.
For that reason, if what worries you is deciding which path to take in your own project, it's usually simpler. resolve it at compile time with compiler and linker options, and with preprocessor-conditioned code. It is not common to want to switch between ARMEL and ARMHF in the same binary at runtime. because it involves different ABIs.
Headers generated by the Makefile: a practical technique
If you want the executable itself to declare its "identity" at build time, a very useful technique is to generate a header from the Makefile During the build phase, the build system detects whether you are compiling for armel or armhf and dumps a constant into a header file, which is then included in the source code.
With that approach, the final binary can expose, for example, a command -version that prints whether it was constructed as ARMEL or ARMHF, without needing to "detect" anything at runtime. In general, this practice fits better with the fact that The ABI is set when compiling and it should not vary dynamically.
Why would you want to know that at runtime?
Those who ask this question are sometimes trying to adapt their behavior in the heat of the moment. But the reality is that The usual practice is to separate the code using preprocessor directives and compile different variants, one for armel and another for armhf. Mixing both in the same artifact is not realistic, because each one depends on its own set of libraries and a different ABI.
Hence the general recommendation is to make the decision before compilingand at runtime, simply validate that the target environment has the appropriate libraries for the binary you are about to launch. To audit an already built binary, Reader It remains the most direct tool.
Multiarch: Why you have both arm-linux-gnueabihf and arm-linux-gnueabi
If when listing directories on your system you see that you have arm-linux-gnueabihf and arm-linux-gnueabi coexisting is not necessarily a mistake: that is multiarchIt allows you to install and use multiple architectures or ABI variants in parallel, facilitating cross-compilations and testing.
In Debian environments, for example, there were times when it was decided Remove multiarch in default Wheezy images because the support was green and causing more problems than benefits. Subsequently, Jessie and later versions improved support multiarch, making armel/armhf coexistence more viable without so many headaches.
Interpreting the routes correctly is not enough
Seeing a library in /usr/arm-linux-gnueabihf/lib suggests it's ARMHF, and the same with /usr/arm-linux-gnueabi/lib for ARMEL. But if you want to be sure, Open the ELF and look at its attributesThe paths are helpful as a guide, although in complex systems with multiple archives or manual backups, can mislead.
Again, the exit of readelf -A It provides conclusive proof: the presence of Tag_ABI_VFP_args for hard-float, and its absence for soft-float. Furthermore, the other flags will corroborate this. instructions and extensions that the binary may require.
What else can you learn from -A, besides FP
The ARM attributes section that shows -A doesn't just tell you if there's a VFP. It also tells you if the binary It is marked as Thumb, the architecture variant (e.g., ARMv7), and whether there is support NEONThese details help you verify that the binary is compatible with the target hardware and prevent surprises during deployment.
Consider, for example, validating a runtime environment: along with verifying ARMEL vs ARMHF, Check Thumb and NEON It can alert you to subtle incompatibilities that are not visible at first glance.
Frequently Asked Questions and Quick Tips
- Can I detect it without readelf? Technically, you could infer it from the path or how your own project was built, but the solid method is to inspect the ELF file. `readelf -A` provides the evidence within the binary itself.
- Is it enough to see "VFP" at the exit? Seeing general references to VFP indicates capabilities, but the decisive line is Tag_ABI_VFP_args in ARMHF. Its absence in ARMEL is equally significant.
- What if my readelf says “AAPCS (VFP variant)”? It's an alternative form of the same idea seen in certain builds, such as those based on elftoolchain-0.6.1"AAPCS (base variant)" or "toolchain-specific" may also appear.
- Why do I have both versions installed? By multiarchThis is normal in some environments. Just make sure you link and run with the library set that corresponds to the binary you're using.
A brief note on the sources and their funding
Some technical publications that address these topics include messages for support their work through cryptocurrency donations, platforms like Patreon, or affiliate links to stores like Amazon or AliExpress. It's a common practice in independent media, which sometimes They use affiliate links in their articles to earn a commission if you make a purchase after clicking.
Mental checklist when auditing a binary
Before accepting a library or executable for your device, review these points: readelf query -A, looks for the presence or absence of Tag_ABI_VFP_argsLook at the target architecture, and check if Thumb and NEON fit with the hardware you're going to run it on.
If you work with multiple toolchains installed via multiarch, Pay special attention to the routes The compiler and linker environment variables must be configured to avoid mixing ARMEL headers and libraries with ARMHF binaries, or vice versa. A small oversight can cause problems. confusing symptoms at link time or runtime.
Typical mistakes to avoid
A classic mistake is to be overconfident and assume that a library is hard-float because it is located under arm-linux-gnueabihf, without confirming the ELF attributesAnother common mistake is trying to link an ARMHF executable with ARMEL libraries (or vice versa), which usually results in symbol failures or strange behavior.
It's also common to want to "detect at runtime" to change library paths on the fly. Remember that The ARMEL/ARMHF choice is not improvised at runtimeIt's hardcoded into the binary. Adjust your deployments and packages so that each executable receives the libraries for its own variant.
If you need a quick reference, think of these steps: locate the library or executable (for example, libm.so.6), launch readelf -A Against the file, filter by FP if you want to get straight to the point, and check if there is a line Tag_ABI_VFP_args (hard-float) or if missing (soft-float). Then, check architecture, Thumb, and NEON to complete the binary profile.
When comparing ARMEL and ARMHF in the same system, remember the multiarch context And the fact that the key difference lies in the floating-point ABI defined by the AAPCS. From that perspective, interpreting readelf's attributes becomes a quick and accurate exercise.
It is clear that, with the right tool and knowing where to look, Determining whether an ARM ELF is ARMHF or ARMEL is a matter of secondsThe key is to identify the Tag_ABI_VFP_args attribute for hard-float and leverage readelf -A to obtain additional context about the architecture, Thumb, and NEON. Bearing in mind the particularities of multiarch and that the ABI choice is fixed at compilation, You'll avoid confusion and save time when validating binaries and libraries in your projects.