文章目录

  • 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

更多相关文章

  1. Android(安卓)中的拿来主义(编译,反编译,AXMLPrinter2,smali,baksm
  2. Gradle Android最新自动化编译脚本教程(提供demo源码)
  3. Android(安卓)源码编译加速 使用ccache
  4. 在 Ubuntu 下使用 Android(安卓)NDK r4b 编译 FFmpeg 0.6.3
  5. Android(安卓)NDK开发扫盲及最新CMake的编译使用
  6. SWIG与JAVA的交互指南一
  7. Android(安卓)NDK编译本地文件以及引用第三方so文件
  8. 编译Android源码致命错误解决方案
  9. 如何在Android中使用汇编语言

随机推荐

  1. Flutter入门,学习历程,进入开发,在安卓手机
  2. Android传统布局
  3. Android-Universal-Image-Loader 源码解
  4. Android(安卓)Studio插件开发利器Exynap
  5. Android开发中的单元测试-初级教程(01)
  6. [置顶] 编译androidc模块
  7. Android面试基础题总结二
  8. Android中启动其他Activity并返回结果
  9. React Navigation - StackNavigator
  10. android 对话框Dialog背景透明