分析gcc -v的詳細信息的意義java
首先咱們須要清楚一點,咱們並不能徹底弄清楚gcc -v的全部信息,由於畢竟咱們並非GCC編譯器集合的實現者,對於這些信息,他們纔是最清楚的。因爲咱們不能將全部的信息都搞清楚,因此咱們只分析關鍵信息。雖然咱們不能將全部信息都所有弄清楚,可是分析裏面的關鍵信息仍是很是有意義的,咱們能夠經過這些信息弄清楚不少事情,好比:linux
①經過這些信息,我就知道gcc其實最終仍是調用ccp/cc1/as/collect2(或ld)等程序來實現編譯的四個過程的。c++
②知道c的啓動代碼是怎麼來的程序員
③知道爲何在程序中調用printf、scanf、malloc等函數時,咱們不須要主動連接這些函數的動態庫,可是依然可以使用這些函數bootstrap
gcc -v詳細信息分析ubuntu
源代碼windows
#include <stdio.h> #include <stdlib.h> #define NUM 100 int main() { #if 0 printf("Test condition macro\n"); #endif printf("Hello World\n"); return 0; }
gcc test.c -o test -vapp
[root@localhost ~]# gcc test.c -o test -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper Target: x86_64-redhat-linux Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux Thread model: posix gcc version 4.8.5 20150623 (Red Hat 4.8.5-28) (GCC) COLLECT_GCC_OPTIONS='-o' 'test' '-v' '-mtune=generic' '-march=x86-64' /usr/libexec/gcc/x86_64-redhat-linux/4.8.5/cc1 -quiet -v test.c -quiet -dumpbase test.c -mtune=generic -march=x86-64 -auxbase test -version -o /tmp/cceAJij2.s GNU C (GCC) version 4.8.5 20150623 (Red Hat 4.8.5-28) (x86_64-redhat-linux) compiled by GNU C version 4.8.5 20150623 (Red Hat 4.8.5-28), GMP version 6.0.0, MPFR version 3.1.1, MPC version 1.0.1 GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include-fixed" ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/include" #include "..." search starts here: #include <...> search starts here: /usr/lib/gcc/x86_64-redhat-linux/4.8.5/include /usr/local/include /usr/include End of search list. GNU C (GCC) version 4.8.5 20150623 (Red Hat 4.8.5-28) (x86_64-redhat-linux) compiled by GNU C version 4.8.5 20150623 (Red Hat 4.8.5-28), GMP version 6.0.0, MPFR version 3.1.1, MPC version 1.0.1 GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 Compiler executable checksum: 949efbe3007c23535d2d04d4c10b1f32 COLLECT_GCC_OPTIONS='-o' 'test' '-v' '-mtune=generic' '-march=x86-64' as -v --64 -o /tmp/ccL4wd1V.o /tmp/cceAJij2.s GNU assembler version 2.27 (x86_64-redhat-linux) using BFD version version 2.27-27.base.el7 COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/ LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS='-o' 'test' '-v' '-mtune=generic' '-march=x86-64' /usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o test /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. /tmp/ccL4wd1V.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o [root@localhost ~]#
Using built-in specs. //編譯連接詳細信息
COLLECT_GCC=gcc //編譯時所調用的總調度程序eclipse
Target: x86_64-linux-gnu //gcc編譯獲得的可執行文件的運行環境,cpu:64位x86, OS:linux, gnu:gcc的開發組織jvm
Configured with: ../configure --prefix=/usr //gcc配置信息
gcc配置信息
什麼是gcc配置信息
gcc也是一個程序,也是被別人開發出來的,應該是c/c++語言寫的。編寫gcc這個編譯器程序的人,在編譯gcc時所給的信息就是配置信息,這些信息會決定編譯gcc哪些代碼,不編譯哪些代碼,最終獲得針對某個環境(OS/CPU)的gcc可執行程序。
gcc程序如何面對衆多環境的
gnu開發的gcc能夠面對不少的環境,好比windows x86環境,Linux x86環境,Linux arm環境。編寫gcc的人,爲了讓程序可以應對各類環境(OS、cpu),gcc程序裏面會包含應對各個環境的代碼,若是你想獲得針對某個環境gcc可執行程序,就必須只編譯針對該環境的代碼,其它代碼不編譯。
如何選擇只編譯gcc程序針對某個環境的代碼
經過條件編譯來選擇,就能夠在預編譯階段決定你要保留哪些代碼,放棄哪些代碼,編譯時只編譯你保留的代碼。
好比
gcc.c #define X86_LINUX #ifdef X86_LINUX 針對x8六、Linux環境的代碼。 #endif #ifdef ARM_LINUX 針對arm、Linux環境的代碼。 #endif #ifdef X86_WINDOWS 針對X8六、windows環境的代碼。 #endif
經過條件編譯所需的宏,就能讓條件編譯保留和編譯只針對某個環境的代碼。可是因爲c條件編譯使用的宏實在是太多了,因此咱們不可能本身一個一個的定義這些宏,因此就須要經過配置信息自動生成須要的宏。
配置信息保存在哪裏呢?
配置信息保存在配置文件中,咱們配置信息時,其實就是修改配置文件中的內容。運行配置文件時,根據配置信息的要求,會自動生成須要的宏定義,並把這些宏定義保存到相應的.h(頭文件)中。再將.h給c/c++程序,預處理時,條件編譯根據.h中定義的宏定義,就能決定保留和編譯哪些代碼。編譯時就只編譯保留的代碼,最後就獲得了針對某個環境的gcc可執行程序,不過這些配置信息會保留gcc中,gcc -v時會顯示出來。
Configured with: ../configure
這句話僅僅只是向咱們代表,gcc的配置信息實際上是來源於這個文件,這個配置文件並不在個人電腦上,而是在gcc開發者的電腦上,編譯gcc時gcc開發者會去設置這個配置文件。
--prefix=/usr
路徑固定前綴,也就是說gcc所使用到的路徑都是/usr打頭的,換句話說gcc所用到的文件,都在這個/usr目錄下。
--with-bugurl=http://bugzilla.redhat.com/bugzilla
gcc bug報告說明書:若是你發現了gcc的bug,須要按照README.Bugs說明書的要求來提交gcc的bug
--enable-languages
gcc編譯器集合所支持的語言,不過想要編譯java等其它語言,須要下載相應的插件
--libdir=/usr/lib
GCC編譯器集合「自帶庫」所在目錄
--enable-java-awt=gtk
使用gcc編譯帶界面的java程序,圖形界面底層調用的是ubuntu的gtk基礎圖形庫。gcc能夠編譯java,可是java程序運行須要相應的運行環境(最起碼要有個java虛擬機jvm)如下信息描述的就是java的運行環境
--with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc
gcc基本選項:
-march=x86-64:intel 64位 x86 cpu
-mtune=generic:
編譯獲得的機器指令,屬於通用指令集(同款的不一樣型號的cpu都支持的指令集)。若是須要指定某型號cpu的特殊指令集時,就不能寫成generic,而要寫特殊指令集名稱COLLECT_GCC_OPTIONS='-o' 'helloworld' '-v' '-mtune=generic' '-march=x86-64'
crt1.o、crti.o、crtbegin.o:
用於生成程序的「啓動代碼」,這三個.o是由GCC編譯器集合提供的(由GCC開發者編寫的),crt就是c/c++ run time的意思,翻譯爲中文就是「運行時環境」。
啓動代碼的做用:搭建c/c++的運行環境
crt1.o:彙編寫的。
①裏面的_start是整個程序的開始(入口)
②main函數是由crt1.o調用
③c/c++函數運行所須要的棧,是由crt1.o創建的
crti.o:在調用main以前,實現c的一些初始化工做,好比全局變量初始化
crtbegin.o:在調用main以前,實現c++的一些初始化,好比調用全局構造函數,建立全局對象
crtend.o、crtn.o:
用於生成掃尾代碼,程序運行結束時,作一些掃尾工做,這兩個.o也是由gcc開發者編寫的,爲了方便描述,咱們每每將掃尾代碼認爲是啓動代碼的一部分。
crtend.o:掃尾作什麼?好比調用c++析構函數,釋放全局對象的空間
crtn.o:掃尾作什麼?好比,接收main函數的返回值並處理
①若是程序是裸機運行的,返回值到掃尾代碼這裏就結束了,裸機時返回值的意義不大
②若是程序是基於OS運行的,掃尾代碼會將返回值交給OS
頭文件包含
包含""所指定的頭文件:到程序員本身指定的路徑下去搜索
#include "..." search starts here:
包含<>所指定的頭文件:到系統指定的路徑下去找
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/5/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
彙編
因爲as的路徑已經被加入到了環境變量中,所以調用as時,並不須要指定as的路徑。生成xxx.s彙編文件是一個臨時文件,/tmp目錄專門用於存放Linux系統所生成的臨時文件,一旦編譯獲得了可執行文件,這個xxx.s將會別刪除
連接
collect2爲連接器,因爲collect2的路徑沒有加入環境變量,所以須要咱們本身指明collect2所在的路徑。
/usr/lib/gcc/x86_64-linux-gnu/5/collect2
collect2的選項和參數
...
給程序指定動態連接器:程序運行起來後,用於加載動態庫
-dynamic-linker /lib64/ld-linux-x86-64.so.2
最終生成的可執行文件