前言
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个步骤cc1
、as
、collect2
,具体如下:
预处理和编译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
可以从中看到,使用了cc1
将helloWorld.c
编译生成ccfIwQTj.s
。
汇编as
继续查找ccfIwQTj.s
,会看到:
as -v --64 -o /tmp/ccUwhs9h.o /tmp/ccfIwQTj.s
可以从中看到,使用了as
将ccfIwQTj.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
可以从中看到,使用了collect2
将ccUwhs9h.o
和其它许多文件一起链接生成了helloWorld
。
小插曲
编译过程可以大体分为4个步骤:预处理,编译,汇编,链接。可我们目前发现gcc -v
只有使用了三步,这里是怎么回事呢?
实际上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.mk、Android.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
可以从中看到,使用了clang
将helloWorld.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相似,可以使用相同的参数来跟踪编译过程。