UNIX LINUX 平台可執行檔案載入過程

2021-06-28 09:23:09 字數 2185 閱讀 7879

本文討論了 unix/linux 平台下三種主要的可執行檔案格式:a.out(assembler and link editor output 彙編器和鏈結編輯器的輸出)、coff(common object file format 通用物件檔案格式)、elf(executable and linking format 可執行和鏈結格式)。首先是對可執行檔案格式的乙個綜述,並通過描述 elf 檔案載入過程以揭示可執行檔案內容與載入執行操作之間的關係。隨後依此討論了此三種檔案格式,並著重討論 elf 檔案的動態連線機制,其間也穿插了對各種檔案格式優缺點的評價。最後對三種可執行檔案格式有乙個簡單總結,並提出作者對可檔案格式評價的一些感想。

可執行檔案格式綜述

相對於其它檔案型別,可執行檔案可能是乙個作業系統中最重要的檔案型別,因為它們是完成操作的真正執行者。可執行檔案的大小、執行速度、資源占用情況以及可擴充套件性、可移植性等與檔案格式的定義和檔案載入過程緊密相關。研究可執行檔案的格式對編寫高效能程式和一些黑客技術的運用都是非常有意義的。

不管何種可執行檔案格式,一些基本的要素是必須的,顯而易見的,檔案中應包含**和資料。因為檔案可能引用外部檔案定義的符號(變數和函式),因此重定位資訊和符號資訊也是需要的。一些輔助資訊是可選的,如除錯資訊、硬體資訊等。

基本上任意一種可執行檔案格式都是按區間儲存上述資訊,稱為段(segment)或節(section)。不同的檔案格式中段和節的含義可能有細微區別,但根據上下文關係可以很清楚的理解,這不是關鍵問題。最後,可執行檔案通常都有乙個檔案頭部以描述本檔案的總體結構。

相對可執行檔案有三個重要的概念:編譯(compile)、連線(link,也可稱為鏈結、聯接)、載入(load)。源程式檔案被編譯成目標檔案,多個目標檔案被連線成乙個最終的可執行檔案,可執行檔案被載入到記憶體中執行。因為本文重點是討論可執行檔案格式,因此載入過程也相對重點討論。下面是linux平台下elf檔案載入過程的乙個簡單描述。

1:核心首先讀elf檔案的頭部,然後根據頭部的資料指示分別讀入各種資料結構,找到標記為可載入(loadable)的段,並呼叫函式 mmap()把段內容載入到記憶體中。在載入之前,核心把段的標記直接傳遞給 mmap(),段的標記指示該段在記憶體中是否可讀、可寫,可執行。顯然,文字段是唯讀可執行,而資料段是可讀可寫。這種方式是利用了現代作業系統和處理器對記憶體的保護功能。

2:核心分析出elf檔案標記為 pt_interp 的段中所對應的動態聯結器名稱,並載入動態聯結器。現代 linux 系統的動態聯結器通常是 /lib/ld-linux.so.2,相關細節在後面有詳細描述。

3:核心在新程序的堆疊中設定一些標記-值對,以指示動態聯結器的相關操作。

4:核心把控制傳遞給動態聯結器。

5:動態聯結器檢查程式對外部檔案(共享庫)的依賴性,並在需要時對其進行載入。

6:動態聯結器對程式的外部引用進行重定位,通俗的講,就是告訴程式其引用的外部變數/函式的位址,此位址位於共享庫被載入在記憶體的區間內。動態連線還有乙個延遲(lazy)定位的特性,即只在"真正"需要引用符號時才重定位,這對提高程式執行效率有極大幫助。

7:動態聯結器執行在elf檔案中標記為 .init 的節的**,進行程式執行的初始化。在早期系統中,初始化**對應函式 _init(void)(函式名強制固定),在現代系統中,則對應形式為

void

__attribute((constructor))

init_function(void)

其中函式名為任意。

8:動態聯結器把控制傳遞給程式,從 elf 檔案頭部中定義的程式進入點開始執行。在 a.out 格式和elf格式中,程式進入點的值是顯式存在的,在 coff 格式中則是由規範隱含定義。

從上面的描述可以看出,載入檔案最重要的是完成兩件事情:引導程式段和資料段到記憶體;進行外部定義符號的重定位。重定位是程式連線中乙個重要概念。我們知道,乙個可執行程式通常是由乙個含有 main() 的主程式檔案、若干目標檔案、若干共享庫(shared libraries)組成。(注:採用一些特別的技巧,也可編寫沒有 main 函式的程式,請參閱 參考資料 2)乙個 c 程式可能引用共享庫定義的變數或函式,換句話說就是程式執行時必須知道這些變數/函式的位址。在靜態連線中,程式所有需要使用的外部定義都完全包含在可執行程式中,而動態連線則只在可執行檔案中設定相關外部定義的一些引用資訊,真正的重定位是在程式執行之時。靜態連線方式有兩個大問題:如果庫中變數或函式有任何變化都必須重新編譯連線程式;如果多個程式引用同樣的變數/函式,則此變數/函式會在檔案/記憶體中出現多次,浪費硬碟/記憶體空間。比較兩種連線方式生成的可執行檔案的大小,可以看出有明顯的區別。

golang 編譯不同平台可執行檔案

1 mac下編譯linux,windows平台的64位可執行程式 cgo enabled 0 goos linux goarch amd64 go build test.go cgo enabled 0 goos windows goarch amd64 go build test.go 2 lin...

linux 執行可執行檔案

1 首先,需要了解一下a.out這個目標檔案。a.out在linux下是elf executable linkable format 檔案格式,該目標檔案由乙個檔案頭 段 資料段 已初始化 從定位資訊區 符號表及符號名字字串構成,如下左圖所示,經過鏈結後生成執行檔案如下右圖所示,需要說明的是1 bs...

API函式執行可執行檔案

shellexecute hwnd hwnd,父視窗控制代碼 lpcstr lpoperation,操作型別 lpcstr lpfile,要進行操作的檔案或路徑 lpcstr lpparameters,當lpoperation為 explore 時指定要傳遞的引數,通常設為null lpcstr l...