文章目录
ARM 原生程序的生成过程
- Android 平台上的 ARM 原生程序是用 Android NDK 开发的,整个原生程序的编译生成工作由 Android NDK 提供的编译工具链完成
- Android NDK 目前提供了两套 ARM 编译器,分别是基于 GNU GCC 编译器的 gcc 编译器和基于LLVM 编译套件的 Clang 编译器
- Android NDK 打算从 r17 版本开始移除对 gcc 的支持,只提供 Clang 编译开发 Android 原生程序,但实际却是仍可用 gcc 开发
- 现在用 Android NDK 写一个 ARM 原生程序,然后分析编译器执行的每一步操作,从而了解原生程序的整个生成过程。代码如下:
- 系统:Ubuntu 18.04
- Android NDK 版本:r10e
- 用传统的 gcc 工具编译这段代码,只要执行
gcc hello.c -o hello
命令;用 Android NDK 要麻烦一些 - 在命令行下指定 CC 环境变量,用 arm-linux-androideabi-gcc 编译器单独编译代码时要指定 --sysroot 选项(官方解释)
- 执行如下命令,可在命令行下编译 hello.c 代码:
- 第一行:指定 Android NDK 的根目录,方便在下面的命令中引用
- 第二行:指定 SYSROOT 环境变量,arm-linux-androideabi-gcc 编译器在编译代码时会到此目录下查找头文件和库。此处使用的是 android-21,表示系统版本是 Android 5.0(这是第一个引入 arm64-v8a 架构的系统版本)
- 第三行:指定 CC 环境变量,指向具体的编译器路径
- 第四行:使用由 CC 环境变量指定的编译器编译代码。后面的
-fPIE -pie
参数,作用是生成与位置无关的代码,在 Android 5.0 及以上版本的系统中必须设置此参数,不然程序运行时会产生异常,提示信息为“error: only position independent executables (PIE) are supported”。编译成功后,会生成 hello 可执行文件 - 若是编译 C++ 代码,第四行命令可改为
export CXX="$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-g++ --sysroot=$SYSROOT"
- 直接执行 hello 程序会提示“该文件无法执行”
- 可将文件传入 Android 手机或模拟器执行,命令和输出如下:
- 上面用
$CC
编译的 Android 原生程序没指定-march
参数,故默认的是 ARM 架构,对应的处理器是 armeabi,采用的指令集是 ARMv5TE - 若想生成其他 ARM 架构的程序,如 ARMv7-A,可执行如下命令:
$CC -march=armv7-a hello.c -o hello
- 除了用 arm-linux-androideabi-gcc 编译器,还可用 Android NDK 提供的 Clang 编译器生成程序,二者只在指定编译工具链的环境变量设置上有所不同
- (本书成书时)Android NDK 提供的 LLVM 编译套件中虽有 Clang 前端,但没用于处理代码的后端工具,如汇编器 as 和链接器 ld。用 Clang 只能完成编译工作,要想完成汇编和链接工作,还得借助 gcc 的 arm-linux-androideabi-as 和 arm-linux-androideabi-ld。在设置 CC 环境变量时,指定
-gcc-toolchain
即可将后端部分传给 gcc。除此之外,用 Clang 编译器编译不同架构的代码应通过-target
参数指定,而不应通过 gcc 的-march
。指定-target
的值为“armv7-linux-androideabi”,表示生成 ARMv7-A 架构的程序。若没指定-target
的值,默认情况下其值为当前编译系统平台架构的名称 - CC 环境变量的设置和 hello.c 的编译可执行如下命令:
- 无论 gcc 或 Clang,执行一次完整的编译工作通常分为预处理、编译、汇编、链接四个步骤,各步骤的工作细节有所不同
预处理
- 在预处理阶段,编译器将处理代码中的预处理指令。如,
#include
中包含的头文件会全部被编译出来,#define
预定义、#if
预条件处理等也都会被编译器处理。详细的输出信息可通过向编译器传递-E
选项查看。以 hello.c 为例,gcc 和 Clang 都可执行$CC -E -fPIE -pie hello.c -o hello.i
命令完成代码的预处理 - hello.i 内容如下:
# 1 "hello.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 325 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "hello.c" 2
# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/stdio.h" 1 3 4
# 49 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/stdio.h" 3 4
# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/cdefs.h" 1 3 4
# 77 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/cdefs.h" 3 4
# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/cdefs_elf.h" 1 3 4
# 78 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/cdefs.h" 2 3 4
# 547 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/cdefs.h" 3 4
# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/android/api-level.h" 1 3 4
# 548 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/cdefs.h" 2 3 4
# 50 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/stdio.h" 2 3 4
# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/types.h" 1 3 4
# 31 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/types.h" 3 4
# 1 "/root/android-ndk-r10e/toolchains/llvm-3.5/prebuilt/linux-x86_64/bin/../lib/clang/3.5/include/stddef.h" 1 3 4
# 47 "/root/android-ndk-r10e/toolchains/llvm-3.5/prebuilt/linux-x86_64/bin/../lib/clang/3.5/include/stddef.h" 3 4
typedef int ptrdiff_t;
# 58 "/root/android-ndk-r10e/toolchains/llvm-3.5/prebuilt/linux-x86_64/bin/../lib/clang/3.5/include/stddef.h" 3 4
typedef unsigned int size_t;
# 86 "/root/android-ndk-r10e/toolchains/llvm-3.5/prebuilt/linux-x86_64/bin/../lib/clang/3.5/include/stddef.h" 3 4
typedef unsigned int wchar_t;
# 32 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/types.h" 2 3 4
# 1 "/root/android-ndk-r10e/toolchains/llvm-3.5/prebuilt/linux-x86_64/bin/../lib/clang/3.5/include/stdint.h" 1 3 4
# 63 "/root/android-ndk-r10e/toolchains/llvm-3.5/prebuilt/linux-x86_64/bin/../lib/clang/3.5/include/stdint.h" 3 4
# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/