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

2023-05-16

前言

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.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

可以从中看到,使用了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相似,可以使用相同的参数来跟踪编译过程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C/C++程序的编译过程 的相关文章

  • 详解STM32CubeIDE 中 HAL库的串口中断接收函数 HAL_UART_Receive_IT

    MX串口配置方法见 xff1a CubeIDE 利用自带HAL库 串口收发 一 代码自动生成以后的项目及代码结构 xff1a main c中 xff0c 调用了串口初始化 串口初始化函数赋值了串口的参数 相当于底层的初始化 xff0c 配置
  • RTK-Real Time kinematic实时动态

    目前 xff0c GNSSj接收机约99 的时间都用作RTK模式进行测量 xff0c 只有1 的时间用作静态测量做控制网等 所以 xff0c 大部分人都习惯把GNSS接收机喊成RTK了 不过除去GNSS接收机 xff0c 你知道RTK是什么
  • USB-PD3.0(Power Delivery)充电协议

    USB PD xff08 Power Delivery xff09 是基于USB Type C的一种电源供电标准 xff0c 最大供电功率可达100瓦 xff08 W xff09 xff1b 随着USB Type C的普及 xff0c 越来
  • Lua ---- LFS库的使用

    lfs attributes filepath aname 获取路径指定属性 lfs chdir path 改变当前工作目录 xff0c 成功返回true xff0c 失败返回nil加上错误信息 lfs currentdir 获取当前工作目
  • USB2.0实际传输速度

    USB2 0规范中传输速度是480 Mbps 即60 MB s 但是很多USB2 0设备在实际工作时的数据传输速度却与此相差甚远 xff0c 比如用PC用U盘拷个东西 xff0c 往往比60MB s慢很多 xff0c 这是为什么呢 xff1
  • Type-C协议-CC检测原理

    Type C协议简介 xff08 CC检测原理 xff09 1 简介 越来越多的手机开始采用Type C作为充电和通信端口 xff0c Type C连接器实物和PIN定义如下图 xff1a Type C连接器中有两个管脚CC1和CC2 xf
  • 基地址和偏移地址

    首先必须明白 cpu和内存的区别 cpu 中央处理器 内存是物理数据存放的地方 cpu不直接存放数据而是通过内存来存放数据 cpu和内存之间通过20条地址总线相连接 xff0c 地址总线就是cpu通过地址找到对应的内存的物理数据的传递工具
  • RZ、NRZ、NRZ1、曼彻斯特编码

    1 RZ Return Zero Code 编码 也称为归零码 xff0c 特性就是在一个周期内 xff0c 用二进制传输数据位 xff0c 在数据位脉冲结束后 xff0c 需要维持一段时间的低电平 RZ编码又分为两种 xff1a 单极性归
  • LTE中RB和RE、REG、CCE的定义

    一 RB RE REG CCE定义 xff1a 1 RB Resource Block xff1a 频率上连续12个子载波 xff0c 时域上一个slot xff0c 称为1个RB xff0c 即1RB 61 12个子载波 RB 61 12
  • UART 波形

    先介绍一些概念 起始位 先发出一个逻辑 0 的信号 xff0c 表示传输数据的开始 校验位 xff1a 数据位加上这一位后 xff0c 使得 1 的位数应为偶数 偶校验 或奇数 奇校验 xff0c 以此来校验数据传送的正确性 就比如传输 A
  • UART中的硬件流控RTS与CTS

    在RS232中本来CTS 与RTS 有明确的意义 xff0c 但自从贺氏 HAYES 推出了聪明猫 SmartModem 后就有点混淆了 xff0c 不过现在这种意义为主流意义的 xff0c 各大芯片制造厂家对UART控制器的流控基本采用H
  • AC-DC电源

    电源是什么 xff1f 电源是将来自能量源 xff08 如供电网 xff09 的电流转换为负载 xff08 如电机或电子设备 xff09 用电所需的电压和电流值的电气设备 电源的目的是以适当的电压和电流为负载供电 因此电流必须以受控的方 式
  • VYSOR-投屏软件

    前言 使用Vysor软件进行投屏 无需root 示 xff1a 以下是本篇文章正文内容 xff0c 下面案例可供参考 一 Vysor是什么 xff1f vysor是一个免root实现电脑控制手机的chrome插件 xff0c 不需要root
  • Camera ZSL

    camera的ZSL是什么 ZSL Zero Shutter Lag 零快门延迟 ubiFocus 高通对照片后期的一种处理技术 xff0c 可以利用多张照片来实现拍照不对焦 xff0c 拍好之后随便选择对焦点的功能 原文链接 xff1a
  • Unity3D中uGUI事件系统简述及使用方法总结

    Unity3D的uGUI系统的将UI可能触发的事件分为12个类型 xff0c 即EventTriggerType枚举的12个值 如下图所示 xff1a 先以PointerClick为例 这个是用于某点点击事件 其他事件都可以根据相同的办法调
  • USB 2.0 A型、B型、Mini和Micro接口 type-c 定义及封装

    USB全 称Universal Serial Bus xff08 通用串行总线 xff09 xff0c 目前USB 2 0接口分为四种类型A型 B型 Mini型还有后来补充的Micro型接口 xff0c 每种接口都分插头和插座两个部分 xf
  • UVC摄像头开发(一)

    近期 xff0c 要做一个usb摄像头的开发 xff0c 然后看了韦东山老师的第三期视频 xff0c 讲UVC协议摄像头开发的教程 做一些记录 xff0c 以免忘记 1 当我们拿到一个摄像头 xff0c 怎么知道它的一些信息呢 xff1f
  • 分析锂电池充放电保护电路的特点及工作原理

    锂在元素周期表上位于第3位 xff0c 因外层电子数为1个 xff0c 容易失去从而形成稳定结构 xff0c 故锂是一种非常活泼的金属 由锂元素制成的锂离子电池 xff0c 具有放电电流大 内阻低 寿命长 无记忆效应等优点 xff0c 现已
  • vscode相关配置

    转载 作者 ifredom 原文链接 xff1a https blog csdn net win7583362 article details 79315055 配置详解 editor是针对vscode的风格设置 例如 tabSize xf
  • odroid x2安装ubuntu系统,HDMI显示,说说折腾事儿。

    安装的是Lubuntu14 04 xff0c odroid官网有下载 xff0c 烧写软件用官网下载的win32disk imager for odroid 不要用通用的win32disk image 可能不行 write烧写完毕之后记得v

随机推荐

  • VC http post 文件到服务器

    首先引入 include 34 afxinet h 34 其次准备好HTTP POST服务器 STDMETHODIMP CPostPDFToA4 t5PostPDFToA4 BSTR strFileName BSTR strFileFull
  • 宏参数

    一 可变参数宏 和 VA ARGS 1 1 直接替代 结果 xff1a weight 61 1 shipping 61 2 span class hljs comment define PR printf VA ARGS span PR s
  • 记Datax3.0解决MySQL抽数到HDFSNULL变为空字符的问题

    一 背景 使用Datax3 0 地址 xff1a https github com alibaba DataX 在HDFS读的时候开放了如下nullFormat选项 xff1a nullFormat 描述 xff1a 文本文件中无法使用标准
  • vector用法总结(定义,操作,方法,注意点)

    一 vector的基本概念 vector是同一种类型的对象的集合 xff0c 每个对象都有一个对应的整数索引值 和string对象一样 xff0c 标准库负责管理存储元素的相关内存 我们把vector称为容器 xff0c 是因为它可以包含其
  • windows c编写串口通信

    一 介绍 平时调试协议直接上其他平台不方便调试 xff0c 这里以windows平台的c语言实现串口通信 二 准备 1 串口调试助手 2 虚拟串口工具 3 dev c 43 43 4 vscode xff08 可以不用 xff09 三 代码
  • C++接口定义及实现举例

    C 43 43 接口定义及实现举例 一 接口的定义 有时候 xff0c 我们得提供一些接口给别人使用 接口的作用 xff0c 就是提供一个与其他系统交互的方法 其他系统无需了解你内部细节 xff0c 并且也无法了解内部细节 xff0c 只能
  • 2016年终总结

    关键点 xff1a 程序员修炼之道Java编程思想 xff08 Java并发 异常处理 xff09 代码大全2Bat批处理Shell编程正则表达式Shader之初体验Unity5 x资源打包和加载Android单机游戏 xff0c 医疗项目
  • 嵌入式debian没有lsusb命令解决

    问题 bash lsusb command not found 解决
  • Postman抓包教程

    目录 什么是抓包 xff1f 如何使用 Postman 进行抓包 查看历史抓包数据 使用抓包数据进行接口测试和开发 抓包技巧和注意事项 什么是抓包 xff1f 在计算机网络中 xff0c 抓包是指捕获网络流量的过程 抓包工具可以截获进出计算
  • Cmakelists 使用 gcc/g++

    转载 https blog csdn net afei article details 81201039 常用变量 预定义变量 PROJECT SOURCE DIR xff1a 工程的根目录 PROJECT BINARY DIR xff1a
  • ROS 环境配置问题

    如果 roslaunch beginner tutorials turtlemimic launch 发现 turtlemimic launch is neither a launch file in package beginner tu
  • getdate()函数

    定义和用法 getdate 函数取得日期 xff0f 时间信息 语法 getdate timestamp 参数 描述 timestamp 可选 规定 Unix 时间格式中的时间 说明 返回一个根据 timestamp 得出的包含有日期信息的
  • linux系统的嵌入式设备调试422串口

    1 登陆linux系统 xff0c 查看当前可用的设备 xff0c 在终端输入 xff1a dmesg grep ttyS 例如会显示如下 xff0c 当前ttyS0可用 2 可先使用linux系统中的串口调试工具 cutecom 检查线路
  • Windows核心编程之邮槽实现进程间通信

    邮槽是Windows系统提供的一种单向通信的机制 即进程中的一方只能写入或读取数据 xff0c 而另一方则只能读取或写入数据 通过邮槽 xff0c 用户可以实现一对多或跨网络的进程之间的通信 但是 xff0c 邮槽能传输的数据非常小 xff
  • 图像连通域分析

    转自 xff1a https blog csdn net tiandijun article details 51279643 xff0c 转载仅为方便学习 一 前言 二值图像的图像的亮度值只有两个状态 xff1a 黑 0 和白 255 二
  • C++ wchar_t *和char * 互转

    转自 xff1a https blog csdn net lightspear article details 54695123 说道wchar t和char两个类型大家都不会陌生 wchar t xff1a 在windows下是Unico
  • element table中加入input、checkbox 数据无法正常渲染问题

    需要注意的是template 的 scope属性 xff0c 最重要的就是一定要加 lt div gt 标签 lt el table key 61 34 tableUpdate 34 data 61 34 list 34 gt lt el
  • mfc连接access数据库

    ADO Active Data Object xff0c 活动数据对象 实际上是一种基于 COM 组件对象模型 的自动化接口 IDispatch 技术 xff0c 并以 OLE DB 对象连接和镶入的数据库 为基础 xff0c 经过 OLE
  • STM32串口之空闲中断

    背景 在做Nbiot的一个路灯项目 xff0c NBiot模块一般都是串口接口 xff0c 使用AT指令集 xff0c 对接中国移动onenet平台 先用串口助手去测试 xff0c 流程测试OK之后需要在MCU上重新写一遍 一开始用的STC
  • C/C++程序的编译过程

    前言 C C 43 43 代码是如何变成一个可以在硬件上运行的程序的呢 xff0c 我们从一个简单的 34 Hello World 34 程序说起 Hello World编译 有个流传挺广的笑话 xff1a 某程序员对书法十分感兴趣 xff