Compiling#
在過去,計算機概論課程中,講者及教材上往往會這麼解釋高階語言
到執行檔
的過程:
但事實上,該過程可以被拆解成更詳細的步驟,如下圖:
東西突然有點多,先大致說明一下表示方式:
…
(虛線):使用或透過什麼工具。→
(箭頭):所產生的結果。顏色表示對象:
- █ 紫色區域:編譯過程的前後,也表示開頭與結尾。
- █ 黃色區域:所使用的工具,通常工具本身也是程式。
- █ 藍色區域:檔案的各個階段。
這兩項會再特別提出說明:
- █ 綠色區域:執行檔。
- █ 橘色區域:Loader (載入器),用於將程式傾印到記憶體上的程式。
先從最左上角的 █ 紫色區域 開始觀看:
1. 撰寫 Source Code#
Developer (開發者)
透過Editor (編輯器)
撰寫程式(高階語言)
,此刻的檔案為Source Code (原始碼)
。該步驟應該撰寫過程式的都不陌生。
2. 產生優化後的 Source Code#
接著當你開始嘗試編譯該檔案時,其實當下並非直接編譯,而是會先透過一個叫做「Preprocessor (預處理器)
」的程式對 Source Code 進行優化,刪除原始碼中的Comment (註解)
(程式執行是不需要註解的),並將其中所使用到的Library (函式庫)
檔案路徑給標記上去,方便後續連結
程式。而透過Preprocessor
處理後所產生的檔案稱為「Expanded Source Code
」,其檔案內容大概長這個樣子(此刻對觀看已經非常不友善了):
3. 編譯成 Assembly Language#
接著就透過Compiler (編譯器)
對優化過的原始檔進行Compile (編譯)
,之後產出Assembly Language (組合語言)
檔案。其檔案內容大致長這樣(對於只寫過高階語言的朋友來說,應該已經進到另外一個領域了):
4. 組譯成 Object File#
已經有了Assembly Language (組合語言)
檔案,那麼就可以透過Assembler (組譯器)
翻譯成Machine Code (機器語言)
,而在Assemble (組譯)
成Executable (執行檔)
之前,其實還可以分成兩個步驟。在開始組譯後,會先產生出「Object File (目的檔)
」,該檔案已經是Binary
了,而Object File
中會標記著各種所需要的Library (函式庫)
位置,此刻的Library
還並未被整合成一個檔案。除此之外,該檔案裡頭還會存放前一章提到檔案中的各種區段,包括.text
、.data
、.rodata
、.bss
等,但在這邊,正確來說應該稱呼它們為「Sections
」,而不是「Segments
」,這部份稍後再提,只要先知道這是指一樣的東西即可。此刻檔案中已經包含各個Section
的資料,也就是說程式主體已經都被轉成Binary
了,距離完成只差最後一步,就是將Library
也整合進來。
5. 連結成 Executable#
為了完成最後整合Library
的步驟,因此就有了Linker (連結器)
。舉例在Ubuntu 20
OS 下,沒意外的話Linker
位置會在「/lib/ld-linux.so.2
」,而這只是一個捷徑,該捷徑所指向的檔案才是真正的Linker
,但每台裝置所使用的 Linker 來源或是版本可能不同,例如我的捷徑連結至「/lib/i386-linux-gnu/ld-2.31.so
」。到了編譯的最後一步,將Object File
與那些Library
統整成一個可執行檔,當然Object File
可能包含數個,一樣可以進行合併,最後產生出Executable (執行檔)
。
6. Loader#
到目前為止,Executable
已經被生產出來。
儘管已經在第五步驟將Library
給連結了,但… 並不代表程式全部都會被合併。
在一般廣義上的Link (連結)
可以被分成兩種來看,一種是「Static Linking (靜態連結)
」,另外一種則是「Dynamic Linking (動態連結)
」。這主要關聯到Library
中的「Statically-Linked Library (靜態連結函式庫)
」及「Dynamic-Link Library (動態連結函式庫)
」:
Statically-Linked Library (靜態連結函式庫)
- 在
Linux
OS 上,靜態庫本身包含著ELF
檔案,但自身並非是ELF
檔案,通常為「.a
」副檔名,表示「Archive
」,如同字面上的意思,如果 ELF 是書的話,那他就是裝書的書櫃。其內容多半較為簡單,無複雜操作,過於複雜、龐大的程式更適合以動態連結
的方式進行,以避免最終產出的執行檔過大。 - 在透過
Linker
連結的過程中會被合併至Object File
當中。
- 在
Dynamic-Link Library (動態連結函式庫)
- 與
靜態連結函式庫
最大的不同在於,動態連結函式庫
並不會被直接整合到執行檔中,Um… 至少在Linking
階段不會。 - 由於不會預先被整合進執行檔,因此要求各個環境之間需擁有相同函式庫才能夠在開啟執行檔時被預期的載入相關程式碼。
Windows
OS 中常見的.dll
檔便是「Dynamic-Link Library
」的縮寫。
- 與
從上述內容可以發現,動態庫在執行檔生成的連結過程中,僅僅是被作為標記
使用,而真正的程式碼則是在程式啟動時才被載入
,並且若開發人員使用的動態函式庫在使用者環境上沒有或是版本有著差異,那將可能無法順利執行程式。
其實說到這已經很明顯了,Loader
指的正是用於在程式啟動時,協助載入動態庫的程式。
至於… 前面提到的「ELF
」是什麼?
What is ELF?#
「ELF
」為一種檔案格式,全稱為「Executable and Linkable Format (可執行與可連結格式)
」,在過去也被稱作「Extensible Linking Format
」,是一種Unix
的執行檔格式。
ELF
檔案主要有三種類型,分別是「Executable (執行檔)
」、「Object File (目的檔)
」、「Shared Object (動態鏈接庫、共享庫)
」。
Executable
:在前面已經提到了,這也應該是大家最為熟悉的。在Linux
OS 下副檔名多半為.out
、無附檔名
等。Object File
:又稱作「Relocatable
」,前面提到時沒說到,但Object File
也是ELF
檔案。在Linux
OS 下副檔名多半為.o
。Shared Object
:又稱作「Shared Library
」,其實與Executable
大致相同,最大差異僅僅是Shared Object
可用於共享,時常用於程式動態連結階段被Loader
連結的子程式,而Executable
則通常不被當子程式使用,除此之外… 似乎是一樣的,總之就… 都是執行檔
。在Linux
OS 下副檔名多半為.so
。
而ELF
檔案是可以被大致分成四塊區域來看的,分別是:
ELF Header#
作為檔頭
當然就是紀錄著檔案的一些基本資訊啦!
包含標示著該檔案是 ELF;是什麼類型的 ELF;為幾位元系統執行檔;版本;記憶體資料存放方式(Little-or-Big Endian)… 等。
當然也包括標注Program Header
與Section Header Table
在檔案中的位置,這麼一來就可以透過該檔頭定位所有區塊了。
舉例,我有一執行檔名為「hello-word
」,透過指令「readelf -h
hello-world」即可查看該檔案的Header
:
1ELF Header:
2 Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
3 Class: ELF64
4 Data: 2's complement, little endian
5 Version: 1 (current)
6 OS/ABI: UNIX - System V
7 ABI Version: 0
8 Type: DYN (Shared object file)
9 Machine: Advanced Micro Devices X86-64
10 Version: 0x1
11 Entry point address: 0x10a0
12 Start of program headers: 64 (bytes into file)
13 Start of section headers: 12616 (bytes into file)
14 Flags: 0x0
15 Size of this header: 64 (bytes)
16 Size of program headers: 56 (bytes)
17 Number of program headers: 13
18 Size of section headers: 64 (bytes)
19 Number of section headers: 29
20 Section header string table index: 28
這些資訊倒也不用全都了解,但基本的要先知道就好。
從圖中可以看見,Header
中有一個叫做Magic
的數字串,又被稱作「Magic Number (魔法數字)
」,佔了檔案最開頭的16 Bytes
,Magic Number
對於檔案辨識是重要的一個指標,這些數字也被統稱「EI_IDENT
」。
Magic:
7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
0x00
~0x03
(EI_MAG0
~EI_MAG3
):開頭的「7f
」同時也是執行檔中的第一個字節,表記著該檔案是可執行檔。接著的「45
」、「4c
」、「46
」表示著格式,也就是「ELF
」三個英文字所個別對應的ASCII Code
。0x04
(EI_CLASS
):。欲執行檔案的系統位元,目前大多是使用0x02
,也就是64-Bits
,而0x01
表示32-Bits
。(為什麼沒有16-Bits
呢?詳情可以看這篇文章:「linux - Why there is no 16-bit information in ELF magic number? - Stack Overflow」)0x05
(EI_DATA
):資料於記憶體中的存放格式,大多數為0x01
,代表Little-Endian
,目前大多數系統都是採用這種格式的。而0x02
則表示Big-Endian
(關於 Big-Endian 更多資訊可以看這篇文章:「Big Endian 與他們的現狀 - HackMD」)。若是你還不懂Big-Endian
及Little-Endian
是什麼的話,也不用擔心,之後該系列文章提到時會再詳細說明。0x06
(EI_VERSION
):標示了ELF
版本,但就目前應該只會看見0x01
,也就是第一版本,未來可能會繼續更新也說不定。0x07
(EI_OSABI
):標記著「OS Application Binary Interface
」,也就是該 ELF 檔案於何統作業系統上運作(至於什麼是ABI
,可以看維基百科解釋:應用二進位介面 - Wiki)。而例子中該欄位值為「0x00
」,表示未指定。0x08
(EI_ABIVERSION
):標示「Application Binary Interface Version
」,意思是該 ELF 檔案所使用的ABI
版本。(該字節可以為0x00
,用以表示不指定版本)0x09
~0x15
(EI_PAD
):僅僅是用於「Padding (填充)
」,剛好湊個 2 的次方數(16 Bytes
),也是給未來的版本預留資訊的儲存空間。
至於這些資訊,剛才所說的指令會自動將這些資訊整理成文字顯示,各位就不用辛苦的記憶對應的意思了,例如上面終端輸出的這段:
1Class: ELF64
2Data: 2's complement, little endian
3Version: 1 (current)
4OS/ABI: UNIX - System V
5ABI Version: 0
6Type: DYN (Shared object file)
7Machine: Advanced Micro Devices X86-64
8Version: 0x1
Program Header#
接著是Program Header
。
該區塊用於存放關於執行檔於執行時的必要資訊,包含動態連結函式庫
為何、各個Segments
對應Sections
的分佈、Segments
的權限(是否可讀/寫/執行…)等。
一樣拿hello-world
這支檔案舉例,透過指令「readelf -l
hello-world」可以查看檔案的Program Header
:
1Elf file type is DYN (Shared object file)
2Entry point 0x10a0
3There are 13 program headers, starting at offset 64
4
5Program Headers:
6 Type Offset VirtAddr PhysAddr
7 FileSiz MemSiz Flags Align
8 PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
9 0x00000000000002d8 0x00000000000002d8 R 0x8
10 INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318
11 0x000000000000001c 0x000000000000001c R 0x1
12 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
13 LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
14 0x0000000000000790 0x0000000000000790 R 0x1000
15 LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000
16 0x00000000000002a5 0x00000000000002a5 R E 0x1000
17 LOAD 0x0000000000002000 0x0000000000002000 0x0000000000002000
18 0x00000000000001b0 0x00000000000001b0 R 0x1000
19 LOAD 0x0000000000002d88 0x0000000000003d88 0x0000000000003d88
20 0x0000000000000288 0x00000000000003d0 RW 0x1000
21 DYNAMIC 0x0000000000002da0 0x0000000000003da0 0x0000000000003da0
22 0x0000000000000200 0x0000000000000200 RW 0x8
23 NOTE 0x0000000000000338 0x0000000000000338 0x0000000000000338
24 0x0000000000000020 0x0000000000000020 R 0x8
25 NOTE 0x0000000000000358 0x0000000000000358 0x0000000000000358
26 0x0000000000000044 0x0000000000000044 R 0x4
27 GNU_PROPERTY 0x0000000000000338 0x0000000000000338 0x0000000000000338
28 0x0000000000000020 0x0000000000000020 R 0x8
29 GNU_EH_FRAME 0x0000000000002014 0x0000000000002014 0x0000000000002014
30 0x0000000000000054 0x0000000000000054 R 0x4
31 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
32 0x0000000000000000 0x0000000000000000 RW 0x10
33 GNU_RELRO 0x0000000000002d88 0x0000000000003d88 0x0000000000003d88
34 0x0000000000000278 0x0000000000000278 R 0x1
35
36 Section to Segment mapping:
37 Segment Sections...
38 00
39 01 .interp
40 02 .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
41 03 .init .plt .plt.got .plt.sec .text .fini
42 04 .rodata .eh_frame_hdr .eh_frame
43 05 .init_array .fini_array .dynamic .got .data .bss
44 06 .dynamic
45 07 .note.gnu.property
46 08 .note.gnu.build-id .note.ABI-tag
47 09 .note.gnu.property
48 10 .eh_frame_hdr
49 11
50 12 .init_array .fini_array .dynamic .got
資訊很多,按部就班來讀,主要先找到「Program Headers:
」,先單看這塊區域:
1 Type Offset VirtAddr PhysAddr
2 FileSiz MemSiz Flags Align
3 PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
4 0x00000000000002d8 0x00000000000002d8 R 0x8
5 INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318
6 0x000000000000001c 0x000000000000001c R 0x1
7 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
8 LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
9 0x0000000000000790 0x0000000000000790 R 0x1000
10 LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000
11 0x00000000000002a5 0x00000000000002a5 R E 0x1000
12 LOAD 0x0000000000002000 0x0000000000002000 0x0000000000002000
13 0x00000000000001b0 0x00000000000001b0 R 0x1000
14 LOAD 0x0000000000002d88 0x0000000000003d88 0x0000000000003d88
15 0x0000000000000288 0x00000000000003d0 RW 0x1000
16 DYNAMIC 0x0000000000002da0 0x0000000000003da0 0x0000000000003da0
17 0x0000000000000200 0x0000000000000200 RW 0x8
18 NOTE 0x0000000000000338 0x0000000000000338 0x0000000000000338
19 0x0000000000000020 0x0000000000000020 R 0x8
20 NOTE 0x0000000000000358 0x0000000000000358 0x0000000000000358
21 0x0000000000000044 0x0000000000000044 R 0x4
22 GNU_PROPERTY 0x0000000000000338 0x0000000000000338 0x0000000000000338
23 0x0000000000000020 0x0000000000000020 R 0x8
24 GNU_EH_FRAME 0x0000000000002014 0x0000000000002014 0x0000000000002014
25 0x0000000000000054 0x0000000000000054 R 0x4
26 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
27 0x0000000000000000 0x0000000000000000 RW 0x10
28 GNU_RELRO 0x0000000000002d88 0x0000000000003d88 0x0000000000003d88
29 0x0000000000000278 0x0000000000000278 R 0x1
在最上面標示了欄位名稱(有兩行),分別是「Type
」、「Offset
」、「VirtAddr
」、「PhysAddr
」、「FileSiz
」、「MemSiz
」、「Flags
」、「Align
」。
該Program Header Table
的結構可以在檔案「/usr/include/elf.h
」中找到:
1/* Program segment header. */
2
3typedef struct
4{
5 Elf32_Word p_type; /* Segment type */
6 Elf32_Off p_offset; /* Segment file offset */
7 Elf32_Addr p_vaddr; /* Segment virtual address */
8 Elf32_Addr p_paddr; /* Segment physical address */
9 Elf32_Word p_filesz; /* Segment size in file */
10 Elf32_Word p_memsz; /* Segment size in memory */
11 Elf32_Word p_flags; /* Segment flags */
12 Elf32_Word p_align; /* Segment alignment */
13} Elf32_Phdr;
14
15typedef struct
16{
17 Elf64_Word p_type; /* Segment type */
18 Elf64_Word p_flags; /* Segment flags */
19 Elf64_Off p_offset; /* Segment file offset */
20 Elf64_Addr p_vaddr; /* Segment virtual address */
21 Elf64_Addr p_paddr; /* Segment physical address */
22 Elf64_Xword p_filesz; /* Segment size in file */
23 Elf64_Xword p_memsz; /* Segment size in memory */
24 Elf64_Xword p_align; /* Segment alignment */
25} Elf64_Phdr;
對照上面所看到的欄位可以發現是一樣的,並且在右方有著註解告知該欄位為 Segment 的什麼資訊。
這麼一來我們就可以知道各個欄位代表的是什麼了,接著先來看剛剛輸出的那些Type
分別代表什麼,一樣在/usr/include/elf.h
中可以找到如下資訊:
1/* Legal values for p_type (segment type). */
2
3#define PT_NULL 0 /* Program header table entry unused */
4#define PT_LOAD 1 /* Loadable program segment */
5#define PT_DYNAMIC 2 /* Dynamic linking information */
6#define PT_INTERP 3 /* Program interpreter */
7#define PT_NOTE 4 /* Auxiliary information */
8#define PT_SHLIB 5 /* Reserved */
9#define PT_PHDR 6 /* Entry for header table itself */
10#define PT_TLS 7 /* Thread-local storage segment */
11#define PT_NUM 8 /* Number of defined types */
12#define PT_LOOS 0x60000000 /* Start of OS-specific */
13#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */
14#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */
15#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */
16#define PT_LOSUNW 0x6ffffffa
17#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */
18#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */
19#define PT_HISUNW 0x6fffffff
20#define PT_HIOS 0x6fffffff /* End of OS-specific */
21#define PT_LOPROC 0x70000000 /* Start of processor-specific */
22#define PT_HIPROC 0x7fffffff /* End of processor-specific */
對照剛剛表中有的Type
分別是:
PHDR
Entry for header table itself
指Program Header
,也就是該表本身的資訊。INTERP
Program interpreter
基本上就是標記著要連結動態函式庫所使用的程式,以Linux
來說就是之前所提到的/lib/i386-linux-gnu/ld-2.31.so
。LOAD
Loadable program segment
要載入的區段,包含Text Segment
及Data Segment
… 等。DYNAMIC
Dynamic linking information
動態連結的相關資訊。NOTE
Auxiliary information
只是輔助用的,此區是非必要的。GNU_PROPERTY
請自行閱讀linux-abi-draft.pdf 2.1.5的部份GNU_EH_FRAME
GCC .eh_frame_hdr segmentGNU_STACK
Indicates stack executabilityGNU_RELRO
Read-only after relocation
最後這四項暫時不討論。
Sections#
要查看hello-world
這個檔案的Sections
,可以使用指令「readelf -S
hello-world」:
1There are 29 section headers, starting at offset 0x3148:
2
3Section Headers:
4 [Nr] Name Type Address Offset
5 Size EntSize Flags Link Info Align
6 [ 0] NULL 0000000000000000 00000000
7 0000000000000000 0000000000000000 0 0 0
8 [ 1] .interp PROGBITS 0000000000000318 00000318
9 000000000000001c 0000000000000000 A 0 0 1
10 [ 2] .note.gnu.propert NOTE 0000000000000338 00000338
11 0000000000000020 0000000000000000 A 0 0 8
12 [ 3] .note.gnu.build-i NOTE 0000000000000358 00000358
13 0000000000000024 0000000000000000 A 0 0 4
14 [ 4] .note.ABI-tag NOTE 000000000000037c 0000037c
15 0000000000000020 0000000000000000 A 0 0 4
16 [ 5] .gnu.hash GNU_HASH 00000000000003a0 000003a0
17 0000000000000028 0000000000000000 A 6 0 8
18 [ 6] .dynsym DYNSYM 00000000000003c8 000003c8
19 0000000000000108 0000000000000018 A 7 1 8
20 [ 7] .dynstr STRTAB 00000000000004d0 000004d0
21 0000000000000117 0000000000000000 A 0 0 1
22 [ 8] .gnu.version VERSYM 00000000000005e8 000005e8
23 0000000000000016 0000000000000002 A 6 0 2
24 [ 9] .gnu.version_r VERNEED 0000000000000600 00000600
25 0000000000000040 0000000000000000 A 7 2 8
26 [10] .rela.dyn RELA 0000000000000640 00000640
27 0000000000000108 0000000000000018 A 6 0 8
28 [11] .rela.plt RELA 0000000000000748 00000748
29 0000000000000048 0000000000000018 AI 6 24 8
30 [12] .init PROGBITS 0000000000001000 00001000
31 000000000000001b 0000000000000000 AX 0 0 4
32 [13] .plt PROGBITS 0000000000001020 00001020
33 0000000000000040 0000000000000010 AX 0 0 16
34 [14] .plt.got PROGBITS 0000000000001060 00001060
35 0000000000000010 0000000000000010 AX 0 0 16
36 [15] .plt.sec PROGBITS 0000000000001070 00001070
37 0000000000000030 0000000000000010 AX 0 0 16
38 [16] .text PROGBITS 00000000000010a0 000010a0
39 00000000000001f5 0000000000000000 AX 0 0 16
40 [17] .fini PROGBITS 0000000000001298 00001298
41 000000000000000d 0000000000000000 AX 0 0 4
42 [18] .rodata PROGBITS 0000000000002000 00002000
43 0000000000000014 0000000000000000 A 0 0 4
44 [19] .eh_frame_hdr PROGBITS 0000000000002014 00002014
45 0000000000000054 0000000000000000 A 0 0 4
46 [20] .eh_frame PROGBITS 0000000000002068 00002068
47 0000000000000148 0000000000000000 A 0 0 8
48 [21] .init_array INIT_ARRAY 0000000000003d88 00002d88
49 0000000000000010 0000000000000008 WA 0 0 8
50 [22] .fini_array FINI_ARRAY 0000000000003d98 00002d98
51 0000000000000008 0000000000000008 WA 0 0 8
52 [23] .dynamic DYNAMIC 0000000000003da0 00002da0
53 0000000000000200 0000000000000010 WA 7 0 8
54 [24] .got PROGBITS 0000000000003fa0 00002fa0
55 0000000000000060 0000000000000008 WA 0 0 8
56 [25] .data PROGBITS 0000000000004000 00003000
57 0000000000000010 0000000000000000 WA 0 0 8
58 [26] .bss NOBITS 0000000000004040 00003010
59 0000000000000118 0000000000000000 WA 0 0 64
60 [27] .comment PROGBITS 0000000000000000 00003010
61 000000000000002b 0000000000000001 MS 0 0 1
62 [28] .shstrtab STRTAB 0000000000000000 0000303b
63 000000000000010a 0000000000000000 0 0 1
64Key to Flags:
65 W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
66 L (link order), O (extra OS processing required), G (group), T (TLS),
67 C (compressed), x (unknown), o (OS specific), E (exclude),
68 l (large), p (processor specific)
可以看到除先前所介紹的.text
、.bss
、.data
、.rodata
等 Sections 外,還多了許多 Sections。
至於其他未介紹的 Sections 作用為何,煩請讀者自行尋找相關資料,在此為防離題及篇幅過長不做討論。
另外,如同Program Header Table
一樣,Section Header Table
結構也能於檔案「/usr/include/elf.h
」中找到:
1/* Section header. */
2
3typedef struct
4{
5 Elf32_Word sh_name; /* Section name (string tbl index) */
6 Elf32_Word sh_type; /* Section type */
7 Elf32_Word sh_flags; /* Section flags */
8 Elf32_Addr sh_addr; /* Section virtual addr at execution */
9 Elf32_Off sh_offset; /* Section file offset */
10 Elf32_Word sh_size; /* Section size in bytes */
11 Elf32_Word sh_link; /* Link to another section */
12 Elf32_Word sh_info; /* Additional section information */
13 Elf32_Word sh_addralign; /* Section alignment */
14 Elf32_Word sh_entsize; /* Entry size if section holds table */
15} Elf32_Shdr;
16
17typedef struct
18{
19 Elf64_Word sh_name; /* Section name (string tbl index) */
20 Elf64_Word sh_type; /* Section type */
21 Elf64_Xword sh_flags; /* Section flags */
22 Elf64_Addr sh_addr; /* Section virtual addr at execution */
23 Elf64_Off sh_offset; /* Section file offset */
24 Elf64_Xword sh_size; /* Section size in bytes */
25 Elf64_Word sh_link; /* Link to another section */
26 Elf64_Word sh_info; /* Additional section information */
27 Elf64_Xword sh_addralign; /* Section alignment */
28 Elf64_Xword sh_entsize; /* Entry size if section holds table */
29} Elf64_Shdr;
以及 Table 結構的解釋:
1/* Legal values for sh_type (section type). */
2
3#define SHT_NULL 0 /* Section header table entry unused */
4#define SHT_PROGBITS 1 /* Program data */
5#define SHT_SYMTAB 2 /* Symbol table */
6#define SHT_STRTAB 3 /* String table */
7#define SHT_RELA 4 /* Relocation entries with addends */
8#define SHT_HASH 5 /* Symbol hash table */
9#define SHT_DYNAMIC 6 /* Dynamic linking information */
10#define SHT_NOTE 7 /* Notes */
11#define SHT_NOBITS 8 /* Program space with no data (bss) */
12#define SHT_REL 9 /* Relocation entries, no addends */
13#define SHT_SHLIB 10 /* Reserved */
14#define SHT_DYNSYM 11 /* Dynamic linker symbol table */
15#define SHT_INIT_ARRAY 14 /* Array of constructors */
16#define SHT_FINI_ARRAY 15 /* Array of destructors */
17#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */
18#define SHT_GROUP 17 /* Section group */
19#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */
20#define SHT_NUM 19 /* Number of defined types. */
21#define SHT_LOOS 0x60000000 /* Start OS-specific. */
22#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */
23#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */
24#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */
25#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */
26#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */
27#define SHT_SUNW_move 0x6ffffffa
28#define SHT_SUNW_COMDAT 0x6ffffffb
29#define SHT_SUNW_syminfo 0x6ffffffc
30#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */
31#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */
32#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */
33#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */
34#define SHT_HIOS 0x6fffffff /* End OS-specific type */
35#define SHT_LOPROC 0x70000000 /* Start of processor-specific */
36#define SHT_HIPROC 0x7fffffff /* End of processor-specific */
37#define SHT_LOUSER 0x80000000 /* Start of application-specific */
38#define SHT_HIUSER 0x8fffffff /* End of application-specific */
References#
- 《Linux系统中编译、链接的基石-ELF文件:扒开它的层层外衣,从字节码的粒度来探索-面包板社区》https://www.eet-china.com/mp/a60007.html
- 《Wiki - 靜態函式庫》https://zh.wikipedia.org/zh-tw/%E9%9D%99%E6%80%81%E5%BA%93
- 《Wiki - 動態連結函式庫》https://zh.wikipedia.org/zh-tw/%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5%E5%BA%93
- 《linux - What’s the difference of section and segment in ELF file format - Stack Overflow》https://stackoverflow.com/questions/14361248/whats-the-difference-of-section-and-segment-in-elf-file-format
- 《執行檔格式 - ELF - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天》https://ithelp.ithome.com.tw/articles/10222650