ELF 之 Program Loading 教學文件, #2: Program Header Table

jollen 發表於 March 8, 2007 4:19 PM

了解系統行為的研究方法,我認為有效的步驟應分成三個階段來進行。

初次入門:一開始進行研究時,因為對於系統的基本觀念還不夠完備,因此「學中做、做中學」成了最有效率的入門方式,透過「概念的實作」與「實作讀到的概念」的過程,最可以幫助我們在短時間內掌握重要的核心知識。以 ELF 專欄為例,在入門時期,我用了 loader 0.1~0.5 共 5 個小範例來陳述 ELF 的格式以及 section 的觀念。

掌握觀念:首先發表一個自己的看法。「將所有理論或概念全部動手實作一遍」,在我看來,並不是很有效率的辦法,這種做法應當很有幫助,但是卻會延緩學習速度,因此,這個時 期的重點如果是在「通盤掌握整體的重要關鍵」,那麼「善用工具來做分析」自然是最有成 效的方式。以「工具來操作並驗證觀念」是建議的做法,另外一個理由是,這種做法比較能貼近實務面。在「ELF 之 Program Loading 教學文件」的日記裡,我將會以此做法來分享教學文件。

思考與研究:這個階段有點像是「實驗室」的做法,在這裡不再贅述。

Program Header Table

這裡有幾個重要的觀念:

1. Program header table 是程式要能執行的重要資訊,program header table 紀錄 ELF image 裡的 'segment' 分佈,請參考 Jollen's Blog「ELF 之 Program Loading 教學文件, #1: Segment 的觀念」的說明。

2. 對 dynamic loader/linker 來說,ELF 的 'section' 是通透性的(transparent),也就是在整個載入的過程裡,dynamic loader/linker 並不會知道他所載入的 'segment' 與實際的 section 有何關係。整個載入過程只讀取 program header table 所紀錄的 'segment'。

3. 也就是說,section header table 在此時期是用不到的,就算沒有 section header table 也不影響程式載入與執行;這個觀念可參考 Jollen's Blog「「Truncate It」小技倆的原始碼與原理」。

使用 readelf 來觀察執行檔的 program header table:

# readelf -l hello

Elf file type is EXEC (Executable file)
Entry point 0x8048278
There are 6 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x000c0 0x000c0 R E 0x4
  INTERP         0x0000f4 0x080480f4 0x080480f4 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x00408 0x00408 R E 0x1000
  LOAD           0x000408 0x08049408 0x08049408 0x00100 0x00104 RW  0x1000
  DYNAMIC        0x000414 0x08049414 0x08049414 0x000c8 0x000c8 RW  0x4
  NOTE           0x000108 0x08048108 0x08048108 0x00020 0x00020 R   0x4

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame
   03     .data .dynamic .ctors .dtors .jcr .got .bss
   04     .dynamic
   05     .note.ABI-tag

Program header table 的 data structure 如下(/usr/include/elf.h):

/* Program segment header.  */

typedef struct
{
  Elf32_Word    p_type;                 /* Segment type */
  Elf32_Off     p_offset;               /* Segment file offset */
  Elf32_Addr    p_vaddr;                /* Segment virtual address */
  Elf32_Addr    p_paddr;                /* Segment physical address */
  Elf32_Word    p_filesz;               /* Segment size in file */
  Elf32_Word    p_memsz;                /* Segment size in memory */
  Elf32_Word    p_flags;                /* Segment flags */
  Elf32_Word    p_align;                /* Segment alignment */
} Elf32_Phdr;

'readelf' 的列表與 program header table 的 data structure 相符,每個 field 所紀錄的資訊可參考註解說明,或是參照完整的 System V ABI 文件。

幾個重要資訊說明如下。

1. Segment 的個數與大小紀錄在 ELF 檔頭裡:

# readelf -h hello
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048278
  Start of program headers:          52 (bytes into file)
  Start of section headers:          1784 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         6
  Size of section headers:           40 (bytes)
  Number of section headers:         25
  Section header string table index: 24

2. Program header table 的 p_type 欄位必須先進行研究(/usr/include/elf.h):

/* Legal values for p_type (segment type).  */

#define PT_NULL         0               /* Program header table entry unused */
#define PT_LOAD         1               /* Loadable program segment */
#define PT_DYNAMIC      2               /* Dynamic linking information */
#define PT_INTERP       3               /* Program interpreter */
#define PT_NOTE         4               /* Auxiliary information */
#define PT_SHLIB        5               /* Reserved */
#define PT_PHDR         6               /* Entry for header table itself */
#define PT_TLS          7               /* Thread-local storage segment */
#define PT_NUM          8               /* Number of defined types */
#define PT_LOOS         0x60000000      /* Start of OS-specific */
#define PT_GNU_EH_FRAME 0x6474e550      /* GCC .eh_frame_hdr segment */
#define PT_LOSUNW       0x6ffffffa
#define PT_SUNWBSS      0x6ffffffa      /* Sun Specific segment */
#define PT_SUNWSTACK    0x6ffffffb      /* Stack segment */
#define PT_HISUNW       0x6fffffff
#define PT_HIOS         0x6fffffff      /* End of OS-specific */
#define PT_LOPROC       0x70000000      /* Start of processor-specific */
#define PT_HIPROC       0x7fffffff      /* End of processor-specific */

標示紅色的部份是我們在 'readelf -l' 列表中所看到的 type。

3. p_flags 的意義也要先行了解(/usr/include/elf.h):

/* Legal values for p_flags (segment flags).  */

#define PF_X            (1 << 0)        /* Segment is executable */
#define PF_W            (1 << 1)        /* Segment is writable */
#define PF_R            (1 << 2)        /* Segment is readable */
#define PF_MASKOS       0x0ff00000      /* OS-specific */
#define PF_MASKPROC     0xf0000000      /* Processor-specific */

Segment Permissions

Segment permission 是 p_flags 的組合,在 'readelf -l' 列表中則是相對應於 Flg 一欄。

Segment Contents

Segment 由哪些 section 所構成,同樣可由 'readelf -l' 列表的「Section to Segment mapping」部份觀察。

小結

以下表格整理目前為止所得到的結論。

Segment Sections Type Flg
00   PHDR PF_R + PF_X
01 .interp INTERP PF_R
02 .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame LOAD PF_R + PF_X
03 .data .dynamic .ctors .dtors .jcr .got .bss LOAD PF_R + PF_W
04 .dynamic DYNAMIC PF_R + PF_W
05 .note.ABI-tag NOTE PF_R

下則日記將會針對 segment type 所討論。

讀者留言 (0)

留言功能維護中。將於近日重新開放。

連絡作者

Jollen Chen,Moko365(仕橙3G教室)講師,熱愛研究 Linux 與 Android 技術。曾為 Motorola、HTC、Foxconn、LG、OPPO、騰迅、廣達電腦、緯創、仁寶等超過 50 家企業講授課程。目前在 MokoVersity 擔任軟體工程師,撰寫 Node.js 程式,也在幾家科技廠兼任 Android Framework 研發顧問。您可透過電子郵件 <jollen (at) jollen (dot) org> 或這裡與我連絡。