关于android:Android平台上ABI的确认

37次阅读

共计 3172 个字符,预计需要花费 8 分钟才能阅读完成。

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

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
ARMv5 armeabi armeabi
ARMv7 armeabi-v7a armeabi,armeabi-v7a
ARMv8 arm64-v8a armeabi,armeabi-v7a,arm64-v8a
x86 x86 armeabi,armeabi-v7a,x86
x86_64 x86_64 armeabi,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 确定过程

正文完
 0