說說編譯連結系統中的符號(symbol)、重定位(relocation)、字串表(string-table)和節(section)

來源:互聯網
上載者:User
作者:liigo日期:2009/11連結:http://blog.csdn.net/liigo/archive/2009/11/23/4858535.aspx轉載請註明出處:http://blog.csdn.net/liigo  編譯(compile)和連結(link),是電腦程式設計語言的通用處理系統。編譯,是把程式原始碼轉換為目標檔案;連結,是把目標檔案轉換為可執行檔。把編譯和連結分成兩個相對獨立的子系統,是為了簡化,是為了分而治之,也有基於通用性的考慮。編譯器(compiler)的任務是把程式原始碼編譯為目標檔案。通常每一種程式設計語言,都會有它自己的編譯器,各種編譯器的輸出都是目標檔案(.obj, .lib, .o, .a, ...)。連結器(linker)的任務是把編譯器產生的目標檔案經過一系列處理,最終產生可執行檔(.exe, .dll, .so, ...)。

  如果目標檔案的規範是唯一的,所有編譯器都輸出特定格式的目標檔案,連結器也都接收這種格式的目標檔案,大概是一種比較理想的狀態,編譯器和連結器實現起來也會相對容易,而且通用性好,基至連結器只需有一個就行了,這也許是當初設計編譯連結系統的初衷之一吧。但現實是很殘酷的,目前來說,目標檔案的格式不僅沒有統一,反而分化的很厲害,粗略統計竟有逾十種之多,且互不相容。編譯器和連結器的開發人員,往往只能選擇支援一種或少數幾種目標檔案格式。通用連結器成為一種奢求,從來不曾出現過,這幾乎完全背離了其設計初衷。  好在小範圍的應用通用連結器,還是可行的。例如,把D語言原始碼編譯為.obj目標檔案,就能和C語言編譯產生的其它.obj目標檔案一起,用C語言的連結器連結產生EXE。又如一款新誕生的程式設計語言(就說易語言吧),不想重複開發專用連結器,可以考慮編譯產生C語言格式的目標檔案,進而得以使用現有的C語言連結器連結產生可執行檔,如此一來可大幅減少開發工作量,降低研發成本的同時還提高了系統的開放性。  目標檔案是溝通編譯器和連結器的橋樑,它既是編譯器的輸出,又是連結器的輸入;而“符號(symbol)”是目標檔案中的核心元素,是編譯系統和連結系統中最重要的操作對象。通俗的說,編譯器的任務是“建立符號”(以及與符號相關向輔助設施),連結器的任務是“使用符號”(以及與符號相關向輔助設施)。

    下面結合我(liigo)的個人理解,說說目標檔案的基本元素,及其內部結構。就以我目前接觸最多的COFF格式的目標檔案為例。  目標檔案中的基本元素有,符號(symbol)、重定位(relocation)、字串表(string-table)和節(section)。這裡說的是邏輯概念。  符號(symbol)是很什嗎?說不清楚,因為不好理解(對讀者而言),也不好表達(對作者而言)。舉例吧,假設程式原始碼中有變數有常量有函數,那麼編譯之後那些變數常量函數都會各自成為一個符號,供它處引用。是不是可以把符號理解為“比變數常量函數更高層次上的抽象”呢?大概可以吧。正是因為符號是更高層次上的抽象,脫離了程式設計語言概念上的變數常量和函數,因而連結器才有可以做到與具體的程式設計語言無關。符號的主要屬性有:名稱(符號匹配完全基於名稱文本),所屬節(section)的序號,(符號實體)在節中的位移,範圍(OBJ內部私人,或全域公開)。符號主要有兩大類:一類是定義性質的(如變數定義、函數定義),其內容(如變數的值、函數體等)儲存於指定的節中某個位移處;另一類是聲明性質的(如變數聲明、函式宣告),沒有內容(因而不需要所屬節、位移等屬性),連結器會根據名稱在其它obj檔案或其它lib檔案中找到這個符號的定義。這裡體現了連結器中“連結”二字的含義:一方聲明(依賴、使用)一個符號,另一方定義這個符號,雙方通過符號名稱連結到一起。聲明符號可以在定義符號之前,甚至在符號還沒有定義的情況下。聲明一個符號是編譯器的行為,只是表示對該符號的依賴,相應的符號定義可以由他人(或編譯器)在其他時間完成,只要連結器工作時能夠(在其他目標檔案中)找到定義就OK。從邏輯上說,符號通常指的是變數(變數的地址)和函數(函數可執行體首地址)。在OBJ中儲存時,符號對應某個節(section)中的某處位移;而在連結時(或連結的後期),符號則對應某個確定的記憶體位址(此地址由連結器指派,有了地址後才能執行後續的重定位操作)。符號在OBJ檔案中是順序儲存的,所有符號的結構體組成一個數組,稱為符號表。在OBJ檔案內部,通常通過符號表中的索引(>=0)指代某個符號。如果指代其它OBJ中的符號呢?先在本OBJ內定義一個相同名稱的“聲明性質”的符號,然後通過符號索引指代本OBJ內的這個同名符號,將來連結器工作時,所有同名稱的符號都被視為同一個實體並指派唯一的地址。  節(section)是資料的容器,是儲存資料的地方。節記憶體儲的資料通常有:變數的值,常量的值,函數體,等。節的基本屬性有:資料長度,資料在檔案中的位移,是否可讀可寫可執行,重定位表。在連結時,節總是作為一個整體參予連結的,它是不可分的。編譯時間節劃分的比較小比較多,有利於連結時按需提取,有利於最佳化編譯後的EXE或DLL的尺寸。分析VC6編譯器產生的OBJ檔案可知,一般一個函數會單獨使用一個節(section)儲存。如果看看C語言標準庫的原始碼,會發現它往往把一個函數寫到一個單獨的源檔案中,這樣編譯時間一個函數就會產生一個OBJ檔案,盡量做到了細化。在OBJ中,所有節的節頭(section-header)順序儲存形成一個數組,稱為節頭表或節表。通常通過OBJ檔案內節表中的序號(>=1)指代某個節。  重定位表(relocation)是從屬於節(section的重要元素,用於修正節資料中的地址部分。分析編譯器編譯產生的函數代碼的話,會發現它產生的不是完整的真正可執行檔代碼,而只是代碼模板,其中涉及地址之處,往往簡單的使用0x00000000佔位,同時在此處綁定一個符號(symbol)用於修正此地址。為什麼會這樣呢?因為在編譯器工作時,它並不知道符號(變數、函數等)地址,可能該符號來自另一個OBJ(或另一個LIB),甚至連它有沒有定義都無法知曉。編譯器只能先留下空白給連結器。通俗的說,編譯器出了一個完形填空的題目,要連結器解答。重定位表可以理解為編譯器給連結器提供的資訊,它是由多個重定位項組成的數組,其中每一個重定位的基本屬性有:被修正地址在節資料中的位移,用於提供地址的符號索引,重定位類型(絕對位置、相對定位等)。連結器工作時,根據重定位項中的符號索引得到符號名稱,進而查詢得到符號地址(連結器負責指派符號地址),根據被修正地址在節中的位移以及節的地址(連結器負責指派節的地址)得到被修正地址的地址,再根據重定位類型,將符號的地址填過去。舉個例子,C語言代碼 int a = 1;,對變數賦值,編譯結果(不考慮編譯最佳化)可能是 mov dword ptr [0x00000000], 0x12345678,相應的X86指令序列為 C7 05 00 00 00 00 78 56 34 12,中間的四位元組的0就是預留位置,將來需要連結器把變數a的地址覆蓋上去,這是絕對位置;再如C代碼 f();,編譯結果(不考慮編譯最佳化)可能是 call dword ptr [0x00000000],相應的X86指令序列為 FF 15 00 00 00 00,中間的四位元組的0就是預留位置,將來需要連結器把“函數f的地址與下一指令地址的差值”覆蓋上去,這是相對定位的例子。具體是採用絕對位置還是相對定位還是其它定位方式,是由編譯器產生的重定位表指定的,取決於編譯器選擇產生的指令代碼。地址預留位置也不見得一定是零,可以是任意數值(可正可負),表示相對目標地址的前後位移量,連結器重定位時填寫的地址其實是在此數值基礎上與目標地址相加而得到的。以上說的是連結產生EXE或DLL時由連結器執行的重定位,將來DLL或EXE被載入時PE載入器還會執行一次重定位(重定位表由連結器產生,EXE中通常可省略),這兩個階段的重定位雖然細節上不同,但原理是一致的。  字串表(string-table)是OBJ檔案或LIB檔案中的輔助設施,用於集中儲存一些名稱文本,如長度大於8位元組的符號名稱、段名稱,以及長度大於15位元組的連結成員(link member, 見於LIB中)的名稱。字串表存在的目的主要是用於最佳化OBJ或LIB檔案的尺寸。以符號名稱為例,在OBJ中,一個符號所對應的結構體大小是固定的,共18位元組,其中留出8個位元組用於儲存符號名稱。如果符號名稱比較短,小於等於8個位元組,則直接存到這個結構體中(不儲存C文本結尾字元'/0');如果符號名稱長度大於8位元組,則把名稱存到字串表(string-table)中,然後把這個名稱在字串表中的位移記錄到前面提到的8個位元組地區處(在第一個字元前加'/'作為區分名稱和位移的標記)。

    至於LIB檔案,相比OBJ就簡單多了,它僅是OBJ檔案的打包整理和索引,完整地包含了庫中所有OBJ檔案的內容,並提供了庫中公開符號的名稱索引表(根據一個符號名稱可以快速查詢到它是否在本庫中定義,以及在哪個OBJ中定義)。在物理上,LIB檔案的前面部分由三個固定的連結成員(linker member)組成,後面是順序儲存各OBJ檔案內容(也稱為linker member),每個連結成員均有一個資料頭(header)。第一個固定連結成員(1st linker member),僅因相容原因而保留,已被第二個固定連結成員(2nd linker member)取代,後者記錄了符號名稱索引資訊和後面各OBJ成員的基本資料,第三個固定成員(3rd linker member)記錄長文本(可能被省略)。寫的不是很條理,有點亂,請多包涵。liigo, 2009。

參考資料:<<Microsoft Portable Executable and Common Object File Format Specification>>http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspxhttp://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/pecoff_v8.docx

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.