ELF 文件結構及靜態(tài)鏈接

簡介

C/C++ 代碼在變成可執(zhí)行文件之前需要經歷預處理、編譯箫津、匯編以及鏈接這幾個步驟俭嘁,最終生成的可執(zhí)行文件包含了能夠被系統(tǒng)處理的機器碼‰燃眨可執(zhí)行文件必須按照特定的格式進行組織才能被系統(tǒng)加載氓栈、執(zhí)行,所以可執(zhí)行文件是特定于操作系統(tǒng)的婿着。對于 Linux 來說是 ELF(Executable Linkable Format) 格式的文件授瘦,Windows 是 PE(Portable) 格式。對于 Java 代碼竟宋,編譯生成的 Class 文件也是有著特定的格式提完,才能被 JVM 執(zhí)行。

一個程序一般由多個文件組成丘侠,文件之間會有變量和函數(shù)的引用徒欣,每個文件各自編譯生成中間文件后必須經過鏈接才能生成最終的可執(zhí)行文件。根據(jù)鏈接方式的不同可以分為靜態(tài)鏈接和動態(tài)鏈接蜗字,靜態(tài)鏈接是在鏈接期間重定位所有的符號引用打肝,而動態(tài)鏈接則是在裝載或者執(zhí)行期間進行。

本文主要分析 Linux 下 ELF 文件的格式以及靜態(tài)鏈接的過程挪捕。

目標文件的格式

源代碼被編譯生成的文件叫做目標粗梭,目標文件與可執(zhí)行文件的格式是類似的,只是還沒有經歷鏈接级零,其中包含的有些地址還沒有被調整断医。

目標文件中包含機器碼、數(shù)據(jù)、符號表以及調試信息等孩锡,這些屬性按照不同的段(Section ) 進行存儲酷宵。段就是一定長度的的區(qū)域亥贸,不同的屬性放在不同名字的段躬窜,具體如下所示:

c_code_storage.jpg
c_code_storage.jpg

可以看出,代碼放在了名為 .text 的段炕置,變量 global_init_varstatic_var 放在了名為 .data 的段荣挨,變量 global_uninit_varstatic_var 放在名為 .bss 的段。.bss 段存放的是未初始化的全局變量和局部靜態(tài)變量朴摊。

上圖的 EFL 文件除了幾個段默垄,還有文件頭(File Header),其中包含了文件是否可執(zhí)行甚纲、是靜態(tài)鏈接還是動態(tài)鏈接以及目標硬件口锭、操作系統(tǒng)等信息,還包括一個段表介杆,段表是一個數(shù)組結構鹃操,描述了文件中各個段在文件中的偏移位置及段的屬性等。用 readelf -h 可以讀取上面代碼編譯后目標文件的頭信息春哨,如下圖:

elf_header.png
elf_header.png

從上圖可以看到荆隘,其中包含了文件的魔數(shù)(Magic) 、字長(class)赴背、CPU 類型等信息椰拒,如果是可執(zhí)行文件,還包括程序的入口地址凰荚。Start of section headers 的值是段表的偏移量燃观。

目標文件中除了上面介紹的代碼段和數(shù)據(jù)段,還有很多其它段便瑟,readelf -S 命令可以查看段表的信息缆毁,如下圖:

elf_sections.png
elf_sections.png

可以看出,上面的目標文件總共有 12 個段胳徽,第一個為無效段积锅,實際上是 11 個段。其中有字符串表 .strtab养盗、符號表 .symtab 以及注釋信息 .comment 等缚陷。還有一個段是 .rela.txt 段,這個是重定位表往核,在靜態(tài)鏈接過程中需要用到箫爷。

靜態(tài)鏈接

在了解了 ELF 文件的結構之后,接下來介紹靜態(tài)鏈接的過程。以下面的代碼為例:

/* a.c */
extern int shared;

int main()
{
        int a = 100;
        swap(&a, &shared);
}

/* b.c */
int shared = 1;

void swap(int *a, int *b)
{
        *a ^= *b ^= *a ^= *b;
}

在上面的代碼中虎锚,b.c 定義了全局符號硫痰,分別是變量 shared 和函數(shù) swapa.c 定義了一個全局符號 main窜护。在 a.c 中引用了 b.c 里面的 sharedswap效斑。用 gcc -c -fno-stack-protector a.c b.c 編譯這兩個文件之后(-fno-stack-protector 是關閉堆棧保護功能),生成了兩個目標文件 a.ob.o柱徙,下一步就是要把這兩個文件鏈接在一起缓屠,形成最終的可執(zhí)行文件。

空間與地址分配

靜態(tài)鏈接的第一步是把多個目標文件進行合并护侮,一般采用相似段合并的方式敌完。通過掃描所有的輸入目標文件,并且獲得它們各個段的長度羊初、屬性和位置滨溉,并且將輸入目標文件中的符號表中所有的符號定義和符號引用收集起來,統(tǒng)一放到一個全局符號表长赞。多個目標文件合并后如下圖所示:

obj_merge.jpg
obj_merge.jpg

符號地址的確定

利用上一步收集到的數(shù)據(jù)晦攒,進行符號解析與重定位、調整代碼中的地址等涧卵。利用命令 ld a.o b.o -e main -o aba.ob.o 鏈接(-e main 是將 main 函數(shù)作為程序的入口)勤家,生成可執(zhí)行文件 ab。鏈接前后段的地址信息如下所示:

a.o_b.o_ab_address.png
a.o_b.o_ab_address.png

上圖是 a.o柳恐、b.o 以及鏈接后的 ab 的地址信息伐脖。其中 Size 是段的大小,VMA 是虛擬地址乐设。對于 a.ob.o.text 段來說讼庇,大小分別是 0000002c0000004b, 加起來正好是 ab.text 段的大小 00000077近尚。另外蠕啄, a.ob.o 的 VMA 都是 00000000,此時它們還沒有分配地址戈锻,而在 ab 中歼跟,地址變?yōu)?00000000004000e8,這就是分配的虛擬地址格遭,當 ab 被加載到內存中后哈街, .text 段的起始地址便是這個。

段的地址被確定后拒迅,內部函數(shù)和變量的地址也就確定了骚秦,因為在每個段內她倘,符號的表示是一個相對于段起始位置的偏移量。當段的起始位置被確定后作箍,每個符號只要在偏移量的基礎上加上這個起始位置的地址就行硬梁。但是對于引用的外部符號來說,它們的地址還不得知胞得,需要經過符號解析和重定位的過程荧止。

符號解析與重定位

在 a.c 中引用了變量 shared 和函數(shù) swap ,單獨編譯 a.c 的時候并不知道 b.c 這個文件懒震,所以在 a.o 中罩息,用到 shared 的地方用 0 地址代替嗤详,等到鏈接階段个扰,能夠確定這個變量的地址了,再把地址進行調整葱色。

這里的問題是鏈接器如何知道哪些指令需要被調整呢递宅?這就用到上面提到過的重定位表,命令 objdump -r a.o 可以查看 a.o 中的重定位表苍狰,如下圖:

relocation_table.png
relocation_table.png

每一個需要被重定位的地方叫做一個重定位入口办龄,可以看到,a.o 中需要重定位的兩個符號 sharedswap淋昭。將重定位入口的地址進行修正俐填,才能完成鏈接過程,最終生成的可執(zhí)行文件便可以被系統(tǒng)正常運行翔忽。

總結

代碼從文本形式到最終的可執(zhí)行文件需要經歷多個過程英融,其中鏈接主要做的是多個目標文件的合并以及符號的解析與重定位,最終生成特定格式的可執(zhí)行文件歇式。本文大概地介紹了 ELF 文件的結構和靜態(tài)鏈接的主要步驟驶悟,更詳細的內容可以查看相關書籍深入了解。

參考

  • 《程序員的自我修養(yǎng):鏈接材失、裝載與庫》
  • 《深入理解計算機系統(tǒng)》
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末痕鳍,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子龙巨,更是在濱河造成了極大的恐慌笼呆,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旨别,死亡現(xiàn)場離奇詭異诗赌,居然都是意外死亡,警方通過查閱死者的電腦和手機昼榛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門境肾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來剔难,“玉大人,你說我怎么就攤上這事奥喻∨脊” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵环鲤,是天一觀的道長纯趋。 經常有香客問我,道長冷离,這世上最難降的妖魔是什么吵冒? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮西剥,結果婚禮上痹栖,老公的妹妹穿的比我還像新娘。我一直安慰自己瞭空,他們只是感情好揪阿,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著咆畏,像睡著了一般南捂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上旧找,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天溺健,我揣著相機與錄音,去河邊找鬼钮蛛。 笑死鞭缭,一個胖子當著我的面吹牛,可吹牛的內容都是我干的愿卒。 我是一名探鬼主播缚去,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼琼开!你這毒婦竟也來了易结?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤柜候,失蹤者是張志新(化名)和其女友劉穎搞动,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體渣刷,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡鹦肿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了辅柴。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片箩溃。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡瞭吃,死狀恐怖,靈堂內的尸體忽然破棺而出涣旨,到底是詐尸還是另有隱情歪架,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布霹陡,位于F島的核電站和蚪,受9級特大地震影響,放射性物質發(fā)生泄漏烹棉。R本人自食惡果不足惜攒霹,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望浆洗。 院中可真熱鬧催束,春花似錦、人聲如沸辅髓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽洛口。三九已至,卻和暖如春凯沪,著一層夾襖步出監(jiān)牢的瞬間第焰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工妨马, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留挺举,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓烘跺,卻偏偏與公主長得像湘纵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子滤淳,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345