ABI

ABI 即 Application binary interface,是 CPU 与指令集专属的应用程序二进制接口。它定义了一套规则,允许编译好的二进制目标代码能在所有兼容该ABI的操作系统中无需改动就能运行。不同的 Android 设备使用不同的 CPU,而不同的 CPU 支持不同的指令集。

ABI 包含以下信息:

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

ABI 和指令集

  • NDK 以前支持 ARMv5 (armeabi) 以及 32 位和 64 位 MIPS,但 NDK r17 已不再支持。
  • APK 在路径 /lib/<abi>/lib<name>.so 中寻找 NDK 生成的库。
  • 安装应用时,软件包管理器服务将扫描 APK,按照主辅及偏好顺序查找共享库,将它们复制到应用的原生库目录 。
  • 强制使用特定 ABI 安装 APK,通过 adb install --abi abi-identifier path_to_apk
  • armeabi不支持硬件辅助的浮点运算,所有浮点运算都使用编译器的 libgcc.a 静态库中的软件辅助函数。
ABI支持的指令集备注
armeabi-v7aarmeabi,Thumb-2,VFPv3-D16与 ARMv5/v6 设备不兼容。
arm64-v8aAArch64
x86x86 (IA-32),MMX,SSE/2/3,SSSE3不支持 MOVBE 或 SSE4。
x86_64x86-64,MMX,SSE/2/3,SSSE3,SSE4.1、4.2,POPCNT

ABI 与 CPU

  • 大多数 CPU 都支持多种 ABI,其中一个为主 ABI,其他为辅助 ABI。
  • 获得最佳性能,最好使用 CPU 的主 ABI。
  • Android 系统根据特定的系统属性来决定主辅 ABI。
  • x86设备包含ARM模拟层,能够很好地运行ARM类型的so库,但并不保证100%不发生Crash。
  • 64位设备(arm64-v8a, x86_64, mips64)能够运行32位的so库。但是以32位模式运行时,会丢失专为64位优化过的性能特征(ART, WebView, Media, etc.)。
CPU主ABI支持的ABI
ARMv5armeabiarmeabi
ARMv7armeabi-v7aarmeabi ,armeabi-v7a
ARMv8arm64-v8aarmeabi ,armeabi-v7a,arm64-v8a
x86x86armeabi ,armeabi-v7a ,x86
x86_64x86_64armeabi ,x86 ,x86_64

ABI 的系统属性

[ro.product.cpu.abilist] : [arm64-v8a, armeabi-v7a, armeabi]
[ro.product.cpu.abilist32] : [armeabi-v7a, armeabi]
[ro.product.cpu.abilist64] : [arm64-v8a]
  • ro.product.cpu.abilist 的值表明当前系统所支持所有的ABI类型
  • ro.product.cpu.abilist32ro.product.cpu.abilist64 分别表示系统所支持的32位和64位的 ABI 类型。
  • 这些属性值中 ABI 的排序代表着系统偏好。比如 ro.product.cpu.abilist 的值里 arm64-v8a 排在第一个,就表明如果没有指定,arm64-v8a 就会成为 APP 进程默认启动的关联 ABI。

为应用指定 ABI

应用编译时,默认情况下,Gradle 会为所有 NDK 支持的 ABI 构建独立的 .so 文件,并将这些文件全部打包到您的应用中。如果您希望 Gradle 仅构建和打包原生库的特定 ABI 配置,可以在模块级 build.gradle 文件中使用 ndk.abiFilters 标志指定这些配置,如下所示:

android {
  ...
  defaultConfig {
    ...
    externalNativeBuild {
      cmake {...}
      // or ndkBuild {...}
    }

    // Similar to other properties in the defaultConfig block,
    // you can configure the ndk block for each product flavor
    // in your build configuration.
    ndk {
      // Specifies the ABI configurations of your native
      // libraries Gradle should build and package with your app.
      abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
                   'arm64-v8a'
    }
  }
  buildTypes {...}
  externalNativeBuild {...}
}

应用安装时 ABI 的确认

应用安装后,会在 /data/system/packages.xm 中添加记录,其中 primaryCpuAbi 决定了该应用使用的 ABI。 primaryCpuAbi 的具体确认过程见附件文档,总结流程如下:

  1. 如果 apk 包中 lib 文件夹下有 .so 库,就根据这个 .so 库的架构模式,确定 app 的 primaryCpuAbi 的值。
  2. 对于 system app, 如果没法通过第一步确定 primaryCpuAbi 的值,PKMS 会根据 /system/app/${APP_NAME}/lib64/system/app/${APP_NAME}/lib 这两个文件夹是否存在,来确定它的 primaryCpuAbi 的值。
  3. 对于还没有确定的 app, 在最后还会将自己的 primaryCpuAbi 值与和他使用相同 UID 的 package 的值设成一样。
  4. 对于到这里还没有确认 primaryCpuAbi 的 app,就会在启动进程时使用 ro.product.cpu.abilist 这个 property 的值的第一项作为它关联的 ABI。

ABI 的编译配置

Android.bp 通过 compile_multilib 来决定编译32位还是64位的 ABI。例如,

cc_binary {
    srcs: ["test.c"],
    name: "test",
    compile_multilib: "both", // 同时编译32位和64位
    //compile_multilib: "32", // 只编译32位
    //compile_multilib: "64", // 只编译64位
    //compile_multilib: "first", // 根据主ABI的arch编译 
    //compile_multilib: "prefer32", // 如果系统支持32位则编译32位,否则编译64位
}

Android.mk 通过 LOCAL_MULTILIB 来控制编译。例如,

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
 
LOCAL_MULTILIB := both # 同时编译32位和64位
# LOCAL_MULTILIB := 32 # 只编译32位
# LOCAL_MULTILIB := 64 # 只编译64位
# LOCAL_MULTILIB := first # 根据主ABI的arch编译

TARGET_PREFER_32_BIT := true # 如果系统支持32位则编译32位,否则编译64位

LOCAL_SRC_FILES := test.c
LOCAL_MODULE := test
LOCAL_MODULE_TAGS := optinal
include $(BUILD_EXECUTABLE)
参考文档:

Android ABI
Android中app进程ABI确定过程


戈壁老王
143 声望61 粉丝

做为一个不称职的老年码农,一直疏忽整理笔记,开博记录一下,用来丰富老年生活,