逆向工程 --- 编译安卓so
编译安卓平台的共享库(.so 文件)主要有以下几种常用方法,核心工具是 Android NDK (Native Development Kit):
📌 核心方法
-
使用
ndk-build脚本 (基于传统的Android.mk/Application.mk)-
原理: NDK 提供的
ndk-build脚本是一个封装好的工具,它会调用底层的编译工具链(通常是make和你的Android.mk/Application.mk文件)。 -
步骤:
-
安装 NDK: 下载并安装 Android NDK。
-
创建
jni目录: 在你的 Android 项目根目录下创建一个名为jni的文件夹。 -
编写
Android.mk: 在jni目录下创建Android.mk文件。这个 Makefile 定义了如何编译你的源代码(指定源文件、库依赖、编译标志、链接标志、输出模块名等)。 -
(可选) 编写
Application.mk: 在jni目录下创建Application.mk文件(可选,但常用)。它用于配置项目范围的设置,如目标 ABI (APP_ABI)、目标平台 (APP_PLATFORM)、使用的 STL (APP_STL)、编译模式 (APP_OPTIM) 等。 -
运行
ndk-build: 在项目根目录(包含jni目录的上级目录)打开命令行/终端,运行ndk-build命令(确保ndk-build在系统 PATH 中,或者指定其完整路径,如path/to/your/ndk/ndk-build)。
-
-
输出: 编译生成的
.so文件会默认放在项目根目录下的libs/目录里,按 ABI 分类(如libs/armeabi-v7a/,libs/arm64-v8a/,libs/x86/等)。 -
优点: 直接,传统方式,NDK 原生支持。
-
缺点: 语法相对 CMake 稍显复杂,灵活性稍低。
-
-
使用 CMake (官方推荐,与 Android Studio/Gradle 集成最佳)
-
原理: CMake 是一个跨平台的构建系统生成器。你编写一个
CMakeLists.txt文件来描述项目的构建过程,然后 CMake 会根据目标平台生成相应的构建文件(如 Ninja 或 Makefile),最后由生成的构建文件调用 NDK 工具链进行实际编译。 -
步骤 (通常与 Android Studio/Gradle 结合):
-
安装 NDK 和 CMake: 在 Android Studio 的 SDK Manager 中安装 NDK 和 CMake。
-
创建
CMakeLists.txt文件: 在项目的合适位置(通常放在模块的cpp目录旁)创建CMakeLists.txt文件。这个文件使用 CMake 语法定义库、可执行文件、源文件、头文件路径、链接库等。 -
配置
build.gradle(Module level): 在模块的build.gradle文件中配置 CMake:android { ... defaultConfig { ... externalNativeBuild { cmake { cppFlags "-std=c++17" // 设置 C++标准版本等编译标志 arguments "-DANDROID_STL=c++_shared" // 设置 CMake 变量 } } ndk { abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' // 指定目标 ABI } } ... externalNativeBuild { cmake { path "src/main/cpp/CMakeLists.txt" // 指向你的 CMakeLists.txt 文件 version "3.22.1" // 指定 CMake 版本 } } } -
构建项目: 在 Android Studio 中点击
Build>Make Project,或者使用 Gradle 命令行任务 (./gradlew assembleDebug或./gradlew assembleRelease)。Gradle 会自动调用 CMake 和 NDK 工具链进行编译。
-
-
输出: 编译生成的
.so文件会打包到最终的 APK 中,并在构建过程中存放在模块的build/intermediates/cmake/目录下(具体路径取决于构建变体)。 -
优点:
-
官方推荐,与 Android Studio 和 Gradle 深度集成,体验流畅。
-
跨平台(同一套
CMakeLists.txt理论上也可用于其他平台)。 -
语法更现代、清晰、功能强大,社区支持好。
-
管理大型项目或复杂依赖更方便。
-
-
缺点: 需要学习 CMake 语法。
-
-
使用独立的工具链 (Standalone Toolchain)
-
原理: 利用 NDK 提供的
make_standalone_toolchain.py脚本(或在较新 NDK 中直接使用clang)创建一个针对特定目标平台(如arm-linux-androideabi)的独立编译工具链环境(包含编译器clang++、链接器、sysroot 等)。然后,你可以像在 Linux 上编译普通共享库一样,使用这个工具链里的编译器进行编译,或者将其集成到你自己的构建系统(如 Autotools)中。 -
步骤:
-
创建独立工具链: 使用 NDK 中的
make_standalone_toolchain.py(旧版推荐)或直接设置环境变量指向 NDK 的toolchains/llvm/prebuilt/<host>/bin目录下的编译器(新版更推荐直接使用)。 -
设置环境变量: 设置
CC,CXX,LD,AR,STRIP,CFLAGS,CXXFLAGS,LDFLAGS等环境变量,指向独立工具链中的工具,并包含必要的头文件和库路径(--sysroot)。 -
运行 configure / make: 如果你的项目使用 Autotools (
./configure && make),或者直接使用编译命令:$TOOLCHAIN/bin/clang++ --target=armv7a-linux-androideabi21 -fPIC -shared -o libnative.so source1.cpp source2.cpp
-
-
输出: 编译生成的
.so文件在你指定的输出目录。 -
优点: 灵活性最高,可以无缝集成到现有的、非 Android 特定的构建系统(如 Autotools)中,或者用于交叉编译大型第三方库。
-
缺点: 配置相对复杂,需要手动管理环境变量和编译/链接标志,与 Android Studio/Gradle 集成度低。
-
🔧 关键要素(所有方法都需要考虑)
-
NDK 版本: 确保使用合适的 NDK 版本。较新的 NDK 移除或废弃了一些旧工具链和 API。Android Studio 的 SDK Manager 是获取官方 NDK 的最佳途径。
-
ABI (Application Binary Interface): 你需要为哪些 CPU 架构编译
.so文件?常见的有:-
armeabi-v7a(32 位 ARM, 兼容性强) -
arm64-v8a(64 位 ARM, 现代主流手机) -
x86(32 位 Intel/AMD, 模拟器/旧设备) -
x86_64(64 位 Intel/AMD, 现代模拟器/部分设备) -
mips/mips64(已废弃,基本无需考虑)
在Application.mk(APP_ABI) 或 Gradle (ndk.abiFilters) 中指定。编译所有支持的 ABI 会使 APK 变大。
-
-
Android 平台版本 (
APP_PLATFORM/minSdkVersion): 指定你的库所支持的最低 Android API 级别。这会影响可用的 NDK API (如libandroid.so, OpenSL ES, Vulkan 等)。通常与minSdkVersion保持一致或略高。在Application.mk或 CMake (通过minSdkVersion或-DANDROID_PLATFORM) 中设置。 -
C++ 运行时库 (
APP_STL): 选择使用哪个 C++ 标准库实现:-
c++_shared: 动态链接的 libc++ (推荐,节省空间,但需打包多个.so或确保设备存在)。 -
c++_static: 静态链接的 libc++ (增大单个.so体积,但无需额外打包)。 -
system: 使用系统默认(旧且不推荐,兼容性差)。 -
none: 无 C++ 标准库(纯 C 项目)。
在Application.mk或 CMake (-DANDROID_STL=...) 中指定。
-
-
编译器和编译标志: NDK 现在默认且强烈推荐使用 Clang 编译器 (
clang,clang++)。GCC 已被废弃。在Android.mk(LOCAL_CFLAGS,LOCAL_CPPFLAGS,LOCAL_CPP_FEATURES) 或CMakeLists.txt(target_compile_options,target_compile_features,add_compile_options) 中设置优化级别 (-O2,-Os)、调试信息 (-g)、C/C++ 标准 (-std=c++17)、警告级别 (-Wall,-Werror) 等。 -
链接标志: 在
Android.mk(LOCAL_LDLIBS,LOCAL_LDFLAGS) 或CMakeLists.txt(target_link_libraries,target_link_options) 中指定链接的库(如-llog链接 Android Log 库)和其他链接器选项。
📌 推荐选择
-
对于新的 Android 项目或在 Android Studio 中开发: 强烈推荐使用 CMake + Gradle。这是官方标准流程,集成度最高,管理方便,功能强大。
-
维护旧的基于
ndk-build的项目: 继续使用ndk-build和Android.mk/Application.mk。 -
交叉编译大型第三方库 (如 OpenCV, FFmpeg) 到 Android: 通常使用 独立工具链 方法,结合库自身的构建系统 (如
configure脚本) 来完成。有时这些库也直接提供了 CMake 支持。
✅ 验证 .so 文件
-
file命令 (Linux/macOS):file libnative.so会显示文件类型和 ELF 信息,确认其是针对 Android 的正确 ABI (如ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked). -
nm命令:nm -D libnative.so可以查看动态符号表,检查预期的函数是否被导出。 -
readelf命令:readelf -d libnative.so查看动态段信息,检查依赖的动态库 (NEEDED) 是否正确(如liblog.so,libc++_shared.so)。 -
在设备/模拟器上运行: 最终极的测试是将包含
.so的 APK 安装到目标设备或模拟器上运行,看功能是否正常,并通过logcat查看是否有加载库或符号找不到的错误。
选择哪种方法取决于你的项目需求、熟悉程度以及是否要与 Android Studio/Gradle 集成。CMake + Gradle 是目前最主流和推荐的方式。
浙公网安备 33010602011771号