NDK编译——ABI管理

文章的内容是从Android开发者官网扒的,为了防止再登不上去,记录一下。官网网址:https://developer.android.com/ndk/guides/abis.html


ABI 管理

本页内容

  1. 支持的 ABI
  2. 为特定 ABI 生成代码
  3. Android 平台上的 ABI 管理

不同 Android 手机使用不同的 CPU,因此支持不同的指令集。CPU 与指令集的每种组合都有其自己的应用二进制界面(或 ABI)。 ABI 可以非常精确地定义应用的机器代码在运行时如何与系统交互。 您必须为应用要使用的每个 CPU 架构指定 ABI。

典型的 ABI 包含以下信息:

  • 机器代码应使用的 CPU 指令集。
  • 运行时内存存储和加载的字节顺序。
  • 可执行二进制文件(例如程序和共享库)的格式,以及它们支持的内容类型。
  • 用于解析内容与系统之间数据的各种约定。这些约定包括对齐限制,以及系统如何使用堆栈和在调用函数时注册。
  • 运行时可用于机器代码的函数符号列表 - 通常来自非常具体的库集。

本页枚举了 NDK 支持的 ABI,并且提供每个 ABI 如何运行的信息。

支持的 ABI


每个 ABI 支持一个或多个指令集。表 1 提供每个 ABI 支持的指令集概览。

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

ABI 支持的指令集 说明
armeabi ARMV5TE 和更高版本Thumb-1 无硬浮点。
armeabi-v7a armeabiThumb-2VFPv3-D16其他(可选) 与 ARMv5、v6 设备不兼容。
arm64-v8a AArch-64
x86 x86 (IA-32)MMXSSE/2/3SSSE3 不支持 MOVBE 或 SSE4。
x86_64 x86-64MMXSSE/2/3SSSE3SSE4.1、4.2POPCNT
mips MIPS32r1 及更高版本 使用硬浮点,并且假设 CPU:FPU 时钟比率为 2:1 以获取最大兼容性。 不提供 micromips 或 MIPS16。
mips64 MIPS64r6  

有关下面每个 ABI 的更多详细信息。

armeabi

此 ABI 适用于基于 ARM、至少支持 ARMv5TE 指令集的 CPU。 请参阅以下文档了解详情:

  • ARM 架构参考手册
  • ARM 架构的过程调用标准
  • ARM ELF 文件格式
  • ARM 架构的应用二进制界面 (ABI)
  • ARM 架构的基本平台 ABI
  • ARM 架构的 C 库 ABI
  • ARM 架构的 C++ ABI
  • ARM 架构的运行时 ABI
  • ELF System V 应用二进制界面
  • 通用/Itanium C++ ABI

AAPCS 标准将 EABI 定义为类似但不同 ABI 的系列。 此外,Android 还采用小字节序 ARM GNU/Linux ABI。

此 ABI 不支持硬件辅助的浮点计算。 相反,所有浮点运算都使用编译器 libgcc.a 静态库中的软件帮助程序函数。

armeabi ABI 支持 ARM 的 Thumb(亦称 Thumb-1)指令集。NDK 默认生成 Thumb 代码,除非您在 Android.mk 文件中使用 LOCAL_ARM_MODE 变量指定不同的行为。

armeabi-v7a

此 ABI 可扩展 armeabi 以包含多个 CPU 指令集扩展。 此 Android 特定 ABI 支持的指令扩展包括:

  • Thumb-2 指令集扩展,其性能堪比 32 位 ARM 指令,简洁性类似于 Thumb-1。
  • VFP 硬件 FPU 指令。更具体一点,包括 VFPv3-D16,它除了 ARM 核心中的 16 个 32 位寄存器之外,还包含 16 个专用 64 位浮点寄存器。

v7-a ARM 规格描述的其他扩展,包括 高级 SIMD(亦称 NEON)、VFPv3-D32 和 ThumbEE,都是此 ABI 可选的。 由于不能保证它们存在,因此系统在运行时应检查扩展是否可用。 如果不可用,则必须使用替代代码路径。此检查类似于系统在检查或使用 MMX、SSE2 及 x86 CPU 上其他专用指令集时所执行的检查。

如需了解有关如何执行这些运行时检查的信息,请参阅 cpufeatures 库。另外,有关 NDK 支持为 NEON 构建机器代码的信息,请参阅 NEON 支持。

armeabi-v7a ABI 使用 -mfloat-abi=softfp 开关强制实施规则,要求编译器在函数调用时必须传递核心寄存器对中的所有双精度值,而不是专用浮点值。 系统可以使用 FP 寄存器执行所有内部计算。 这样可极大地加速计算。

arm64-v8a

此 ABI 适用于基于 ARMv8、支持 AArch64 的 CPU。它还包含 NEON 和 VFPv4 指令集。

如需了解详细信息,请参阅 ARMv8 技术预览,并联系 ARM 了解进一步的详细信息。

x86

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

  • 指令一般由具有编译器标志的 GCC 生成,如下所示:
    -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32
    

    这些标志指向 Pentium Pro 指令集,伴随 MMX、SSE、SSE2、SSE3 及 SSSE3 指令集扩展。生成的代码在顶层 Intel 32 位 CPU 之间进行了均衡优化。

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

  • 使用标准 Linux x86 32 位调用约定,与 SVR 使用的约定相反。如需了解详细信息,请参阅不同 C++ 编译器和操作系统的调用约定的第 6 节“寄存器的使用”。

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

  • MOVBE
  • SSE4 的任何变体。

您仍可使用这些扩展,只要您使用运行时功能探测来启用它们,并且为不支持它们的设备提供备用方法。

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

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

  • GCC 在线文档: Intel 386 和 AMD x86-64 选项
  • 不同 C++ 编译器和操作系统的调用约定
  • Intel IA-32 Intel 架构软件开发者手册第 2 卷:指令集参考
  • Intel IA-32 Intel 架构软件开发者手册第 3 卷:系统编程指南
  • System V 应用二进制界面: Intel386 处理器架构补充

x86_64

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

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

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

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

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

  • MOVBE
  • SHA
  • AVX
  • AVX2

您仍可使用这些扩展,只要您使用运行时功能探测来启用它们,并且为不支持它们的设备提供备用方法。

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

  • 不同 C++ 编译器和操作系统的调用约定
  • Intel64 和 IA-32 架构软件开发者手册第 2 卷:指令集参考
  • Intel64 和 IA-32 架构软件开发者手册第 3 卷:系统编程

mips

此 ABI 适用于基于 MIPS、至少支持 MIPS32r1 指令集的 CPU。它包含以下功能:

  • MIPS32 修订版 1 ISA
  • 小字节序
  • O32
  • 硬浮点
  • 无 DSP 应用特定的扩展

如需了解详细信息,请参阅以下文档:

  • 编程者的架构 ("MIPSARCH")
  • ELF System V 应用二进制界面
  • Itanium/通用 C++ ABI

如需了解更具体的详细信息,请参阅 MIPS32 架构。常见问答请参阅 MIPS FAQ。

mips64

此 ABI 适用于 MIPS64 R6。如需了解详细信息,请参阅 MIPS64 架构。

为特定 ABI 生成代码


默认情况下,NDK 为 armeabi ABI 生成机器代码。但您可以通过向 Application.mk 文件添加以下行生成 ARMv7-a 兼容的机器代码。

APP_ABI := armeabi-v7a

要为两个或更多不同的 ABI 构建机器代码,请使用空格作为分隔符。例如:

APP_ABI := armeabi armeabi-v7a

此设置指示 NDK 为机器代码构建两个版本:此行中所列的每个 ABI 一个。 如需了解有关可以为 APP_ABI 变量指定的值的详细信息,请参阅 Android.mk。

构建多个机器代码版本时,构建系统会将库复制到应用项目路径,并最终将它们封装到 APK 中,从而创建一个胖二进制文件。 胖二进制文件大于只包含一个系统的机器代码的二进制文件;权衡方式是兼容性更广,但 APK 更大。

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

Android 平台上的 ABI 管理


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

应用包中的原生代码

Play 商店和软件包管理器专家预期在 APK 中符合以下模式的文件路径上查找 NDK 生成的库:

/lib//lib.so

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

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

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

/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

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

Android 平台 ABI 支持

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

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

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

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

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

基于 MIPS 的典型设备只定义主要 ABI:mips

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

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

lib//lib.so

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

lib//lib.so

找到所需的库时,软件包管理器会将它们复制到应用的 data 目录 (data/data//lib/) 下的 /lib/lib.so

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


你可能感兴趣的:(NDK开发)