在前面我們已經大概看到過elf檔案的整體布局情況,本文將更詳細地對elf的檔案結構進行闡述。乙個典型的elf檔案結構如下圖所示:
elf檔案格式標準中提供了兩種檢視,分別是鏈結檢視和執行檢視。鏈結檢視是以節(section)為單位,執行檢視是以段(segment)為單位。顧名思義,鏈結檢視就是在程式鏈結時用到的檢視,而執行檢視則是在程式被裝載執行時用到的檢視,二者分別對應於可重定位檔案和可載入檔案。
elf檔案頭(elf header)主要標記了elf檔案型別、結構和程式開始執行的入口位址,並提供了其他elf頭(如節頭表和程式頭表)在檔案中的偏移位置。使用readelf工具來檢視乙個elf檔案的檔案頭:
elf檔案頭的資料結構定義如下:
typedef struct elfn_ehdr;
程式頭部表(program header table)是對二進位制檔案中段的描述,本質上是記錄段表項的陣列,是程式裝載必需的一部分。段在程式被系統載入執行時被解析,其描述了磁碟上可執行檔案的記憶體布局以及如何對映到記憶體中。elf檔案頭中名為e_phoff
(程式頭部表偏移量)的偏移量記錄了程式頭部表的位置。使用readelf工具檢視elf檔案中的程式頭部表資訊:
由上面的輸出資訊可以看出,程式頭部表記錄了elf檔案中所有段的屬性資訊,包括段的型別、檔案偏移以及對映到記憶體時的位址資訊。程式頭部表本質上是段資訊的陣列,對於每個段(陣列項),elf使用如下資料結構進行描述:
typedef struct elf64_phdr;
elf檔案格式定義了5中不同型別的段,其描述如下:
節頭表(section header table)用於描述elf檔案的各個節區的位置和大小,主要用於鏈結和除錯。須注意的是,節頭對於程式的執行不是必需的,沒有節頭表,程式仍可以正常執行。使用readelf工具檢視elf檔案的所有節:
與程式頭部表類似,節區頭部表是由節構成的陣列。對於節的資料結構定義如下:
typedef struct elf64_shdr;
elf檔案中定義的一些比較重要的節和節型別羅列如下:
節(section)型別
描述.text
已編譯程式的機器**,即**段
.rodata
儲存了唯讀的資料
.data
已初始化的全域性和靜態c變數,即資料段
.bss
未初始化的全域性和靜態c變數,以及所有被初始化為0的全域性或靜態變數
.rel.text
被模組引用或定義的外部函式的重定位資訊,對應於.text
.rel.data
被模組引用或定義的所有全域性變數的重定位資訊,對應於.data
.debug
除錯符號表,其條目是程式中定義的區域性變數和型別定義,程式中定義和引用的全域性變數,以及原始的c原始檔。只有以-g選項呼叫編譯器驅動程式時,才會得到這張表
.line
原始c源程式中行號和.text節中機器指令之間的對映,用於除錯
.symtab
符號表,存放在程式中定義和引用的函式和全域性變數的資訊
.strtab
字串表,包含.symtab和.debug節中的符號表,以及節頭部的節名字。字串表是以null結尾的字串的序列
.shstrtab
節名字串表,在可執行檔案是必須存在的
.ctors .dtors
c++建構函式和析構函式存放的段
.dynsym
用於儲存與動態鏈結相關的符號
.dynstr
動態符號字串表,與靜態鏈結使用.strtab對應
.hash
在動態鏈結的情況下,用於加快符號查詢過程
.interp
儲存可執行檔案使用的動態鏈結器的路徑
.plt
程式鏈結表,和.got.plt配合實現函式呼叫的延遲繫結
.got
.got.plt
ELF檔案格式
在介紹elf格式之前,先簡單說明一下可執行檔案的生成流程 1 編寫c原始檔,或彙編原始檔 2 準備共享庫格式的目標檔案 shared object file 如數學庫 標準庫 2 用編譯器 compiler 將c編譯成可重定位格式的目標檔案 relocatable object file 用彙編器 ...
ELF檔案格式
1.目標檔案 編譯器和彙編器生成可重定位目標檔案 包括共享目標檔案 鏈結器生成可執行目標檔案。2.可重定位目標檔案和可執行目標檔案的格式 可重定位目標檔案格式 可執行目標檔案格式 3.下面我們開始分析上面 而對於未被初始化的全域性變數和靜態區域性變數,編譯的時候並未被分配空間,而是僅僅在.bss段中...
ELF檔案格式
elf指executable and linking format,不僅包含可執行檔案,也包含庫檔案,包括靜態庫和動態庫。準備的說,也就是三種 這不廢話嗎 可執行檔案 靜態鏈結庫 動態鏈結庫 要觀察elf的具體資訊,可以用以下幾個工具 nm lists symbols from object fil...