ELF文件格式解析

 http://www.chinaunix.net 作者:dreamice  發表於:2008-07-30 21:12:49

 

1 Executable and Linkable Format(ELF)初稿,圖請參考ELF_Format手冊 

1.1 Preface 
ELF-可執行鏈接格式最初是由UNIX系統實驗室(USL)作爲應用程序二進制接口(ABI)開發和發行。工具接口標準委員會TIS已經將ELF作爲運行在Intel32位架構之上的各類型操作系統的可導出對象文件格式標準。ELF標準爲開發者提供了一組橫跨多運行環境的二進制接口定義來組織軟件開發。 
1.2 對象文件 $leads................ 
1.2.1 介紹 
本部分描述了iABI對象文件格式,也稱之爲ELF。有三種主要類型的對象文件: 
1. 可重組(relocatable)文件包含了適合用來鏈接其他對象文件的代碼和數據,從而創建出可執行或可共享的對象文件; 
2. 可執行(executable)文件包含了用於執行的程序,該文件規定了exec如何創建一個程序的進程映像; 
3. 可共享對象(shared object)文件包含了用來在兩個上下文之間鏈接的代碼和數據。首先,鏈接器ld將該文件和其他的可重組文件或可共享對象文件進行處理後,創建出新對象文件,其次,動態鏈接器將該新對象文件與可執行文件或共享對象組合,來共同創建一個進程映像; 
經過彙編器以及鏈接器創建成的對象文件,其是在處理器上可直接執行的程序的二進制代表。本部分主要描述文件格式以及其如何用來構建程序。後一部分也描述了對象文件,集中在程序執行所必須的信息上。 
1.2.1.1 文件格式 
在程序鏈接和程序執行過程都涉及到對象文件。出於方便和效率,對象文件格式圖從鏈接和運行兩個視角來展示文件的內容。 

ELF header位於文件的開始處,其用來描述文件的組織結構。Section包含了大量的對象文件信息,從鏈接的視角來看就是指令、數據、符號表、重組信息等等。Segment和Program是從程序執行視角來觀看的,這將在下部分講解。 
如果存在Program Header table的話,其將告訴操作系統如何創建進程映像。用來創建進程映像(執行程序)的文件必須包含program header table。可重組(relocatable)文件可以沒有該信息。Section header table包含了用來描述文件section的信息。每個section在該表中都有一個對應的表項,每個表項給出了諸如section名稱、尺寸等等信息。用於鏈接的文件必須有section header table,其他的對象文件可有可無。 
這裏需要注意的是,雖然圖中Program header table緊接着ELF header,section header table緊接着sections,實際的文件中並不一定是這樣。而且,sections和segments也可以不按次序排放,只有ELF header是固定在文件的首部。 
1.2.1.2 數據的表示 
對象文件格式支持8位、32位等架構的大量處理器。然而,爲了保證其容易擴展到更多的體系架構,因此對象文件提供了一些機器獨立的控制數據,用來按照統一的方式標明和解釋對象文件的內容。對象文件中其餘的數據都是按照目標處理器硬編碼的,當然不用考慮該文件是在哪個文件上創建的。 

對象文件格式中定義的所有數據結構定義都沿守自然尺寸以及對齊原則。必要時,數據結構可以包含填補內容來保證4字節對象的4字節對齊。數據也可以相對於文件起始位置對齊,例如,包含Elf32_Addr成員的數據結構在文件中將會按照4字節對齊。爲了保證可移植性,EFL中不使用bit域。 
1.2.2 ELF Header 
1.2.2.1 ELF Header結構 
一些對象文件控制結構可能會增長,因爲ELF header包含了這些結構的實際尺寸。如果對象文件格式發生改變,那麼程序有可能會碰到控制結構比原來大或者比原來小的情況,這樣,程序有可能就會忽略一些額外的信息。至於這些丟失的信息如何處理,則依賴於上下文環境。 

e_ident 這段最先開始的字節標識該文件爲對象文件,並且提供了機器獨立的數據,其用來解釋文件內容。完整的描述見下面的ELF Identification。 
e_type 該成員指定了對象文件的類型。雖然核心文件內容沒有明確的規定,但是ET_CORE類型則專門爲該文件保留。 
從0xFF00到0xFFFF,爲特定處理器保留。剩餘所有其他的值也是保留的,用於新類型的對象文件。 ET_NONE 0 No 
ET_REL 1 Relocatable 
ET_EXEC 2 Executable 
ET_DYN 3 Shared 
ET_CORE 4 Core 
ET_LOPROC 0xff00 Processor-specific 
ET_HIPROC 0xffff Processor-specific 
e_machine 該成員指定了文件適用的計算機架構。除了定義的值之外,其他的值保留,在必要時用於新的計算機架構。 
EM_NONE 0 No 
EM_M32 1 AT&T 
EM_SPARC 2 SPARC 
EM_386 3 Intel 
EM_68K 4 Motorola 
EM_88K 5 Motorola 
EM_860 7 Intel 
EM_MIPS 8 MIPS 
e_version 該成員指定了對象文件版本,1表示該文件爲最初文件格式。如果擴展了,則使用更高的編號。 EV_NONE 0 Invalid 
EV_CURRENT 1 Current 
e_entry 
該成員標明瞭系統接管該進程控制的第一條指令的邏輯地址,從而開始了該進程。如果沒有關聯切入點,則默認爲0。 
e_phoff 該成員包含了program header table的文件偏移地址(按字節),如果該文件沒有program header table,則該成員爲0。 
e_shoff 該成員包含了section header table的文件偏移地址(按字節),如果該文件沒有section header tables,則該成員爲0。 
e_flags 該成員包含了文件與處理器相關的標識。 
e_ehsize 該成員包含了ELF header的尺寸(按字節)。 
e_phentsize 包含了program header table中一條表項的字節數,所有表項大小相等。 
e_phnum 包含了program header table中表項的數目。如果該文件沒有program header table,則該值爲0。 
e_shentsize 包含了section header table中一條表項的字節數,所有表項大小相等。 
e_shnum 包含了section header table中表項的數目。如果該文件不含section header table,則該值爲0。 
e_shstrndx section name string table(段名稱字符串表)獨立構成一個section。 
e_shstrndx成員包含了該section在section header table中表項的索引號。如果該文件不含section name string table,該成員中填充的值爲SHN_UNDEF,見‘‘Sections’’和 ‘‘String Table’’。 
1.2.2.2 ELF HeaderELF Identification 
像上面提及到的一樣,ELF提供了對象文件框架來支持多處理器、多數據編碼、多類型的機器。爲了支持這些對象文件家族,文件的初始字節必須指明如何解析本文件,而且該指明過程必須和處理器(查詢解析信息就是通過該處理器進行的)無關以及和該文件剩餘的內容無關。 
EFL Header的初始字節也就是e_ident成員。 

每個字節的具體值及其含義如下: 

EI_MAG0 到EI_MAG3 
文件的頭4個字節包含了特徵碼,其用來表明該文件是一個ELF對象文件 
ELFMAG0 0x7f e_ident[EI_MAG0] 
ELFMAG1 ’E’ e_ident[EI_MAG1] 
ELFMAG2 ’L’ e_ident[EI_MAG2] 
ELFMAG3 ’F’ e_ident[EI_MAG3] 
EI_CLASS 
e_ident[EI_CLASS]指明瞭文件的種類或容量。 

ELF文件格式在設計時就考慮到了在不同總線寬度的機器中通用,而不用考慮到諸如把最大總線寬度的機器碼強加到最小機器上去。 
類別ELFCLASS 32支持高達4GB的虛擬地址空間和文件尺寸,其使用上面定義的基本類型。 
類別ELFCLASS64爲64位架構保留,其也表明了對象文件可能的改變,不過64位格式還沒有定義。 
其他的類別也會在需要時定義,相應的基本類型和尺寸也會發生變化。 ELFCLASSNONE 0 Invalid 
ELFCLASS32 1 32-bit 
ELFCLASS64 2 64-bit 
EI_DATA 
e_ident[EI_DATA]規定了對象文件中和處理器相關的數據的如何編碼。更多的下面會詳解。 
ELFDATANONE 0 Invalid 
ELFDATA2LSB 1 See below 
ELFDATA2MSB 2 See below 
EI_VERSION 
e_ident[EI_DATA]指定了EFL header版本號。當前情況下,必須是EV_CURRENT,後面會詳細解釋。 
EI_PAD 
該值標明瞭在e_ident中無用字節的開始,這些字節被設置爲0,爲保留;讀取對象文件的進程應該忽略這些字節。如果未來某些未用字節派上用場了,該值在也許會改變。 

文件的數據解碼規定了如何翻譯文件中的基本對象。如上所述,類別ELFCLASS32文件使用佔用1、2、4字節的對象。在編碼的前提下,對象的排布如下圖所示,序號在每個單元的左上角。 
ELFDATA2LSB編碼規範規定了最末端字節佔用最低地址: 


ELFDATA2MSB編碼規範規定了最末端字節佔用最高地址: 

1.2.2.3 ELF Header機器信息 
對於32位Intel體系架構而言,文件標識e_ident中相關成員的值應該如下: 
e_ident[EI_CLASS]= ELFCLASS32 
e_ident[EI_DATA]= ELFDATA2LSB 
同時在ELF header中的e_machine成員必須爲EM_286。ELF header中的e_flags成員包含了和文件相關的比特標誌位。32位Intel體系架構不需要任何標誌位,因此該值爲0。 
1.2.3 Section header table及section特徵 
1.2.3.1 section header table概述 
對象文件的section header tables使得可以輕而易舉的定位所有的section,section header table 是一個Elf32_Shdr結構的數組。section header table index是數組中某個元素的下標。ELF header中的e_shoff成員給出了section header table到文件頭的偏移(按字節),e_shnum給出了section header table中包含多少個表項,e_shentsize給出了每個表項的字節數。 
1.2.3.2 section header table特殊的section index 
某些section header table index是保留的,對象文件中的section不應該佔用這些特殊index。 


SHN_UNDEF 該值標明瞭一個沒有定義的、丟失的、或者沒有意義的section引用,例如,一個“已經定義了”的符號引用了SHN_UNDEF section序號的,則該符號實際被當作爲未定義符號。 
注意:雖然索引0被保留爲一個未定義的值,但是section header table仍然包含了用於0索引的表項,也就是說,如果e_shnum=6,則索引爲0-5 。 
SHN_LORESERVE 該值指定了保留索引的低端。 
SHN_LOPROC 至SHN_HIPROC 此範圍內的索引用於特定的處理器,也是保留的。 
SHN_ABS 該值指定了相應引用的絕對值。例如,引用了SHN_ABS section序號的已定義符號,其地址將是絕對的,不會被重組所影響。 
SHN_COMMON 引用該section的符號都是常用的符號,例如FORTRAN COMMON或沒有分配的C外部變量。 
SHN_HIRESERVE 該值指定了保留索引高端,系統保留索引在SHN_LORESERVE和SHN_HIRESERVE範圍之間(包含SHN_LORESERVE和SHN_HIRESERVE)。Section head table中不會包含這些保留索引的表項。 

1.2.3.3 section的特徵 
除了ELF header、program header table、section header table之外,Section包含了對象文件中的所有信息。另外,對象文件的section還必須滿足以下幾個要求: 
1. 對象文件中的每個section在section header table中都必須有一個表項來描述它。即使一個section都沒有,section header table也可以存在; 
2. 每個section在文件中都佔用一段連續字節的空間(當然也可以爲空); 
3. 一個文件中的sections不能重疊,文件中的一個字節不能同時位於一個以上的section中; 
4. 對象文件中可以有不活動的空間,大量的Headers以及sections不一定會覆蓋對象文件的每個字節,不活動數據的內容是不確定的。 
1.2.4 section header tablesection header 
1.2.4.1 section header結構 
section header的結構如下: 

1. sh_name: 該成員指定了section的名稱,其值爲section “section header string table”(注意section header string table本身構成了一個Section)中某一表項的索引,該索引中爲一個以nul終止的字符串; 
2. sh_type: 該成員一句section的內容和語義將section分類。section類型及其描述下面將會有詳細描述; 
3. sh_flags: Section支持使用1bit標誌來描述大量的屬性,下面將會詳細描述; 
4. sh_addr: 如果某個section將會出現在進程的內存映射中,該成員給出的就是該section第一個字節駐留在內存中的地址。否則,該成員將會爲0; 
5. sh_offsect: 該成員給出了從文件開始處到section中第一個字節的偏移(按字節)。這裏需要提到的SHT_NOBITS類型的section,其在文件中不佔用任何空間,其sh_offsect則爲其在文件中的虛位置; 
6. sh_size: 該成員給出了section的大小尺寸(按字節),如果該section類型不是SHT_NOBITS,則該size即爲該section在文件中所佔的字節數。如果是,則即使該值不爲0,但是其也不會在文件中佔用任何空間; 
7. sh_link: 該成員包含了一個section header table的索引鏈接,其如何解釋依賴於該section的類型,下面將會詳細描述; 
8. sh_info: 該成員包含了些額外的信息,其如何解釋也依賴於section的類型; 
9. sh_addralign: 一些section可能會有地址對齊約束,例如我們要求,如果section包含了一個雙字,則系統必須確保整個section按照雙字對齊。也就是說,sh_addr取模sh_addralign的值必須爲0。目前只有0和2的整數次冪值可以在此處使用,0和1意味着無需對齊; 
10. sh_entsize: 有些section包含的是一組固定大小表項的表,例如符號表,對於這樣的section,該成員給出了每個表項的尺寸大小。如果section不包括這樣的內容,則該值爲0 。 
1.2.4.2 section headersection type 
section header的sh_type成員包含了如下的語義: 

1. SHT_NULL: 該值表示該section header是不活動的,其沒有對應的section,此時section header中其他的成員都沒有意義; 
2. SHT_PROGBITS: 該section包含了程序自定義信息,其格式和含義由程序自行決定; 
3. SHT_SYMTAB和SHT_DYNSYM: 該section包含了符號表。當前每種類型的section在對象文件中只能有一個,該限制在未來有望被放開。SHT_SYMTAB爲連接器提供了符號,當然其也包含了用於動態鏈接的符號。但是由於一個完整的符號表可能會包含了大量的對於動態鏈接沒有用的符號,所以,對象文件或許也應該包含一個SHT_DYNSYM section,其中包含了一組最小化的動態鏈接符號,用來節省空間; 
4. SHT_STRTAB: 該section包含了一個string table。一個對象文件可以有多個string table sections; 
5. SHT_RELA: 該section包含了重組表項(with explicit addends); 
6. SHT_HASH: 該section包含了符號hash表,所有參與動態鏈接的對象都必須包含一個符號hash表; 
7. SHT_DYNAMIC: 該section包含了動態鏈接信息; 
8. SHT_NOTE: 該section包含了特定的信息,詳見Note Section; 
9. SHT_NOBITS: 該section在文件中不佔用空間,但是其類似與SHT_PROGBITS,雖然該section不包含任何字節,但是其sh_offset成員仍然包含了概念上的相對文件起始的偏移地址; 
10. SHT_REL: 該section包含了重組表項(without explicit addends); 
11. SHT_SHLIB: 該section類型是保留的,還沒有被指定語義,和ABI一致的程序不應該包含該section; 
12. SHT_LOPROC到SHT_HIPROC: 該範圍內的值是爲特定處理器語義保留的; 
13. SHT_LOUSER: 該值指定了爲應用程序編程者保留的索引範圍的低端; 
14. SHT_HIUSER: 該值指定了爲應用程序編程者保留的索引範圍的高端,在SHT_LOUSER到SHT_HIUSER之間的類型的section可以被應用程序使用,而不會同當前的或者未來的爲系統定義的類型起衝突; 
除上述類型之以外,剩餘所有的類型值統統保留, 
1.2.4.3 section headersection flag屬性 
section header的sh_flags成員包含了1比特標誌,用來描述該section的屬性,定義的值如下: 

如果sh_flags中其中一個標誌bit位被設置,則該屬性就爲section打開了。 
1. SHF_WRITE: 該section包含了在進程執行期間可寫的數據; 
2. SHF_ALLOC: 該section在進程執行期間會佔用內存。一些控制section不會駐留在對象文件的內存映像裏,對於這些section,該屬性就爲OFF; 
3. SHF_EXECINSTR: 該section包含了可執行的機器指令; 
4. SHF_MASKPROC: 所有包含在該mask中的屬性都用於特定處理器的語義; 
1.2.4.4 section header sh_link和sh_info屬性 
section header中的sh_link和sh_info成員根據section type的不同,包含了不同的特殊信息。 
Figure1-13: sh_link和sh_info說明 
sh_type sh_link sh_info 
SHT_DYNAMIC 對於當前的sh_type,該屬性的值即爲該section所使用的string table section在section header table中的索引 0 
SHT_HASH 對於當前的sh_type,該屬性的值即爲該hash表 section所使用的符號表section在section header table中的索引 0 
SHT_REL 
SHT_RELA 對於當前的sh_type,該屬性的值即爲該section所相關的符號表section在section header table中的索引 對於當前的sh_type,該屬性的值即爲該section將要應用重組的section在section header table中的索引 
SHT_SYMTAB 
SHT_DYNSYM 對於當前的sh_type,該屬性的值即爲該section所相關的符號表string在section header table中的索引 比符號表中最後一個Local符號(STB_LOCAL)索引還要大的數值 
other SHN_UNDEF 0 

1.2.4.5 section header特殊的section 

幾個特殊的sections說明: 
1. .bss,該section包含了在內存中的程序的未初始化的數據,當程序開始運行時,系統將用0來初始化該區域。該section不佔用文件空間,該section type = SHT_NOBITS; 
2. .comment,該section包含了版本控制信息; 
3. .data和.data1,該section包含了在內存中的程序的初始化數據; 
4. .debug,該section包含了符號調試信息,其中內容沒有硬性規定; 
5. .dynamic,該section包含了動態鏈接信息,該section屬性將包含SHF_ALLOC比特位,而SHF_WRITE比特位是否爲1取決於處理器; 
6. .dynstr,該section包含了用於動態鏈接的字符串,通常是符號表項名稱字符串; 
7. .dynsym,該section包含了動態鏈接符號表; 
8. .fini,該section包含了用於終止進程可執行指令代碼; 
9. .got,該section包含了全局偏移表; 
10. .hash,該section包含了符號hash表; 
11. .init,該section包含了用於初始化進程的可執行代碼,也就是說,當一個程序開始運行的時候,系統將會執行在該section中的代碼,然後纔會調用程序的入口點(對於C程序而言就是main); 
12. .interp,該section包含了程序解釋其的路徑; 
13. .line,該section包含了符號調試信息的行號,其用於描述程序源代碼和機器碼之間的相應關係; 
14. .note,該section包含了供應商及程序兼容信息等; 
15. .plt,該section包含了程序鏈接表; 
16. .relname和.relaname,該section包含了relocation信息,該section的屬性包括了SHF_ALLOC比特位,通常,name爲將要被重組的section的名稱,例如如果要重組.text,那麼名稱就爲.rel.text或者.rela.text; 
17. .rodata和.rodata1,該section包含了只讀數據,通常進程中的不可寫段,例如Program Header; 
18. .shstrtab,該section包含了section名稱; 
19. .strtab,該section包含了符號表項名稱字符串,如果文件包含了一個可加載的並且包含了符號字符串表的segment,則section的SHF_ALLOC比特位屬性將被設置; 
20. .symtab,該section包含了符號表,如果文件包含了一個可加載的並且包含了符號表的segment,則section的SHF_ALLOC比特位屬性將被設置; 
21. .text,該section包含了程序的可執行指令。

 

帶有(.)前綴的section是系統保留的,當然應用程序也可以在這些section語義允許的情況下使用這些section。應用程序可是使用前綴來命名其自己使用的section,以避免和系統section衝突。 
1.2.5 特殊的sectionString table 
String table section包含了null終止的字符序列,也就是字符串。對象文件使用這些字符串來代表符號和section名稱。對一個字符串的引用其實就是字符串表的索引。第一個字節,也就是0號索引,通常是一個null字符。同樣地,字符串表的最後一個字節也是null,確保所有的字符串都是以null結尾。索引爲0的字符串爲0,即要麼是沒有名字要麼是空名字,這要取決於上下文。空字符串表section也是允許的,此情況下,其section header的sh_size成員必須爲0 。 
首先,section header string table section在section header中表項的索引通過ELF header的e_shstrndx成員找到,然後section header的sh_name定位該string table中的某一表項。例如: 

從圖中我們可以看出,一個string table index可以指向該section中的任何字節,一個字符串可能會出現多次,當然也允許字符串未被引用; 
1.2.6 特殊的sectionSymbol Table 
1.2.6.1 什麼是內核符號表? 
內核並不使用符號名。它是通過變量或函數的地址(指針)來使用變量或函數的,而 不是使用size_t BytesRead,內核更喜歡使用(例如)c0343f20來引用 這個變量。而另一方面,人們並不喜歡象c0343f20這樣的名字。我們跟喜歡使用象 size_t BytesRead這樣的表示。通常,這並不會帶來什麼問題。內核主要 是用C語言寫成的,所以在我們編程時編譯器/連接程序允許我們使用符號名,並且使 內核在運行時使用地址表示。這樣大家都滿意了。然而,存在一種情況,此時我們需要知道一個符號的地址(或者一個地址對應的 符號)。這是通過符號表來做到的,與gdb能夠從一個地址給出函數名(或者給出一個 函數名的地址)的情況很相似。符號表是所有符號及其對應地址的一個列表。例如: 
c03441a0 B dmi_broken 
c03441a4 B is_sony_vaio_laptop 
c034420c b pirq_router_dev 
c0344220 b ascii_buffer 
c0344224 b ascii_buf_bytes 
你可以看出名稱爲dmi_broken的變量位於內核地址c03441a0處。 
1.2.6.2 符號表項表項格式 
對象文件的符號表用來保存用於定位和重組程序的符號定義和引用的信息,其可以獨自佔用一個section。符號表項0 STN_UNDEF指明瞭該表中第一項,其用與未定義符號。表項的初始內容將在後面講到。 

符號表項的內容格式如下: 

1. st_name 該成員包含了對象文件的符號字符串表中一個表項的索引號,其保存了該符號的名稱;如果該值爲非0,其就是一個用來描述該符號名稱的字符表索引,否則,該符號表項沒有名字; 
2. st_value 該成員給出了相關符號的值,至於其中的內容是個絕對的值還是地址等等,取決於上下文; 
* 在可重組文件中,st_value包含了符號的對齊約束,該符號必須位於index爲SHN_COMMON符號的section中; 
* 在可重組文件中,st_value包含了已定義符號的section偏移。也就是說,st_value爲自st_shndx指向的section開始處的偏移; 
* 在可執行和共享對象文件中,st_value包含了邏輯地址,爲了使得這些文件的符號更利於動態連接器使用,對應section的offset給定的是內存邏輯地址; 

3. st_size 許多符號都有尺寸,例如,一個數據對象的尺寸就是該對象中包含的字節數。如果符號沒有大小或者大小未知,則該值爲0; 
4. st_info 該成員指定了符號的類型和綁定屬性,詳述見下,其和bind以及type結合計算方法如下: 
#define ELF32_ST_BIND(i) ((i)>>4) 
#define ELF32_ST_TYPE(i) ((i)&0xf) 
#define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf));//b=bind,t=type 
5. st_other 該成員未定義; 
6. st_shndx 每個符號表項都和某section相關,該成員保存了此section在section head table中的索引。 
1.2.6.3 符號表項&#61664;綁定行爲 
符號的綁定決定了鏈接的可見性以及行爲: 

1. STB_LOCAL 本地符號,對於外部對象文件不可見。同樣名稱的符號也許會存在於多個文件中而不會互相干擾; 
2. STB_GLOBAL 全局符號,一個對象文件定義,所有對象文件共享; 
3. STB_WEAK 類似於全局符號,不過其優先權更低; 
4. STB_LOPROC到STB_HIPROC 該範圍內的符號爲處理器特定的語義; 

在每個符號表中,所有STB_LOCAL類型的符號優先級最高。 
1.2.6.4 符號表項&#61664;符號類型 
符號類型提供了實體的分類: 

1. STT_NOTYPE 該符號類型未指定; 
2. STT_OBJECT 該符號和數據對象相關,例如變量,數組等等; 
3. STT_FUNC 該符號和函數或者其他可執行代碼相關; 
4. STT_SECTION 該符號和section相關,其綁定行爲一般爲STB_LOCAL; 
5. STT_FILE 通常文件符號的名字給出了該對象文件相關的源文件的名字,其綁定行爲一般爲STB_LOCAL; 
6. STT_LOPROC 到 STT_HIPROC該範圍內的符號爲處理器特定的語義; 

1.2.7 特殊的section&#61664;Relocation 
重組就是將符號定義和符號引用連接起來的過程;例如,當程序調用函數時,調用指令必須被傳遞給執行時正確的目的地址。換句話說,可重組的文件必須包含了這樣的信息:該信息描述瞭如何修改可重組文件的section內容,從而使得可執行或者共享對象文件包含了正確的進程內存映像信息。而Relocation表項就是可重組文件需要包含的信息; 

1. r_offset 該成員給出了應用重組動作的位置,對於一個可重組文件而言,該值就是從該section開始到重組將要影響的存儲單元之間的字節偏移。對於可執行文件或共享對象文件而言,該值就是將要被重組影響的存儲單元的邏輯地址; 
2. r_info 該成員給出了該需要重組的符號表索引,以及將要重組的類型。類如,一個調用指令的重組表項包含了將要被調用函數的符號表索引。如果其索引爲STN_UNDEF,則使用0作爲符號值。重組類型是和處理器相關的。其計算方式如下: 

3. r_addend 該成員指定了常量加數用來計算將要被存儲到重組域中的值; 

關於重組的詳細信息不再敘述,請查閱ELF Format。 
1.3 程序的加載 
1.3.1 介紹 
本部分介紹了對象文件信息以及創建運行進程的系統行爲。這裏的一些信息適用於所有的系統,另一些是和處理器相關的。 
可執行和共享對象文件只是靜態地代表了程序。要執行這樣的程序,操作系統利用該文件來創建動態程序表現,或進程映像。進程印象包含了段,段包含了text、data、stack等等。本部分主要討論以下幾個問題: 
1. Program header 該部分補充了前一章講述的內容,主要描述了程序執行時的對象文件結構,以及用於定位段映像的program header table基本數據結構; 
2. Program loading 給定一個對象文件,操作系統必須將其加載如內存來作爲程序運行; 
3. Dynamic linking 詳細信息不再敘述,請查閱ELF Format。 
1.3.2 Program header table 
可執行或共享對象文件的program header table是一個數據結構數組,每一個表項都描述了一個段或者其他系統用來爲程序運行做準備的信息。對象文件的段可以包含多個section,下面的“段內容”將會詳細描述。program header table只對可執行和共享對象文件有意義。文件通常通過其ELF header中的e_phentsize和e_phnum成員來指定其自身的program header table大小。 
1.3.3 Program header table&#61664;Program header 

1. p_type 該成員表明了該數組元素所描述的段的類型以及如何解釋該數組元素的屬性; 
2. p_offset 該成員段的第一字節至文件開始處的偏移; 
3. p_vaddr 該成員給出了段的第一字節在內存中的邏輯地址; 
4. p_paddr 基本無用; 
5. p_filesz 該成員給出了該段在文件中佔用的字節數,可以爲0; 
6. p_memsz 該成員給出了該段在內存映像中佔用的字節,可以爲0; 
7. p_flags 該成員給出了段相關的標誌; 
8. p_align 當程序加載後,可加載的進程段的p_vaddr和p_offset取模page size(4KB)之後的值必須相等。該成員給出該段應該在內存和文件中的對齊值。 
1.3.4 Program header table&#61664;Program header &#61664;Type 
一些表項描述了進程段,其他的一些給出了對進程映像並無作用的輔助信息。段表項可以按照任何順序出現,除非明確指定。 




1. PT_NULL 該元素不適用,其告訴program header table忽略它; 
2. PT_LOAD 其指定了可加載段,由p_filesz 和 p_memsz來描述。文件中的字節被映射到內存段中。如果段的內存尺寸大於其在文件中的尺寸,那麼額外的數據將被填充爲0。可加載的段,在program header table中通常按照p_vaddr升序排列; 
3. PT_DYNAMIC 該成員指定了動態鏈接信息; 
4. PT_INTERP 指定了翻譯器的未知; 
5. PT_NOTE 指定了輔助信息的位置和大小; 
6. PT_SHLIB 保留; 
7. PT_PHDR 如果存在,指定了program header table本身在文件/內存中的位置和大小; 
8. PT_LOPROC 至 PT_HIPROC 保留; 
1.3.5 Base Address 
可執行和共享對象文件都有基地址,其是程序對象文件內存映射後的最低的虛擬地址。基地址的用途就是用來在動態鏈接過程中重組內存映像;詳細信息不再敘述,請查閱ELF Format。 
1.3.6 初始化和終止函數 
當動態連接器已經建立起進程映像並且執行完畢重組後,每一個共享對象都有機會執行一些初始化代碼,這些初始化代碼並不是按照特定順序的,但是所有共享對象的初始化都是發生在可執行文件活動控制權之前。程序可以通過動態結構中的DT_INIT和DT_FINI動態表項來定義動態section,通常這些代碼都駐留在.init和.fini section中,詳細信息不再敘述,請查閱ELF Format。 
1.3.7 程序的加載 
當操作系統創建或者增加一個進程映像的時候,其理論上將會拷貝文件的段到虛擬內存段中。系統讀取文件內容的實際依賴於程序的執行環境的行爲。一個進程在運行的過程中,如果沒有引用邏輯頁面,則其也不需要物理頁面,通常大部分頁面進程都是沒有使用的。因此,延緩讀取物理頁面可以提高系統性能。爲了獲取該效率,可執行和共享對象文件的段的文件偏移及虛擬地址必須在按頁面(4KB)取模後相等。 




雖然上面的例子文件中,對於text和data段而言,文件偏移和邏輯地址取模4KB後都是相等的。但是: 
1. text的第一個頁面包含了ELF header,program header table以及其他的信息; 
2. text的最後一個頁面包含了data的開始部分數據的拷貝; 
3. data的第一個頁面包含了text的末尾數據的拷貝; 
4. data的最後一個頁面也許包含了和運行進程不相關的文件信息; 
理論上講,系統對待每個段的內存權限都是相互獨立的。段地址不得不調整來確保地址空間中的每個邏輯頁面都有自己的權限;在上面的例子中,包含了text結尾和data開始的區域將要被映射兩次:一次就是包含了text和data開始部分,另一個就是text末尾部分和data;data段的末尾還需要對爲初始化數據的特殊處理,系統通常將其清零。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章