天天看點

生成可執行檔案過程

C語言編譯全過程

    編譯的概念:編譯程式讀取源程式(字元流),對之進行詞法和文法的分析,将進階語言指令轉換為功能等效的彙編代碼,再由彙程式設計式轉換為機器語言,并且按照作業系統對可執行檔案格式的要求連結生成可執行程式。     編譯的完整過程:C源程式-->預編譯處理(.c)-->編譯、優化程式(.s、.asm)-->彙程式設計式(.obj、.o、.a、.ko)-->連結程式(.exe、.elf、.axf等)

1. 編譯預處理     讀取c源程式,對其中的僞指令(以#開頭的指令)和特殊符号進行處理 僞指令主要包括以下四個方面:  (1)宏定義指令,如#define Name TokenString,#undef等。 對于前一個僞指令,預編譯所要做的是将程式中的所有Name用TokenString替換,但作為字元串常量的 Name則不被替換。對于後者,則将取消對某個宏的定義,使以後該串的出現不再被替換。  (2)條件編譯指令,如#ifdef,#ifndef,#else,#elif,#endif等。 這些僞指令的引入使得程式員可以通過定義不同的宏來決定編譯程式對哪些代碼進行處理。預編譯程式将根據有關的檔案,将那些不必要的代碼過濾掉  (3) 頭檔案包含指令,如#include "FileName"或者#include <FileName>等。 在頭檔案中一般用僞指令#define定義了大量的宏(最常見的是字元常量),同時包含有各種外部符号的聲明。     采用頭檔案的目的主要是為了使某些定義可以供多個不同的C源程式使用。因為在需要用到這些定義的C源程式中,隻需加上一條#include語句即可,而不必再在此檔案中将這些定義重複一遍。預編譯程式将把頭檔案中的定義統統都加入到它所産生的輸出檔案中,以供編譯程式對之進行處理。     包含到c源程式中的頭檔案可以是系統提供的,這些頭檔案一般被放在/usr/include目錄下。在程式中#include它們要使用尖括号(< >)。另外開發人員也可以定義自己的頭檔案,這些檔案一般與c源程式放在同一目錄下,此時在#include中要用雙引号("")。  (4)特殊符号,預編譯程式可以識别一些特殊的符号。 例如在源程式中出現的LINE辨別将被解釋為目前行号(十進制數),FILE則被解釋為目前被編譯的C源程式的名稱。預編譯程式對于在源程式中出現的這些串将用合适的值進行替換。    預編譯程式所完成的基本上是對源程式的“替代”工作。經過此種替代,生成一個沒有宏定義、沒有條件編譯指令、沒有特殊符号的輸出檔案。這個檔案的含義同沒有經過預處理的源檔案是相同的,但内容有所不同。下一步,此輸出檔案将作為編譯程式的輸出而被翻譯成為機器指令。  2. 編譯、優化階段     經過預編譯得到的輸出檔案中,隻有常量;如數字、字元串、變量的定義,以及C語言的關鍵字,如main,if,else,for,while,{,}, +,-,*,/等等。     編譯程式所要作得工作就是通過詞法分析和文法分析,在确認所有的指令都符合文法規則之後,将其翻譯成等價的中間代碼表示或彙編代碼。     優化處理是編譯系統中一項比較艱深的技術。它涉及到的問題不僅同編譯技術本身有關,而且同機器的硬體環境也有很大的關系。優化一部分是對中間代碼的優化。這種優化不依賴于具體的計算機。另一種優化則主要針對目标代碼的生成而進行的。     對于前一種優化,主要的工作是删除公共表達式、循環優化(代碼外提、強度削弱、變換循環控制條件、已知量的合并等)、複寫傳播,以及無用指派的删除,等等。 後一種類型的優化同機器的硬體結構密切相關,最主要的是考慮是如何充分利用機器的各個硬體寄存器存放的有關變量的值,以減少對于記憶體的通路次數。另外,如何根據機器硬體執行指令的特點(如流水線、RISC、CISC、VLIW等)而對指令進行一些調整使目标代碼比較短,執行的效率比較高,也是一個重要的研究課題。     經過優化得到的彙編代碼必須經過彙程式設計式的彙編轉換成相應的機器指令,方可能被機器執行。   3. 彙編過程     彙編過程實際上指把彙編語言代碼翻譯成目标機器指令的過程。對于被翻譯系統處理的每一個C語言源程式,都将最終經過這一處理而得到相應的目标檔案。目标檔案中所存放的也就是與源程式等效的目标的機器語言代碼。  目标檔案由段組成。通常一個目标檔案中至少有兩個段:     代碼段:該段中所包含的主要是程式的指令。該段一般是可讀和可執行的,但一般卻不可寫。     資料段:主要存放程式中要用到的各種全局變量或靜态的資料。一般資料段都是可讀,可寫,可執行的。  UNIX環境下主要有三種類型的目标檔案:  (1)可重定位檔案 其中包含有适合于其它目标檔案連結來建立一個可執行的或者共享的目标檔案的代碼和資料。  (2)共享的目标檔案    這種檔案存放了适合于在兩種上下文裡連結的代碼和資料。    第一種是連結程式可把它與其它可重定位檔案及共享的目标檔案一起處理來建立另一個 目标檔案;   第二種是動态連結程式将它與另一個可執行檔案及其它的共享目标檔案結合到一起,建立一個程序映象。 (3)可執行檔案     它包含了一個可以被作業系統建立一個程序來執行之的檔案。 彙程式設計式生成的實際上是第一種類型的目标檔案。對于後兩種還需要其他的一些處理方能得到,這個就是連結程式的工作了。   4. 連結程式       由彙程式設計式生成的目标檔案并不能立即就被執行,其中可能還有許多沒有解決 的問題。     例如,某個源檔案中的函數可能引用了另一個源檔案中定義的某個符号(如變量或者函數調用等);在程式中可能調用了某個庫檔案中的函數,等等。所有的這些問題,都需要經連結程式的處理方能得以解決。     連結程式的主要工作就是将有關的目标檔案彼此相連接配接,也即将在一個檔案中引用的符号同該符号在另外一個檔案中的定義連接配接起來,使得所有的這些目标檔案成為一個能夠诶作業系統裝入執行的統一整體。     根據開發人員指定的同庫函數的連結方式的不同,連結處理可分為兩種:  (1)靜态連結     在這種連結方式下,函數的代碼将從其所在地靜态連結庫中被拷貝到最終的可執行程式中。這樣該程式在被執行時這些代碼将被裝入到該程序的虛拟位址空間中。靜态連結庫實際上是一個目标檔案的集合,其中的每個檔案含有庫中的一個或者一組相關函數的代碼。  (2) 動态連結     在此種方式下,函數的代碼被放到稱作是動态連結庫或共享對象的某個目标檔案中。連結程式此時所作的隻是在最終的可執行程式中記錄下共享對象的名字以及其它少量的登記資訊。在此可執行檔案被執行時,動态連結庫的全部内容将被映射到運作時相應程序的虛位址空間。動态連結程式将根據可執行程式中記錄的資訊找到相應的函數代碼。     對于可執行檔案中的函數調用,可分别采用動态連結或靜态連結的方法。使用動态連結能夠使最終的可執行檔案比較短小,并且當共享對象被多個程序使用時能節約一些記憶體,因為在記憶體中隻需要儲存一份此共享對象的代碼。但并不是使用動态連結就一定比使用靜态連結要優越。在某些情況下動态連結可能帶來一些性能上損害。 總結:     C語言編譯的整個過程是非常複雜的,裡面涉及到的編譯器知識、硬體知識、工具鍊知識都是非常多的,深入了解整個編譯過程對工程師了解應用程式的編寫是有很大幫助的,希望大家可以多了解一些,在遇到問題時多思考、多實踐。     一般情況下,我們隻需要知道分成編譯和連接配接兩個階段,編譯階段将源程式(*.c)轉換成為目标代碼(,一般是obj檔案,至于具體過程就是上面說的那些階段),連接配接階段是把源程式轉換成的目标代碼(obj檔案)與你程式裡面調用的庫函數對應的代碼連接配接起來形成對應的可執行檔案(exe檔案)就可以了,其他的都需要在實踐中多多體會才能有更深的了解。    

自動生成 Makefile 的全過程詳解! automake/autoconf 入門

automake/autoconf 入門

作為Linux 下的程式開發人員,大家一定都遇到過Makefile ,用make 指令來編譯自己寫的程式确實是很友善。一般情況下,大家都是手工寫一個簡單Makefile ,如果要想寫出一個符合自由軟體慣例的Makefile 就不那麼容易了。

在本文中,将給大家介紹如何使用autoconf 和automake 兩個工具來幫助我們自動地生成符合自由軟體慣例的Makefile ,這樣就可以象常 見的GNU 程式一樣,隻要使用“./configure” ,“make” ,“make instal” 就可以把程式安裝到Linux 系統中去了。這将特别适合想做開放源代碼軟體的程式開發人員,又或如果你隻是自己寫些小的Toy 程式,那麼這 個文章對你也會有很大的幫助。

一、Makefile 介紹

Makefile 是用于自動編譯和連結的,一個工程有很多檔案組成,每一個檔案的改變都會導緻工程的重新連結,但是不是所有的檔案都需要重新編譯,Makefile 中紀錄有檔案的資訊,在make 時會決定在連結的時候需要重新編譯哪些檔案。

Makefile 的宗旨就是:讓編譯器知道要編譯一個檔案需要依賴其他的哪些檔案。當那些依賴檔案有了改變,編譯器會自動的發現最終的生成檔案已經過時,而重新編譯相應的子產品。

Makefile 的基本結構不是很複雜,但當一個程式開發人員開始寫Makefile 時,經常會懷疑自己寫的是否符合慣例,而且自己寫的 Makefile 經常和自己的開發環境相關聯,當系統環境變量或路徑發生了變化後,Makefile 可能還要跟着修改。這樣就造成了手工書寫 Makefile 的諸多問題,automake 恰好能很好地幫助我們解決這些問題。

使用automake ,程式開發人員隻需要寫一些 簡單的含有預定義宏的檔案,由autoconf 根據一個宏檔案生成configure ,由automake 根據另一個宏檔案生成Makefile.in , 再使用configure 依據Makefile.in 來生成一個符合慣例的Makefile 。下面我們将詳細介紹Makefile 的automake 生成 方法。

二、使用的環境

本文所提到的程式是基于Linux 發行版本:Fedora Core release 1 ,它包含了我們要用到的autoconf ,automake 。

三、從helloworld 入手

我們從大家最常使用的例子程式helloworld 開始。

下面的過程如果簡單地說來就是:

建立三個檔案:

helloworld.c

configure.in

Makefile.am

然後執行:

aclocal; autoconf; automake --add-missing; ./configure; make; ./helloworld

就可以看到Makefile 被産生出來,而且可以将helloworld.c 編譯通過。

很簡單吧,幾條指令就可以做出一個符合慣例的Makefile ,感覺如何呀。

現在開始介紹詳細的過程:

1 、建目錄

在你的工作目錄下建一個helloworld 目錄,我們用它來存放helloworld 程式及相關檔案,如在/home/my/build 下:

$ mkdir helloword

$ cd helloworld

2 、 helloworld.c

然後用你自己最喜歡的編輯器寫一個hellowrold.c 檔案,如指令:vi helloworld.c 。使用下面的代碼作為helloworld.c 的内容。

int main(int argc, char** argv)

{

printf("Hello, Linux World!/n");

return 0;

}

完成後儲存退出。

現在在helloworld 目錄下就應該有一個你自己寫的helloworld.c 了。

3 、生成configure

我們使用autoscan 指令來幫助我們根據目錄下的源代碼生成一個configure.in 的模闆檔案。

指令:

$ autoscan

$ ls

configure.scan helloworld.c

執行後在hellowrold 目錄下會生成一個檔案:configure.scan ,我們可以拿它作為configure.in 的藍本。

現在将configure.scan 改名為configure.in ,并且編輯它,按下面的内容修改,去掉無關的語句:

============================configure.in 内容開始=========================================

# -*- Autoconf -*-

# Process this file with autoconf to produce a configure script.

AC_INIT(helloworld.c)

AM_INIT_AUTOMAKE(helloworld, 1.0)

# Checks for programs.

AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_OUTPUT(Makefile)

============================configure.in 内容結束=========================================

然後執行指令aclocal 和autoconf ,分别會産生aclocal.m4 及configure 兩個檔案:

$ aclocal

$ls

aclocal.m4 configure.in helloworld.c

$ autoconf

$ ls

aclocal.m4 autom4te.cache configure configure.in helloworld.c

大家可以看到configure.in 内容是一些宏定義,這些宏經autoconf 處理後會變成檢查系統特性、環境變量、軟體必須的參數的shell 腳本。

autoconf 是用來生成自動配置軟體源代碼腳本(configure )的工具。configure 腳本能獨立于autoconf 運作,且在運作的過程中,不需要使用者的幹預。

要生成configure 檔案,你必須告訴autoconf 如何找到你所用的宏。方式是使用aclocal 程式來生成你的aclocal.m4 。

aclocal 根據configure.in 檔案的内容,自動生成aclocal.m4 檔案。aclocal 是一個perl 腳本程式,它的定義是:“aclocal - create aclocal.m4 by scanning configure.ac” 。

autoconf 從configure.in 這個列舉編譯軟體時所需要各種參數的模闆檔案中建立configure 。

autoconf 需要GNU m4 宏處理器來處理aclocal.m4 ,生成configure 腳本。

m4 是一個宏處理器。将輸入拷貝到輸出,同時将宏展開。宏可以是内嵌的,也可以是使用者定義的。除了可以展開宏,m4 還有一些内建的函數,用來引用檔案,執行指令,整數運算,文本操作,循環等。m4 既可以作為編譯器的前端,也可以單獨作為一個宏處理器。

4 、建立Makefile.am

建立Makefile.am 檔案,指令:

$ vi Makefile.am

内容如下:

AUTOMAKE_OPTIONS=foreign

bin_PROGRAMS=helloworld

helloworld_SOURCES=helloworld.c

automake 會根據你寫的Makefile.am 來自動生成Makefile.in 。

Makefile.am 中定義的宏和目标, 會指導automake 生成指定的代碼。例如,宏bin_PROGRAMS 将導緻編譯和連接配接的目标被生成。

5 、運作automake

指令:

$ automake --add-missing

configure.in: installing `./install-sh'

configure.in: installing `./mkinstalldirs'

configure.in: installing `./missing'

Makefile.am: installing `./depcomp'

automake 會根據Makefile.am 檔案産生一些檔案,包含最重要的Makefile.in 。

6 、執行configure 生成Makefile

$ ./configure

checking for a BSD-compatible install... /usr/bin/install -c

checking whether build environment is sane... yes

checking for gawk... gawk

checking whether make sets $(MAKE)... yes

checking for gcc... gcc

checking for C compiler default output... a.out

checking whether the C compiler works... yes

checking whether we are cross compiling... no

checking for suffix of executables...

checking for suffix of object files... o

checking whether we are using the GNU C compiler... yes

checking whether gcc accepts -g... yes

checking for gcc option to accept ANSI C... none needed

checking for style of include used by make... GNU

checking dependency style of gcc... gcc3

configure: creating ./config.status

config.status: creating Makefile

config.status: executing depfiles commands

$ ls -l Makefile

-rw-rw-r-- 1 yutao yutao 15035 Oct 15 10:40 Makefile

你可以看到,此時Makefile 已經産生出來了。

7 、使用Makefile 編譯代碼

$ make

if gcc -DPACKAGE_NAME="" -DPACKAGE_TARNAME="" -DPACKAGE_VERSION="" -

DPACKAGE_STRING="" -DPACKAGE_BUGREPORT="" -DPACKAGE="helloworld" -DVERSION="1.0"

-I. -I. -g -O2 -MT helloworld.o -MD -MP -MF ".deps/helloworld.Tpo" /

-c -o helloworld.o `test -f 'helloworld.c' || echo './'`helloworld.c; /

then mv -f ".deps/helloworld.Tpo" ".deps/helloworld.Po"; /

else rm -f ".deps/helloworld.Tpo"; exit 1; /

fi

gcc -g -O2 -o helloworld helloworld.o

運作helloworld

$ ./helloworld

Hello, Linux World!

這樣helloworld 就編譯出來了,你如果按上面的步驟來做的話,應該也會很容易地編譯出正确的helloworld 檔案。你還可以試着使用一些其 他的make 指令,如make clean ,make install ,make dist ,看看它們會給你什麼樣的效果。感覺如何?自己也能寫出這麼專業的Makefile ,老闆一定會對你刮目相看。

四、深入淺出

針對上面提到的各個指令,我們再做些詳細的介紹。

1 、 autoscan

autoscan 是用來掃描源代碼目錄生成configure.scan 檔案的。autoscan 可以用目錄名做為參數,但如果你不使用參數的話,那麼 autoscan 将認為使用的是目前目錄。autoscan 将掃描你所指定目錄中的源檔案,并建立configure.scan 檔案。

2 、 configure.scan

configure.scan 包含了系統配置的基本選項,裡面都是一些宏定義。我們需要将它改名為configure.in

3 、 aclocal

aclocal 是一個perl 腳本程式。aclocal 根據configure.in 檔案的内容,自動生成aclocal.m4 檔案。aclocal 的定義是:“aclocal - create aclocal.m4 by scanning configure.ac” 。

4 、 autoconf

autoconf 是用來産生configure 檔案的。configure 是一個腳本,它能設定源程式來适應各種不同的作業系統平台,并且根據不同的系統來産生合适的Makefile ,進而可以使你的源代碼能在不同的作業系統平台上被編譯出來。

configure.in 檔案的内容是一些宏,這些宏經過autoconf 處理後會變成檢查系統特性、環境變量、軟體必須的參數的shell 腳本。configure.in 檔案中的宏的順序并沒有規定,但是你必須在所有宏的最前面和最後面分别加上AC_INIT 宏和AC_OUTPUT 宏。

在configure.ini 中:

# 号表示注釋,這個宏後面的内容将被忽略。

AC_INIT(FILE)

這個宏用來檢查源代碼所在的路徑。

AM_INIT_AUTOMAKE(PACKAGE, VERSION)

這個宏是必須的,它描述了我們将要生成的軟體包的名字及其版本号:PACKAGE 是軟體包的名字,VERSION 是版本号。當你使用make dist 指令時,它會給你生成一個類似helloworld-1.0.tar.gz 的軟體發行包,其中就有對應的軟體包的名字和版本号。

AC_PROG_CC

這個宏将檢查系統所用的C 編譯器。

AC_OUTPUT(FILE)

這個宏是我們要輸出的Makefile 的名字。

我們在使用automake 時,實際上還需要用到其他的一些宏,但我們可以用aclocal 來幫我們自動産生。執行aclocal 後我們會得到aclocal.m4 檔案。

産生了configure.in 和aclocal.m4 兩個宏檔案後,我們就可以使用autoconf 來産生configure 檔案了。

5 、 Makefile.am

Makefile.am 是用來生成Makefile.in 的,需要你手工書寫。Makefile.am 中定義了一些内容:

AUTOMAKE_OPTIONS

這個是automake 的選項。在執行automake 時,它會檢查目錄下是否存在标準GNU 軟體包中應具備的各種檔案,例如AUTHORS 、ChangeLog 、NEWS 等檔案。我們将其設定成foreign 時,automake 會改用一般軟體包的标準來檢查。

bin_PROGRAMS

這個是指定我們所要産生的可執行檔案的檔案名。如果你要産生多個可執行檔案,那麼在各個名字間用空格隔開。

helloworld_SOURCES

這個是指定産生“helloworld” 時所需要的源代碼。如果它用到了多個源檔案,那麼請使用空格符号将它們隔開。比如需要 helloworld.h ,helloworld.c 那麼請寫成helloworld_SOURCES= helloworld.h helloworld.c 。

如果你在bin_PROGRAMS 定義了多個可執行檔案,則對應每個可執行檔案都要定義相對的filename_SOURCES 。

6 、 automake

我們使用automake --add-missing 來産生Makefile.in 。

選項--add-missing 的定義是“add missing standard files to package” ,它會讓automake 加入一個标準的軟體包所必須的一些檔案。

我們用automake 産生出來的Makefile.in 檔案是符合GNU Makefile 慣例的,接下來我們隻要執行configure 這個shell 腳本就可以産生合适的 Makefile 檔案了。

7 、 Makefile

在符合GNU Makefiel 慣例的Makefile 中,包含了一些基本的預先定義的操作:

make

根據Makefile 編譯源代碼,連接配接,生成目标檔案,可執行檔案。

make clean

清除上次的make 指令所産生的object 檔案(字尾為“.o” 的檔案)及可執行檔案。

make install

将編譯成功的可執行檔案安裝到系統目錄中,一般為/usr/local/bin 目錄。

make dist

産生釋出軟體封包件(即distribution package )。這個指令将會将可執行檔案及相關檔案打包成一個tar.gz 壓縮的檔案用來作為釋出軟體的軟體包。

它會在目前目錄下生成一個名字類似“PACKAGE-VERSION.tar.gz” 的檔案。PACKAGE 和VERSION ,是我們在configure.in 中定義的AM_INIT_AUTOMAKE(PACKAGE, VERSION) 。

make distcheck

生成釋出軟體包并對其進行測試檢查,以确定釋出包的正确性。這個操作将自動把壓縮封包件解開,然後執行configure 指令,并且執行make ,來确認編譯不出現錯誤,最後提示你軟體包已經準備好,可以釋出了。

===============================================

helloworld-1.0.tar.gz is ready for distribution

===============================================

make distclean

類似make clean ,但同時也将configure 生成的檔案全部删除掉,包括Makefile 。

五、結束語

通過上面的介紹,你應該可以很容易地生成一個你自己的符合GNU 慣例的Makefile 檔案及對應的項目檔案。

如果你想寫出更複雜的且符合慣例的Makefile ,你可以參考一些開放代碼的項目中的configure.in 和Makefile.am 檔案,比如:嵌入式資料庫sqlite ,單元測試cppunit 。

gcc 從語言編譯全過程 預處理---->編譯---->彙編----->連結

文章分類:C++程式設計

在Linux下進行C語言程式設計,必然要采用GNU GCC來編譯C源代碼生成可執行程式。

一、GCC快速入門

Gcc指令的一般格式為:Gcc [選項] 要編譯的檔案 [選項] [目标檔案]

其中,目标檔案可預設,Gcc預設生成可執行的檔案名為:編譯檔案.out

我們來看一下經典入門程式"Hello World!"

# vi hello.c

#include <stdlib.h>

#include <stdio.h>

void main(void)

{

printf("hello world!/r/n");

}

用gcc編譯成執行程式。

#gcc hello.c

該指令将hello.c直接生成最終二進制可執行程式a.out

這條指令隐含執行了(1)預處理、(2)彙編、(3)編譯并(4)連結形成最終的二進制可執行程式。這裡未指定輸出檔案,預設輸出為a.out。

如何要指定最終二進制可執行程式名,那麼用-o選項來指定名稱。比如需要生成執行程式hello.exe

那麼

#gcc hello.c -o hello.exe

二、GCC的指令剖析--四步走

從上面我們知道GCC編譯源代碼生成最終可執行的二進制程式,GCC背景隐含執行了四個階段步驟。

GCC編譯C源碼有四個步驟:

預處理-----> 編譯 ----> 彙編 ----> 連結

現在我們就用GCC的指令選項來逐個剖析GCC過程。

1)預處理(Pre-processing)

在該階段,編譯器将C源代碼中的包含的頭檔案如stdio.h編譯進來,使用者可以使用gcc的選項”-E”進行檢視。

用法:#gcc -E hello.c -o hello.i

作用:将hello.c預處理輸出hello.i檔案。

[root]# gcc -E hello.c -o hello.i

[root]# ls

hello.c  hello.i

[root]# vi hello.i

# 1 "hello.c"

# 1 "<built-in>"

# 1 "<command line>"

# 1 "hello.c"

# 1 "/usr/include/stdlib.h" 1 3

# 25 "/usr/include/stdlib.h" 3

# 1 "/usr/include/features.h" 1 3

# 291 "/usr/include/features.h" 3

# 1 "/usr/include/sys/cdefs.h" 1 3

# 292 "/usr/include/features.h" 2 3

# 314 "/usr/include/features.h" 3

# 1 "/usr/include/gnu/stubs.h" 1 3

# 315 "/usr/include/features.h" 2 3

# 26 "/usr/include/stdlib.h" 2 3

# 3 "hello.c" 2

void main(void)

{

printf("hello world!/r/n");

}

2)編譯階段(Compiling)

第二步進行的是編譯階段,在這個階段中,Gcc首先要檢查代碼的規範性、是否有文法錯誤等,以确定代碼的實際要做的工作,在檢查無誤後,Gcc把代碼翻譯成彙編語言。使用者可以使用”-S”選項來進行檢視,該選項隻進行編譯而不進行彙編,生成彙編代碼。

選項 -S

用法:[root]# gcc –S hello.i –o hello.s

作用:将預處理輸出檔案hello.i彙編成hello.s檔案。

[[email protected] hello-gcc]# ls

hello.c  hello.i  hello.s

如下為hello.s彙編代碼

[[email protected] hello-gcc]# vi hello.s

.file   "hello.c"

.section    .rodata

.LC0:

.string "hello world!/r/n"

.text

.globl main

.type   main,@function

main:

pushl   %ebp

movl    %esp, %ebp

subl    $8, %esp

andl    $-16, %esp

movl    $0, %eax

subl    %eax, %esp

subl    $12, %esp

pushl   $.LC0

call    printf

addl    $16, %esp

movl    $0, %eax

leave

ret

.Lfe1:

.size   main,.Lfe1-main

.ident  "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"

3)彙編階段(Assembling)

彙編階段是把編譯階段生成的”.s”檔案轉成二進制目标代碼.

選項 -c

用法:[root]# gcc –c hello.s –o hello.o

作用:将彙編輸出檔案test.s編譯輸出test.o檔案。

[root]# gcc -c hello.s -o hello.o

[root]# ls

hello.c  hello.i  hello.o  hello.s

4)連結階段(Link)

在成功編譯之後,就進入了連結階段。

無選項連結

用法:[root]# gcc hello.o –o hello.exe

作用:将編譯輸出檔案hello.o連結成最終可執行檔案hello.exe。

[root]# ls

hello.c  hello.exe  hello.i  hello.o  hello.s

運作該可執行檔案,出現正确的結果如下。

[[email protected] Gcc]# ./hello

Hello World!

在這裡涉及到一個重要的概念:函數庫。

讀者可以重新檢視這個小程式,在這個程式中并沒有定義”printf”的函數實作,且在預編譯中包含進的”stdio.h”中也隻有該函數的聲明,而沒有定義函數的實作,那麼,是在哪裡實作”printf”函數的呢?最後的答案是:系統把這些函數實作都被做到名為libc.so.6的庫檔案中去了,在沒有特别指定時,gcc會到系統預設的搜尋路徑”/usr/lib”下進行查找,也就是連結到libc.so.6庫函數中去,這樣就能實作函數”printf” 了,而這也就是連結的作用。

你可以用ldd指令檢視動态庫加載情況:

[root]# ldd hello.exe

libc.so.6 => /lib/tls/libc.so.6 (0x42000000)

/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

函數庫一般分為靜态庫和動态庫兩種。靜态庫是指編譯連結時,把庫檔案的代碼全部加入到可執行檔案中,是以生成的檔案比較大,但在運作時也就不再需要庫檔案了。其字尾名一般為”.a”。動态庫與之相反,在編譯連結時并沒有把庫檔案的代碼加入到可執行檔案中,而是在程式執行時由運作時連結檔案加載庫,這樣可以節省系統的開銷。動态庫一般字尾名為”.so”,如前面所述的libc.so.6就是動态庫。gcc在編譯時預設使用動态庫。

繼續閱讀