第七章 ARM 反汇编基础(三)(ARM 原生程序的生成过程)
16lz
2021-01-26
文章目录
- ARM 原生程序的生成过程
- 预处理
- 编译
- 汇编
- 链接
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 "" 1# 1 "" 3# 325 "" 3# 1 "" 1# 1 "" 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 4typedef 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 4typedef 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 4typedef 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/usr/include/stdint.h" 1 3 4# 32 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/stdint.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# 33 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/stdint.h" 2 3 4# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/machine/wchar_limits.h" 1 3 4# 34 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/stdint.h" 2 3 4typedef signed char __int8_t;typedef unsigned char __uint8_t;typedef short __int16_t;typedef unsigned short __uint16_t;typedef int __int32_t;typedef unsigned int __uint32_t;typedef long long __int64_t;typedef unsigned long long __uint64_t;typedef int __intptr_t;typedef unsigned int __uintptr_t;typedef __int8_t int8_t;typedef __uint8_t uint8_t;typedef __int16_t int16_t;typedef __uint16_t uint16_t;typedef __int32_t int32_t;typedef __uint32_t uint32_t;typedef __int64_t int64_t;typedef __uint64_t uint64_t;typedef __intptr_t intptr_t;typedef __uintptr_t uintptr_t;typedef int8_t int_least8_t;typedef uint8_t uint_least8_t;typedef int16_t int_least16_t;typedef uint16_t uint_least16_t;typedef int32_t int_least32_t;typedef uint32_t uint_least32_t;typedef int64_t int_least64_t;typedef uint64_t uint_least64_t;typedef int8_t int_fast8_t;typedef uint8_t uint_fast8_t;typedef int64_t int_fast64_t;typedef uint64_t uint_fast64_t;typedef int32_t int_fast16_t;typedef uint32_t uint_fast16_t;typedef int32_t int_fast32_t;typedef uint32_t uint_fast32_t;typedef uint64_t uintmax_t;typedef int64_t intmax_t;# 64 "/root/android-ndk-r10e/toolchains/llvm-3.5/prebuilt/linux-x86_64/bin/../lib/clang/3.5/include/stdint.h" 2 3 4# 33 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/types.h" 2 3 4# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/linux/types.h" 1 3 4# 21 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/linux/types.h" 3 4# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm/types.h" 1 3 4# 19 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm/types.h" 3 4# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm-generic/types.h" 1 3 4# 21 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm-generic/types.h" 3 4# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm-generic/int-ll64.h" 1 3 4# 21 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm-generic/int-ll64.h" 3 4# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm/bitsperlong.h" 1 3 4# 19 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm/bitsperlong.h" 3 4# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm-generic/bitsperlong.h" 1 3 4# 20 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm/bitsperlong.h" 2 3 4# 22 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm-generic/int-ll64.h" 2 3 4typedef __signed__ char __s8;typedef unsigned char __u8;typedef __signed__ short __s16;typedef unsigned short __u16;typedef __signed__ int __s32;typedef unsigned int __u32;__extension__ typedef __signed__ long long __s64;__extension__ typedef unsigned long long __u64;# 22 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm-generic/types.h" 2 3 4# 20 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm/types.h" 2 3 4# 22 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/linux/types.h" 2 3 4# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/linux/posix_types.h" 1 3 4# 21 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/linux/posix_types.h" 3 4# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/linux/stddef.h" 1 3 4# 19 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/linux/stddef.h" 3 4# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/linux/compiler.h" 1 3 4# 20 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/linux/stddef.h" 2 3 4# 22 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/linux/posix_types.h" 2 3 4typedef struct { unsigned long fds_bits[1024 / (8 * sizeof(long))];} __kernel_fd_set;typedef void (*__kernel_sighandler_t)(int);typedef int __kernel_key_t;typedef int __kernel_mqd_t;# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm/posix_types.h" 1 3 4# 21 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm/posix_types.h" 3 4typedef unsigned short __kernel_mode_t;typedef unsigned short __kernel_ipc_pid_t;typedef unsigned short __kernel_uid_t;typedef unsigned short __kernel_gid_t;typedef unsigned short __kernel_old_dev_t;# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm-generic/posix_types.h" 1 3 4# 21 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm-generic/posix_types.h" 3 4# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm/bitsperlong.h" 1 3 4# 22 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm-generic/posix_types.h" 2 3 4typedef long __kernel_long_t;typedef unsigned long __kernel_ulong_t;typedef __kernel_ulong_t __kernel_ino_t;typedef int __kernel_pid_t;# 49 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm-generic/posix_types.h" 3 4typedef __kernel_long_t __kernel_suseconds_t;typedef int __kernel_daddr_t;typedef unsigned int __kernel_uid32_t;typedef unsigned int __kernel_gid32_t;typedef __kernel_uid_t __kernel_old_uid_t;typedef __kernel_gid_t __kernel_old_gid_t;# 71 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm-generic/posix_types.h" 3 4typedef unsigned int __kernel_size_t;typedef int __kernel_ssize_t;typedef int __kernel_ptrdiff_t;# 84 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm-generic/posix_types.h" 3 4typedef struct { int val[2];} __kernel_fsid_t;typedef __kernel_long_t __kernel_off_t;typedef long long __kernel_loff_t;typedef __kernel_long_t __kernel_time_t;typedef __kernel_long_t __kernel_clock_t;typedef int __kernel_timer_t;typedef int __kernel_clockid_t;typedef char * __kernel_caddr_t;typedef unsigned short __kernel_uid16_t;typedef unsigned short __kernel_gid16_t;# 33 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/asm/posix_types.h" 2 3 4# 33 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/linux/posix_types.h" 2 3 4# 25 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/linux/types.h" 2 3 4typedef __u16 __le16;typedef __u16 __be16;typedef __u32 __le32;typedef __u32 __be32;typedef __u64 __le64;typedef __u64 __be64;typedef __u16 __sum16;typedef __u32 __wsum;# 36 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/types.h" 2 3 4typedef __kernel_gid32_t __gid_t;typedef __gid_t gid_t;typedef __kernel_uid32_t __uid_t;typedef __uid_t uid_t;typedef __kernel_pid_t __pid_t;typedef __pid_t pid_t;typedef uint32_t __id_t;typedef __id_t id_t;typedef unsigned long blkcnt_t;typedef unsigned long blksize_t;typedef __kernel_caddr_t caddr_t;typedef __kernel_clock_t clock_t;typedef __kernel_clockid_t __clockid_t;typedef __clockid_t clockid_t;typedef __kernel_daddr_t daddr_t;typedef unsigned long fsblkcnt_t;typedef unsigned long fsfilcnt_t;typedef __kernel_mode_t __mode_t;typedef __mode_t mode_t;typedef __kernel_key_t __key_t;typedef __key_t key_t;typedef __kernel_ino_t __ino_t;typedef __ino_t ino_t;typedef uint32_t __nlink_t;typedef __nlink_t nlink_t;typedef void* __timer_t;typedef __timer_t timer_t;typedef __kernel_suseconds_t __suseconds_t;typedef __suseconds_t suseconds_t;typedef uint32_t __useconds_t;typedef __useconds_t useconds_t;typedef uint32_t dev_t;typedef __kernel_time_t __time_t;typedef __time_t time_t;typedef __kernel_off_t off_t;typedef __kernel_loff_t loff_t;typedef loff_t off64_t;# 122 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/types.h" 3 4typedef int32_t __socklen_t;typedef __socklen_t socklen_t;typedef __builtin_va_list __va_list;# 138 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/types.h" 3 4typedef __kernel_ssize_t ssize_t;typedef unsigned int uint_t;typedef unsigned int uint;# 1 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/sysmacros.h" 1 3 4# 36 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/sysmacros.h" 3 4static __inline__ int major(dev_t _dev){ return (_dev >> 8) & 0xfff;}static __inline__ int minor(dev_t _dev){ return (_dev & 0xff) | ((_dev >> 12) & 0xfff00);}static __inline__ dev_t makedev(int __ma, int __mi){ return ((__ma & 0xfff) << 8) | (__mi & 0xff) | ((__mi & 0xfff00) << 12);}# 146 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/sys/types.h" 2 3 4typedef unsigned char u_char;typedef unsigned short u_short;typedef unsigned int u_int;typedef unsigned long u_long;typedef uint32_t u_int32_t;typedef uint16_t u_int16_t;typedef uint8_t u_int8_t;typedef uint64_t u_int64_t;# 51 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/stdio.h" 2 3 4# 1 "/root/android-ndk-r10e/toolchains/llvm-3.5/prebuilt/linux-x86_64/bin/../lib/clang/3.5/include/stdarg.h" 1 3 4# 30 "/root/android-ndk-r10e/toolchains/llvm-3.5/prebuilt/linux-x86_64/bin/../lib/clang/3.5/include/stdarg.h" 3 4typedef __builtin_va_list va_list;# 50 "/root/android-ndk-r10e/toolchains/llvm-3.5/prebuilt/linux-x86_64/bin/../lib/clang/3.5/include/stdarg.h" 3 4typedef __builtin_va_list __gnuc_va_list;# 53 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/stdio.h" 2 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# 54 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/stdio.h" 2 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# 57 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/stdio.h" 2 3 4typedef off_t fpos_t;# 75 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/stdio.h" 3 4struct __sbuf { unsigned char *_base; int _size;};# 108 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/stdio.h" 3 4typedef struct __sFILE { unsigned char *_p; int _r; int _w; short _flags; short _file; struct __sbuf _bf; int _lbfsize; void *_cookie; int (*_close)(void *); int (*_read)(void *, char *, int); fpos_t (*_seek)(void *, fpos_t, int); int (*_write)(void *, const char *, int); struct __sbuf _ext; unsigned char *_up; int _ur; unsigned char _ubuf[3]; unsigned char _nbuf[1]; struct __sbuf _lb; int _blksize; fpos_t _offset;} FILE;extern FILE __sF[];# 220 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/stdio.h" 3 4void clearerr(FILE *);int fclose(FILE *);int feof(FILE *);int ferror(FILE *);int fflush(FILE *);int fgetc(FILE *);char *fgets(char * __restrict, int, FILE * __restrict);FILE *fopen(const char * __restrict , const char * __restrict);int fprintf(FILE * __restrict , const char * __restrict, ...) __attribute__((__format__(printf, 2, 3))) __attribute__((__nonnull__ (2)));int fputc(int, FILE *);int fputs(const char * __restrict, FILE * __restrict);size_t fread(void * __restrict, size_t, size_t, FILE * __restrict);FILE *freopen(const char * __restrict, const char * __restrict, FILE * __restrict);int fscanf(FILE * __restrict, const char * __restrict, ...) __attribute__((__format__(scanf, 2, 3))) __attribute__((__nonnull__ (2)));int fseek(FILE *, long, int);long ftell(FILE *);size_t fwrite(const void * __restrict, size_t, size_t, FILE * __restrict);int getc(FILE *);int getchar(void);ssize_t getdelim(char ** __restrict, size_t * __restrict, int, FILE * __restrict);ssize_t getline(char ** __restrict, size_t * __restrict, FILE * __restrict);void perror(const char *);int printf(const char * __restrict, ...) __attribute__((__format__(printf, 1, 2))) __attribute__((__nonnull__ (1)));int putc(int, FILE *);int putchar(int);int puts(const char *);int remove(const char *);void rewind(FILE *);int scanf(const char * __restrict, ...) __attribute__((__format__(scanf, 1, 2))) __attribute__((__nonnull__ (1)));void setbuf(FILE * __restrict, char * __restrict);int setvbuf(FILE * __restrict, char * __restrict, int, size_t);int sscanf(const char * __restrict, const char * __restrict, ...) __attribute__((__format__(scanf, 2, 3))) __attribute__((__nonnull__ (2)));FILE *tmpfile(void);int ungetc(int, FILE *);int vfprintf(FILE * __restrict, const char * __restrict, __va_list) __attribute__((__format__(printf, 2, 0))) __attribute__((__nonnull__ (2)));int vprintf(const char * __restrict, __va_list) __attribute__((__format__(printf, 1, 0))) __attribute__((__nonnull__ (1)));int dprintf(int, const char * __restrict, ...) __attribute__((__format__(printf, 2, 3))) __attribute__((__nonnull__ (2)));int vdprintf(int, const char * __restrict, __va_list) __attribute__((__format__(printf, 2, 0))) __attribute__((__nonnull__ (2)));char* gets(char*) ;int sprintf(char* __restrict, const char* __restrict, ...) __attribute__((__format__(printf, 2, 3))) __attribute__((__nonnull__ (2)));char* tmpnam(char*) ;int vsprintf(char* __restrict, const char* __restrict, __va_list) __attribute__((__format__(printf, 2, 0))) __attribute__((__nonnull__ (2)));char* tempnam(const char*, const char*) ;extern int rename(const char*, const char*);extern int renameat(int, const char*, int, const char*);int fgetpos(FILE * __restrict, fpos_t * __restrict);int fsetpos(FILE *, const fpos_t *);int fseeko(FILE *, off_t, int);off_t ftello(FILE *);int snprintf(char * __restrict, size_t, const char * __restrict, ...) __attribute__((__format__(printf, 3, 4))) __attribute__((__nonnull__ (3)));int vfscanf(FILE * __restrict, const char * __restrict, __va_list) __attribute__((__format__(scanf, 2, 0))) __attribute__((__nonnull__ (2)));int vscanf(const char *, __va_list) __attribute__((__format__(scanf, 1, 0))) __attribute__((__nonnull__ (1)));int vsnprintf(char * __restrict, size_t, const char * __restrict, __va_list) __attribute__((__format__(printf, 3, 0))) __attribute__((__nonnull__ (3)));int vsscanf(const char * __restrict, const char * __restrict, __va_list) __attribute__((__format__(scanf, 2, 0))) __attribute__((__nonnull__ (2)));# 317 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/stdio.h" 3 4FILE *fdopen(int, const char *);int fileno(FILE *);int pclose(FILE *);FILE *popen(const char *, const char *);void flockfile(FILE *);int ftrylockfile(FILE *);void funlockfile(FILE *);int getc_unlocked(FILE *);int getchar_unlocked(void);int putc_unlocked(int, FILE *);int putchar_unlocked(int);# 349 "/root/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/stdio.h" 3 4int asprintf(char ** __restrict, const char * __restrict, ...) __attribute__((__format__(printf, 2, 3))) __attribute__((__nonnull__ (2)));char *fgetln(FILE * __restrict, size_t * __restrict);int fpurge(FILE *);void setbuffer(FILE *, char *, int);int setlinebuf(FILE *);int vasprintf(char ** __restrict, const char * __restrict, __va_list) __attribute__((__format__(printf, 2, 0))) __attribute__((__nonnull__ (2)));FILE *funopen(const void *, int (*)(void *, char *, int), int (*)(void *, const char *, int), fpos_t (*)(void *, fpos_t, int), int (*)(void *));# 2 "hello.c" 2int main(int argc, char const *argv[]){ printf("Hello ARM!\n"); return 0;}
- 预处理阶段 gcc 和 Clang 所做的工作差不多
编译
- 在编译阶段,编译器首先检查代码的规范性,及其中是否存在语法错误等,以确定代码实际要做的工作。确认无误后,编译器把代码翻译成 ARM 汇编语言的代码,输出信息可通过向编译器传递
-S
选项查看 - 以 hello.c 为例,执行
$CC hello.i -S -fPIE -o hello.s
命令后会生成 hello.s 汇编文件,根据 CC 环境变量指定编译器的不同,生成的汇编代码也会不同 - gcc 生成的代码如下:
.arch armv5te.fpu softvfp.eabi_attribute 20, 1.eabi_attribute 21, 1.eabi_attribute 23, 3.eabi_attribute 24, 1.eabi_attribute 25, 1.eabi_attribute 26, 2.eabi_attribute 30, 6.eabi_attribute 34, 0.eabi_attribute 18, 4.file"hello.c".section.rodata.align2.LC0:.ascii"Hello ARM!\000".text.align2.globalmain.typemain, %functionmain:@ args = 0, pretend = 0, frame = 8@ frame_needed = 1, uses_anonymous_args = 0stmfdsp!, {fp, lr}addfp, sp, #4subsp, sp, #8strr0, [fp, #-8]strr1, [fp, #-12]ldrr3, .L3.LPIC0:addr3, pc, r3movr0, r3blputs(PLT)movr3, #0movr0, r3subsp, fp, #4@ sp neededldmfdsp!, {fp, pc}.L4:.align2.L3:.word.LC0-(.LPIC0+8).sizemain, .-main.ident"GCC: (GNU) 4.9 20140827 (prerelease)".section.note.GNU-stack,"",%progbits
- hello.s 展示了一个 ARM 汇编程序源文件的完整结构
.
开头的汇编指令是汇编器提供的伪指令,不同的伪指令描述了汇编程序的不同部分.arch
:指定汇编程序使用的处理器架构,默认情况下,armeabi 指定的处理器架构为 ARMv5TE.section
:声明一个段.ascii
:声明一个字符串
- 将 CC 环境变量定义为 Clang,并将
-target
设置为armv5te-linux-androideabi
,生成 hello.s
- hello.s 内容如下:
.text.syntax unified.cpuarm1022e.eabi_attribute6, 4@ Tag_CPU_arch.eabi_attribute8, 1@ Tag_ARM_ISA_use.eabi_attribute15, 1@ Tag_ABI_PCS_RW_data.eabi_attribute16, 1@ Tag_ABI_PCS_RO_data.eabi_attribute17, 2@ Tag_ABI_PCS_GOT_use.eabi_attribute20, 1@ Tag_ABI_FP_denormal.eabi_attribute21, 1@ Tag_ABI_FP_exceptions.eabi_attribute23, 3@ Tag_ABI_FP_number_model.eabi_attribute24, 1@ Tag_ABI_align_needed.eabi_attribute25, 1@ Tag_ABI_align_preserved.eabi_attribute18, 4@ Tag_ABI_PCS_wchar_t.eabi_attribute26, 2@ Tag_ABI_enum_size.file"hello.i".globlmain.align2.typemain,%functionmain: @ @main.fnstart.Leh_func_begin0:@ BB#0: @ %entry.save{r11, lr}push{r11, lr}.setfpr11, spmovr11, sp.pad#16subsp, sp, #16ldrr2, .LCPI0_2.LPC0_0:addr2, pc, r2ldrr3, .LCPI0_1addr2, r3, r2ldrr3, .LCPI0_0strr3, [r11, #-4]strr0, [sp, #8]strr1, [sp, #4]movr0, r2blprintf(PLT)ldrr1, .LCPI0_0strr0, [sp] @ 4-byte Spillmovr0, r1movsp, r11pop{r11, pc}.align2@ BB#1:.LCPI0_0:.long0 @ 0x0.LCPI0_1:.long.L.str(GOTOFF).LCPI0_2:.long_GLOBAL_OFFSET_TABLE_-(.LPC0_0+8).Ltmp0:.sizemain, .Ltmp0-main.cantunwind.fnend.type.L.str,%object @ @.str.section.rodata.str1.1,"aMS",%progbits,1.L.str:.asciz"Hello ARM!\n".size.L.str, 12.ident"clang version 3.5 "
- 可看出,Clang 用的指令集为 arm1022e,生成的汇编代码也与 gcc 不同
- Clang 在生成 hello.s 期间还做了许多内部转换工作。Clang 作为 LLVM 套件的 C 语言编译器前端,在生成汇编代码前会将代码转换成 LLVM 特有的中间代码 Bitcode 字节码,然后将 Bitcode 字节码转换成与机器相关的汇编指令
- 要想生成 Bitcode 字节码,可在调用 Clang 时使用
-emit-llvm
参数 - hello.c 可执行如下命令生成 hello.bc:
- Bitcode 是 LLVM IR(一种程序代码的中间语法表达)的表现形式,它即可存在于二进制格式的
.bc
文件中,也可存在于可读的文本格式的.ll
文件中。可用llvm-dis
命令将.bc
文件转换成可读的.ll
文件,也可用llvm-as
命令将.ll
转换为.bc
- hello.ll 的内容如下:
; ModuleID = 'hello.bc'target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-n32-S64"target triple = "armv5e--linux-androideabi"@.str = private unnamed_addr constant [12 x i8] c"Hello ARM!\0A\00", align 1; Function Attrs: nounwinddefine i32 @main(i32 %argc, i8** %argv) #0 {entry: %retval = alloca i32, align 4 %argc.addr = alloca i32, align 4 %argv.addr = alloca i8**, align 4 store i32 0, i32* %retval store i32 %argc, i32* %argc.addr, align 4 store i8** %argv, i8*** %argv.addr, align 4 %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([12 x i8]* @.str, i32 0, i32 0)) ret i32 0}declare i32 @printf(i8*, ...) #1attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="true" }attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="true" }!llvm.module.flags = !{!0, !1}!llvm.ident = !{!2}!0 = metadata !{i32 1, metadata !"wchar_size", i32 4}!1 = metadata !{i32 1, metadata !"min_enum_size", i32 4}!2 = metadata !{metadata !"clang version 3.5 "}
- 将 hello.bc 转换为 hello.s:
- 这和之前生成的 hello.s 是一样的
汇编
- 汇编阶段,执行如下命令,生成 hello.o:
$CC -c hello.s -o hello.o
- 它的内部环境变量 CC 将汇编文件传给汇编器生成目标文件
- arm-linux-androideabi-gcc 用的汇编器是 arm-linux-androideabi-as,用的汇编格式为 GNU ARM,它有自己的语法结构
- Clang 是 LLVM 的一部分,目前(成书时)Clang 在编译时用的汇编器仍是 arm-linux-androideabi-as。因此,无论是 gcc 还是 Clang,汇编工作统一由 arm-linux-androideabi-as 完成,实际上它完成了如下工作:
链接
- 最后一步为链接,即将所有的目标合并,生成最终的可执行程序或动态库,命令如下:
$CC hello.o -fPIE -pie -o hello
- 在它的内部,其实是由 CC 指定的编译器将目标文件传给链接器,从而完成链接工作的
- 注意:除了在编译时要指定
-fPIE
,链接时也要指定 - Clang 没有自己的链接器,两个编译器都是调用 arm-linux-androideabi-ld 完成链接工作。因此,实际是进行了如下操作:
-xxx
:指额外的链接参数,如库的路径与引用的库,具体的参数可通过如下命令看到:
$CC hello.o -o hello -v
更多相关文章
- Android(安卓)中的拿来主义(编译,反编译,AXMLPrinter2,smali,baksm
- Gradle Android最新自动化编译脚本教程(提供demo源码)
- Android(安卓)源码编译加速 使用ccache
- 在 Ubuntu 下使用 Android(安卓)NDK r4b 编译 FFmpeg 0.6.3
- Android(安卓)NDK开发扫盲及最新CMake的编译使用
- SWIG与JAVA的交互指南一
- Android(安卓)NDK编译本地文件以及引用第三方so文件
- 编译Android源码致命错误解决方案
- 如何在Android中使用汇编语言