NDK Programmer's Guide
ABI Management

Introduction
Supported ABIs
Generating code for a Specific ABI
ABI management on the Android Platform

Introduction

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:

  • The CPU instruction set that the machine code should use.
  • The endianness of memory stores and loads at runtime.
  • The format of executable binaries (shared libraries, programs, etc.), and the types of content they support.
  • Various conventions used to pass data between your code and the system. These include alignment constraints, as well as how registers and/or the stack are used when functions are called.
  • Alignment and size constraints for enum types, structure fields, and arrays.
  • The list of function symbols available to your machine code at runtime, generally from a very specifically selected set of libraries.

This document lists the exact ABIs supported by the Android NDK and the official Android platform releases.

Supported ABIs

Overview

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
mipsMIPS32r1 and laterHard float.
mips 64MIPS64r6

Table 1. Respective ABI capabilities

More detailed information about each ABI appears below.

armeabi

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 (armeabi-v7a-hard)

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:

  • The Thumb-2 instruction set extension.
  • The VFP hardware FPU instructions. More specifically, VFPv3-D16, which corresponds to 16 dedicated 64-bit floating point registers provided by the CPU.

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.

armeabi-v7a-hard

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/.

arm64-v8a

This ABI is for ARMv8-based CPUs that support AArch64.

For more information, see the ARMv8 Technology Preview, and contact ARM for further details.

x86

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:

  • instructions normally generated by GCC with the following compiler flags:
-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:

  • The MOVBE instruction.
  • Any variant of SSE4.

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:

x86_64

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:

  • the MOVBE instruction
  • the SHA instruction
  • the AVX extension
  • the AVX2 extension

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:

mips

This ABI is for MIPS-based CPUs that support at least the MIPS32r1 instruction set. It includes the following features:

  • MIPS32 revision 1 ISA
  • Little-endian
  • O32
  • Hard-float
  • No DSP application-specific extensions

    For more information, please refer to the following documentation.

  • ELF for the MIPS Architecture ("MIPSELF")
  • FAQ for MIPS Toolchains ("MIPSFAQ")
  • Toolchain Specifics ("MIPSTOOL")
  • SDE Library ("MIPSSDE")
  • Instruction Set Quick Reference ("MIPSISA")
  • Architecture for Programmers ("MIPSARCH")
  • ELF System V Application Binary Interface: DRAFT - 24 April 2001
  • Generic C++ ABI

    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.

mips64

This ABI is for MIPS64 R6.

Generating code for a specific ABI

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.

ABI Management on the Android platform

This section provides details about how the Android platform manages native code in APKs.

Native code in app packages

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.

Android Platform ABI support

The Android system knows at runtime which ABI(s) it supports, because either one or two build-specific system properties indicate:

  • The "primary" ABI for the device, corresponding to the machine code used in the system image itself.
  • An optional "secondary" ABI, corresponding to another ABI that is also supported by the system image.

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.

Automatic extraction of native code at install time

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.