C代碼變成可執(zhí)行文件的過程

C代碼是如何變成程序的

C語言是一門典型的編譯語言鸽粉,源代碼文件需要編譯成目標(biāo)代碼文件才能運(yùn)行杖剪。可以認(rèn)為程序文件就是編譯好的目標(biāo)代碼文件梨睁。以GCC的編譯過程為例。GCC的翻譯過程可以分成四個(gè)階段:預(yù)處理器娜饵、編譯器坡贺、匯編器、鏈接器箱舞,執(zhí)行這四個(gè)階段的程序一起構(gòu)成了一個(gè)編譯系統(tǒng)遍坟。

GCC編譯系統(tǒng)

預(yù)處理器

預(yù)處理器(cpp)負(fù)責(zé)對(duì)源代碼進(jìn)行文本處理。它根據(jù)以字符#開頭的命令晴股,修改原始的C代碼愿伴。如:

  1. include <stdio.h> 從編譯器的內(nèi)置查找路徑的根部開始查找stdio.h文件,讀取其內(nèi)容电湘,并把它直接插入到程序文本中公般。

  2. include ”my_header.h” 與上條的區(qū)別就是查找路徑是從當(dāng)前代碼文件所在目錄開始万搔。

  3. define MACRO_NAME CONTEXT 將原始代碼中所有的MACRO_NAME文本都替換成CONTEXT,這種替換可能會(huì)引起很多難以理解的錯(cuò)誤官帘。

  4. define FUNC_NAME(PARA_LIST) CONTEXT 與上條類似,區(qū)別在于會(huì)在查找到FUNC_NAME的地方進(jìn)行參數(shù)匹配昧谊,并將CONTEXT中出現(xiàn)的參數(shù)名稱用對(duì)應(yīng)的文本進(jìn)行替換刽虹。

  5. define MACRO_NAME #undef MACRO_NAME 前者用于單純的宏定義,后者用于取消宏定義呢诬。

  6. ifdef #ifndef #else #endif 這幾個(gè)都是用于條件編譯的命令涌哲,用于決定被包括的文本是否加入到處理后的文本中。

常用的預(yù)處理命令就是這些尚镰,處理后就得到了另一個(gè)C代碼文件阀圾,一般用.i作為擴(kuò)展名。這部分有一個(gè)常用的技巧:header guard狗唉,用于防止頭文件被重復(fù)加載初烘。假設(shè)一個(gè)場(chǎng)景,某個(gè)工程中的3個(gè)文件:main.c分俯、a.h肾筐、b.h,其中每個(gè)文件的開頭有這樣的文本:

//main.c
#include ”a.h”
#include ”b.h”
//a.h
#include ”b.h”
void func_a();
//b.h
void func_b();

上面提到了預(yù)處理器在處理#include時(shí)是直接的文本插入缸剪,處理后的main.i文件的內(nèi)容是:

//main.i
void func_b();
void func_a();
void func_b();

b.h的內(nèi)容被載入了兩次吗铐!這個(gè)例子足夠簡(jiǎn)單,出現(xiàn)這種問題不會(huì)發(fā)生錯(cuò)誤杏节,但如果b.h文件很大唬渗,重復(fù)加載后可能會(huì)出現(xiàn)很多問題,還會(huì)導(dǎo)致編譯時(shí)間的延長(zhǎng)奋渔。這種情況下我們可以使用header guard來防止頭文件被重復(fù)加載镊逝,中間省略的部分即頭文件的正式內(nèi)容:

#ifndef XXX_YYY_ZZZ
#define XXX_YYY_ZZZ
...
#endif

其中XXX_YYY_ZZZ是你自定義的宏名字。如果為每個(gè)頭文件選擇一個(gè)不重復(fù)的宏名字卒稳,這個(gè)宏組合保證了每個(gè)頭文件只會(huì)被一個(gè)代碼文件載入一次蹋半,因?yàn)榈诙屋d入時(shí)XXX_YYY_ZZZ宏已經(jīng)定義過了,就直接跳到了#endif的后面充坑。

編譯階段

編譯器(ccl)將文本文件hello.i翻譯成文本文件hello.s减江,它包含一個(gè)匯編語言程序。匯編語言程序中的每條語句都以一種標(biāo)準(zhǔn)的文本格式確切地描述了一條低級(jí)機(jī)器語言指令捻爷。匯編語言為不同高級(jí)語言的不同編譯器提供了通用的輸出語言辈灼,例如C編譯器和Fortran編譯器產(chǎn)生的輸出文件用的都是一樣的匯編語言。
例如也榄,hello.c為:

#include <stdio.h>
int main(int argc, char *argv[])
{
    printf("hello world\n");   
    return 0;
}

運(yùn)行g(shù)cc –S hello.c可以得到hello.s文件巡莹,其內(nèi)容為:

.file   "hello.c"
.def    ___main;    .scl    2;  .type   32; .endef
.section .rdata,"dr"
LC0:
.ascii "hello world\0"
.text
.globl  _main
.def    _main;  .scl    2;  .type   32; .endef
_main:
LFB6:
.cfi_startproc
pushl   %ebp
.cfi_def_cfa_offset 8

所有以字符.開頭的行都是指導(dǎo)匯編器和鏈接器的命令司志,其它行則是被翻譯成匯編語言的代碼。

匯編階段

接下來降宅,匯編器(as)將hello.s翻譯成機(jī)器語言指令骂远,把這些指令打包成一種叫做可重定位目標(biāo)程序的格式,并將結(jié)果保存在目標(biāo)文件hello.o中腰根。hello.o文件是一個(gè)二進(jìn)制文件激才,它的字節(jié)編碼是機(jī)器語言指令而不是字符,如果我們?cè)谖谋揪庉嬈髦写蜷_hello.o文件额嘿,看到的將是一堆亂碼瘸恼。運(yùn)行g(shù)cc –c hello.c可以得到hello.o文件,它是二進(jìn)制格式册养,無法直接查看东帅,可以用反匯編器來查看它的編碼:objdump –d code.o以一種典型的可重定位目標(biāo)格式ELF為例。ELF文件的頭部數(shù)據(jù)包含了:

  1. 生成該文件的系統(tǒng)的字的大小和字節(jié)順序球拦。
  2. 幫助鏈接器語法分析和解釋目標(biāo)文件信息的數(shù)據(jù)靠闭。ELF文件中包含的數(shù)據(jù)可分成幾個(gè)節(jié),每個(gè)節(jié)的位置和大小是由節(jié)頭部表描述的:
  3. text 機(jī)器代碼
  4. rodata 只讀數(shù)據(jù)刘莹,比如雙引號(hào)括起的字符串等阎毅。
  5. data 已初始化的全局變量。
  6. bss 未初始化的全局變量点弯。在ELF文件中它只是占位符扇调,在目標(biāo)文件中不占據(jù)實(shí)際的空間。
  7. symtab 一個(gè)符號(hào)表抢肛,存放在程序中定義和引用的函數(shù)和全局變量的信息狼钮。
  8. rel.text 一個(gè).text節(jié)中位置的列表,當(dāng)鏈接器進(jìn)行鏈接時(shí)捡絮,需要修改這些位置熬芜。
  9. rel.data 被引用或定義的全局變量的重定位信息,依賴于其它模塊信息的已初始化的全局變量福稳,其值在鏈接時(shí)需要被修改涎拉。
  10. debug 調(diào)試符號(hào)表。
  11. line 機(jī)器代碼與源文件行號(hào)的對(duì)應(yīng)關(guān)系的圆,只有在-g選項(xiàng)時(shí)才會(huì)產(chǎn)生鼓拧。
  12. .strtab 一個(gè)字符串表,包括.symtab和.debug中的符號(hào)表越妈,以及每個(gè)節(jié)的名字季俩。
典型的ELF可重定位目標(biāo)文件

鏈接階段

鏈接器(ld)負(fù)責(zé)將多個(gè)可重定位目標(biāo)文件(.o文件)合并為一個(gè)可執(zhí)行文件,如hello程序文件就是由hello.o和printf.o文件合并得來的梅掠。合并過程中鏈接器負(fù)責(zé)解析符號(hào)表酌住,并修改不同編譯模塊間的引用信息店归,如hello.o的main函數(shù)調(diào)用printf函數(shù)時(shí),機(jī)器代碼的跳轉(zhuǎn)位置直到鏈接階段才會(huì)確定酪我,鏈接器會(huì)將跳轉(zhuǎn)位置修改為printf函數(shù)的入口位置消痛。鏈接器解析本地符號(hào)的引用是非常簡(jiǎn)單的。編譯器只允許每個(gè)模塊中每個(gè)本地符號(hào)只有一個(gè)定義都哭。不過肄满,對(duì)全局符號(hào)的解析就很復(fù)雜。如果鏈接器在所有模塊中都找不到某個(gè)符號(hào)時(shí)质涛,它就輸出”undefined reference”錯(cuò)誤信息并終止。如果所有符號(hào)的解析都順利完成掰担,鏈接器最后會(huì)輸出所有符號(hào)的引用位置都確定了的可執(zhí)行文件汇陆。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市带饱,隨后出現(xiàn)的幾起案子毡代,更是在濱河造成了極大的恐慌,老刑警劉巖勺疼,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件教寂,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡执庐,警方通過查閱死者的電腦和手機(jī)酪耕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來轨淌,“玉大人迂烁,你說我怎么就攤上這事〉蒺模” “怎么了盟步?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)躏结。 經(jīng)常有香客問我却盘,道長(zhǎng),這世上最難降的妖魔是什么媳拴? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任黄橘,我火速辦了婚禮,結(jié)果婚禮上禀挫,老公的妹妹穿的比我還像新娘旬陡。我一直安慰自己,他們只是感情好语婴,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布描孟。 她就那樣靜靜地躺著驶睦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匿醒。 梳的紋絲不亂的頭發(fā)上场航,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音廉羔,去河邊找鬼溉痢。 笑死,一個(gè)胖子當(dāng)著我的面吹牛憋他,可吹牛的內(nèi)容都是我干的孩饼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼竹挡,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼镀娶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起揪罕,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤梯码,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后好啰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體轩娶,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年框往,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鳄抒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡搅窿,死狀恐怖嘁酿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情男应,我是刑警寧澤闹司,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站沐飘,受9級(jí)特大地震影響游桩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜耐朴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一借卧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧筛峭,春花似錦铐刘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽檩禾。三九已至,卻和暖如春疤祭,著一層夾襖步出監(jiān)牢的瞬間盼产,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國打工勺馆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留戏售,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓草穆,卻偏偏與公主長(zhǎng)得像灌灾,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子悲柱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容