Skip to content

Latest commit

 

History

History
342 lines (248 loc) · 23.3 KB

编译过程.md

File metadata and controls

342 lines (248 loc) · 23.3 KB

C/C++程序的编译过程

前言

C/C++代码是如何变成一个可以在硬件上运行的程序的呢,我们从一个简单的"Hello World"程序说起。

Hello World编译

有个流传挺广的笑话:

某程序员对书法十分感兴趣,退休后决定在这方面有所建树。于是花重金购买了上等的文房四宝。一日,饭后突生雅兴,一番磨墨拟纸,并点上了上好的檀香,颇有王羲之风范,又具颜真卿气势,定神片刻,泼墨挥毫,郑重地写下:Hello World

C代码的"Hello World",helloWorld.c 如下:

/*include head file*/
#include <stdio.h>

/*the main function*/
int main(int argc,char *argv[])
{
    printf("Hello World!\n");
    return 0 ;
}

我们先使用gcc来编译:

$ gcc helloWorld.c 
$ ./a.out 
Hello World!

可以看到,一气呵成,"Hello World!" 已经直接打印到屏幕上。那么这里面发生了那些事情呢?

万事来求help,我们可以运行gcc --help查看帮助说明,其中一条:

-v         Display the programs invoked by the compiler.

也就是说加-v参数可以显示编译调用的程序的详细信息,更多关于gcc -v的剖析可参考:剖析gcc -v输出

输出信息如下:

$ gcc helloWorld.c -o helloWorld -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/8/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Uos 8.3.0.3-3+rebuild' --with-bugurl=file:///usr/share/doc/gcc-8/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-8 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 8.3.0 (Uos 8.3.0.3-3+rebuild) 
COLLECT_GCC_OPTIONS='-o' 'helloWorld' '-v' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/8/cc1 -quiet -v -imultiarch x86_64-linux-gnu helloWorld.c -quiet -dumpbase helloWorld.c -mtune=generic -march=x86-64 -auxbase helloWorld -version -o /tmp/ccfIwQTj.s
GNU C17 (Uos 8.3.0.3-3+rebuild) version 8.3.0 (x86_64-linux-gnu)
        compiled by GNU C version 8.3.0, GMP version 6.1.2, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.20-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/8/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-linux-gnu/8/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/8/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.
GNU C17 (Uos 8.3.0.3-3+rebuild) version 8.3.0 (x86_64-linux-gnu)
        compiled by GNU C version 8.3.0, GMP version 6.1.2, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.20-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: b0aa96b3fb90562a90ec754591a6a020
COLLECT_GCC_OPTIONS='-o' 'helloWorld' '-v' '-mtune=generic' '-march=x86-64'
 as -v --64 -o /tmp/ccUwhs9h.o /tmp/ccfIwQTj.s
GNU assembler version 2.31.1 (x86_64-linux-gnu) using BFD version (GNU Binutils for Uos) 2.31.1
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/8/:/usr/lib/gcc/x86_64-linux-gnu/8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/8/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/8/:/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/8/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/8/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-o' 'helloWorld' '-v' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/8/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/8/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/8/lto-wrapper -plugin-opt=-fresolution=/tmp/ccXGG9qg.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o helloWorld /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/8/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/8 -L/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/8/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/8/../../.. /tmp/ccUwhs9h.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/8/crtend.o /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-o' 'helloWorld' '-v' '-mtune=generic' '-march=x86-64'

信息太多,来消化一下,剔除额外信息,我们可以看到3个步骤cc1ascollect2,具体如下:

预处理和编译cc1

在这段信息中搜索helloWorld.c,会看到:

/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -quiet -v -imultiarch x86_64-linux-gnu helloWorld.c -quiet -dumpbase helloWorld.c -mtune=generic -march=x86-64 -auxbase helloWorld -version -o /tmp/ccfIwQTj.s

可以从中看到,使用了cc1helloWorld.c编译生成ccfIwQTj.s

汇编as

继续查找ccfIwQTj.s,会看到:

as -v --64 -o /tmp/ccUwhs9h.o /tmp/ccfIwQTj.s

可以从中看到,使用了asccfIwQTj.s汇编生成ccfIwQTj.o

链接collect2

继续查找ccfIwQTj.o,会看到:

/usr/lib/gcc/x86_64-linux-gnu/8/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/8/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/8/lto-wrapper -plugin-opt=-fresolution=/tmp/ccXGG9qg.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o helloWorld /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/8/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/8 -L/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/8/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/8/../../.. /tmp/ccUwhs9h.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/8/crtend.o /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crtn.o

可以从中看到,使用了collect2ccUwhs9h.o和其它许多文件一起链接生成了helloWorld

小插曲

编译过程可以大体分为4个步骤:预处理,编译,汇编,链接。可我们目前发现gcc -v只有使用了三步,这里是怎么回事呢?

GCC编译的背后( 预处理和编译 汇编和链接 )中提到:

实际上gcc在这里是调用了cpp的(虽然我们通过gcc的-v仅看到cc1),cpp即The C Preprocessor,主要用来预处理宏定义、文件包含、条件编译等。

我们使用cpp来测试一下,同样加"-v"参数:

$ cpp helloWorld.c -o helloWorld.i -v
Using built-in specs.
COLLECT_GCC=cpp
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Uos 8.3.0.3-3+rebuild' --with-bugurl=file:///usr/share/doc/gcc-8/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-8 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 8.3.0 (Uos 8.3.0.3-3+rebuild) 
COLLECT_GCC_OPTIONS='-E' '-o' 'helloWorld.i' '-v' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/8/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu helloWorld.c -o helloWorld.i -mtune=generic -march=x86-64
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/8/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-linux-gnu/8/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/8/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/8/:/usr/lib/gcc/x86_64-linux-gnu/8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/8/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/8/:/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/8/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/8/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-E' '-o' 'helloWorld.i' '-v' '-mtune=generic' '-march=x86-64'

发现其中依然是使用cc1:

/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu helloWorld.c -o helloWorld.i -mtune=generic -march=x86-64

和前面helloWorld.c到编译生成cfIwQTj.s时对比:

/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -quiet -v -imultiarch x86_64-linux-gnu helloWorld.c -quiet -dumpbase helloWorld.c -mtune=generic -march=x86-64 -auxbase helloWorld -version -o /tmp/ccfIwQTj.s

从中可以看到cc1预处理时加了-E参数,可以理解成cc1加了-E参数时,只做预处理操作,不加时-E参数时,预处理和编译同时完成,所以导致只有3个步骤,只是预处理编译cc1合并成一个步骤了。

四个步骤

接下来我们重新来分解一下这4个步骤,使用更简单的参数,控制每个过程。参考文章:hello程序是如何被编译出来的?

提示:下面几个命令中若加"-v"参数,同样可以查看到的具体调用的程序和参数,就是前面各个步骤的分解。

预处理

预处理主要是处理源代码中以#开头的指令(#pragma 除外),例如本文hello world程序中的#include,预处理之后会将stdio.h的内容插入到预处理指令的位置。 想要只生成预处理之后的内容,gcc -E -o helloWorld.i helloWorld.c等效于cpp -o helloWorld.i helloWorld.c

gcc -E -o helloWorld.i helloWorld.c     # -E参数表示只进行预处理

生成的helloWorld.i即为预处理之后的内容,有兴趣的可以打开文件查看里面的内容,会发现stdio.h的位置被其实际内容所替代。预处理之后,注释内容也会被删除,宏定义会被展开。

知道这个技能后,我们可以在使用一些复杂的宏定义时,可以先进行预处理,再查看其预处理后的代码,来快速排错和理解代码。

编译

预处理之后就需要对生成的预处理文件进行词法分析,语法分析,语义分析,最终产生汇编代码文件,说白点可以简单理解为将C代码“翻译”成汇编代码。该过程是核心同时也是较复杂的一个过程。我们可以通过命令:

gcc -S -o helloWorld.s helloWorld.c     # -S参数表示只到生成汇编为止

生成的汇编代码helloWorld.s如下:

        .file   "helloWorld.c"
        .text
        .section        .rodata
.LC0:
        .string "Hello World!"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    %edi, -4(%rbp)
        movq    %rsi, -16(%rbp)
        movl    $.LC0, %edi
        call    puts
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Uos 8.3.0.3-3+rebuild) 8.3.0"
        .section        .note.GNU-stack,"",@progbits

汇编

汇编是将汇编代码翻译成机器可执行的指令,生成目标文件。整个过程较为简单,几乎只是按照汇编指令和机器指令进行一一翻译。我们可以用下面的命令获得汇编后的内容:

gcc -c -o helloWorld.o helloWorld.c     # -c参数表示只到生成机器可执行的指令文件,不进行链接

链接

链接是以某种方式将各个目标文件整个在一起,生成最后的可执行文件。我们的hello程序中调用了printf函数,但是并不存在于helloWorld.o中,而是存在于libc.so或libc.a中,因此我们需要通过链接将它们融合在一起。

gcc -o helloWorld.o helloWorld.c

在实际项目中,我们就可以通过查看链接信息来确定,链接那些外部库,指定的路径是否正确。

总结

编译过程分为4个步骤:

  • 将.c源程序预处理为.i文件
  • 编译器将.i文件编译成.s汇编程序
  • 汇编器将.s汇编程序编译成.o可重定位目标文件
  • 链接器将可重定位目标文件链接成可执行文件

在编译过程中使用"-v"参数,可以详细的查看编译过程,在实际开发中非常有用。

One more thing:Android NDK中编译过程

示例代码

jni工程在中,同样的"Hello World"示例代码,增加了ndk编译脚本Application.mkAndroid.mk,进入jni目录中使用ndk-build编译:

chenls@chenls-PC:jni$ ndk-build
[arm64-v8a] Compile        : helloWorld <= helloWorld.c
[arm64-v8a] Executable     : helloWorld
[arm64-v8a] Install        : helloWorld => libs/arm64-v8a/helloWorld

ndk-build 脚本文档中提到参数V=1启动构建,并显示构建命令。

其实这里就跟gcc中-v类似,我们试一下:

chenls@chenls-PC:jni$ ndk-build V=1
rm -f /home/chenls/Desktop/Android-Native-Development/source/helloWorld/libs/arm64-v8a/*
rm -f /home/chenls/Desktop/Android-Native-Development/source/helloWorld/libs/arm64-v8a/gdbserver
rm -f /home/chenls/Desktop/Android-Native-Development/source/helloWorld/libs/arm64-v8a/gdb.setup
[arm64-v8a] Compile        : helloWorld <= helloWorld.c
rm -f /home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a/objs/helloWorld/helloWorld.o
/home/chenls/Android/Sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin/clang -MMD -MP -MF /home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a/objs/helloWorld/helloWorld.o.d -target aarch64-none-linux-android26 -fdata-sections -ffunction-sections -fstack-protector-strong -funwind-tables -no-canonical-prefixes  --sysroot /home/chenls/Android/Sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/sysroot -g -Wno-invalid-command-line-argument -Wno-unused-command-line-argument  -D_FORTIFY_SOURCE=2 -fpic -O2 -DNDEBUG  -I/home/chenls/Desktop/Android-Native-Development/source/helloWorld/jni -fstack-protector  -DANDROID  -nostdinc++ -Wformat -Werror=format-security  -c  /home/chenls/Desktop/Android-Native-Development/source/helloWorld/jni/helloWorld.c -o /home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a/objs/helloWorld/helloWorld.o
[arm64-v8a] Executable     : helloWorld
/home/chenls/Android/Sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ -Wl,--gc-sections -Wl,-rpath-link=/home/chenls/Android/Sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/26 -Wl,-rpath-link=/home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a /home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a/objs/helloWorld/helloWorld.o -lgcc -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libgcc_real.a -latomic -Wl,--exclude-libs,libatomic.a -target aarch64-none-linux-android26 -no-canonical-prefixes    -Wl,--build-id   -nostdlib++ -Wl,--no-undefined -Wl,--fatal-warnings  -lc -lm -o /home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a/helloWorld
[arm64-v8a] Install        : helloWorld => libs/arm64-v8a/helloWorld
install -p /home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a/helloWorld /home/chenls/Desktop/Android-Native-Development/source/helloWorld/libs/arm64-v8a/helloWorld
/home/chenls/Android/Sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip --strip-unneeded  /home/chenls/Desktop/Android-Native-Development/source/helloWorld/libs/arm64-v8a/helloWorld

同样来消化一下,共分成了两步:

使用clang生成.o文件

在这段信息中搜索helloWorld.c,会看到:

/home/chenls/Android/Sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin/clang -MMD -MP -MF /home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a/objs/helloWorld/helloWorld.o.d -target aarch64-none-linux-android26 -fdata-sections -ffunction-sections -fstack-protector-strong -funwind-tables -no-canonical-prefixes  --sysroot /home/chenls/Android/Sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/sysroot -g -Wno-invalid-command-line-argument -Wno-unused-command-line-argument  -D_FORTIFY_SOURCE=2 -fpic -O2 -DNDEBUG  -I/home/chenls/Desktop/Android-Native-Development/source/helloWorld/jni -fstack-protector  -DANDROID  -nostdinc++ -Wformat -Werror=format-security  -c  /home/chenls/Desktop/Android-Native-Development/source/helloWorld/jni/helloWorld.c -o /home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a/objs/helloWorld/helloWorld.o

可以从中看到,使用了clanghelloWorld.c预处理、编译、汇编生成helloWorld.o文件。

使用clang++链接

继续查找helloWorld.o,会看到:

/home/chenls/Android/Sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ -Wl,--gc-sections -Wl,-rpath-link=/home/chenls/Android/Sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/26 -Wl,-rpath-link=/home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a /home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a/objs/helloWorld/helloWorld.o -lgcc -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libgcc_real.a -latomic -Wl,--exclude-libs,libatomic.a -target aarch64-none-linux-android26 -no-canonical-prefixes    -Wl,--build-id   -nostdlib++ -Wl,--no-undefined -Wl,--fatal-warnings  -lc -lm -o /home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a/helloWorld

可以从中看到,使用了clang++helloWorld.o链接生成helloWorld

clang的使用

这里可以发现,ndk编译时使用了clang,而clang和gcc非常类似,它同样可以使用我们前面提到的参数,下面来看看具体操作。

提示:下面几个命令中若加"-v"参数,同样可以查看到的具体调用的程序和参数,和gcc非常类似。

clang预处理

只需要把生成文件helloWorld.o改成helloWorld.i,然后加上-E参数,就可以得到预处理文件:

/home/chenls/Android/Sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin/clang -MMD -MP -MF /home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a/objs/helloWorld/helloWorld.o.d -target aarch64-none-linux-android26 -fdata-sections -ffunction-sections -fstack-protector-strong -funwind-tables -no-canonical-prefixes  --sysroot /home/chenls/Android/Sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/sysroot -g -Wno-invalid-command-line-argument -Wno-unused-command-line-argument  -D_FORTIFY_SOURCE=2 -fpic -O2 -DNDEBUG  -I/home/chenls/Desktop/Android-Native-Development/source/helloWorld/jni -fstack-protector  -DANDROID  -nostdinc++ -Wformat -Werror=format-security  -c  /home/chenls/Desktop/Android-Native-Development/source/helloWorld/jni/helloWorld.c -o /home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a/objs/helloWorld/helloWorld.i -E

clang编译

只需要把生成文件helloWorld.o改成helloWorld.s,然后加上-S参数,就可以得到汇编文件:

/home/chenls/Android/Sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin/clang -MMD -MP -MF /home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a/objs/helloWorld/helloWorld.o.d -target aarch64-none-linux-android26 -fdata-sections -ffunction-sections -fstack-protector-strong -funwind-tables -no-canonical-prefixes  --sysroot /home/chenls/Android/Sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/sysroot -g -Wno-invalid-command-line-argument -Wno-unused-command-line-argument  -D_FORTIFY_SOURCE=2 -fpic -O2 -DNDEBUG  -I/home/chenls/Desktop/Android-Native-Development/source/helloWorld/jni -fstack-protector  -DANDROID  -nostdinc++ -Wformat -Werror=format-security  -c  /home/chenls/Desktop/Android-Native-Development/source/helloWorld/jni/helloWorld.c -o /home/chenls/Desktop/Android-Native-Development/source/helloWorld/obj/local/arm64-v8a/objs/helloWorld/helloWorld.s -S

ndk-build编译过程总结

ndk编译时内部调用了clang,而clang与gcc相似,可以使用相同的参数来跟踪编译过程。