NDK Programmer's Guide
|
Introduction
Supported ABIs
Generating code for a Specific ABI
ABI management on the Android Platform
Every piece of native code generated with the Android NDK matches a given application binary interface ("ABI") that defines exactly how your application's machine code is expected to interact with the system at runtime.
A typical ABI describes things in excruciating detail, and typically includes the following information:
This document lists the exact ABIs supported by the Android NDK and the official Android platform releases.
Table 1 provides an at-a-glance overview of each ABI's supported instruction sets and capabilities.
Instruction Set(s) | Notes | |
---|---|---|
armeabi | ARMV5TE and later Thumb-1 | No hard float. |
armeabi-v7a | armeabi Thumb-2 VFPv3-D16 other optional | Hard float when specified as
armeabi-v7a-hard .Incompatible with ARMv5, v6 devices. |
arm64-v8a | AArch-64 | |
x86 | x86 (IA-32) MMX SSE/2/3 SSSE3 | No support for MOVBE or SSE4. |
x86_64 | x86-64 MMX SSE/2/3 SSSE3 SSE4.1, 4.2 POPCNT | |
mips | MIPS32r1 and later | Hard float. |
mips 64 | MIPS64r6 |
Table 1. Respective ABI capabilities
More detailed information about each ABI appears below.
This is the name of an ABI for ARM-based CPUs that support at least the ARMv5TE instruction set. Please refer to the following documentation for more details:
Note that the AAPCS standard defines EABI as a family of similar but distinct ABIs. Also, Android follows the little-endian ARM GNU/Linux ABI as documented in the ARM GNU/Linux Application Binary Interface Supplement
This ABI does not support hardware-assisted floating point
computations. Instead, all floating-point operations use software helper
functions from the compiler's libgcc.a
static library.
The armeabi ABI supports ARM’s Thumb (a.k.a. Thumb-1) instruction set. The NDK generates Thumb
code by default unless you specify different behavior using the
LOCAL_ARM_MODE
variable in your Android.mk
file (see
the Andr
oid.mk section for details).
armeabi-v7a extends armeabi to include a few CPU instruction set extensions as described in the following document:
The instruction extensions supported by this Android-specific ABI are:
Other extensions described by the v7-a ARM, like Advanced SIMD (a.k.a. NEON), VFPv3-D32, or ThumbEE are optional to this ABI. This means that developers should check at runtime whether the extensions are available. If not, they must use alternative code paths. This check is similar to the one typically performed to check/use MMX), SSE2 and other specialized instruction sets on x86 CPUs.
For information about how to perform these runtime checks, refer to CPU Features. Also, for information about the NDK's support for building NEON-capable machine code, see CPU-ARM-NEO N.
Note that armeabi-v7a uses the -mfloat-abi=softfp
switch to
enforce the rule that all double values must be passed during function calls in
"core" register pairs, instead of dedicated FP ones. All internal computations
can be performed, however, with the FP registers, which speeds them up
greatly.
Although this constraint results in a modest performance hit, it ensures
compatibility with all existing armeabi binaries. If you need the additional
performance, you can specify your ABI as armeabi-v7a-hard instead. Doing so
allows you to use hard floats, while still linking with Android native APIs
that use softfp
. For more information, look inside the
Android.mk
file in /tests/device/hard-float/jni/
.
NOTE: The armeabi-v7a machine code does not run on ARMv5 or ARMv6 based devices.
NOTE: You cannot specify APP_ABI
as both armeabi-v7a and
armeabi-v7a-hard. In either case, the build system places the shared libraries
in the armeabi-v7a/
directory.
This variant of the armeabi-v7a ABI is unique to the NDK. The NDK build system adds the following flags in addition to those that it uses for the armeabi-v7a ABI.
TARGET_CFLAGS += -mhard-float -D_NDK_MATH_NO_SOFTFP=1 TARGET_LDFLAGS += -Wl,--no-warn-mismatch -lm_hard
All code is compiled with hard-float and linked with libm_hard.a. This
is the same math library as libm.a, except that it follows hard-float ABI
conventions. The generated shared libraries are stored in
lib/armeabi-v7a/
.
This ABI is for ARMv8-based CPUs that support AArch64.
For more information, see the ARMv8 Technology Preview, and contact ARM for further details.
This is the name of an ABI for CPUs supporting the instruction set commonly referred to as "x86" or "IA-32". Characteristics of this ABI include:
-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32
These flags target the the Pentium Pro instruction set, along with the the MMX, SSE, SSE2, SSE3, and SSSE3 instruction set extensions. The generated code is a balanced optimization across the top Intel 32-bit CPUs.
NOTE: The compiler flags shown above do not represent a comprehensive
list. In particular, they do not include compiler optimization options used by
default and/or recommended for x86 performance improvement. For performance
optimization hints on x86 GCC, refer to Intel's article titled GCC
x86 Performance. Also, to investigate what other flags may be useful,
especially -f*
and -m
, see GCC online documentation: Intel 386 and AMD x86-64
Options.
The ABI does not include any other optional IA-32 instruction set extensions, including, but not limited to:
You can still use these, as long as you use runtime feature-probing to enable them, and provide fallbacks for devices that do not support them.
The NDK toolchain assumes 16 byte stack alignment. Tools and options used by default enforce this rule. It is the user's responsibilty to ensure that stack alignment is maintained if they write assembly code, and to ensure that other compilers also obey this rule.
Please refer to the following documents for more details:
This ABI is for CPUs supporting the instruction set commonly referred to as "x86-64." It supports instructions normally generated by GCC with the following compiler flags:
-march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel
These flags target the x86-64 instruction set, according to the GCC documentation. along with the MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1,SSE4.2, and POPCNT instruction-set extensions. The generated code is a balanced optimization across the top Intel 64-bit CPUs.
NOTE: The compiler flags shown above do not represent a comprehensive list. In particular, they do not include compiler optimization options used by default and/or recommended for x86-64 performance improvement. For performance optimization hints on x86-64 GCC, refer to Intel's article titled GCC x86 Performance.
The ABI does not include any other optional x86-64 instruction set extensions, including, but not limited to:
You can still use these, as long as you use runtime feature probing to enable them, and provide fallbacks for devices that do not support them.
Please refer to the following documents for more details:
This ABI is for MIPS-based CPUs that support at least the MIPS32r1 instruction set. It includes the following features:
No DSP application-specific extensions
For more information, please refer to the following documentation.
The MIPS-specific documentation is available here, with further information here.
NOTE:: This ABI assumes a CPU:FPU clock ratio of 2:1 for maximum compatibility.
NOTE:: Neither MIPS16 nor micromips is provided.
This ABI is for MIPS64 R6.
By default, the NDK generates machine code for the armeabi ABI. You can
generate ARMv7-a-compatible machine code, instead, by adding the following line
to your Application.mk
file.
APP_ABI := armeabi-v7a
It is also possible to build machine code for two or more distinct ABIs, using spaces as delimiters. For example:
APP_ABI := armeabi armeabi-v7a
This instructs the NDK to build two versions of your machine code: one for each ABI listed on this line. The build system copies the libraries to your application project path, and ultimately packages them into your APK, creating a fat binary.
At installation time, the package manager unpacks only the most appropriate machine code for the target device. For details, see Automatic extraction of native code at install time.
You can also generate machine code for all NDK-supported ABIs:
APP_ABI := all
Doing so will ensure that your app package contains libraries for all target ABIs. The tradeoff is that a fat binary is larger than one for a single system.
This section provides details about how the Android platform manages native code in APKs.
Both the Play Store and Package Manager expect to find NDK-generated libraries on filepaths inside the APK matching the following pattern:
lib/<abi>/lib<name>.so
<abi> is one of the ABI names listed in the Supported ABIs section of this document, and <name> is the name that the app uses to load the library from the VM. For example:
System.loadLibrary("<name>");
Since APK files are just zip files, it is trivial to examine them and confirm that the shared native libraries are where they belong.
If the native shared libraries are not where the system expects to find
them, it can't do anything with them. In such a case, the app itself has to
copy the libraries over, and perform dlopen()
.
Multiple libraries can be placed into a fat binary. For example:
lib/armeabi/libfoo.so lib/armeabi-v7a/libfoo.so lib/arm64-v8a/libfoo.so lib/x86/libfoo.so lib/x86_64/libfoo.so lib/mips/libfoo.so lib/mips64/libfoo.so
NOTE:: ARMv7-based Android devices running 4.0.3 or earlier
install native libraries from the 'armeabi' directory instead of the
'armeabi-v7a' directory if both exist. lib/armeabi
is listed after
lib/armeabi-v7a
in APK. This issue is fixed from 4.0.4.
The Android system knows at runtime which ABI(s) it supports, because either one or two build-specific system properties indicate:
To achieve the best performance for your NDK component, you should compile directly for the primary ABI.
For example, a typical ARMv5TE-based device would only define the primary ABI: armeabi. By contrast, a typical ARMv7-based device would define the primary ABI as armeabi-v7a and the secondary one as armeabi, since it can run application native binaries generated for each of them.
Many x86-based devices can also run armeabi-v7a and armeabi NDK binaries, and defining the primary ABI as x86 and the secondary one as armeabi-v7a.
A typical MIPS-based device only defines a primary abi: mips.
When installing an application, the package manager service scans the APK, and looks for any shared library of the form:
lib/<primary-abi>/lib<name>.so
If none is found, and a secondary ABI is defined, the service scans for shared libraries of the form:
lib/<secondary-abi>/lib<name>.so
When it finds libraries that it's looking for, the package manager
copies them to /lib/lib<name>.so
, under the application's
data directory (/data/data/<package_name>/lib/
). If there is
no .so
file, the application builds and installs, but crashes at
runtime.
This mechanism ensures that the system extracts the best machine code from the package at installation time.