=================== 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 n a m e . V a r
10 i a b l e a b l e
20 x x
+ 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
在內存中映像相關的最低虛擬地址?;刂返挠猛局皇窃趧討B鏈接過程中重定位
該程序的內存映像。
一個可執行的 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 pad
namesz 7
descsz 8
type 3
name X Y Z spc
C o pad
desc word0
word1
注意:系統保留的注解信息沒有名字 (namesz==0) ,有一個零長度的名字
(name[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