ELF文件詳解—初步認識

ELF文件詳解—初步認識

轉自:https://blog.csdn.net/daide2012/article/details/73065204

一、? 引言

在講解ELF文件格式之前,我們來回顧一下,一個用C語言編寫的高級語言程序是從編寫到打包、再到編譯執行的基本過程,我們知道在CPU上執行的是低級別的機器語言,從高級語言到低級別的機器語言肯定是要經過翻譯過程,這個過程大體的過程如下圖所示:

?


在Unix系統中,從源文件到可執行目標文件是由編譯驅動程序完成的,如大名鼎鼎的gcc,翻譯過程包括圖中的是個階段;

?? 預處理階段

預處理器(cpp)根據以字符#開頭的命令修給原始的C程序,結果得到另一個C程序,通常以.i作為文件擴展名。主要是進行文本替換、宏展開、刪除注釋這類簡單工作。

對應的命令:linux> gcc -E hello.c hello.i?

?? 編譯階段

編譯器將文本文件hello.i翻譯成hello.s,包含相應的匯編語言程序

對應的命令:linux> gcc -S hello.c hello.s?

?? 匯編階段

將.s文件翻譯成機器語言指令,把這些指令打包成一種叫做可重定位目標程序的格式,并將結果保存在目標文件.o中(把匯編語言翻譯成機器語言的過程)。

把一個源程序翻譯成目標程序的工作過程分為五個階段:詞法分析;語法分析;語義檢查和中間代碼生成;代碼優化;目標代碼生成。主要是進行詞法分析和語法分析,又稱為源程序分析,分析過程中發現有語法錯誤,給出提示信息。

對應的命令:linux> gcc -c hello.c hello.o

?? 鏈接階段

此時hello程序調用了printf函數。 printf函數存在于一個名為printf.o的單獨的預編譯目標文件中。 鏈接器(ld)就負責處理把這個文件并入到hello.o程序中,結果得到hello文件,一個可執行文件。最后可執行文件加載到儲存器后由系統負責執行, ?函數庫一般分為靜態庫和動態庫兩種。靜態庫是指編譯鏈接時,把庫文件的代碼全部加入到可執行文件中,因此生成的文件比較大,但在運行時也就不再需要庫文件了。其后綴名一般為.a。動態庫與之相反,在編譯鏈接時并沒有把庫文件的代碼加入到可執行文件中,而是在程序執行時由運行時鏈接文件加載庫,這樣可以節省系統的開銷。動態庫一般后綴名為.so,gcc在編譯時默認使用動態庫。

二、目標文件

由上面的過程,我們可以看出在經過匯編器和連接器作用后都會輸出一個目標文件,那這兩個目標文件有什么樣的區別呢?說到這里我們先引入目標文件的形式

2.1 三種目標文件形式

(1)可重定位目標文件:包含二進制代碼和數據,其形式可以和其他目標文件進行合并,創建一個可執行目標文件

(2)可執行目標文件:包含二進制代碼和數據,可直接被加載器加載執行

(3)共享目標文件:可被動態的加載和鏈接(本文暫時不討論)

由此我們可知由匯編器生成的就是可重定位目標文件,經過鏈接器作用后才生成可執行目標文件,鏈接器的作用就是以一組可重定位目標文件作為輸入,生成可加載和運行的可執行目標文件,具體需要完成以下兩個工作:

?? 符號解析:符號解析的目的是將目標文件中每個符號(靜態變量、函數、全局變量)和其定義進行關聯

?? 重定位:將每個符號的定義與具體在虛擬內存中的位置進行關聯

最終生成可執行目標文件

說到這里好像還是沒有說清楚這兩種目標文件有什么區別,我們還是先把這個問題放一下,相信你看完下一節,應該會有答案,下面我們開始引入目標文件ELF文件。

三、ELF文件

目標文件再不同的系統或平臺上具有不同的命名格式,在Unix和X86-64 Linux上稱為ELF(Executable and Linkable Format, ELF)。

ELF文件格式提供了兩種不同的視角,在匯編器和鏈接器看來,ELF文件是由Section Header Table描述的一系列Section的集合,而執行一個ELF文件時,在加載器(Loader)看來它是由Program Header Table描述的一系列Segment的集合


左邊是從匯編器和鏈接器的視角來看這個文件,開頭的ELF Header描述了體系結構和操作系統等基本信息,并指出Section Header Table和Program Header Table在文件中的什么位置,Program Header Table在匯編和鏈接過程中沒有用到,所以是可有可無的,Section Header Table中保存了所有Section的描述信息。右邊是從加載器的視角來看這個文件,開頭是ELF Header,Program Header Table中保存了所有Segment的描述信息,Section Header Table在加載過程中沒有用到,所以是可有可無的。注意Section Header Table和Program Header Table并不是一定要位于文件開頭和結尾的,其位置由ELF Header指出,上圖這么畫只是為了清晰。


我們在匯編程序中用.section聲明的Section會成為目標文件中的Section,此外匯編器還會自動添加一些Section(比如符號表)。Segment是指在程序運行時加載到內存的具有相同屬性的區域,由一個或多個Section組成,比如有兩個Section都要求加載到內存后可讀可寫,就屬于同一個Segment。有些Section只對匯編器和鏈接器有意義,在運行時用不到,也不需要加載到內存,那么就不屬于任何Segment。


目標文件需要鏈接器做進一步處理,所以一定有Section Header Table;可執行文件需要加載運行,所以一定有Program Header Table;而共享庫既要加載運行,又要在加載時做動態鏈接,所以既有Section Header Table又有Program Header Table。

關于目標文件的具體節的數據結構,有興趣的讀者參照北大的一個資料寫的非常詳細

點擊打開鏈接


下面用readelf工具讀出目標文件max.o的ELF Header和Section Header Table,然后我們逐段分析。



接下來我們來看Section Header Table格式



從Section Header中讀出各Section的描述信息,其中.text.data是我們在匯編程序中聲明的Section,而其它Section是匯編器自動添加的。Addr是這些段加載到內存中的地址(我們講過程序中的地址都是虛擬地址),加載地址要在鏈接時填寫,現在空缺,所以是全0。OffSize兩列指出了各Section的文件地址,比如.data從文件地址0x60開始,一共0x38個字節,回去翻一下程序,.data中定義了14個4字節的整數,一共是56個字節,也就是0x38個。根據以上信息可以描繪出整個目標文件的布局。

起始文件地址

Section或Header

0

ELF Header

0x34

.text

0x60

.data

0x98

.bss(此段為空)

0x98

.shstrtab

0xc8

Section Header Table

0x208

.symtab

0x288

.strtab

0x2b0

.rel.text

?

這個文件不大,我們直接用hexdump或者使用010 Editor工具把目標文件的字節全部打印出來看。



3.1 .shstrtab.strtab

.shstrtab.strtab這兩個Section中存放的都是ASCII碼:



可見.shstrtab中保存著各Section的名字,.strtab中保存著程序中用到的符號的名字。每個名字都是以'\0'結尾的字符串。

我們知道,C語言的全局變量如果在代碼中沒有初始化,就會在程序加載時用0初始化。這種數據屬于.bss段,在加載時它和.data段一樣都是可讀可寫的數據,但是在ELF文件中.data段需要占用一部分空間保存初始值,而.bss段則不需要。也就是說,.bss段在文件中只占一個Section Header而沒有對應的Section,程序加載時.bss段占多大內存空間在Section Header中描述。在我們這個例子中沒有用到.bss段,以后我們會看到這樣的例子。


3.2.rel.text和.symtab

我們繼續分析readelf輸出的最后一部分,是從.rel.text.symtab這兩個Section中讀出的信息。


.rel.text告訴鏈接器指令中的哪些地方需要重定位,我們在下一節討論。

.symtab是符號表。Ndx列是每個符號所在的Section編號,例如data_items在第3個Section里(也就是.data),各Section的編號見Section Header Table。Value列是每個符號所代表的地址,在目標文件中,符號地址都是相對于該符號所在Section的相對地址,比如data_items位于.data段的開頭,所以地址是0,_start位于.text段的開頭,所以地址也是0,但是start_looploop_exit相對于.text段的地址就不是0了。從Bind這一列可以看出_start這個符號是GLOBAL的,而其它符號是LOCAL的,GLOBAL符號是在匯編程序中用.globl指示聲明過的符號。

3.3 .text節

通過使用objdump工具可以把程序中的機器指令進行反匯編(Disassemble),得到其匯編代碼




四、可執行文件

先看可執行文件header的變化




在看section header的變化



.text.data的加載地址分別改成了0x08048074和0x0804 90a0。.bss段沒有用到,所以被刪掉了。.rel.text段就是用于鏈接過程的,鏈接完了就沒用了,所以也刪掉了。

在看多出來的兩個program header




多出來的Program Header Table描述了兩個Segment的信息。.text段和前面的ELFHeader、Program Header Table一起組成一個Segment(FileSiz指出總長度是0x9e),.data段組成另一個Segment(總長度是0x38)。VirtAddr列指出第一個Segment加載到虛擬地址0x0804 8000(注意在x86平臺上后面的PhysAddr列是沒有意義的),第二個Segment加載到地址0x0804 90a0。Flg列指出第一個Segment的訪問權限是可讀可執行,第二個Segment的訪問權限是可讀可寫。最后一列Align的值0x1000(4K)是x86平臺的內存頁面大小。在加載時要求文件中的一頁對應內存中的一頁,對應關系如下圖所示。



這個可執行文件很小,總共也不超過一頁大小,但是兩個Segment必須加載到內存中兩個不同的頁面,因為MMU的權限保護機制是以頁為單位的,一個頁面只能設置一種權限。此外還規定每個Segment在文件頁面內偏移多少加載到內存頁面仍然偏移多少,比如第二個Segment在文件中的偏移是0xa0,在內存頁面0x0804 9000中的偏移仍然是0xa0,所以是從0x0804 90a0開始,這樣規定是為了簡化鏈接器和加載器的實現。從上圖也可以看出.text段的加載地址應該是0x0804 8074,也正是_start符號的地址和程序的入口地址。

原來目標文件符號表中的Value都是相對地址,現在都改成絕對地址了。此外還多了三個符號__bss_start_edata_end,這些是在鏈接過程中添進去的,加載器可以利用這些信息把.bss段初始化為0。

再看一下反匯編的結果:




到此為止ELF文件的問題已介基本介紹,關于共享目標文件的格式和加載過程將在后續補上。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/532781.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/532781.shtml
英文地址,請注明出處:http://en.pswp.cn/news/532781.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

埃及分數問題c語言,埃及分數問題(轉)

今日,小雨和小明來到網絡中心,繼續與劉老師討論“數的認識”問題。劉老師說:“還有一種‘埃及分數’需要認識。這是一類分裂分數的思維題,對思維能力的訓練很有價值。”小明說:“有意思,愿洗耳恭聽。”劉老…

linux常用命令--開發調試篇

前言 Linux常用命令中有一些命令可以在開發或調試過程中起到很好的幫助作用,有些可以幫助了解或優化我們的程序,有些可以幫我們定位疑難問題。本文將簡單介紹一下這些命令。 轉自:https://www.yanbinghu.com/2018/09/26/61877.html 示例程序…

簡單有趣的c語言小程序,一個有趣的小程序

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓源碼:#include #include #include #include #include HINSTANCE g_hInstance 0;LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR lpCmdLine,int nSh…

linux下ora 01110,ORA-01003ORA-01110

Oracle 9i數據庫登錄時,提示ORA-01003&ORA-01110,大概意思是數據文件存儲介質損壞。startup nomount,正常;alter database mount,也正常;alter database open,提示如下:alter database open*ERROR 位于第 1 行:ORA…

x11轉發:通過ssh遠程使用GUI程序

x11轉發:通過ssh遠程使用GUI程序 我們常常使用ssh服務遠程操控服務器,大多數操作我們都可以通過命令行命令來實現。 ssh遠程無法查看GUI程序 現在,筆者在x11-test目錄下放入一張圖片test.jpg,并通過opnencv-python寫一個簡單的…

操作系統引導詳細過程

操作系統引導詳細過程 轉自:https://blog.csdn.net/lijie45655/article/details/89366372 就直觀而言,我們所見到計算機啟動的過程是:按下電腦開機鍵,系統在黑色的屏幕下打印出一些英文語句、然后進入進度條狀態,最后…

android 自定義透明 等待 dialog,Android自定義Dialog內部透明、外部遮罩效果

Android自定義Dialog內部透明、外部遮罩效果發布時間:2020-09-09 03:01:41來源:腳本之家閱讀:117作者:zst1303939801本文實例為大家分享了Android自定義Dialog遮罩效果的具體代碼,供大家參考,具體內容如下圖…

對比損失的PyTorch實現詳解

對比損失的PyTorch實現詳解 本文以SiT代碼中對比損失的實現為例作介紹。 論文:https://arxiv.org/abs/2104.03602 代碼:https://github.com/Sara-Ahmed/SiT 對比損失簡介 作為一種經典的自監督損失,對比損失就是對一張原圖像做不同的圖像…

android 融云瀏覽大圖,融云 Android sdk kit 頭像昵稱更新機制

先申明筆者的實現方式不是唯一 也不一定是最優化的方案 如果您看到此篇博文 有不同看法 或者 更好的優化 更高的效率 歡迎在評論發表意見 融云官網點我融云頭像機制相關視頻詳解首先跟大家說一下 kit 跟 lib 的頭像機制 kit 是已經包含融云已經給開發者定制好的界面 諸如 會話界…

RuntimeError: Expected to have finished reduction in the prior iteration before starting a new one.

RuntimeError: Expected to have finished reduction in the prior iteration before starting a new one. 報錯信息 報錯信息: RuntimeError: Expected to have finished reduction in the prior iteration before starting a new one. This error indicates tha…

android訪問重定向地址,如何從android中重定向url加載圖像(示例代碼)

嗨,我正面臨這個問題我從RESTCall獲取了一個URL網址是http://hck.re/kWWxUI但是當我在瀏覽器中檢查時,它會重定向到https://s3-ap-southeast-1.amazonaws.com/he-public-data/afreen2ac5a33.jpg如何將此圖像加載到我的imageView中我已經知道如何將畢加索…

Linux中的awk、sed、grep及正則表達式詳解

Linux中的awk、sed、grep及正則表達式詳解 簡介 awk、sed和grep是Linux中文本操作的三大利器。 其中awk適用于取列,sed適用于取行,grep適用于過濾。 正則表達式 首先我們來介紹一下正則表達式,正則表達式(regular expression)描述了一種…

android聚焦時如何給控件加邊框,edittext設置獲得焦點時的邊框顏色

第一步:為了更好的比較,準備兩個一模一樣的EditText(當Activity啟動時,焦點會在第一個EditText上,如果你不希望這樣只需要寫一個高度和寬帶為0的EditText即可避免,這里就不這么做了),代碼如下:a…

gcc參數 -i, -L, -l, -include

gcc參數 -i, -L, -l, -include -i,-L,-l,-include -l和-L -l參數就是用來指定程序要鏈接的庫,-l參數緊接著就是庫名,那么庫名跟真正的庫文件名有什么關系呢?就拿數學庫來說,他的庫名是m&…

xargs 命令教程

xargs 命令教程 轉自:http://www.ruanyifeng.com/blog/2019/08/xargs-tutorial.html 作者: 阮一峰 日期: 2019年8月 8日 xargs是 Unix 系統的一個很有用的命令,但是常常被忽視,很多人不了解它的用法。 本文介紹如…

android strictmode有什么作用,Android 性能優化 之 StrictMode

8種機械鍵盤軸體對比本人程序員,要買一個寫代碼的鍵盤,請問紅軸和茶軸怎么選?StrictMode概述StrictMode 是用來檢測程序中違例情況的開發者工具。使用StrictMode,系統檢測出主線程違例的情況會做出相應的反應,如日志打…

curl 的用法指南

curl 的用法指南 轉自:http://www.ruanyifeng.com/blog/2019/09/curl-reference.html 作者: 阮一峰 日期: 2019年9月 5日 簡介 curl 是常用的命令行工具,用來請求 Web 服務器。它的名字就是客戶端(client&#xf…

怎么在html顯示已登錄狀態,jQuery Ajax 實現在html頁面實時顯示用戶登錄狀態

當網站是全靜態的html頁面時,而又希望網站會員在登錄之后并在所有頁面頭部顯示登錄狀態,如用戶名等,如果未登錄就是未登錄狀態,下面給大家來分享實現的方法。一、在html靜態頁面中加入div,并指定ID如:二、新…

互斥鎖、條件變量、信號量淺析

互斥鎖、條件變量、信號量淺析 互斥鎖與條件變量 條件變量是為了保證同步 條件變量用在多線程多任務同步的,一個線程完成了某一個動作就通過條件變量告訴別的線程,別的線程再進行某些動作(大家都在semtake的時候,就阻塞在哪里&a…

xpwifi熱點設置android,教你在XP電腦中開啟設置WiFi熱點使用的步驟

對于系統中網絡的連接問題是最重要的,那在處理不同的錯誤的情況中,對于無線網絡的設置也就是我們說的WiFi的使用也是會遇到問題的,那在操作的時候對于電腦中是怎么實現設置WiFi熱點的的,對于這個問題今天小編就來跟大家分享一下教…