0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux系統(tǒng)如何解析ELF文件

Linux愛(ài)好者 ? 來(lái)源:Linux愛(ài)好者 ? 作者:Linux愛(ài)好者 ? 2022-04-10 16:36 ? 次閱讀


大家好,我是 ELF 文件,大名叫 Executable and Linkable Format。

經(jīng)常在 Linux 系統(tǒng)中開(kāi)發(fā)的小伙伴們,對(duì)于我肯定是再熟悉不過(guò)了,特別是那些需要了解編譯、鏈接的家伙們,估計(jì)已經(jīng)把我研究的透透的。

為了結(jié)識(shí)更多的小伙伴,今天呢,就是我的開(kāi)放日,我會(huì)像洋蔥一樣,一層一層地?fù)荛_(kāi)我的心,讓更多的小伙伴來(lái)了解我,歡迎大家前來(lái)圍觀。

以前啊,我看到有些小伙伴在研究我的時(shí)候,看一下頭部的匯總信息,然后再瞅幾眼 Section 的布局,就當(dāng)做熟悉我了。

從科學(xué)的態(tài)度上來(lái)說(shuō),這是遠(yuǎn)遠(yuǎn)不夠的,未達(dá)究竟。

當(dāng)你面對(duì)編譯、鏈接的詳細(xì)過(guò)程時(shí),還是會(huì)一臉懵逼。

今天,我會(huì)從字節(jié)碼的顆粒度,毫無(wú)保留、開(kāi)誠(chéng)布公、知無(wú)不言、言無(wú)不盡、赤膽忠心、一片丹心、鞠躬盡瘁、死而后已的把自己剖析一遍,讓各位看官大開(kāi)眼界、大飽眼福。

您了解這些知識(shí)之后呢,在今后繼續(xù)學(xué)習(xí)編譯、鏈接的底層過(guò)程,以及一個(gè)可執(zhí)行程序在從硬盤加載到內(nèi)存、一直到 main 函數(shù)的執(zhí)行,心中就會(huì)非常的敞亮。

也就是說(shuō),掌握了 ELF 文件的結(jié)構(gòu)和內(nèi)容,是理解編譯、鏈接和程序執(zhí)行的基礎(chǔ)。

你們不是有一句俗話嘛:磨刀不誤砍柴工!

好了,下面我們就開(kāi)始吧!


文件很單純,復(fù)雜的是人

作為一種文件,那么肯定就需要遵守一定的格式,我也不例外。

從宏觀上看,可以把我拆卸成四個(gè)部分:

bd686e72-b721-11ec-aa7f-dac502259ad0.png

圖中的這幾個(gè)概念,如果不明白的話也沒(méi)關(guān)系,下面我會(huì)逐個(gè)說(shuō)明的。

在 Linux 系統(tǒng)中,一個(gè) ELF 文件主要用來(lái)表示 3 種類型的文件:

既然可以用來(lái)表示 3 種類型的文件,那么在文件中,肯定有一個(gè)地方用來(lái)區(qū)分這 3 種情況。

也許你已經(jīng)猜到了,在我的頭部?jī)?nèi)容中,就存在一個(gè)字段,用來(lái)表示:當(dāng)前這個(gè) ELF 文件,它到底是一個(gè)可執(zhí)行文件?是一個(gè)目標(biāo)文件?還是一個(gè)共享庫(kù)文件?

另外,既然我可以用來(lái)表示 3 種類型的文件,那么就肯定是在 3 種不同的場(chǎng)合下被使用,或者說(shuō)被不同的家伙來(lái)操作我:

可執(zhí)行文件:被操作系統(tǒng)中的加載器從硬盤上讀取,載入到內(nèi)存中去執(zhí)行;

目標(biāo)文件:被鏈接器讀取,用來(lái)產(chǎn)生一個(gè)可執(zhí)行文件或者共享庫(kù)文件;

共享庫(kù)文件:在動(dòng)態(tài)鏈接的時(shí)候,由 ld-linux.so 來(lái)讀取;

就拿鏈接器和加載器來(lái)說(shuō)吧,這兩個(gè)家伙的性格是不一樣的,它們看我的眼光也是不一樣的。

鏈接器在看我的時(shí)候,它的眼睛里只有 3 部分內(nèi)容:

bd8a11e4-b721-11ec-aa7f-dac502259ad0.png

也就是說(shuō),鏈接器只關(guān)心 ELF header, Sections 以及 Section header table 這 3 部分內(nèi)容。

加載器在看我的時(shí)候,它的眼睛里是另外的 3 部分內(nèi)容:

bd9b788a-b721-11ec-aa7f-dac502259ad0.png

加載器只關(guān)心 ELF header, Program header table 和 Segment 這 3 部分內(nèi)容。

對(duì)了,從加載器的角度看,對(duì)于中間部分的 Sections, 它改了個(gè)名字,叫做 Segments(段)。換湯不換藥,本質(zhì)上都是一樣一樣的。

可以理解為:一個(gè) Segment 可能包含一個(gè)或者多個(gè) Sections,就像下面這樣:

bdb06880-b721-11ec-aa7f-dac502259ad0.png

這就好比超市里的貨架上擺放的商品:有礦泉水、可樂(lè)、啤酒,巧克力,牛肉干,薯片。

從理貨員的角度看:它們屬于 6 種不同的商品;但是從超市經(jīng)理的角度看,它們只屬于 2 類商品:飲料和零食。

怎么樣?現(xiàn)在對(duì)我已經(jīng)有一個(gè)總體的印象了吧?

其實(shí)只要掌握到 2 點(diǎn)內(nèi)容就可以了:

一個(gè) ELF 文件一共由 4 個(gè)部分組成;

鏈接器和加載器,它們?cè)谑褂梦业臅r(shí)候,只會(huì)使用它們感興趣的部分;

還有一點(diǎn)差點(diǎn)忘記給你提個(gè)醒了:在 Linux 系統(tǒng)中,會(huì)有不同的數(shù)據(jù)結(jié)構(gòu)來(lái)描述上面所說(shuō)的每部分內(nèi)容。

我知道有些小伙伴比較性急,我先把這幾個(gè)結(jié)構(gòu)體告訴你。

初次見(jiàn)面,先認(rèn)識(shí)一下即可,千萬(wàn)不要深究哦。

描述 ELF header 的結(jié)構(gòu)體:

bde4c666-b721-11ec-aa7f-dac502259ad0.png

描述 Program header table 的結(jié)構(gòu)體:

bdfda2d0-b721-11ec-aa7f-dac502259ad0.png

描述 Section header table 的結(jié)構(gòu)體:

be0f60e2-b721-11ec-aa7f-dac502259ad0.png
ELF header(ELF 頭)

頭部?jī)?nèi)容,就相當(dāng)于是一個(gè)總管,它決定了這個(gè)完整的 ELF 文件內(nèi)部的所有信息,比如:

這是一個(gè) ELF 文件;

一些基本信息:版本,文件類型,機(jī)器類型;

Program header table(程序頭表)的開(kāi)始地址,在整個(gè)文件的什么地方;

Section header table(節(jié)頭表)的開(kāi)始地址,在整個(gè)文件的什么地方;

你是不是有點(diǎn)納悶,好像沒(méi)有說(shuō) Sections(從鏈接器角度看) 或者 Segments(從加載器角度看) 在 ELF 文件的什么地方。

為了方便描述,我就把 Sections 和 Segments 全部統(tǒng)一稱為 Sections 啦!

其實(shí)是這樣的,在一個(gè) ELF 文件中,存在很多個(gè) Sections,這些 Sections 的具體信息,是在 Program header table 或者 Section head table 中進(jìn)行描述的。

就拿 Section head table 來(lái)舉例吧:

假如一個(gè) ELF 文件中一共存在 4 個(gè) Section: .text、.rodata、.data、.bss,那么在 Section head table 中,將會(huì)有 4 個(gè) Entry(條目)來(lái)分別描述這 4 個(gè) Section 的具體信息(嚴(yán)格來(lái)說(shuō),不止 4 個(gè) Entry,因?yàn)檫€存在一些其他輔助的 Sections),就像下面這樣:

be255b22-b721-11ec-aa7f-dac502259ad0.png

在開(kāi)頭我就說(shuō)了,我要用字節(jié)碼的粒度,扒開(kāi)來(lái)給你看!

為了不耍流氓,我還是用一個(gè)具體的代碼示例來(lái)描述,只有這樣,你才能看到實(shí)實(shí)在在的字節(jié)碼。

程序的功能比較簡(jiǎn)單:

be3df1a0-b721-11ec-aa7f-dac502259ad0.png

// mymath.c

int my_add(int a, int b)
{
    return a + b;
}
// main.c

#include 
extern int my_add(int a, int b);

int main()
{
   int i = 1;
   int j = 2;
   int k = my_add(i, j);
   printf("k = %d 
", k);
}

從剛才的描述中可以知道:動(dòng)態(tài)庫(kù)文件 libmymath.so, 目標(biāo)文件 main.o 和 可執(zhí)行文件 main,它們都是 ELF 文件,只不過(guò)屬于不同的類型。

這里就以可執(zhí)行文件 main 來(lái)拆解它!

我們首先用指令 readelf -h main 來(lái)看一下 main 文件中,ELF header 的信息。

readelf 這個(gè)工具,可是一個(gè)好東西?。∫欢ㄒ煤玫睦盟?。

be5daa86-b721-11ec-aa7f-dac502259ad0.png

這張圖中顯示的信息,就是 ELF header 中描述的所有內(nèi)容了。這個(gè)內(nèi)容與結(jié)構(gòu)體 Elf32_Ehdr 中的成員變量是一一對(duì)應(yīng)的!

有沒(méi)有發(fā)現(xiàn)圖中第 15 行顯示的內(nèi)容:Size of this header: 52 (bytes)。

也就是說(shuō):ELF header 部分的內(nèi)容,一共是 52 個(gè)字節(jié)。那么我就把開(kāi)頭的這 52 個(gè)字節(jié)碼給你看一下。

這回,我用 od -Ax -t x1 -N 52 main 這個(gè)指令來(lái)讀取 main 中的字節(jié)碼,簡(jiǎn)單解釋一下其中的幾個(gè)選項(xiàng):

-Ax: 顯示地址的時(shí)候,用十六進(jìn)制來(lái)表示。如果使用 -Ad,意思就是用十進(jìn)制來(lái)顯示地址;

-t -x1: 顯示字節(jié)碼內(nèi)容的時(shí)候,使用十六進(jìn)制(x),每次顯示一個(gè)字節(jié)(1);

-N 52:只需要讀取 52 個(gè)字節(jié);

be7b6238-b721-11ec-aa7f-dac502259ad0.png

這 52 個(gè)字節(jié)的內(nèi)容,你可以對(duì)照上面的結(jié)構(gòu)體中每個(gè)字段來(lái)解釋了。

首先看一下前 16 個(gè)字節(jié)。

在結(jié)構(gòu)體中的第一個(gè)成員是 unsigned char e_ident[EI_NIDENT];,EI_NIDENT 的長(zhǎng)度是 16,代表了 EL header 中的開(kāi)始 16 個(gè)字節(jié),具體含義如下:

0 - 15 個(gè)字節(jié)

be8fb986-b721-11ec-aa7f-dac502259ad0.png

怎樣樣?我以這樣的方式徹底暴露自己,向你表白,足以表現(xiàn)出我的誠(chéng)心了吧?!

如果被感動(dòng)了,別忘記在文章的最底部,點(diǎn)擊一下在看和收藏,也非常感謝您轉(zhuǎn)發(fā)給身邊的小伙伴。贈(zèng)人玫瑰,手留余香!

為了權(quán)威性,我把官方文檔對(duì)于這部分的解釋也貼給大家看一下:

bea5f35e-b721-11ec-aa7f-dac502259ad0.png

關(guān)于大端、小端格式,這個(gè) main 文件中顯示的是 1,代表小端格式。啥意思呢,看下面這張圖就明白了:

beb2e71c-b721-11ec-aa7f-dac502259ad0.png

那么再來(lái)看一下大端格式:

bec74fae-b721-11ec-aa7f-dac502259ad0.png

好了,下面我們繼續(xù)把剩下的 36 個(gè)字節(jié)(52 - 16 = 32),也以這樣的字節(jié)碼含義畫出來(lái):

16 - 31 個(gè)字節(jié):

bed8d60c-b721-11ec-aa7f-dac502259ad0.png

32 - 47 個(gè)字節(jié):

befb7bee-b721-11ec-aa7f-dac502259ad0.png

48 - 51 個(gè)字節(jié):

bf208c36-b721-11ec-aa7f-dac502259ad0.png

具體的內(nèi)容就不用再解釋了,一切都在感情深、一口悶,話不多說(shuō),都在酒里~~ 哦不對(duì),重點(diǎn)都在圖里!


字符串表表項(xiàng) Entry

在一個(gè) ELF 文件中,存在很多字符串,例如:變量名、Section名稱、鏈接器加入的符號(hào)等等,這些字符串的長(zhǎng)度都是不固定的,因此用一個(gè)固定的結(jié)構(gòu)來(lái)表示這些字符串,肯定是不現(xiàn)實(shí)的。

于是,聰明的人類就想到:把這些字符串集中起來(lái),統(tǒng)一放在一起,作為一個(gè)獨(dú)立的 Section 來(lái)進(jìn)行管理。

在文件中的其他地方呢,如果想表示一個(gè)字符串,就在這個(gè)地方寫一個(gè)數(shù)字索引:表示這個(gè)字符串位于字符串統(tǒng)一存儲(chǔ)地方的某個(gè)偏移位置,經(jīng)過(guò)這樣的按圖索驥,就可以找到這個(gè)具體的字符串了。

比如說(shuō)啊,下面這個(gè)空間中存儲(chǔ)了所有的字符串:

bf7385da-b721-11ec-aa7f-dac502259ad0.png

在程序的其他地方,如果想引用字符串 “hello,world!”,那么就只需要在那個(gè)地方標(biāo)明數(shù)字 13 就可以了,表示:這個(gè)字符串從偏移 13 個(gè)字節(jié)處開(kāi)始。

那么現(xiàn)在,咱們?cè)倩氐竭@個(gè) main 文件中的字符串表,

在 ELF header 的最后 2 個(gè)字節(jié)是 0x1C 0x00,它對(duì)應(yīng)結(jié)構(gòu)體中的成員 e_shstrndx,意思是這個(gè) ELF 文件中,字符串表是一個(gè)普通的 Section,在這個(gè) Section 中,存儲(chǔ)了 ELF 文件中使用到的所有的字符串。

既然是一個(gè) Section,那么在 Section header table 中,就一定有一個(gè)表項(xiàng) Entry 來(lái)描述它,那么是哪一個(gè)表項(xiàng)呢?

這就是 0x1C 0x00 這個(gè)表項(xiàng),也就是第 28 個(gè)表項(xiàng)。

這里,我們還可以用指令 readelf -S main 來(lái)看一下這個(gè) ELF 文件中所有的 Section 信息:

bf99b1c4-b721-11ec-aa7f-dac502259ad0.png

其中的第 28 個(gè) Section,描述的正是字符串表 Section:

bfb34f62-b721-11ec-aa7f-dac502259ad0.png

可以看出來(lái):這個(gè) Section 在 ELF 文件中的偏移地址是 0x0016ed,長(zhǎng)度是 0x00010a 個(gè)字節(jié)。

下面,我們從 ELF header 的二進(jìn)制數(shù)據(jù)中,來(lái)推斷這信息。


讀取字符串表 Section 的內(nèi)容

那我就來(lái)演示一下:如何通過(guò) ELF header 中提供的信息,把字符串表這個(gè) Section 給找出來(lái),然后把它的字節(jié)碼打印出來(lái)給各位看官瞧瞧。

要想打印字符串表 Section 的內(nèi)容,就必須知道這個(gè) Section 在 ELF 文件中的偏移地址。

要想知道偏移地址,只能從 Section head table 中第 28 個(gè)表項(xiàng)描述信息中獲取。

要想知道第 28 個(gè)表項(xiàng)的地址,就必須知道 Section head table 在 ELF 文件中的開(kāi)始地址,以及每一個(gè)表項(xiàng)的大小。

正好最后這 2 個(gè)需求信息,在 ELF header 中都告訴我們了,因此我們倒著推算,就一定能成功。

ELF header 中的第 32 到 35 字節(jié)內(nèi)容是:F8 17 00 00(注意這里的字節(jié)序,低位在前),表示的就是 Section head table 在 ELF 文件中的開(kāi)始地址(e_shoff)。

0x000017F8 = 6136,也就是說(shuō) Section head table 的開(kāi)始地址位于 ELF 文件的第 6136 個(gè)字節(jié)處。

知道了開(kāi)始地址,再來(lái)算一下第 28 個(gè)表項(xiàng) Entry 的地址。

ELF header 中的第 46、47 字節(jié)內(nèi)容是:28 00,表示每個(gè)表項(xiàng)的長(zhǎng)度是 0x0028 = 40 個(gè)字節(jié)。

注意這里的計(jì)算都是從 0 開(kāi)始的,因此第 28 個(gè)表項(xiàng)的開(kāi)始地址就是:6136 + 28 * 40 = 7256,也就是說(shuō)用來(lái)描述字符串表這個(gè) Section 的表項(xiàng),位于 ELF 文件的 7256 字節(jié)的位置。

bfda5e40-b721-11ec-aa7f-dac502259ad0.png

既然知道了這個(gè)表項(xiàng) Entry 的地址,那么就扒開(kāi)來(lái)看一下其中的二進(jìn)制內(nèi)容:

執(zhí)行指令:od -Ad -t x1 -j 7256 -N 40 main。

其中的 -j 7256 選項(xiàng),表示跳過(guò)前面的 7256 個(gè)字節(jié),也就是我們從 main 這個(gè) ELF 文件的 7256 字節(jié)處開(kāi)始讀取,一共讀 40 個(gè)字節(jié)。

bff6c062-b721-11ec-aa7f-dac502259ad0.png

這 40 個(gè)字節(jié)的內(nèi)容,就對(duì)應(yīng)了 Elf32_Shdr 結(jié)構(gòu)體中的每個(gè)成員變量:

c01c136c-b721-11ec-aa7f-dac502259ad0.png

這里主要關(guān)注一下上圖中標(biāo)注出來(lái)的 4 個(gè)字段:

sh_name: 暫時(shí)不告訴你,馬上就解釋到了;

sh_type:表示這個(gè) Section 的類型,3 表示這是一個(gè) string table;

sh_offset: 表示這個(gè) Section,在 ELF 文件中的偏移量。0x000016ed = 5869,意思是字符串表這個(gè) Section 的內(nèi)容,從 ELF 文件的 5869 個(gè)字節(jié)處開(kāi)始;

sh_size:表示這個(gè) Section 的長(zhǎng)度。0x0000010a = 266 個(gè)字節(jié),意思是字符串表這個(gè) Section 的內(nèi)容,一共有 266 個(gè)字節(jié)。

還記得剛才我們使用 readelf 工具,讀取到字符串表 Section 在 ELF 文件中的偏移地址是 0x0016ed,長(zhǎng)度是 0x00010a 個(gè)字節(jié)嗎?

與我們這里的推斷是完全一致的!

既然知道了字符串表這個(gè) Section 在 ELF 文件中的偏移量以及長(zhǎng)度,那么就可以把它的字節(jié)碼內(nèi)容讀取出來(lái)。

執(zhí)行指令: od -Ad -t c -j 5869 -N 266 main,所有這些參數(shù)應(yīng)該不用再解釋了吧?!

c068999e-b721-11ec-aa7f-dac502259ad0.png

看一看,瞧一瞧,是不是這個(gè) Section 中存儲(chǔ)的全部是字符串?

剛才沒(méi)有解釋 sh_name 這個(gè)字段,它表示字符串表這個(gè) Section 本身的名字,既然是名字,那一定是個(gè)字符串。

但是這個(gè)字符串不是直接存儲(chǔ)在這里的,而是存儲(chǔ)了一個(gè)索引,索引值是 0x00000011,也就是十進(jìn)制數(shù)值 17。

現(xiàn)在我們來(lái)數(shù)一下字符串表 Section 內(nèi)容中,第 17 個(gè)字節(jié)開(kāi)始的地方,存儲(chǔ)的是什么?

不要偷懶,數(shù)一下,是不是看到了:“.shstrtab” 這個(gè)字符串(?是字符串的分隔符)?!

好了,如果看到這里,你全部都能看懂,那么關(guān)于字符串表這部分的內(nèi)容,說(shuō)明你已經(jīng)完全理解了,給你一百個(gè)贊!??!


讀取代碼段的內(nèi)容

從下面的這張圖(指令:readelf -S main):

bf99b1c4-b721-11ec-aa7f-dac502259ad0.png

可以看到代碼段是位于第 14 個(gè)表項(xiàng)中,加載(虛擬)地址是 0x08048470,它位于 ELF 文件中的偏移量是 0x000470,長(zhǎng)度是 0x0001b2 個(gè)字節(jié)。

那我們就來(lái)試著讀一下其中的內(nèi)容。

首先計(jì)算這個(gè)表項(xiàng) Entry 的地址:6136 + 14 * 40 = 6696。

然后讀取這個(gè)表項(xiàng) Entry,讀取指令是 od -Ad -t x1 -j 6696 -N 40 main:

c0be00b4-b721-11ec-aa7f-dac502259ad0.png

同樣的,我們也只關(guān)心下面這 5 個(gè)字段內(nèi)容:

c0dc0564-b721-11ec-aa7f-dac502259ad0.png

sh_name: 這回應(yīng)該清楚了,表示代碼段的名稱在字符串表 Section 中的偏移位置。0x9B = 155 字節(jié),也就是在字符串表 Section 的第 155 字節(jié)處,存儲(chǔ)的就是代碼段的名字。回過(guò)頭去找一下,看一下是不是字符串 “.text”;

sh_type:表示這個(gè) Section 的類型,1(SHT_PROGBITS) 表示這是代碼;

sh_addr:表示這個(gè) Section 加載的虛擬地址是 0x08048470,這個(gè)值與 ELF header 中的 e_entry 字段的值是相同的;

sh_offset: 表示這個(gè) Section,在 ELF 文件中的偏移量。0x00000470 = 1136,意思是這個(gè) Section 的內(nèi)容,從 ELF 文件的 1136 個(gè)字節(jié)處開(kāi)始;

sh_size:表示這個(gè) Section 的長(zhǎng)度。0x000001b2 = 434 個(gè)字節(jié),意思是代碼段一共有 434 個(gè)字節(jié)。

以上這些分析結(jié)構(gòu),與指令 readelf -S main 讀取出來(lái)的完全一樣!

PS: 在查看字符串表 Section 中的字符串時(shí),不要告訴我,你真的是從 0 開(kāi)始數(shù)到 155 ??!可以計(jì)算一下:字符串表的開(kāi)始地址是 5869(十進(jìn)制),加上 155,結(jié)果就是 6024,所以從 6024 開(kāi)始的地方,就是代碼段的名稱,也就是 “.text”。

知道了以上這些信息,我們就可以讀取代碼段的字節(jié)碼了.使用指令:od -Ad -t x1 -j 1136 -N 434 main 即可。

內(nèi)容全部是黑乎乎的的字節(jié)碼,我就不貼出來(lái)了。


Program header

文章的開(kāi)頭,我就介紹了:我是一個(gè)通用的文件結(jié)構(gòu),鏈接器和加載器在看待我的時(shí)候,眼光是不同的。

為了對(duì) Program header 有更感性的認(rèn)識(shí),我還是先用 readelf 這個(gè)工具來(lái)從總體上看一下 main 文件中的所有段信息。

執(zhí)行指令:readelf -l main,得到下面這張圖:

c10ac3f4-b721-11ec-aa7f-dac502259ad0.png

顯示的信息已經(jīng)很明白了:

這是一個(gè)可執(zhí)行程序;

入口地址是 0x8048470;

一共有 9 個(gè) Program header,是從 ELF 文件的 52 個(gè)偏移地址開(kāi)始的;

布局如下圖所示:

c1221716-b721-11ec-aa7f-dac502259ad0.png

開(kāi)頭我還告訴過(guò)你:Section 與 Segment 本質(zhì)上是一樣的,可以理解為:一個(gè) Secgment 由一個(gè)或多個(gè) Sections 組成。

從上圖中可以看到,第 2 個(gè) program header 這個(gè)段,由那么多的 Section 組成,這下更明白一些了吧?!

從圖中還可以看到,一共有 2 個(gè) LOAD 類型的段:

c1335684-b721-11ec-aa7f-dac502259ad0.png

我們來(lái)讀取第一個(gè) LOAD 類型的段,當(dāng)然還是扒開(kāi)其中的二進(jìn)制字節(jié)碼。

第一步的工作是,計(jì)算這個(gè)段表項(xiàng)的地址信息。

從 ELF header 中得知如下信息:

字段 e_phoff :Program header table 位于 ELF 文件偏移 52 個(gè)字節(jié)的地方。

字段 e_phentsize: 每一個(gè)表項(xiàng)的長(zhǎng)度是 32 個(gè)字節(jié);

字段 e_phnum: 一共有 9 個(gè)表項(xiàng) Entry;

通過(guò)計(jì)算,得到可讀、可執(zhí)行的 LOAD 段,位于偏移量 116 字節(jié)處。

執(zhí)行讀取指令:od -Ad -t x1 -j 116 -N 32 main:

c1494750-b721-11ec-aa7f-dac502259ad0.png

按照上面的慣例,我還是把其中幾個(gè)需要關(guān)注的字段,與數(shù)據(jù)結(jié)構(gòu)中的成員變量進(jìn)行關(guān)聯(lián)一下:

c15b3942-b721-11ec-aa7f-dac502259ad0.png

p_type: 段的類型,1: 表示這個(gè)段需要加載到內(nèi)存中;

p_offset: 段在 ELF 文件中的偏移地址,這里值為 0,表示這個(gè)段從 ELF 文件的頭部開(kāi)始;

p_vaddr:段加載到內(nèi)存中的虛擬地址 0x08048000;

p_paddr:段加載的物理地址,與虛擬地址相同;

p_filesz: 這個(gè)段在 ELF 文件中,占據(jù)的字節(jié)數(shù),0x0744 = 1860 個(gè)字節(jié);

p_memsz:這個(gè)段加載到內(nèi)存中,需要占據(jù)的字節(jié)數(shù),0x0744= 1860 個(gè)字節(jié)。注意:有些段是不需要加載到內(nèi)存中的;

經(jīng)過(guò)上述分析,我們就知道:從 ELF 文件的第 1 到 第 1860 個(gè)字節(jié),都是屬于這個(gè) LOAD 段的內(nèi)容。

在被執(zhí)行時(shí),這個(gè)段需要被加載到內(nèi)存中虛擬地址為 0x08048000 這個(gè)地方,從這里開(kāi)始,又是一個(gè)全新的故事了。


再回顧一下

到這里,我已經(jīng)像洋蔥一樣,把自己的層層外衣都扒開(kāi),讓你看到最細(xì)的顆粒度了,這下子,您是否對(duì)我有足夠的了解了呢?

其實(shí)只要抓住下面 2 個(gè)重點(diǎn)即可:

ELF header 描述了文件的總體信息,以及兩個(gè) table 的相關(guān)信息(偏移地址,表項(xiàng)個(gè)數(shù),表項(xiàng)長(zhǎng)度);

每一個(gè) table 中,包括很多個(gè)表項(xiàng) Entry,每一個(gè)表項(xiàng)都描述了一個(gè) Section/Segment 的具體信息。

鏈接器和加載器也都是按照這樣的原理來(lái)解析 ELF 文件的,明白了這些道理,后面在學(xué)習(xí)具體的鏈接、加載過(guò)程時(shí),就不會(huì)迷路啦!

原文標(biāo)題:Linux 系統(tǒng)中編譯、鏈接的基石 - ELF 文件:扒開(kāi)它的層層外衣,從字節(jié)碼的粒度來(lái)探索

文章出處:【微信公眾號(hào):Linux愛(ài)好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

審核編輯:彭菁

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Linux系統(tǒng)
    +關(guān)注

    關(guān)注

    4

    文章

    590

    瀏覽量

    27317
  • 程序
    +關(guān)注

    關(guān)注

    116

    文章

    3756

    瀏覽量

    80754
  • ELF文件
    +關(guān)注

    關(guān)注

    0

    文章

    14

    瀏覽量

    7125

原文標(biāo)題:Linux 系統(tǒng)中編譯、鏈接的基石 - ELF 文件:扒開(kāi)它的層層外衣,從字節(jié)碼的粒度來(lái)探索

文章出處:【微信號(hào):LinuxHub,微信公眾號(hào):Linux愛(ài)好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    為什么STM32CubeMonitor 1.2.0不解析在Release模式下的.elf 文件中全局變量呢

    它確實(shí)在同一項(xiàng)目的相應(yīng) .elf 文件解析全局變量,在調(diào)試模式下編譯(沒(méi)有優(yōu)化)。(Cube Monitor的)介紹視頻說(shuō)可以解析debug和release .
    發(fā)表于 12-07 06:31

    i.MX6ULL——ElfBoard ELF1板卡 windows 與 ubuntu 系統(tǒng)互傳文件 的方法

    的:內(nèi)核源碼 linux-4.1.15-elf1.tar.bz2、文件系統(tǒng)(本節(jié)以 rootfs.tar.bz2 為例)、命令行測(cè)試程序 elf1_cmd_watchdog.c 放到 Windows
    發(fā)表于 12-02 09:27

    Linux文件系統(tǒng)課程

    本章學(xué)習(xí)目標(biāo)理解什么是文件系統(tǒng)了解文件系統(tǒng)工作原理理解Fedora Core Linux文件系統(tǒng)的結(jié)構(gòu)掌握Fedora Core Linux
    發(fā)表于 04-10 17:07 ?0次下載

    Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》第5章、Linux文件系統(tǒng)與設(shè)備文件系統(tǒng)

    Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》第5章、Linux文件系統(tǒng)與設(shè)備文件系統(tǒng)
    發(fā)表于 10-27 14:13 ?0次下載
    《<b class='flag-5'>Linux</b>設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》第5章、<b class='flag-5'>Linux</b><b class='flag-5'>文件系統(tǒng)</b>與設(shè)備<b class='flag-5'>文件系統(tǒng)</b>

    Linux日志文件系統(tǒng)解析

    在近代歷史上,日志文件系統(tǒng)被認(rèn)為十分奇特,主要是處于研究階段。而如今,日志文件系統(tǒng)(ext3)已經(jīng)成為 Linux的缺省文件系統(tǒng)。本文向大家揭示了日志
    發(fā)表于 11-01 15:23 ?0次下載
    <b class='flag-5'>Linux</b>日志<b class='flag-5'>文件系統(tǒng)</b><b class='flag-5'>解析</b>

    嵌入式bin文件elf文件重點(diǎn)

    執(zhí)行raw binary很簡(jiǎn)單,只需要將程序加載到其起始地址,就可以執(zhí)行; 執(zhí)行ELF程序則需要一個(gè)ELF Loader。 現(xiàn)在知道了吧,uboot和Linux kernel啟動(dòng)的時(shí)候是沒(méi)有E
    的頭像 發(fā)表于 05-15 11:04 ?9119次閱讀
    嵌入式bin<b class='flag-5'>文件</b>和<b class='flag-5'>elf</b><b class='flag-5'>文件</b>重點(diǎn)

    如何在Atmel Studio 6進(jìn)行ELF文件生成

    如何在Atmel Studio 6進(jìn)行ELF文件生成
    的頭像 發(fā)表于 07-04 09:50 ?4433次閱讀

    ELF文件如何在Atmel Studio 6中生成?

    在Atmel Studio 6中ELF文件的生成
    的頭像 發(fā)表于 07-10 10:11 ?4278次閱讀

    簡(jiǎn)單介紹一下LinuxELF格式文件

    ELF(Executable and Linkable Format)即可執(zhí)行連接文件格式,是一種比較復(fù)雜的文件格式,但其應(yīng)用廣泛。
    發(fā)表于 04-27 19:09 ?2.7w次閱讀
    簡(jiǎn)單介紹一下<b class='flag-5'>Linux</b>中<b class='flag-5'>ELF</b>格式<b class='flag-5'>文件</b>

    Linux下可執(zhí)行文件格式

    Linux支持的可執(zhí)行文件主要有:Coff,elf,flat,類似Windows的.exeCoff文件格式? Common Object File Format,最早與
    發(fā)表于 04-02 14:46 ?1555次閱讀

    Linux文件系統(tǒng)解析

    Linux 中,最直觀、最可見(jiàn)的部分就是 文件系統(tǒng)(file system)。下面我們就來(lái)一起探討一下關(guān)于 Linux 中國(guó)的文件系統(tǒng)系統(tǒng)
    的頭像 發(fā)表于 09-16 11:29 ?2426次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>文件系統(tǒng)</b><b class='flag-5'>解析</b>

    單片機(jī)燒錄hex文件是如何解析

    含有單片機(jī)的電子產(chǎn)品在量產(chǎn)的時(shí)候會(huì)用到.hex文件或者.bin。hex是十六進(jìn)制的,包含地址信息和數(shù)據(jù)信息,而bin文件是二進(jìn)制的,只有數(shù)據(jù)而不包含地址。任何文件都有一定的格式規(guī)范,hex文件
    的頭像 發(fā)表于 11-07 10:33 ?6725次閱讀
    單片機(jī)燒錄hex<b class='flag-5'>文件</b>是如<b class='flag-5'>何解析</b>

    如何關(guān)聯(lián)ELF輸出文件并使用vivado對(duì)系統(tǒng)進(jìn)行行為仿真

    本文介紹如何在教程(三)基礎(chǔ)上, 關(guān)聯(lián)ELF輸出文件并使用vivado對(duì)系統(tǒng)進(jìn)行行為仿真。
    的頭像 發(fā)表于 02-08 11:18 ?6047次閱讀
    如何關(guān)聯(lián)<b class='flag-5'>ELF</b>輸出<b class='flag-5'>文件</b>并使用vivado對(duì)<b class='flag-5'>系統(tǒng)</b>進(jìn)行行為仿真

    用于讀取ELF格式文件的詳細(xì)信息的命令:readelf命令

    *.a 文件; (2)可執(zhí)行文件(Executable File),用于生成進(jìn)程映像,載入內(nèi)存執(zhí)行。Linux 環(huán)境下的 ELF 可執(zhí)行文件
    的頭像 發(fā)表于 02-09 18:12 ?4999次閱讀
    用于讀取<b class='flag-5'>ELF</b>格式<b class='flag-5'>文件</b>的詳細(xì)信息的命令:readelf命令

    何解Linux系統(tǒng)中的網(wǎng)絡(luò)連接問(wèn)題?

    何解Linux系統(tǒng)中的網(wǎng)絡(luò)連接問(wèn)題? Linux系統(tǒng)中的網(wǎng)絡(luò)連接問(wèn)題是常見(jiàn)的技術(shù)難題之一,通常涉及在
    的頭像 發(fā)表于 01-12 15:17 ?839次閱讀