1. 程式人生 > >ELF檔案格式詳解

ELF檔案格式詳解

ARM的可執行檔案的格式是ELF格式檔案,下文對ELF格式做個詳細的介紹。

序言
1. OBJECT檔案
   導言
   ELF頭(ELF Header)
   Sections
   String表(String Table)
   Symbol表(Symbol Table)
   重定位(Relocation)
2. 程式裝載與動態連線
   導言
   Program頭(Program Header)
   Program裝載(Program Loading)
   Dynamic連線(Dynamic Linking)
3. C LIBRARY
   C Library

   ________________________________________________________________


          導言

   ________________________________________________________________


  ELF: 可執行連線格式

可執行連線格式是UNIX系統實驗室(USL)作為應用程式二進位制介面
(Application Binary Interface(ABI)而開發和釋出的。工具介面標準委
員會(TIS)選擇了正在發展中的ELF標準作為工作在32位INTEL體系上不同操
作系統之間可移植的二進位制檔案格式。

假定開發者定義了一個二進位制介面集合,ELF標準用它來支援流線型的軟體
發展。 應該減少不同執行介面的數量。因此可以減少重新程式設計重新編譯的
程式碼。


關於這片文件

這篇文件是為那些想建立目標檔案或者在不同的作業系統上執行檔案的開發
著準備的。它分以下三個部分:

* 第一部分, “目標檔案Object Files”描述了ELF目標檔案格式三種主要
  的型別。
* 第二部分, “程式轉載和動態連線”描述了目標檔案的資訊和系統在建立
  執行時程式的行為。
* 第三部分, “C 語言庫”列出了所有包含在libsys中的符號,標準的ANSI C
  和libc的執行程式,還有libc執行程式所需的全域性的資料符號。

注意: 參考的X86體系已經被改成了Intel體系。

   ________________________________________________________________


   1. 目標檔案(Object file)

   ________________________________________________________________


   ========================= 序言 =========================


第一部分描述了iABI的object檔案的格式, 被稱為ELF(Executable
and Linking Format). 在object檔案中有三種主要的型別。

* 一個可重定位(relocatable)檔案儲存著程式碼和適當的資料,用來和其他的
  object檔案一起來建立一個可執行檔案或者是一個共享檔案。
* 一個可執行(executable)檔案儲存著一個用來執行的程式;該檔案指出了
  exec(BA_OS)如何來建立程式程序映象。
* 一個共享object檔案儲存著程式碼和合適的資料,用來被下面的兩個連結器
  連結。第一個是連線編輯器[請參看ld(SD_CMD)],可以和其他的可重定位和
  共享object檔案來建立其他的object。第二個是動態連結器,聯合一個
  可執行檔案和其他的共享object檔案來建立一個程序映象。

一個object檔案被彙編器和聯接器建立, 想要在處理機上直接執行的object
檔案都是以二進位制來存放的。那些需要抽象機制的程式,比如象shell指令碼,
是不被接受的。

在介紹性的材料過後,第一部分主要圍繞著檔案的格式和關於如何建立程式。
第二部分也描述了object檔案的幾個組成部分,集中在執行程式所必須的資訊上。


     檔案格式

Object檔案參與程式的聯接(建立一個程式)和程式的執行(執行一個程式)。
object 檔案格式提供了一個方便有效的方法並行的視角看待檔案的內容,
在他們的活動中,反映出不同的需要。例 1-1圖顯示了一個object檔案的
組織圖。

+ 圖1-1: Object檔案格式

  Linking 視角                      Execution 視角
  ============                      ==============
  ELF header                        ELF header
  Program header table (optional)   Program header table
  Section 1                         Segment 1
  ...                               Segment 2
  Section n                         ...
  Section header table              Section header table (optional)

一個ELF頭在檔案的開始,儲存了路線圖(road map),描述了該檔案的組織情況。
sections儲存著object 檔案的資訊,從連線角度看:包括指令,資料,
符號表,重定位資訊等等。特別sections的描述會出項在以後的第一部分。
第二部分討論了段和從程式的執行角度看檔案。

假如一個程式頭表(program header table)存在,那麼它告訴系統如何來建立一
個程序的記憶體映象。被用來建立程序映象(執行一個程式)的檔案必須要有一個程
序頭表(program header table);可重定位檔案不需要這個頭表。一個
section頭表(section header table)包含了描述檔案sections的資訊。每個
section在這個表中有一個入口;每個入口給出了該section的名字,大小,
等等資訊。在聯接過程中的檔案必須有一個section頭表;其他object檔案可要
可不要這個section頭表。

注意: 雖然圖顯示出程式頭表立刻出現在一個ELF頭後,section頭表跟著其他
section部分出現,事實是的檔案是可以不同的。此外,sections和段(segments)
沒有特別的順序。只有ELF頭(elf header)是在檔案的固定位置。


資料表示
object檔案格式支援8位、32位不同的處理器。不過,它試圖努力的在更大
或更小的體系上執行。因此,object檔案描繪一些控制資料需要用與機器
無關的格式,使它儘可能的用一般的方法甄別object檔案和描述他們的內容。
在object檔案中剩餘的資料使用目標處理器的編碼方式,不管檔案是在哪臺
機子上建立的。

+ 圖 1-2: 32-Bit Data Types

  Name           Size Alignment   Purpose
  ====           ==== =========   =======
  Elf32_Addr      4       4       Unsigned program address
  Elf32_Half      2       2       Unsigned medium integer
  Elf32_Off       4       4       Unsigned file offset
  Elf32_Sword     4       4       Signed large integer
  Elf32_Word      4       4       Unsigned large integer
  unsigned char   1       1       Unsigned small integer

所有的object檔案格式定義的資料結構是自然大小(natural size),為相關
的型別調整指標。如果需要,資料結構中明確的包含了確保4位元組對齊的填
充欄位。來使結構大小是4的倍數。資料從檔案的開始也有適當的對齊。
例如,一個包含了Elf32_Addr成員的結構將會在檔案內對齊到4位元組的邊界上。

因為移植性的原因,ELF不使用位欄位。


   ========================== ELF Header ==========================


一些object檔案的控制結構能夠增長的,所以ELF頭包含了他們目前的大小。
假如object檔案格式改變,程式可能會碰到或大或小他們不希望的控制結構。
程式也有可能忽略額外(extra)的資訊。
對待來歷不明(missing)的資訊依靠上下文來解釋,假如擴充套件被定義,它們
將會被指定。

+ 圖 1-3: ELF Header

  #define EI_NIDENT       16

  typedef struct {
      unsigned char       e_ident[EI_NIDENT];
      Elf32_Half          e_type;
      Elf32_Half          e_machine;
      Elf32_Word          e_version;
      Elf32_Addr          e_entry;
      Elf32_Off           e_phoff;
      Elf32_Off           e_shoff;
      Elf32_Word          e_flags;
      Elf32_Half          e_ehsize;
      Elf32_Half          e_phentsize;
      Elf32_Half          e_phnum;
      Elf32_Half          e_shentsize;
      Elf32_Half          e_shnum;
      Elf32_Half          e_shstrndx;
  } Elf32_Ehdr;

* e_ident

  這個最初的欄位標示了該檔案為一個object檔案,提供了一個機器無關
  的資料,解釋檔案的內容。在下面的ELF的鑑別(ELF Identification)
  部分有更詳細的資訊。

* e_type

  該成員確定該object的型別。

               Name        Value  Meaning
               ====        =====  =======
               ET_NONE         0  No file type
               ET_REL          1  Relocatable file
               ET_EXEC         2  Executable file
               ET_DYN          3  Shared object file
       ET_CORE         4  Core file
       ET_LOPROC  0xff00  Processor-specific
       ET_HIPROC  0xffff  Processor-specific

  雖然CORE的檔案內容未被指明,型別ET_CORE是保留的。
  值從 ET_LOPROC 到 ET_HIPROC(包括ET_HIPROC)是為特殊的處理器保留的。
  如有需要,其他保留的變數將用在新的object檔案型別上。

* e_machine

  該成員變數指出了執行該程式需要的體系結構。

                    Name      Value  Meaning
            ====      =====  =======
                    EM_NONE       0  No machine
    EM_M32        1  AT&T WE 32100
                    EM_SPARC      2  SPARC
                    EM_386        3  Intel 80386
                    EM_68K        4  Motorola 68000
                    EM_88K        5  Motorola 88000
                    EM_860        7  Intel 80860
                    EM_MIPS       8  MIPS RS3000

  如有需要,其他保留的值將用到新的機器型別上。特殊處理器名使用機器名來
  區別他們。例如,下面將要被提到的成員flags使用字首EF_;在一臺EM_XYZ機器
  上,flag稱為WIDGET,那麼就稱為EF_XYZ_WIDGET。

* e_version

  這個成員確定object檔案的版本。

                 Name         Value  Meaning
                 ====         =====  =======
                 EV_NONE          0  Invalid version
EV_CURRENT       1  Current version

  值1表示原來的檔案格式;建立新版本就用>1的數。EV_CURRENT值(上面給
  出為1)如果需要將指向當前的版本號。

* e_entry

  該成員是系統第一個傳輸控制的虛擬地址,在那啟動程序。假如檔案沒有
  如何關聯的入口點,該成員就保持為0。

* e_phoff

  該成員保持著程式頭表(program header table)在檔案中的偏移量(以位元組計數)。
  假如該檔案沒有程式頭表的的話,該成員就保持為0。

* e_shoff

  該成員保持著section頭表(section header table)在檔案中的偏移量(以位元組
  計數)。假如該檔案沒有section頭表的的話,該成員就保持為0。

* e_flags

  該成員儲存著相關檔案的特定處理器標誌。
  flag的名字來自於EF_<machine>_<flag>。看下機器資訊“Machine Information”
  部分的flag的定義。

* e_ehsize

  該成員儲存著ELF頭大小(以位元組計數)。

* e_phentsize

  該成員儲存著在檔案的程式頭表(program header table)中一個入口的大小
  (以位元組計數)。所有的入口都是同樣的大小。

* e_phnum

  該成員儲存著在程式頭表中入口的個數。因此,e_phentsize和e_phnum
  的乘機就是表的大小(以位元組計數).假如沒有程式頭表(program header table),
  e_phnum變數為0。

* e_shentsize

  該成員儲存著section頭的大小(以位元組計數)。一個section頭是在section
  頭表(section header table)的一個入口;所有的入口都是同樣的大小。

* e_shnum

  該成員儲存著在section header table中的入口數目。因此,e_shentsize和
  e_shnum的乘積就是section頭表的大小(以位元組計數)。
  假如檔案沒有section頭表,e_shnum值為0。

* e_shstrndx

  該成員儲存著跟section名字字元表相關入口的section頭表(section header
  table)索引。假如檔案中沒有section名字字元表,該變數值為SHN_UNDEF。
  更詳細的資訊 看下面“Sections”和字串表(“String Table”) 。


  ELF 鑑別(Identification)

在上面提到的,ELF提供了一個object檔案的框架結構來支援多種處理機,多
樣的資料編碼方式,多種機器型別。為了支援這個object檔案家族,最初的幾
個位元組指定用來說明如何解釋該檔案,獨立於處理器,與檔案剩下的內容無關。

ELF頭(也就是object檔案)最初的幾個位元組與成員e_ident相一致。

+ 圖 1-4: e_ident[] Identification Indexes

  Name           Value  Purpose
  ====           =====  =======
  EI_MAG0      0  File identification
  EI_MAG1      1  File identification
  EI_MAG2      2  File identification
  EI_MAG3      3  File identification
  EI_CLASS      4  File class
  EI_DATA      5  Data encoding
  EI_VERSION      6  File version
  EI_PAD      7  Start of padding bytes
  EI_NIDENT     16  Size of e_ident[]

通過索引訪問位元組,以下的變數被定義。

* EI_MAG0 to EI_MAG3

  檔案的前4個字元儲存著一個魔術數(magic number),用來確定該檔案是否
  為ELF的目標檔案。

                  Name       Value  Position
                  ====       =====  ========
  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],用來確定檔案的型別或者說是能力。

                 Name           Value  Meaning
                 ====           =====  =======
                 ELFCLASSNONE       0  Invalid class
                 ELFCLASS32         1  32-bit objects
ELFCLASS64         2  64-bit objects

  檔案格式被設計成在不同大小機器中可移植的,在小型機上的不用大型機上
  的尺寸。型別ELFCLASS32支援虛擬地址空間最大可達4GB的機器;使用上面
  定義過的基本型別。

  型別ELFCLASS64為64位體系的機器保留。它的出現表明了object檔案可能
  改變,但是64位的格式還沒有被定義。如果需要,其他型別將被定義,會
  有不同的型別和不同大小的資料尺寸。

* EI_DATA

  位元組e_ident[EI_DATA]指定了在object檔案中特定處理器資料的編碼
  方式。當前定義了以下編碼方式。

             Name           Value  Meaning
             ====           =====  =======
     ELFDATANONE        0  Invalid data encoding
             ELFDATA2LSB        1  See below
             ELFDATA2MSB        2  See below

  更多的關於編碼的資訊出現在下面。其他值保留,將被分配一個新的編碼
  方式,當然如果必要的話。

* EI_VERSION

  位元組e_ident[EI_VERSION]表明了ELF頭的版本號。
  現在這個變數的是一定要設為EV_CURRENT,作為上面e_version的解釋。

* EI_PAD

  該變數標識了在e_ident中開始的未使用的位元組。那些位元組保留並被設定為
  0;程式把它們從object 檔案中讀出但應該忽略。假如當前未被使用的位元組
  有了新的定義,EI_PAD變數將來會被改變。

一個檔案的資料編碼指出瞭如何來解釋一個基本的object檔案。在上述的
描述中,型別ELFCLAS32檔案使用佔用1,2和4位元組的目標檔案。下面定義的
編碼方式,用下面的圖來描繪。資料出現在左上角。


ELFDATA2LSB編碼指定了2的補數值。
最小有意義的位元組佔有最低的地址。

+ 圖1-5: Data Encoding ELFDATA2LSB

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

ELFDATA2LSB編碼指定了2的補數值。
最大有意義的位元組佔有最低的地址。

+ 圖1-6: Data Encoding ELFDATA2MSB

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


機器資訊

為了確定檔案,32位Intel體系結構的需要以下的變數。

+ 圖1-7: 32-bit Intel Architecture Identification, e_ident

  Position           Value
  ========           =====
  e_ident[EI_CLASS]  ELFCLASS32
  e_ident[EI_DATA]   ELFDATA2LSB

處理器確認ELF頭裡的e_machine成員,該成員必須為EM_386。

ELF報頭裡的e_flags成員儲存了和檔案相關的位標記。32位Intel體系上未
定義該標記;所以這個成員應該為0;


   =========================== Sections ===========================


一個object檔案的section header table可以讓我們定位所有的sections。
section header table是個Elf32_Shdr結構的陣列(下面描述)。一個section
報頭表(section header table)索引是這個陣列的下標。ELF header table
的e_shoff成員給出了section報頭表的偏移量(從檔案開始的計數)。e_shnum
告訴我們section報頭表中包含了多少個入口;e_shentsize 給出了每個
入口的大小。

一些section報頭表索引是保留的;那些特別的索引在一個object檔案中
將沒有與之對應sections。

+ 圖1-8: Special Section Indexes

  Name             Value
  ====             =====
  SHN_UNDEF            0
  SHN_LORESERVE   0xff00
  SHN_LOPROC      0xff00
  SHN_HIPROC      0xff1f
  SHN_ABS         0xfff1
  SHN_COMMON      0xfff2
  SHN_HIRESERVE   0xffff

* SHN_UNDEF

  該值表明沒有定義,缺少,不相關的或者其他涉及到的無意義的section。
  例如,標號“defined”相對於section索引號SHN_UNDEF是一個沒有被
  定義的標號。

注意: 雖然索引0保留作為未定義的值,section報頭表包含了一個索引0的
入口。因此,假如ELF報頭說一個檔案的section報頭表中有6個section入口
的話,e_shnum的值應該是從0到5。最初的入口的內容以後在這個section中
被指定。

* SHN_LORESERVE

  該值指定保留的索引範圍的最小值。

* SHN_LOPROC through SHN_HIPROC

  該值包含了特定處理器語意的保留範圍。


* SHN_ABS

  該變數是相對於相應參考的絕對地址。
  例如,section號的標號是絕對地址,不被重定位影響。

* SHN_COMMON

  該section的標號是一個公共(common)的標號,就象FORTRAN COMMON
  或者不允許的C擴充套件變數。

* SHN_HIRESERVE

  該值指定保留的索引範圍的上限。系統保留的索引值是從SHN_LORESERVE
  到SHN_HIRESERVE;該變數不涉及到section報頭表(section header table)。
  因此,section報頭表不為保留的索引值包含入口。

sections包含了在一個object檔案中的所有資訊,除了ELF報頭,程式報頭
表(program header table),和section報頭表(section header table)。
此外,object檔案的sections滿足幾天條件:

* 每個在object檔案中的section都有自己的一個section的報頭來描述它。
  section頭可能存在但section可以不存在。
* 每個section在檔案中都佔有一個連續順序的空間(但可能是空的)。
* 檔案中的Sections不可能重複。檔案中沒有一個位元組既在這個section中
  又在另外的一個section中。
* object檔案可以有"非活動的"空間。不同的報頭和sections可以不覆蓋到
  object檔案中的每個位元組。"非活動"資料內容是未指定的。

一個section頭有如下的結構。

+ 圖1-9: Section Header

  typedef struct {
      Elf32_Word sh_name;
      Elf32_Word sh_type;
      Elf32_Word sh_flags;
      Elf32_Addr sh_addr;
      Elf32_Off sh_offset;
      Elf32_Word sh_size;
      Elf32_Word sh_link;
      Elf32_Word sh_info;
      Elf32_Word sh_addralign;
      Elf32_Word sh_entsize;
  } Elf32_Shdr;

* sh_name

  該成員指定了這個section的名字。它的值是section報頭字元表section的
  索引。[看以下的“String Table”], 以NULL空字元結束。

* sh_type

  該成員把sections按內容和意義分類。section的型別和他們的描述在下面。

* sh_flags

  sections支援位的標記,用來描述多個屬性。 
  Flag定義出現在下面。

* sh_addr

  假如該section將出現在程序的記憶體映象空間裡,該成員給出了一個該section
  在記憶體中的位置。否則,該變數為0。

* sh_offset

  該成員變數給出了該section的位元組偏移量(從檔案開始計數)。SHT_NOBITS
  型別的section(下面討論)在檔案中不佔空間,它的sh_offset成員定位在
  檔案中的概念上的位置。

* sh_size
  該成員給你了section的位元組大小。除非這個section的型別為SHT_NOBITS,
  否則該section將在檔案中將佔有sh_size個位元組。SHT_NOBITS型別的section
  可能為非0的大小,但是不佔檔案空間。

* sh_link

  該成員儲存了一個section報頭表的索引連線,它的解釋依靠該section的
  型別。以下一個表描述了這些值。

* sh_info

  該成員儲存著額外的資訊,它的解釋依靠該section的型別。以下一個表描
  述了這些值。

* sh_addralign

  一些sections有地址對齊的約束。例如,假如一個section儲存著雙字,系統
  就必須確定整個section是否雙字對齊。所以sh_addr的值以sh_addralign的值
  作為模,那麼一定為0。當然的,僅僅0和正的2的次方是允許的。值0和1意味
  著該section沒有對齊要求。

* sh_entsize

  一些sections儲存著一張固定大小入口的表,就象符號表。對於這樣一個
  section來說,該成員給出了每個入口的位元組大小。如果該section沒有
  儲存著一張固定大小入口的表,該成員就為0。

section頭成員sh_type指出了section的語意。

+ 圖1-10: Section Types, sh_type

  Name               Value
  ====               =====
  SHT_NULL               0
  SHT_PROGBITS           1
  SHT_SYMTAB             2
  SHT_STRTAB          3
  SHT_RELA          4
  SHT_HASH          5
  SHT_DYNAMIC            6
  SHT_NOTE          7
  SHT_NOBITS          8
  SHT_REL          9
  SHT_SHLIB             10
  SHT_DYNSYM            11
  SHT_LOPROC    0x70000000
  SHT_HIPROC    0x7fffffff
  SHT_LOUSER    0x80000000
  SHT_HIUSER    0xffffffff

* SHT_NULL

  該值表明該section頭是無效的;它沒有相關的section。
  該section的其他成員的值都是未定義的。

* SHT_PROGBITS

  該section儲存被程式定義了的一些資訊,它的格式和意義取決於程式本身。

* SHT_SYMTAB and SHT_DYNSYM

  那些sections儲存著一個符號表(symbol table)。一般情況下,一個
  object檔案每個型別section僅有一個,但是,在將來,這個約束可能被放寬。
  典型的是,SHT_SYMTAB為聯結器提供標號,當然它也有可能被動態連線時使用。
  作為一個完整的符號表,它可能包含了一些動態連線時不需要的標號。
  因此,一個object檔案可能也包含了一個SHT_DYNSYM的section,它儲存著
  一個動態連線時所需最小的標號集合來節省空間。
  看下面符號表“Symbol Table”的細節。

* SHT_STRTAB

  該section儲存著一個字串表。一個object檔案可以包含多個字串表的
  section。看下面字串表“String Table”的細節。

* SHT_RELA

  該section儲存著具有明確加數的重定位入口。就象object檔案32位的
  Elf32_Rela型別。一個object檔案可能有多個重定位的sections。具體細節
  看重定位``Relocation''部分。

* SHT_HASH

  該標號儲存著一個標號的雜湊(hash)表。所有的參與動態連線的object
  一定包含了一個標號雜湊表(hash table)。當前的,一個object檔案
  可能只有一個雜湊表。詳細細節看第二部分的雜湊表"Hash Table"。

* SHT_DYNAMIC

  該section儲存著動態連線的資訊。當前的,一個object可能只有一個動態
  的section,但是,將來這個限制可能被取消。詳細細節看第二部分的動態
  section(“Dynamic Section”)。

* SHT_NOTE

  該section儲存著其他的一些標誌檔案的資訊。詳細細節看第二部分的“Note
  Section” 。

* SHT_NOBITS

  該型別的section在檔案中不佔空間,但是類似SHT_PROGBITS。儘管該section
  不包含位元組,sh_offset成員包含了概念上的檔案偏移量。

* SHT_REL

  該section儲存著具有明確加數的重定位的入口。
  就象object檔案32位型別Elf32_Rel型別。一個object檔案可能有多個
  重定位的sections。具體細節看重定位``Relocation''部分。

* SHT_SHLIB

  該section型別保留但語意沒有指明。包含這個型別的section的程式
  是不符合ABI的。

* SHT_LOPROC through SHT_HIPROC

  在這範圍之間的值為特定處理器語意保留的。

* SHT_LOUSER

  該變數為應用程式保留的索引範圍的最小邊界。

* SHT_HIUSER

  該變數為應用程式保留的索引範圍的最大邊界。在SHT_LOUSER和HIUSER的
  section型別可能被應用程式使用,這和當前或者將來系統定義的section
  型別是不矛盾的。

其他section型別的變數是保留的。前面提到過,索引0(SHN_UNDEF)的section
頭存在的,甚至索引標記的是未定義的section引用。這個入口儲存著以下的
資訊。

+ 圖1-11: Section Header Table Entry: Index 0

  Name            Value    Note
  ====            =====    ====
  sh_name           0      No name
  sh_type        SHT_NULL  Inactive
  sh_flags          0      No flags
  sh_addr           0      No address
  sh_offset         0      No file offset
  sh_size           0      No size
  sh_link SHN_UNDEF  No link information
  sh_info     0      No auxiliary information
  sh_addralign     0      No alignment
  sh_entsize        0      No entries

一個section報頭(section header table)的sh_flags成員儲存著1位標記,
用來描述section的屬性。以下是定義的值;其他的值保留。

+ 圖1-12: Section Attribute Flags, sh_flags

  Name                Value
  ====                =====
  SHF_WRITE             0x1
  SHF_ALLOC             0x2
  SHF_EXECINSTR         0x4
  SHF_MASKPROC   0xf0000000

假如在sh_flags中的一個標記位被設定,該section相應的屬性也被開啟。
否則,該屬性沒有被應用。未明的屬性就設為0。

* SHF_WRITE

  該section包含了在程序執行過程中可被寫的資料。

* SHF_ALLOC

  該section在程序執行過程中佔據著記憶體。一些控制section不存在一個
  object檔案的記憶體映象中;對於這些sections,這個屬性應該關掉。

* SHF_EXECINSTR

  該section包含了可執行的機器指令。

* SHF_MASKPROC

  所有的包括在這掩碼中的位為特定處理語意保留的。

在section報頭中,兩個成員sh_link和sh_info的解釋依靠該section的型別。

+ 圖1-13: sh_link and sh_info Interpretation

  sh_type      sh_link                        sh_info
  =======      =======                        =======
  SHT_DYNAMIC  The section header index of    0
               the string table used by
               entries in the section.
  SHT_HASH     The section header index of    0
               the symbol table to which the
               hash table applies.
  SHT_REL,     The section header index of    The section header index of
  SHT_RELA     the associated symbol table.   the section to which the
                                              relocation applies.
  SHT_SYMTAB,  The section header index of    One greater than the symbol
  SHT_DYNSYM   the associated string table.   table index of the last local
                                              symbol (binding STB_LOCAL).
  other        SHN_UNDEF                      0


Special Sections   特殊的Sections

不同的sections儲存著程式和控制資訊。下面列表中的section被系統使用,
指示了型別和屬性。

+ 圖1-14: Special Sections

  Name         Type           Attributes
  ====         ====           ==========
  .bss         SHT_NOBITS     SHF_ALLOC+SHF_WRITE
  .comment     SHT_PROGBITS   none
  .data        SHT_PROGBITS   SHF_ALLOC+SHF_WRITE
  .data1       SHT_PROGBITS   SHF_ALLOC+SHF_WRITE
  .debug       SHT_PROGBITS   none
  .dynamic     SHT_DYNAMIC    see below
  .dynstr      SHT_STRTAB     SHF_ALLOC
  .dynsym      SHT_DYNSYM     SHF_ALLOC
  .fini        SHT_PROGBITS   SHF_ALLOC+SHF_EXECINSTR
  .got         SHT_PROGBITS   see below
  .hash        SHT_HASH       SHF_ALLOC
  .init        SHT_PROGBITS   SHF_ALLOC+SHF_EXECINSTR
  .interp      SHT_PROGBITS   see below
  .line        SHT_PROGBITS   none
  .note        SHT_NOTE       none
  .plt         SHT_PROGBITS   see below
  .rel<name>   SHT_REL        see below
  .rela<name>  SHT_RELA       see below
  .rodata      SHT_PROGBITS   SHF_ALLOC
  .rodata1     SHT_PROGBITS   SHF_ALLOC
  .shstrtab    SHT_STRTAB     none
  .strtab      SHT_STRTAB     see below
  .symtab      SHT_SYMTAB     see below
  .text        SHT_PROGBITS   SHF_ALLOC+SHF_EXECINSTR

* .bss

  該sectiopn儲存著未初始化的資料,這些資料存在於程式記憶體映象中。
  通過定義,當程式開始執行,系統初始化那些資料為0。該section不佔
  檔案空間,正如它的section型別SHT_NOBITS指示的一樣。
 
* .comment

   該section儲存著版本控制資訊。

* .data and .data1
  這些sections儲存著初始化了的資料,那些資料存在於程式記憶體映象中。

* .debug

  該section儲存著為標號除錯的資訊。該內容是未指明的。

* .dynamic

  該section儲存著動態連線的資訊。該section的屬性將包括SHF_ALLOC位。
  是否需要SHF_WRITE是跟處理器有關。第二部分有更詳細的資訊。

* .dynstr

  該section儲存著動態連線時需要的字串,一般情況下,名字字串關聯著
  符號表的入口。第二部分有更詳細的資訊。

* .dynsym

  該section儲存著動態符號表,如“Symbol Table”的描述。第二部分有更
  詳細的資訊。
 
* .fini

  該section儲存著可執行指令,它構成了程序的終止程式碼。
  因此,當一個程式正常退出時,系統安排執行這個section的中的程式碼。

* .got

  該section儲存著全域性的偏移量表。看第一部分的“Special Sections”和
  第二部分的“Global Offset Table”獲得更多的資訊。

* .hash

  該section儲存著一個標號的雜湊表。看第二部分的“Hash Table”獲得更多
  的資訊。

* .init

  該section儲存著可執行指令,它構成了程序的初始化程式碼。
  因此,當一個程式開始執行時,在main函式被呼叫之前(c語言稱為main),
  系統安排執行這個section的中的程式碼。

* .interp

  該section儲存了程式的解釋程式(interpreter)的路徑。假如在這個section
  中有一個可裝載的段,那麼該section的屬性的SHF_ALLOC位將被設定;否則,
  該位不會被設定。看第二部分獲得更多的資訊。

* .line

  該section包含編輯字元的行數資訊,它描述源程式與機器程式碼之間的對於
  關係。該section內容不明確的。

* .note

  該section儲存一些資訊,使用“Note Section”(在第二部分)中提到的格式。

* .plt

  該section儲存著過程連線表(Procedure Linkage Table)。看第一部分的
  ``Special Sections''和第二部分的“Procedure Linkage Table”。

* .rel<name> and .rela<name>

  這些section儲存著重定位的資訊,看下面的``Relocation''描述。
  假如檔案包含了一個可裝載的段,並且這個段是重定位的,那麼該section的
  屬性將設定SHF_ALLOC位;否則該位被關閉。按照慣例,<name>由重定位適用
  的section來提供。因此,一個重定位的section適用的是.text,那麼該名字
  就為.rel.text或者是.rela.text。

* .rodata and .rodata1

  這些section儲存著只讀資料,在程序映象中構造不可寫的段。看第二部分的
  ``Program Header''獲得更多的資料。

* .shstrtab

  該section儲存著section名稱。

* .strtab

  該section儲存著字串,一般地,描述名字的字串和一個標號的入口相關
  聯。假如檔案有一個可裝載的段,並且該段包括了符號字串表,那麼section
  的SHF_ALLOC屬性將被設定;否則不設定。

* .symtab

  該section儲存著一個符號表,正如在這個section裡``Symbol Table''的
  描述。假如檔案有一個可裝載的段,並且該段包含了符號表,那麼section
  的SHF_ALLOC屬性將被設定;否則不設定。

* .text

  該section儲存著程式的``text''或者說是可執行指令。


字首是點(.)的section名是系統保留的,儘管應用程式可以用那些保留的
section名。應用程式可以使用不帶字首的名字以避免和系統的sections
衝突。object檔案格式可以讓一個定義的section部分不出現在上面的列
表中。一個object檔案可以有多個同樣名字的section。

為處理器體系保留的section名的形成是通過置換成一個體系名的縮寫。
該名字應該取自體系名,e_machine使用的就是。例如,.Foo.psect就是
在FOO體系上定義的名字。

現存的副檔名是歷史遺留下來的。

       Pre-existing Extensions
       =======================
.sdata     .tdesc
.sbss      .lit4
.lit8      .reginfo
.gptab     .liblist
.conflict


   =================== 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

  該陣列元素(如果出現),指定了程式頭表本身的位置和大小(包括在檔案中
  和在該程式