GKLBB

当你经历了暴风雨,你也就成为了暴风雨

导航

逆向工程 --- 编译安卓so

编译安卓平台的共享库(.so 文件)主要有以下几种常用方法,核心工具是 Android NDK (Native Development Kit):

📌 核心方法

  1. 使用 ndk-build 脚本 (基于传统的 Android.mk / Application.mk)

    • 原理: NDK 提供的 ndk-build 脚本是一个封装好的工具,它会调用底层的编译工具链(通常是 make 和你的 Android.mk/Application.mk 文件)。

    • 步骤:

      1. 安装 NDK: 下载并安装 Android NDK。

      2. 创建 jni 目录: 在你的 Android 项目根目录下创建一个名为 jni 的文件夹。

      3. 编写 Android.mk: 在 jni 目录下创建 Android.mk 文件。这个 Makefile 定义了如何编译你的源代码(指定源文件、库依赖、编译标志、链接标志、输出模块名等)。

      4. (可选) 编写 Application.mk: 在 jni 目录下创建 Application.mk 文件(可选,但常用)。它用于配置项目范围的设置,如目标 ABI (APP_ABI)、目标平台 (APP_PLATFORM)、使用的 STL (APP_STL)、编译模式 (APP_OPTIM) 等。

      5. 运行 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 稍显复杂,灵活性稍低。

  2. 使用 CMake (官方推荐,与 Android Studio/Gradle 集成最佳)

    • 原理: CMake 是一个跨平台的构建系统生成器。你编写一个 CMakeLists.txt 文件来描述项目的构建过程,然后 CMake 会根据目标平台生成相应的构建文件(如 Ninja 或 Makefile),最后由生成的构建文件调用 NDK 工具链进行实际编译。

    • 步骤 (通常与 Android Studio/Gradle 结合):

      1. 安装 NDK 和 CMake: 在 Android Studio 的 SDK Manager 中安装 NDK 和 CMake。

      2. 创建 CMakeLists.txt 文件: 在项目的合适位置(通常放在模块的 cpp 目录旁)创建 CMakeLists.txt 文件。这个文件使用 CMake 语法定义库、可执行文件、源文件、头文件路径、链接库等。

      3. 配置 build.gradle (Module level): 在模块的 build.gradle 文件中配置 CMake:

        gradle
         
        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 版本
                }
            }
        }
      4. 构建项目: 在 Android Studio 中点击 Build > Make Project,或者使用 Gradle 命令行任务 (./gradlew assembleDebug 或 ./gradlew assembleRelease)。Gradle 会自动调用 CMake 和 NDK 工具链进行编译。

    • 输出: 编译生成的 .so 文件会打包到最终的 APK 中,并在构建过程中存放在模块的 build/intermediates/cmake/ 目录下(具体路径取决于构建变体)。

    • 优点:

      • 官方推荐,与 Android Studio 和 Gradle 深度集成,体验流畅。

      • 跨平台(同一套 CMakeLists.txt 理论上也可用于其他平台)。

      • 语法更现代、清晰、功能强大,社区支持好。

      • 管理大型项目或复杂依赖更方便。

    • 缺点: 需要学习 CMake 语法。

  3. 使用独立的工具链 (Standalone Toolchain)

    • 原理: 利用 NDK 提供的 make_standalone_toolchain.py 脚本(或在较新 NDK 中直接使用 clang)创建一个针对特定目标平台(如 arm-linux-androideabi)的独立编译工具链环境(包含编译器 clang++、链接器、sysroot 等)。然后,你可以像在 Linux 上编译普通共享库一样,使用这个工具链里的编译器进行编译,或者将其集成到你自己的构建系统(如 Autotools)中。

    • 步骤:

      1. 创建独立工具链: 使用 NDK 中的 make_standalone_toolchain.py(旧版推荐)或直接设置环境变量指向 NDK 的 toolchains/llvm/prebuilt/<host>/bin 目录下的编译器(新版更推荐直接使用)。

      2. 设置环境变量: 设置 CCCXXLDARSTRIPCFLAGSCXXFLAGSLDFLAGS 等环境变量,指向独立工具链中的工具,并包含必要的头文件和库路径(--sysroot)。

      3. 运行 configure / make: 如果你的项目使用 Autotools (./configure && make),或者直接使用编译命令:

        bash
         
        $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 编译器 (clangclang++)。GCC 已被废弃。在 Android.mk (LOCAL_CFLAGSLOCAL_CPPFLAGSLOCAL_CPP_FEATURES) 或 CMakeLists.txt (target_compile_optionstarget_compile_featuresadd_compile_options) 中设置优化级别 (-O2-Os)、调试信息 (-g)、C/C++ 标准 (-std=c++17)、警告级别 (-Wall-Werror) 等。

  • 链接标志: 在 Android.mk (LOCAL_LDLIBSLOCAL_LDFLAGS) 或 CMakeLists.txt (target_link_librariestarget_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.solibc++_shared.so)。

  • 在设备/模拟器上运行: 最终极的测试是将包含 .so 的 APK 安装到目标设备或模拟器上运行,看功能是否正常,并通过 logcat 查看是否有加载库或符号找不到的错误。

选择哪种方法取决于你的项目需求、熟悉程度以及是否要与 Android Studio/Gradle 集成。CMake + Gradle 是目前最主流和推荐的方式。

posted on 2025-07-17 00:57  GKLBB  阅读(119)  评论(0)    收藏  举报