so 文件使用
CPU 架构:
- mips / mips64: 极少用于手机可以忽
- x86 / x86_64: x86 架构的手机都会包含由 Intel 提供的称为 Houdini 的指令集动态转码工具,实现对 arm .so 的兼容,再考虑 x86 1% 以下的市场占有率,x86 相关的两个 .so 也是可以忽略的
- armeabi: ARM v5 主要 用于 Android 4. 0 之后 的, CPU 是 32 位 的。这是相当老旧的一个版本,缺少对浮点数计算的硬件支持,在需要大量计算时有性能瓶颈支持, 基本 已经 被 淘汰 了
- armeabi-v7a: ARM v7 目前主流版本,向下兼容armeabi
- arm64-v8a: 主要 用于 Android 5. 0 之后, CPU 是 64 位 的。
查找过程。对于一个cpu是arm64-v8a架构的手机,它运行app时,进入jnilibs去读取库文件时,分两种情况:
- 先看有没有arm64-v8a文件夹,如果没有该文件夹,去找armeabi-v7a文件夹,如果没有,再去找armeabi文件夹,如果连这个文件夹也没有,就抛出异常;
- 如果有arm64-v8a文件夹,那么就去找特定名称的.so文件,注意:如果没有找到,不会再往下找了,而是直接抛出异常Exception:Java.lang.UnsatisfiedLinkError: dlopen failed: library “/***.so” not found 。
兼容性:
- Android 上 启动 每个 App, 都会 为 App 创建 一个 虚拟 机。 Android 的 64 位 系统,加载 32 位 的 so 或者 App 时, 会在 创建 一个 64 位 的 虚拟 机 的 同时, 还 创建 一个 32 位 的 虚拟 机, 这样, 就能 兼容 32 位 的 App 应用 了。所以, 在 App 中, 保留 一个 armeabi- v7a 版本 的 so 就 足够 了。 64 位 Android 系统 会在 32 位 的 虚拟 机上 加载 它。
- 所有的x86/x86_64/armeabi-v7a/arm64-v8a设备都支持armeabi架构的.so文件.
- 只保留这一套so文件并不保证100%不发生crash,特别是对旧设备。
- 也会失去对其他对应CPU架构的一些优化,性能有所损失。
实践:
- 可以全部CPU架构都支持,安装包体积会大,稳定、性能最佳
- 根据覆盖用户做选择
- 可以只是用armeabi-v7a一套
- 通过设置ndk.abiFilters显示指定支持的ABI
栈帧还原:调试信息和符号表
- 主要是各种名字以 .debug_ 开头的 section,通过这些 section 可以建立 so 每条指令与源码文件的映射关系,也就是能够对 so 中每条指令找到其对应的源码文件名、文件行号等信息
- 实际调用的是 NDK 提供的的 strip 命令,所用参数为–strip-unneeded
- 删除了调试信息和符号表的 so ,只能保证获取到崩溃调用栈的每个栈帧的相应指令在 so 中的位置,不一定能获取到符号。带调试信息和符号表的 so 可以将崩溃调用栈的每个栈帧还原成其对应的源码文件名、文件行号、函数名等
Hook:动态符号表是连接当前 so 与外部环境的“桥梁”
- PLT(Procedure Linkage Table):过程链接表,用于存储动态链接库中函数的入口地址。当程序调用一个动态链接库中的函数时,首先会跳转到PLT中的对应条目,然后再通过GOT找到实际的函数地址并执行。
- GOT(Global Offset Table):全局偏移表,用于存储动态链接库中函数和变量的实际地址。在程序运行时,动态链接器(dynamic linker)会根据需要将函数和变量的实际地址填充到GOT中。PLT中的条目会通过GOT来找到函数和变量的实际地址。
- 导出符号存储“ID-地址”的映射关系
- 导入符号记录so 调用其他 so 中的函数