• Android ABI


    目录

    支持的 ABI

    armeabi-v7a

    arm64-v8a

    x86

    x86_64

    为特定 ABI 生成代码

    Android 平台上的 ABI 管理

    应用软件包中的原生代码

    Android 平台 ABI 支持

    安装时自动解压缩原生代码


    ABI就是Application binary interface的意思,即应用程序二进制接口,定义了一套规则。允许编译好的二进制目标代码能在所有兼容该ABI的操作系统中无需改动就能运行。不同的Android手机使用不同的CPU,因此需要提供对应的二进制接口交互规则(即对应的ABI文件)才能进行交互。目前,有部分CPU是能支持多种交互规则,但这是在牺牲性能的前提下所做的兼容。
    不同的 Android 设备使用不同的 CPU,而不同的 CPU 支持不同的指令集。CPU 与指令集的每种组合都有专属的应用二进制接口 (ABI)。ABI 包含以下信息:

    • 可使用的 CPU 指令集(和扩展指令集)。
    • 运行时内存存储和加载的字节顺序。Android 始终是 little-endian。
    • 在应用和系统之间传递数据的规范(包括对齐限制),以及系统调用函数时如何使用堆栈和寄存器。
    • 可执行二进制文件(例如程序和共享库)的格式,以及它们支持的内容类型。Android 始终使用 ELF。如需了解详情,请参阅 ELF System V 应用二进制接口
    • 如何重整 C++ 名称。如需了解详情,请参阅 Generic/Itanium C++ ABI

    本页列举了 NDK 支持的 ABI,并且介绍了每个 ABI 的运行原理。

    ABI 还可以指平台支持的原生 API。如需影响 32 位系统的此类 ABI 问题列表,请参阅 32 位 ABI 错误

    支持的 ABI

    表 1. ABI 和支持的指令集。

    ABI支持的指令集备注
    armeabi-v7a
    • armeabi
    • Thumb-2
    • VFPv3-D16
    与 ARMv5/v6 设备不兼容。
    arm64-v8a
    • AArch64
    x86
    • x86 (IA-32)
    • MMX
    • SSE/2/3
    • SSSE3
    不支持 MOVBE 或 SSE4。
    x86_64
    • x86-64
    • MMX
    • SSE/2/3
    • SSSE3
    • SSE4.1、4.2
    • POPCNT

    注意:NDK 以前支持 ARMv5 (armeabi) 以及 32 位和 64 位 MIPS,但 NDK r17 已不再支持。

    armeabi-v7a

    此 ABI 适用于基于 32 位 ARM 的 CPU。Android 变体包含 Thumb-2 和 VFP 硬件浮点指令(具体而言就是 VFPv3-D16),其中包含 16 个专用 64 位浮点寄存器。

    如需详细了解 ABI 中并非特定于 Android 的部分,请参阅 ARM 架构的应用二进制接口 (ABI)

    默认情况下,NDK 构建系统会生成 Thumb-2 代码,除非您在 Android.mk 中针对 ndk-build 使用 LOCAL_ARM_MODE,或在配置 CMake 时使用 ANDROID_ARM_MODE

    包括高级 SIMD (Neon) 和 VFPv3-D32 在内的其他扩展程序都是可选的。如需了解详情,请参阅 Neon 支持

    armeabi-v7a ABI 使用 -mfloat-abi=softfp 来强制实施以下规则:虽然系统可以执行浮点代码,但编译器在调用函数时必须传递整数寄存器中的所有 float 值以及整数寄存器对中的所有 double 值。

    arm64-v8a

    此 ABI 适用于基于 ARMv8-A 的 CPU,支持 64 位 AArch64 架构。它包含高级 SIMD (Neon) 架构扩展指令集。

    您可以在 C 和 C++ 代码中使用 Neon 内建函数来充分利用高级 SIMD 扩展指令集。针对 Armv8-A 的 Neon 程序员指南详细介绍了 Neon 内建函数和 Neon 编程的概况。

    如需了解 ABI 中并非特定于 Android 的部分的完整详情,请参阅 Arm 的了解架构。Arm 还针对64 位 Android 开发提供了一些移植方面的建议。

    在 Android 中,特定于平台的 x18 寄存器专用于 ShadowCallStack,不应由您的代码使用。当前的 Clang 版本默认使用 Android 中的 -ffixed-x18 选项,因此除非您使用的是手写编译器(或非常旧的编译器),否则无需担心这一点。

    x86

    此 ABI 适用于支持通常称为“x86”、“i386”或“IA-32”的指令集的 CPU。此 ABI 的特性包括:

    • 指令一般由具有编译器标志的 GCC 生成,如下所示:

      -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32
      

      这些标志指向 Pentium Pro 指令集,以及 MMXSSESSE2SSE3 和 SSSE3 扩展指令集。生成的代码在顶层 Intel 32 位 CPU 之间进行了均衡优化。

      如需了解有关编译器标志的更多信息,特别是与性能优化相关的信息,请参阅 GCC x86 性能提示

    • 使用标准 Linux x86 32 位调用规范,与 SVR 使用的规范相反。详情请参阅不同 C++ 编译器和操作系统的调用规范的第 6 部分“寄存器的使用”。

    ABI 不含任何其他可选 IA-32 扩展指令集,例如:

    • MOVBE
    • SSE4 的任何变体。

    您仍可使用这些扩展指令集,只要您使用运行时功能探测来启用它们,并且为不支持它们的设备提供回退机制。

    NDK 工具链假设在函数调用之前进行 16 字节堆栈对齐。默认工具和选项会强制实施此规则。如果编写的是汇编代码,必须确保堆栈对齐,而且其他编译器也遵守此规则。

    请参阅以下文档了解更多详情:

    x86_64

    此 ABI 适用于支持通常称为“x86-64”的指令集的 CPU。它支持 GCC 通常使用以下编译器标志生成的指令:

    -march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel
    

    这些标志指向 x86-64 指令集(根据 GCC 文档),以及 MMXSSESSE2SSE3SSSE3SSE4.1SSE4.2 和 POPCNT 扩展指令集。生成的代码在顶层 Intel 64 位 CPU 之间进行了均衡优化。

    如需了解有关编译器标志的更多信息,特别是与性能优化相关的信息,请参阅 GCC x86 性能提示

    此 ABI 不含任何其他可选的 x86-64 扩展指令集,例如:

    • MOVBE
    • SHA
    • AVX
    • AVX2

    您仍可使用这些扩展指令集,只要您使用运行时功能探测来启用它们,并且为不支持它们的设备提供回退机制。

    请参阅以下文档了解更多详情:

    为特定 ABI 生成代码

    Gradlendk-buildCMake

    默认情况下,Gradle(无论是通过 Android Studio 使用,还是从命令行使用)会针对所有非弃用 ABI 进行构建。要限制应用支持的 ABI 集,请使用 abiFilters。例如,要仅针对 64 位 ABI 进行构建,请在 build.gradle 中设置以下配置:

    1. android {
    2.     defaultConfig {
    3.         ndk {
    4.             abiFilters 'arm64-v8a', 'x86_64'
    5.         }
    6.     }
    7. }

    构建系统的默认行为是将每个 ABI 的二进制文件包括在单个 APK(也称为胖 APK)内。与仅包含单个 ABI 的二进制文件的 APK 相比,胖 APK 要大得多;要权衡的是兼容性更广,但 APK 更大。强烈建议您利用 app bundle 和 APK 拆分减小 APK 的大小,同时仍保持最大限度的设备兼容性。

    在安装时,软件包管理器只解压缩最适合目标设备的机器代码。如需了解详情,请参阅安装时自动解压缩原生代码

    Android 平台上的 ABI 管理

    本部分详细说明了 Android 平台如何管理 APK 中的原生代码。

    应用软件包中的原生代码

    Play 商店和软件包管理器都希望能在 APK 中符合以下格式的文件路径上找到 NDK 生成的库:

    /lib//lib.so
    

    其中, 是支持的 ABI 下列出的 ABI 名称之一, 是您为 Android.mk 文件中的 LOCAL_MODULE 变量定义库时使用的库名称。由于 APK 文件只是 zip 文件,因此打开它们并确认共享原生库位于该位于的位置很简单。

    如果系统在预期位置找不到原生共享库,便无法使用它们。在这种情况下,应用本身必须复制这些库,然后执行 dlopen()

    在胖 APK 中,每个库位于名称与相应 ABI 匹配的目录下。例如,胖 APK 可能包含:

    /lib/armeabi/libfoo.so
    /lib/armeabi-v7a/libfoo.so
    /lib/arm64-v8a/libfoo.so
    /lib/x86/libfoo.so
    /lib/x86_64/libfoo.so
    

    注意:搭载 4.0.3 或更早版本、基于 ARMv7 的 Android 设备从 armeabi 目录(而非 armeabi-v7a 目录,如果两个目录都存在)安装原生库。这是因为在 APK 中,/lib/armeabi/ 在 /lib/armeabi-v7a/ 后面。从 4.0.4 开始,此问题已修复。

    Android 平台 ABI 支持

    Android 系统在运行时知道它支持哪些 ABI,因为 build 特定的系统属性会指示:

    • 设备的主要 ABI,与系统映像本身使用的机器代码对应。
    • (可选)与系统映像也支持的其他 ABI 对应的辅助 ABI。

    此机制确保系统在安装时从软件包提取最佳机器代码。

    为实现最佳性能,应直接针对主要 ABI 进行编译。例如,基于 ARMv5TE 的典型设备只会定义主 ABI:armeabi。相反,基于 ARMv7 的典型设备将主 ABI 定义为 armeabi-v7a,并将辅助 ABI 定义为 armeabi,因为它可以运行为每个 ABI 生成的应用原生二进制文件。

    64 位设备也支持其 32 位变体。以 arm64-v8a 设备为例,该设备也可以运行 armeabi 和 armeabi-v7a 代码。但请注意,如果应用以 arm64-v8a 为目标,而非依赖于运行 armeabi-v7a 版应用的设备,则应用在 64 位设备上的性能要好得多。

    许多基于 x86 的设备也可运行 armeabi-v7a 和 armeabi NDK 二进制文件。对于这些设备,主 ABI 将是 x86,辅助 ABI 是 armeabi-v7a

    您可以为特定 ABI 强制安装 apk。这在测试时非常有用。请使用以下命令:

    adb install --abi abi-identifier path_to_apk
    

    安装时自动解压缩原生代码

    安装应用时,软件包管理器服务将扫描 APK,并查找以下形式的任何共享库:

    lib//lib.so
    

    如果未找到,并且您已定义辅助 ABI,该服务将扫描以下形式的共享库:

    lib//lib.so
    

    找到所需的库时,软件包管理器会将它们复制到应用的原生库目录 (/) 下的 /lib/lib.so。以下代码段会收到 nativeLibraryDir

    KotlinJava

    import android.content.pm.PackageInfo
    import android.content.pm.ApplicationInfo
    import android.content.pm.PackageManager
    ...
    val ainfo = this.applicationContext.packageManager.getApplicationInfo(
            "com.domain.app",
            PackageManager.GET_SHARED_LIBRARY_FILES
    )
    Log.v(TAG, "native library dir ${ainfo.nativeLibraryDir}")
    

    如果根本没有共享对象文件,应用也会构建并安装,但在运行时会崩溃。

  • 相关阅读:
    Pytorch实战教程(二)-PyTorch基础
    Unity 之 Post Processing后处理不同项目配置(URP项目配置)
    Java常见面试题
    机器学习笔记-02
    akshare复权算法-港股复权后数据代码分享
    PI3K α/β 靶向抑制剂协同 BCL-2 阻断过程可用于治疗 DLBCL
    软考高级系统架构师_计算机组成与结构---备考笔记004
    LeetCode每日一题(双指针)
    【LLM】浅谈 StreamingLLM中的attention sink和sink token
    leetcode每日一题寒假版-1805.字符串中不同整数的数目(easy)
  • 原文地址:https://blog.csdn.net/s_nshine/article/details/126217287