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.abilist32
和ro.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
的具体确认过程见附件文档,总结流程如下:
- 如果 apk 包中 lib 文件夹下有 .so 库,就根据这个 .so 库的架构模式,确定 app 的
primaryCpuAbi
的值。 - 对于 system app, 如果没法通过第一步确定
primaryCpuAbi
的值,PKMS 会根据/system/app/${APP_NAME}/lib64
和/system/app/${APP_NAME}/lib
这两个文件夹是否存在,来确定它的primaryCpuAbi
的值。 - 对于还没有确定的 app, 在最后还会将自己的
primaryCpuAbi
值与和他使用相同 UID 的 package 的值设成一样。 - 对于到这里还没有确认
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)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。