計算機系統篇之鏈接(1):gcc/g++的編譯流程

計算機系統篇之鏈接(1):gcc/g++的編譯流程

Author:stormQ

Saturday, 21. December 2019 09:48AM


gcc/g++,實質上是一個編譯驅動器(Compile Driver),對它的調用意味着調用一系列的程序——預處理器、編譯器、彙編器和鏈接器,從而將源代碼轉化成可執行目標文件。具體流程可以分爲如下四個步驟:

1)預處理

首先調用 C 預處理器(名稱爲cpp的可執行目標文件,通常位於/usr/bin/目錄下)將每個源文件(比如:以.c 或 .cc 或. cpp結尾的文件)擴展源代碼——即插入所有用#include命令指定的文件,並擴展所有用#define聲明指定的宏。對一個源文件進行預處理的輸出結果是一個以.i結尾的中間文件,即編碼爲 ASCII 碼的源代碼中間文件。

由源文件生成預處理文件的命令:

# 一個源文件(以.c/.cc/.cpp結尾的文件)對應一個預處理後的源文件(以.i結尾的文件)
$ cpp -std=c++11 main.cpp -o main.i
$ cpp -std=c++11 shm_manager_sim.cpp -o shm_manager_sim.i
$ cpp -std=c++11 subscription_sim.cpp -o subscription_sim.i
$ cpp -std=c++11 topic_manager_sim.cpp -o topic_manager_sim.i

$ g++ -E main.cpp -o main.i
$ g++ -E shm_manager_sim.cpp -o shm_manager_sim.i
$ g++ -E subscription_sim.cpp -o subscription_sim.i
$ g++ -E topic_manager_sim.cpp -o topic_manager_sim.i

注意:

  • main.cpp 中包含了<thread>頭文件(在 C++11 標準中涵蓋)。所以,直接調用預處理器cpp時需要添加-std=c++11選項,而採用g++ -E的方式不需要添加該選項。

  • 如果源文件與其包含的用戶頭文件不在同一個目錄中,需要添加-I選項指定搜索路徑,從而成功地生成.i文件。示例:1)用戶頭文件都處於同一個目錄時,對應的命令:g++ -E main.cpp -o main.i -I <directory>;2)用戶頭文件處於不同目錄時,對應的命令:g++ -E main.cpp -o main.i -I <directory 1> -I <directory 2> -I <directory n>

  • 在某些 gcc/g++ 版本中,預處理器被集成到編譯驅動器中,而不是作爲獨立的程序存在。

2)編譯

其次,調用編譯器(名稱爲cc1——用於編譯C程序的可執行目標文件或cc1plus——用於編譯C++程序的可執行目標文件,在本人的機器上兩者位於/usr/lib/gcc/x86_64-linux-gnu/6目錄下)將擴展後的源代碼(以.i結尾的文件)編譯成彙編代碼(以.s結尾的文件,即編碼爲 ASCII 碼的彙編語言文件)。

由預處理文件生成彙編語言文件的命令:

# 一個預處理後的源文件(以.i結尾的文件)對應一個彙編代碼文件(以.s結尾的文件)
# /usr/lib/gcc/x86_64-linux-gnu/6/cc1plus -o main.s main.cpp <other arguments>
$ /usr/lib/gcc/x86_64-linux-gnu/6/cc1plus -o main.s main.cpp -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE -quiet -dumpbase -mtune=generic -march=x86-64 -auxbase-strip -version -fstack-protector-strong -Wformat -Wformat-security

$ g++ -S main.i -o main.s
$ g++ -S shm_manager_sim.i -o shm_manager_sim.s
$ g++ -S subscription_sim.i -o subscription_sim.s
$ g++ -S topic_manager_sim.i -o topic_manager_sim.s

注意:

  • cc1plus命令中<other arguments>部分如何確定?詳見下文。

  • 如果需要添加調試信息,只能在執行本步驟時加-g選項。其他時期:步驟1)、步驟3)、步驟4)時加-g選項都沒有任何作用。添加-g所生成的彙編代碼文件中可以看到與調試相關的 sections,如:.debug_aranges.debug_info.debug_abbrev.debug_line.debug_str.debug_ranges等。

  • 區分目標文件是 DEBUG 版本還是 RELEASE 版本的方法之一:readelf -S main | grep debug。如果是 DEBUG 版本,會有帶.debug*的信息輸出;否則,什麼也不會輸出。

  • 編譯器的輸入可以是源文件,也可以是預處理後的文件。

  • 對於以 .cpp 結尾的源文件,無論是 gcc 還是 g++,實際調用的編譯器都是 cc1plus。

<other arguments>部分的確定過程:

1)使用-v選項打印

$ g++ -S main.cpp -o main.s -v

輸出結果爲:

$ g++ -S main.cpp -o main.s -v
Using built-in specs.
COLLECT_GCC=g++
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 6.5.0-2ubuntu1~16.04' --with-bugurl=file:///usr/share/doc/gcc-6/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --with-as=/usr/bin/x86_64-linux-gnu-as --with-ld=/usr/bin/x86_64-linux-gnu-ld --program-suffix=-6 --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 --with-sysroot=/ --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 --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-6-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-6-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-6-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --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-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 6.5.0 20181026 (Ubuntu 6.5.0-2ubuntu1~16.04) 
COLLECT_GCC_OPTIONS='-S' '-o' 'main.s' '-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/6/cc1plus -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE main.cpp -quiet -dumpbase main.cpp -mtune=generic -march=x86-64 -auxbase-strip main.s -version -o main.s -fstack-protector-strong -Wformat -Wformat-security
GNU C++14 (Ubuntu 6.5.0-2ubuntu1~16.04) version 6.5.0 20181026 (x86_64-linux-gnu)
	compiled by GNU C version 6.5.0 20181026, GMP version 6.1.0, MPFR version 3.1.4, MPC version 1.0.3, isl version 0.15
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/6"
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/6/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/include/c++/6
 /usr/include/x86_64-linux-gnu/c++/6
 /usr/include/c++/6/backward
 /usr/lib/gcc/x86_64-linux-gnu/6/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/6/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.
GNU C++14 (Ubuntu 6.5.0-2ubuntu1~16.04) version 6.5.0 20181026 (x86_64-linux-gnu)
	compiled by GNU C version 6.5.0 20181026, GMP version 6.1.0, MPFR version 3.1.4, MPC version 1.0.3, isl version 0.15
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: f1f1eb1c4b7ccfc7f1bfde5650f1de21
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-S' '-o' 'main.s' '-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'

2)完整的編譯選項爲 cc1plus 後面的內容,即

-quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE main.cpp -quiet -dumpbase main.cpp -mtune=generic -march=x86-64 -auxbase-strip main.s -version -o main.s -fstack-protector-strong -Wformat -Wformat-security

從上面的輸出結果中可以看到,-o main.smain.cpp。除這部分之外的就是<other arguments>部分對應的內容了。

3)彙編

接下來,調用匯編器(名稱爲as的可執行目標文件,通常位於/usr/bin/目錄下)將彙編代碼(以.s結尾的文件)轉化成可重定位目標代碼(以.o結尾的文件,彙編代碼的二進制表示,但還未填入全局值的地址)。

由彙編語言文件生成可重定位目標文件的命令:

# 一個彙編代碼文件(以.s結尾的文件)對應一個可重定位目標文件(以.o結尾的文件)
$ as main.s -o main.o
$ as shm_manager_sim.s -o shm_manager_sim.o
$ as subscription_sim.s -o subscription_sim.o
$ as topic_manager_sim.s -o topic_manager_sim.o

$ g++ -c main.s -o main.o
$ g++ -c shm_manager_sim.s -o shm_manager_sim.o
$ g++ -c subscription_sim.s -o subscription_sim.o
$ g++ -c topic_manager_sim.s -o topic_manager_sim.o

4)鏈接

最後,調用鏈接器(名稱爲ld的可執行目標文件,通常位於/usr/bin/目錄下)將(多個)可重定位目標文件以及一些必要的系統目標文件組合起來,併產生最終的可執行目標文件。

由(多個)可重定位目標文件生成可執行文件的命令:

# 將多個可重定位目標文件(以.o結尾的文件)組合起來生成一個可執行目標文件
# ld -o main main.o shm_manager_sim.o subscription_sim.o topic_manager_sim.o <system object files and args>
ld -o main main.o shm_manager_sim.o subscription_sim.o topic_manager_sim.o -plugin /usr/lib/gcc/x86_64-linux-gnu/6/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper -plugin-opt=-fresolution=/tmp/ccCu2RiH.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lpthread -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro  /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/6/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/6 -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../../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/6/../../..  -lstdc++ -lm -lgcc_s -lgcc -lpthread -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/6/crtend.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crtn.o

$ g++ -o main main.o shm_manager_sim.o subscription_sim.o topic_manager_sim.o -pthread

注:

  • ld命令中<system object files and args>部分如何確定?詳見下文。

  • g++ -o main main.o shm_manager_sim.o subscription_sim.o topic_manager_sim.o -pthread命令將多個可重定位目標文件(main.o、shm_manager_sim.o、subscription_sim.o、topic_manager_sim.o)和系統目標文件(通過-pthread選項指定)等生成最終的可執行目標文件——main。

<system object files and args>部分的確定過程:

1)使用-v選項打印

$ g++ -v -o main_test main.o  shm_manager_sim.o subscription_sim.o topic_manager_sim.o -pthread

輸出結果爲:

$ g++ -v -o main_test main.o  shm_manager_sim.o subscription_sim.o topic_manager_sim.o -pthread
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 6.5.0-2ubuntu1~16.04' --with-bugurl=file:///usr/share/doc/gcc-6/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --with-as=/usr/bin/x86_64-linux-gnu-as --with-ld=/usr/bin/x86_64-linux-gnu-ld --program-suffix=-6 --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 --with-sysroot=/ --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 --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-6-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-6-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-6-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --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-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 6.5.0 20181026 (Ubuntu 6.5.0-2ubuntu1~16.04) 
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-o' 'main_test' '-pthread' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/6/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/6/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper -plugin-opt=-fresolution=/tmp/ccCu2RiH.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lpthread -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro -o main_test /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/6/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/6 -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../../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/6/../../.. main.o shm_manager_sim.o subscription_sim.o topic_manager_sim.o -lstdc++ -lm -lgcc_s -lgcc -lpthread -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/6/crtend.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-v' '-o' 'main_test' '-pthread' '-shared-libgcc' '-mtune=generic' '-march=x86-64'

2)完整的鏈接選項爲 collect2 後面的內容,即

-plugin /usr/lib/gcc/x86_64-linux-gnu/6/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper -plugin-opt=--plugin /usr/lib/gcc/x86_64-linux-gnu/6/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper -plugin-opt=-fresolution=/tmp/ccCu2RiH.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lpthread -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro -o main_test /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/6/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/6 -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../../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/6/../../.. main.o shm_manager_sim.o subscription_sim.o topic_manager_sim.o -lstdc++ -lm -lgcc_s -lgcc -lpthread -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/6/crtend.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crtn.o

從上面的輸出結果中可以看到,-o main_testmain.o shm_manager_sim.o subscription_sim.o topic_manager_sim.o等。除這部分之外的就是<system object files and args>部分對應的內容了。

如果你覺得本文對你有所幫助,歡迎關注公衆號,支持一下!

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章