歡迎您光臨本站 註冊首頁

ELF文件格式(中文)(二)

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  


=================== String Table 字元串表=========================


String table sections 保存著以NULL終止的一系列字元,一般我們稱為字
符串。object文件使用這些字元串來描繪符號和section名。一個字元串的
參考是一個string table section的索引。第一個位元組,即索引0,被定義保
存著一個NULL字元。同樣的,一個string table的最後一個位元組保存著一個
NULL字元,所有的字元串都是以NULL終止。索引0的字元串是沒有名字或者說
是NULL,它的解釋依靠上下文。一個空的string table section是允許的;
它的section header的成員sh_size將為0。對空的string table來說,非0的
索引是沒有用的。

一個 settion 頭的 sh_name 成員保存了一個對應於該 setion 頭字元表部分
的索引(就象ELF頭的 e_shstrndx 成員所特指的那樣。下表列出了一個有 25 位元組
的字元串表(這些字元串和不同的索引相關聯):


Index +0 +1 +2 +3 +4 +5 +6 +7 +8 +9
===== == == == == == == == == == ==
0 \0 n a m e . \0 V a r
10 i a b l e \0 a b l e
20 \0 \0 x x \0


+ Figure 1-15: String Table Indexes

Index String
===== ======
0 none
1 "name."
7 "Variable"
11 "able"
16 "able"
24 null string


如上所示,一個字元串表可能涉及該 section 中的任意位元組。一個字元串可能
引用不止一次;引用子串的情況是可能存在的;一個字元串也可能被引用若干次;而
不被引用的字元串也是允許存在的。

==================== Symbol Table 符號表=========================


一個object文件的符號表保存了一個程序在定位和重定位時需要的定義和引用的信息。
一個符號表索引是相應的下標。0表項特指了該表的第一個入口,就象未定義的符號
索引一樣。初始入口的內容在該 section 的後續部分被指定。

Name Value
==== =====
STN_UNDEF 0

一個符號表入口有如下的格式:

+ Figure 1-16: Symbol Table Entry

typedef struct {
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;

* st_name

該成員保存了進入該object文件的符號字元串表入口的索引(保留了符號名的表達字元)。
如果該值不為 0 ,則它代表了給出符號名的字元串表索引。否則,該符號無名。

注意:External C 符號和object文件的符號表有相同的名稱。

* st_value

該成員給出了相應的符號值。它可能是絕對值或地址等等(依賴於上下文);
細節如下所述。

* st_size

許多符號和大小相關。比如,一個數據對象的大小是該對象所包含的位元組數目。
如果該符號的大小未知或沒有大小則這個成員為 0 。

* st_info

成員指出了符號的類型和相應的屬性。相應的列表如下所示。下面的代碼說明了
如何操作該值。

#define ELF32_ST_BIND(i) ((i)>>4)
#define ELF32_ST_TYPE(i) ((i)&0xf)
#define ELF32_ST_INFO(b, t) (((b)<<4)+((t)&0xf))

* st_other

該成員目前為 0 ,沒有含義。

* st_shndx

每一個符號表的入口都定義為和某些 section 相關;該成員保存了相關的 section
頭索引。就象 Figure 1-8 {*}和相關的文字所描述的那樣,某些 section 索引
指出了特殊的含義。

一個符號的屬性決定了可鏈接性能和行為。

+ Figure 1-17: Symbol Binding, ELF32_ST_BIND

Name Value
==== =====
STB_LOCAL 0
STB_GLOBAL 1
STB_WEAK 2
STB_LOPROC 13
STB_HIPROC 15

* STB_LOCAL

在包含了其定義的object文件之外的局部符號是不可見的。不同文件中的具有相同
名稱的局部符號並不相互妨礙。

* STB_GLOBAL

全局符號是對所有的object目標文件可見的。一個文件中的全局符號的定義可以
滿足另一個文件中對(該文件中)未定義的全局符號的引用。

* STB_WEAK

弱符號相似於全局符號,但是他們定義的優先順序比較低一些。

* STB_LOPROC through STB_HIPROC

其所包含範圍中的值由相應的處理器語義所保留。

全局符號和弱符號的區別主要在兩個方面。

* 當鏈接器鏈接幾個可重定位的目標文件時,它不允許 STB_GLOBAL 符號的同名
多重定義。另一方面,如果一個全局符號的定義存在則具有相同名稱的弱符號名不會
引起錯誤。鏈接器將認可全局符號的定義而忽略弱符號的定義。與此相似,如果有一個
普通符號(比如,一個符號的 st_shndx 域包含 SHN_COMMON),則一個同名的弱符號
不會引起錯誤。鏈接器同樣認可普通符號的定義而忽略弱符號。

* 當鏈接器搜索檔案庫的時候,它選出包含了未定義的全局符號的存檔成員。該成員
的定義或者是全局的或者是一個弱符號。鏈接器不會為了解決一個未定義的弱符號
選出存檔成員。未定義的弱符號具有 0 值。

在每一個符號表中,所有具有 STB_LOCAL 約束的符號優先於弱符號和全局符號。
就象上面 "sections" 中描述的那樣,一個符號表部分的 sh_info 頭中的成員
保留了第一個非局部符號的符號表索引。

符號的類型提供了一個為相關入口的普遍分類。

+ Figure 1-18: Symbol Types, ELF32_ST_TYPE

Name Value
==== =====
STT_NOTYPE 0
STT_OBJECT 1
STT_FUNC 2
STT_SECTION 3
STT_FILE 4
STT_LOPROC 13
STT_HIPROC 15

* STT_NOTYPE

該符號的類型沒有指定。

* STT_OBJECT

該符號和一個數據對象相關,比如一個變數、一個數組等。

* STT_FUNC

該符號和一個函數或其他可執行代碼相關。

* STT_SECTION

該符號和一個 section 相關。這種類型的符號表入口主要是為了重定位,一般的
具有 STB_LOCAL 約束。


* STT_FILE

按慣例而言,該符號給出了和目標文件相關的源文件名稱。一個具有 STB_LOCAL
約束的文件符號,其 section 索引為 SHN_ABS ,並且它優先於當前對應該文件的
其他 STB_LOCAL 符號。

* STT_LOPROC through STT_HIPROC

該範圍中的值是為處理器語義保留的。

共享文件中的函數符號(具有 STT_FUNC 類型)有特殊的意義。當其他的目標文件
從一個共享文件中引用一個函數時,鏈接器自動的為引用符號創建一個鏈接表。除了
STT_FUNC 之外,共享的目標符號將不會自動的通過鏈接表引用。


如果一個符號涉及到一個 section 的特定定位,則其 section 索引成員 st_shndx
將保留一個到該 section 頭的索引。當該 section 在重定位過程中不斷
移動一樣,符號的值也相應變化,而該符號的引用在程序中指向同樣的定位。某些
特殊的 section 索引有其他的語義。


* SHN_ABS

該符號有一個不會隨重定位變化的絕對值。

* SHN_COMMON

該符號標識了一個沒有被分配的普通塊。該符號的值給出了相應的系統參數,就象
一個 section 的 sh_addralign 成員。也就是說,鏈接器將分配一個地址給
該符號,地址的值是 st_value 的倍數。該符號的大小指出了需要的位元組數。


* SHN_UNDEF

該 section 表索引表明該符號是未定義的。當鏈接器將該目標文件和另一個定義
該符號的文件相裝配的時候,該文件內對該符號的引用將鏈接到當前實際的定義。

如上所述,符號表的 0 索引(STN_UNDEF)是保留的,它包含了如下內容:

+ Figure 1-19: Symbol Table Entry: Index 0

Name Value Note
==== ===== ====
st_name 0 No name
st_value 0 Zero value
st_size 0 No size
st_info 0 No type, local binding
st_other 0
st_shndx SHN_UNDEF No section


Symbol Values(符號值)

符號表入口對於不同的目標文件而言對 st_value 成員有一些不同的解釋。

* 在可重定位文件中, st_value 保存了 section 索引為 SHN_COMMON 符號
的強制對齊值。

* 在可重定位文件中, st_value 保存了一個符號的 section 偏移。也就是說,
st_value 是從 st_shndx 定義的 section 開頭的偏移量。

* 在可執行的和可共享的目標文件中, st_value 保存了一個虛擬地址。為了使
這些文件符號對於動態鏈接器更為有效,文件層面上的 section 偏移讓位於內存
層面上的虛擬地址( section 編號無關的)。


儘管符號表值對於不同的目標文件有相似的含義,相應的程序還是可以有效地訪問數據。


====================== Relocation (重定位)==========================


重定位是連接符號引用和符號定義的過程。比如,當一個程序調用一個函數的時候,
相關的調用必須在執行時把控制傳送到正確的目標地址。換句話說,重定位文件應當
包含有如何修改他們的 section 內容的信息,從而允許可執行文件或共享目標文件
為一個進程的程序映像保存正確的信息。重定位入口就是這樣的數據。

+ Figure 1-20: Relocation Entries

typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
} Elf32_Rel;

typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
Elf32_Sword r_addend;
} Elf32_Rela;

* r_offset

該成員給出了應用重定位行為的地址。對於一個重定位文件而言,該值是從該
section 開始處到受到重定位影響的存儲單位的位元組偏移量。對一個可執行文件
或一個共享目標而言,該值是受到重定位影響的存儲單位的虛擬地址。


* r_info

該成員給出了具有受重定位影響因素的符號表索引和重定位應用的類型。比如,
一個調用指令的重定位入口應當包含被調用函數的符號索引。如果該索引是
STN_UNDEF (未定義的符號索引),重定位將使用 0 作為該符號的值。重定位
類型是和處理器相關的。當正文(text)引用到一個重定位入口的重定位類型或符
號表索引,它表明相應的應用 ELF32_R_TYPE或 ELF32_R_SYM 於入口的 r_info
成員。

#define ELF32_R_SYM(i) ((i)>>8)
#define ELF32_R_TYPE(i) ((unsigned char)(i))
#define ELF32_R_INFO(s, t) ((s)<<8+(unsigned char)(t))

* r_addend

該成員指定一個常量加數(用於計算將要存儲於重定位域中的值)。

如上所述,只有 Elf32_Rela 入口包含一個明確的加數。Elf32_Rel 類型
的入口在可以修改的地址中存儲一個隱含的加數。依賴於處理器結構,一種形式
或其他形式也許是必須的或更為方便的。因此,特定機器的應用應當使用一種排他
性的形式或依賴於上下文的另一種形式。

一個重定位 section 關聯了兩個其他的 section :一個符號表和一個可修改
的 section 。該 section 頭的成員 sh_info 和 sh_link (在上文中的
「 section 」部分中有描述)指示了這種關係。重定位入口中的成員 r_offset
對於不同的目標文件有少許差異。

* 在可重定位文件中,r_offset 表示了一個 section 偏移。也就是說,重定位
section自己描述了如何修改其他在文件中的其他section; 重定位偏移量指
明了一個在第二個section中的存儲器單元。

* 在可執行和共享的目標文件中,r_offset 表示一個虛擬地址。為了使得這些
文件的重定位入口更為有用(對於動態鏈接器而言),該 section 偏移(文件
中)應當讓位於一個虛擬地址(內存中的)。

儘管為了允許相關的程序更為有效的訪問而讓 r_offset 的解釋對於不同的目標
文件有所不同,重定位類型的含義是相同的。


Relocation Types(重定位類型)

重定位入口描述了怎樣變更下面的指令和數據域(位數在表的兩邊角下)。

+ Figure 1-21: Relocatable Fields

+---------------------------+
| word32 |
31---------------------------0


* word32

指定一個以任意位元組對齊方式佔用 4 位元組的 32 位域。這些值使用與 32 位 Intel
體系相同的位元組順序。

3------2------1------0------+
0x01020304 | 01 | 02 | 03 | 04 |
31------+------+------+------0

下面的計算假設正在將一個可重定位文件轉換為一個可執行或共享的目標文件。
從概念上來說,鏈接器合併一個或多個可重定位文件來組成輸出。它首先決定
怎樣合併、定位輸入文件,然後更新符號值,最後進行重定位。對於可執行文件
和共享的目標文件而言,重定位過程是相似的並有相同的結果。下面的描述使用
如下的約定符號。

* A

表示用於計算可重定位的域值的加數。

* B

表示了在執行過程中一個共享目標被載入到內存時的基地址。一般情況下,一個
共享object文件使用的基虛地址為0,但是一個可執行地址就跟共享object文件
不同了。

* G

表示了在執行過程中重定位入口符號駐留在全局偏移表中的偏移。請參閱
第二部分中的「 Global Offset Table (全局偏移表)」獲得更多
的信息。

* GOT

表示了全局偏移表的地址。請參閱第二部分中的「 Global Offset Table
(全局偏移表)」獲得更多的信息。

* L

表示一個符號的過程鏈接表入口的位置( section 偏移或地址)。一個過程
鏈接表入口重定位一個函數調用到正確的目的單元。鏈接器創建初始的鏈接表,
而動態鏈接器在執行中修改入口。
請參閱第二部分中的「 Procedure Linkage Table (過程鏈接表)」獲得更多
的信息

* P

表示(section 偏移或地址)被重定位的存儲單元位置(使用 r_offset 計算的)。

* S

表示索引駐留在重定位入口處的符號值。

一個重定位入口的 r_offset 值指定了受影響的存儲單元的首位元組的偏移
或虛擬地址。重定位類型指定了哪一位(bit)將要改變,以及怎樣計算它們的值。
在 SYSTEM V 體系中僅僅使用 Elf32_Rel 重定位入口,將要被重定位的域中
保留了加數。在所有的情況下,加數和計算結果使用相同位元組順序。

+ Figure 1-22(表 1-22): Relocation Types(重定位類型)

Name Value Field Calculation
==== ===== ===== ===========
R_386_NONE 0 none none
R_386_32 1 word32 S + A
R_386_PC32 2 word32 S + A - P
R_386_GOT32 3 word32 G + A - P
R_386_PLT32 4 word32 L + A - P
R_386_COPY 5 none none
R_386_GLOB_DAT 6 word32 S
R_386_JMP_SLOT 7 word32 S
R_386_RELATIVE 8 word32 B + A
R_386_GOTOFF 9 word32 S + A - GOT
R_386_GOTPC 10 word32 GOT + A - P

有的重定位類型有不同於簡單計算的語義。

* R_386_GOT32

這種重定位類型計算全局偏移表基地址到符號的全局偏移表
入口之間的間隔。這樣另外通知了 link editor 建立一個全局偏移表 。


* R_386_PLT32

這種重定位類型計算符號的過程鏈接表入口地址,並另外通知鏈接器建立一個
過程鏈接表。


* R_386_COPY

鏈接器創建該重定位類型用於動態鏈接。它的偏移成員涉及一個可寫段中的一個
位置。符號表索引指定一個可能存在於當前 object file 或在一個shared object
中的符號。在執行過程中,動態鏈接器把和 shared object 符號相關的數據
拷貝到該偏移所指定的位置。

* R_386_GLOB_DAT

這種重定位類型用於設置一個全局偏移表入口為指定符號的地址。該特定的重定位
類型允許你決定符號和全局偏移表入口之間的一致性。


* R_386_JMP_SLOT {*}

鏈接器創建該重定位類型用於動態鏈接。其偏移成員給出了一個過程鏈接表入口的
位置。動態鏈接器修改該過程鏈接表入口以便向特定的符號地址傳遞控制。
[參閱第二部分中的 "Procedure Linkage Table(過程鏈接表)"]

* R_386_RELATIVE

鏈接器創建該重定位類型用於動態鏈接。其偏移成員給出了包含表達相關地址值
的一個 shared object 中的位置。動態鏈接器計算相應的虛擬地址(把該
shared object 裝載地址和相對地址相加)。該類型的重定位入口必須為
符號表索引指定為 0 。


* R_386_GOTOFF

這種重定位類型計算符號值和全局偏移表地址之間的不同。另外還通知鏈接器
建立全局偏移表(GOT)。



* R_386_GOTPC

這種重定位類型類似於 R_386_PC32 ,不同的是它在計算中使用全局偏移表。
這種重定位中引用的符號通常是 _GLOBAL_OFFSET_TABLE_ ,該符號通知了
鏈接器建立全局偏移表(GOT)。

________________________________________________________________


2. PROGRAM LOADING AND DYNAMIC LINKING
程序裝入和動態鏈接
________________________________________________________________


======================== Introduction(介紹) =========================


第二部分描述了 object file 信息和創建運行程序的系統行為。其中部分信息
適合所有的系統,其他信息是和特定處理器相關的。


可執行和共享的 object file 靜態的描繪了程序。為了執行這樣的程序,系統
用這些文件創建動態的程序表現,或進程映像。一個進程映像有用於保存其代碼、
數據、堆棧等等的段。這個部分的主要章節討論如下的內容。


* 程序頭(Program header)。該章節補充第一部分,描述和程序運行相關的
object file 結構。即文件中主要的數據結構、程序頭表、定位段映像,也
包含了為該程序創建內存映像所需要的信息。

* 載入程序(Program loading)。在給定一個 object file 時,系統為了
讓它運行必須將它載入內存。

* 動態鏈接(Dynamic linking)。在載入了程序之後,系統必須通過解決組
成該進程的 object file之間的符號引用問題來完成進程映像的過程。

注意:指定了處理器範圍的 ELF 常量是有命名約定的。比如,DT_ , PT_ ,
用於特定處理器擴展名,組合了處理器的名稱(如 DT_M32_SPECIAL )。
沒有使用這種約定但是預先存在的處理器擴展名是允許的。


Pre-existing Extensions
(預先存在的擴展名)
=======================
DT_JMP_REL


====================== Program Header(程序頭) ======================


一個可執行的或共享的 object file 的程序頭表是一個結構數組,每一個
結構描述一個段或其他系統準備執行該程序所需要的信息。一個 object file
段包含一個或多個部分(就象下面的「段目錄」所描述的那樣)。程序頭僅僅對於
可執行或共享的 object file 有意義。一個文件使用 ELF 頭的 e_phentsize
和 e_phnum 成員來指定其擁有的程序頭大小。[參閱 第一部分中的 "ELF 頭"]


+ Figure 2-1: Program Header

typedef struct {
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
} Elf32_Phdr;

* p_type

該成員指出了這個數組的元素描述了什麼類型的段,或怎樣解釋該數組元素的信息。
類型值和含義如下所述。

* p_offset

該成員給出了該段的駐留位置相對於文件開始處的偏移。

* p_vaddr

該成員給出了該段在內存中的首位元組地址。

* p_paddr

在物理地址定位有關聯的系統中,該成員是為該段的物理地址而保留的。由於
System V 忽略了應用程序的物理地址定位,該成員對於可執行文件和共享的
object 而言是未指定內容的。


* p_filesz

該成員給出了文件映像中該段的位元組數;它可能是 0 。

* p_memsz

該成員給出了內存映像中該段的位元組數;它可能是 0 。

* p_flags

該成員給出了和該段相關的標誌。定義的標誌值如下所述。

* p_align

就象在後面「載入程序」部分中所說的那樣,可載入的進程段必須有合適的
p_vaddr 、 p_offset 值,取頁面大小的模。該成員給出了該段在內存和
文件中排列值。 0 和 1 表示不需要排列。否則, p_align 必須為正的 2 的冪,
並且 p_vaddr 應當等於 p_offset 模 p_align 。


某些入口描述了進程段;其他的則提供補充信息並且無益於進程映像。已經
定義的入口可以以任何順序出現,除非是下面明確聲明的。後面是段類型值;
其他的值保留以便將來用於其他用途。


+ Figure 2-2: Segment Types, p_type

Name Value
==== =====
PT_NULL 0
PT_LOAD 1
PT_DYNAMIC 2
PT_INTERP 3
PT_NOTE 4
PT_SHLIB 5
PT_PHDR 6
PT_LOPROC 0x70000000
PT_HIPROC 0x7fffffff

* PT_NULL

該數組元素未使用;其他的成員值是未定義的。這種類型讓程序頭表忽略入口。

* PT_LOAD

該數組元素指定一個可載入的段,由 p_filesz 和 p_memsz 描述。文件中
位元組被映射到內存段中。如果該段的內存大小( p_memsz )比文件大小( p_filesz )
要大,則多出的位元組將象段初始化區域那樣保持為 0 。文件的大小不會比內存大小值大。
在程序頭表中,可載入段入口是以 p_vaddr 的升序排列的。

* PT_DYNAMIC

該數組元素指定動態鏈接信息。參閱 後面的「動態部分」以獲得更多信息。

* PT_INTERP

該數組元素指定一個 null-terminated 路徑名的位置和大小(作為解釋程序)。
這種段類型僅僅對可執行文件有意義(儘管它可能發生在一個共享 object 上);
它在一個文件中只能出現一次。如果它出現,它必須先於任何一個可載入段入口。
參閱 後面的「程序解釋器」(Program Interpreter)以獲得更多的信息。


* PT_NOTE

該數組元素指定輔助信息的位置和大小。參閱 後面的「注意部分」以獲得細節。

* PT_SHLIB

該段類型保留且具有未指定的語義。具有一個這種類型數組元素的程序並不
遵守 ABI 。

* PT_PHDR

該數組元素(如果出現),指定了程序頭表本身的位置和大小(包括在文件中
和在該程序的內存映像中)。更進一步來說,它僅僅在該程序頭表是程序內存映像
的一部分時才有效。如果它出現,它必須先於任何可載入段入口。參閱 後面的
「程序解釋器」(Program Interpreter)以獲得更多的信息。


* PT_LOPROC through PT_HIPROC

該範圍中的值保留用於特定處理器的語義。

注意:除非在別處的特殊要求,所有的程序頭的段類型是可選的。也就是說,
一個文件的程序頭表也許僅僅包含和其內容相關的元素。



Base Address(基地址)

可執行和共享的 object file 有一個基地址,該基地址是與程序的 object file
在內存中映像相關的最低虛擬地址。基地址的用途之一是在動態鏈接過程中重定位
該程序的內存映像。


一個可執行的 object file 或 一個共享的 object file 的基地址是在
執行的時候從三個值計算而來的:內存載入地址、頁面大小的最大值 和 程序可
載入段的最低虛擬地址。就象在「程序載入」中所描述的那樣,程序頭中的虛擬地址
也許和程序的內存映像中實際的虛擬地址並不相同。為了計算基地址,必須確定與
PT_LOAD 段 p_vaddr 的最小值相關的內存地址。獲得基地址的方法是將內存
地址截去最大頁面大小的最接近的整數倍。由於依賴載入內存中的文件類型,
該內存地址和 p_vaddr 值可能匹配也可能不匹配。


就象在第一部分中 "Section" 中描述的那樣, .bss section 具有 SHT_NOBITS
的類型。儘管在文件中不佔用空間,它在段的內存映像中起作用。通常,沒有初始化
的數據駐留在段尾,因此使得在相關的程序頭元素中的 p_memsz 比 p_filesz 大。


Note Section(註解部分)

有的時候供應商或系統設計者需要用特定的信息標記一個
object file 以便其他程序檢查其兼容的一致性,等等此類。 SHT_NOTE
類型的 section 和 PT_NOTE 類型的程序頭元素能夠被用於此目的。 section
和程序頭中的註解信息包含了任意數目的入口,每一個入口的格式都是對應於特定
處理器格式的 4-位元組數組。下面的標籤有助於解釋註釋信息的組織形式,但是這些
標籤不是規格說明的一部分。


+ Figure 2-3: Note Information

namesz
descsz
type
name ...
desc ...

* namesz and name

名字中 namesz 的第一個位元組包含了一個 null-terminated 字元
表達了該入口的擁有者或始發者。沒有正式的機制來避免名字衝突。從
慣例來說,供應商使用他們自己的名稱,比如 "XYZ Computer Company" ,
作為標誌。如果沒有提供名字, namesz 值為 0 。 如果有必要,確定
描述信息4-位元組對齊。 這樣的填充信息並不包含在namesz 中。


* descsz and desc

desc 中 descsz 的首位元組包含了註解描述符。ABI 不會在一個描述符內容中
放入任何系統參數。如果沒有描述符, descsz 將為 0 。 如果有必要,確定
描述信息4-位元組對齊。 這樣的填充信息並不包含在descsz中。


* type

該 word 給出了描述符的解釋。每一個創造著(originator) 控制著自己的類型;
對於單單一個類型值的多種解釋是可能存在的。因此,一個程序必須辨認出該名字
和其類型以便理解一個描述符。這個時候的類型必須是非負的。ABI 沒有定義
描述符的含義。

為了舉例說明,下面的解釋段包含兩個入口。

+ Figure 2-4: Example Note Segment

+0 +1 +2 +3
-------------------
namesz 7
descsz 0 No descriptor
type 1
name X Y Z spc
C o \0 pad
namesz 7
descsz 8
type 3
name X Y Z spc
C o \0 pad
desc word0
word1

注意:系統保留的註解信息沒有名字 (namesz==0) ,有一個零長度的名字
(name[0]==『\0『) 現在還沒有類型為其定義。所有其他的名字必須至少有
一個非空的字元。


注意:註解信息是可選的。註解信息的出現並不影響一個程序的 ABI 一致性,
前提是該信息不影響程序的執行行為。否則,該程序將不遵循 ABI 並將出現
未定義的行為。


===================== Program Loading(程序載入) =====================

當創建或增加一個進程映像的時候,系統在理論上將拷貝一個文件的段到一個虛擬
的內存段。系統什麼時候實際地讀文件依賴於程序的執行行為,系統載入等等。一個
進程僅僅在執行時需要引用邏輯頁面的時候才需要一個物理頁面,實際上進程通常會
留下許多未引用的頁面。因此推遲物理上的讀取常常可以避免這些情況,改良系統的
特性。為了在實踐中達到這種效果,可執行的和共享的 object file 必須具有
合適於頁面大小取模值的文件偏移和虛擬地址這樣條件的段映像。

虛擬地址和文件偏移在 SYSTEM V 結構的段中是模 4KB(0x1000) 或大的 2 的冪。
由於 4KB 是最大的頁面大小,因此無論物理頁面大小是多少,文件必須去適合頁面。


+ Figure 2-5: Executable File

File Offset File Virtual Address
=========== ==== ===============
0 ELF header
Program header table
Other information
0x100 Text segment 0x8048100
...
0x2be00 bytes 0x8073eff
0x2bf00 Data segment 0x8074f00
...
0x4e00 bytes 0x8079cff
0x30d00 Other information
...

+ Figure 2-6: Program Header Segments(程序頭段)

Member Text Data
====== ==== ====
p_type PT_LOAD PT_LOAD
p_offset 0x100 0x2bf00
p_vaddr 0x8048100 0x8074f00
p_paddr unspecified unspecified
p_filesz 0x2be00 0x4e00
p_memsz 0x2be00 0x5e24
p_flags PF_R+PF_X PF_R+PF_W+PF_X
p_align 0x1000 0x1000

儘管示例中的文件偏移和虛擬地址在文本和數據兩方面都適合模 4KB ,但是還有
4 個文件頁面混合了代碼和數據(依賴於頁面大小和文件系統塊的大小)。


* 第一個文本頁面包含了 ELF 頭、程序頭以及其他信息。
* 最後的文本頁包含了一個數據開始的拷貝。
* 第一個數據頁面有一個文本結束的拷貝。
* 最後的數據頁面也許會包含與正在運行的進程無關的文件信息。

理論上,系統強制內存中段的區別;段地址被調整為適應每一個邏輯頁面在地址空間
中有一個簡單的准許集合。在上面的示例中,包含文本結束和數據開始的文件區域將
被映射兩次:在一個虛擬地址上為文本而另一個虛擬地址上為數據。


數據段的結束處需要對未初始化的數據進行特殊處理(系統定義的以 0 值開始)。
因此如果一個文件包含信息的最後一個數據頁面不在邏輯內存頁面中,則無關的
數據應當被置為 0 (這裡不是指未知的可執行文件的內容)。在其他三個頁面中
"Impurities" 理論上並不是進程映像的一部分;系統是否擦掉它們是未指定的。
下面程序的內存映像假設了 4KB 的頁面。


+ Figure 2-7: Process Image Segments(進程映像段)

Virtual Address Contents Segment
=============== ======== =======
0x8048000 Header padding Text
0x100 bytes
0x8048100 Text segment
...
0x2be00 bytes
0x8073f00 Data padding
0x100 bytes
0x8074000 Text padding Data
0xf00 bytes
0x8074f00 Data segment
...
0x4e00 bytes
0x8079d00 Uninitialized data
0x1024 zero bytes
0x807ad24 Page padding
0x2dc zero bytes

可執行文件和共享文件在段載入方面有所不同。典型地,可執行文件段包含了
絕對代碼。為了讓進程正確執行,這些段必須駐留在建立可執行文件的虛擬地址
處。因此系統使用不變的 p_vaddr 作為虛擬地址。


另一方面,共享文件段包含與位置無關的代碼。這讓不同進程的相應段虛擬地址
各不相同,且不影響執行。雖然系統為各個進程選擇虛擬地址,它還要維護各個
段的相對位置。因為位置無關的代碼在段間使用相對定址,故而內存中的虛擬地址
的不同必須符合文件中虛擬地址的不同。下表給出了幾個進程可能的共享對象虛擬
地址的分配,演示了不變的相對定位。該表同時演示了基地址的計算。


+ Figure 2-8: Example Shared Object Segment Addresses

Sourc Text Data Base Address
===== ==== ==== ============
File 0x200 0x2a400 0x0
Process 1 0x80000200 0x8002a400 0x80000000
Process 2 0x80081200 0x800ab400 0x80081000
Process 3 0x900c0200 0x900ea400 0x900c0000
Process 4 0x900c6200 0x900f0400 0x900c6000




[火星人 ] ELF文件格式(中文)(二)已經有516次圍觀

http://coctec.com/docs/program/show-post-72245.html