---
GA: G-B8G2L9X80S
---
# Ch3 剖析目的檔
[toc]
## 3.1 目的檔的格式
- 目的檔(Object File)
- 編譯器編譯原始碼產生的檔案就叫做 Object File
- 基本上和執行檔格式相同
- 沒經過連結(Linking)
- 有些符號、位址還沒調整
- 不同系統的執行檔格式名稱:
- Windows: PE (Portable Executable)
- Linux: ELF (Executable Linkable Format)
- macOS: Mach-O (Mach Object File Format)
- 其他不常見的
- Unix: a.out
- MS-DOS: .COM
- ELF 檔案類型
1. 可重定檔案 (Relocatable File)
- 包含程式碼和資料,可以用來 Link 成 Executable 或 Shared Object File
- 副檔名
- Linux: `.o` 檔
- Windows: `.obj` 檔
2. 可執行檔 (Executable File)
- 可以直接執行的檔案
- 副檔名
- Linux: 沒有副檔名
- Windows: `.exe`
3. 共用目的檔 (Shared Object File)
- 包含程式碼和資料,但與單純的 Object File 不同的是可以與其他 object file 產生新的 object file 或 executable
- shared object + objectA + objectB + ... = "new object file" or "exe"
- 副檔名
- Linux: `.so`
- Windows: `.dll`
4. 核心傾印檔 (Core Dump File)
- 當程式 crash 時會產生的檔案,
- 副檔名
- Linux: `core dump`
- https://jasonblog.github.io/note/gdb/rang_cheng_xu_beng_kui_shi_chan_sheng_coredump.html
- Windows: `.dmp`
- 可以用 visual studio 開起來分析
## 3.2 目的檔的內容
**目標文件(Object File)** 裡面有各種 `section` 或是有時會叫做 `segment`。程式經過編譯之後會依據程式的性質被放在各個 `section` 裡面。

- 在檔案的開頭有一個**檔案頭**,裡面存了整個檔案的屬性
- 屬性包含: 是否可執行、是靜態鏈接還是動態鏈接、入口地址(Executable 的)、目標硬體架構、目標 OS 等
- 檔案頭包含了一個 `Section Table`,是一個描述整個檔案存在的所有 `Section` 的 Array
- 存了 `Section` 在檔案中的偏移位置、`Section` 的屬性等
檔頭後接著就是實際的 Seciton:
- 幾個常見的 Section:
- `.text`: Code Section
- 編譯後放指令的地方
- `.data`: Data Section
- 放「有初始化」的 global variable 和 static local variable
- `.bss`
- 放「未初始化」的 global variable 和 static local variable
- 如果沒有經過初始化,系統預設是`0`,故不需要在另外記錄初始值,因此 `.bss` 沒有內容,在檔案中也不占空間。
> 
一個程式的指令(Code)和資料(Data)分開來放有甚麼好處?
1. Code 是 Read-Only 而 Data 是 Read-Write 可以**防止執行時被改寫**。
2. 提高 Cache 的命中率,現代 CPU 一般都是 Instruction Cache 和 Data Cache 分離,因此程式的 Code 和 Data 分開有助於**提高 cache 命中率**。
- 空間局部性: 程式執行時,相鄰的記憶體地址通常會被反覆訪問
- 時間局部性: 程式執行時,同一條指令或數據通常會被反覆訪問
3. 當系統執行著多份同樣的程式時,code 部分都相同,記憶體中只要保留一份,對於 Read-Only 的 data 也一樣,特別在支援 Dynamic-Linking 的系統,可以**節省大量空間**。
## 3.3 SimpleSection.o
這章實際編譯一個 `SimpleSection.c` 來當作範例:
```bash=
gcc -m32 -c SimpleSection.c
```
```cpp=
int printf( const char* format, ...);
int global_init_var = 84;
int global_uninit_var;
void func1(int i) {
printf("%d\n", i);
}
int main() {
static int static_var = 85;
static int static_var2;
int a = 1;
int b;
func1(static_var + static_var2 + a + b);
return a;
}
```
可以使用 `objdump -h SimpleSection.o` 來觀察 Section 的排布即大小:

```bash=
$ objdump -h SimpleSection32NoPie.o
SimpleSection32NoPie.o: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000006a 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000008 00000000 00000000 000000a0 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000004 00000000 00000000 000000a8 2**2
ALLOC
3 .rodata 00000004 00000000 00000000 000000a8 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .comment 0000002c 00000000 00000000 000000ac 2**0
CONTENTS, READONLY
5 .note.GNU-stack 00000000 00000000 00000000 000000d8 2**0
CONTENTS, READONLY
6 .note.gnu.property 0000001c 00000000 00000000 000000d8 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .eh_frame 00000064 00000000 00000000 000000f4 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
```
| Section | Size | Offset |
| -------- | -------- | ------ |
| `.text` | 0x6a | 0x34 |
| `.data` | 0x8 | 0xa0 |
| `.bss` | 0x4 | 0xa8 |
| `.rodata` | 0x4 | 0xa8 |
- VMA (Virtual Memory Address)
- LMA (Load Memory Address)
- 大多數情況 VMA 與 LMA 會相同
- 但在一些特殊情況 e.g. 程式被 load 到 ROM (LMA) 然後在執行的時候先加載到 RAM (VMA)
> https://www.zeuthen.desy.de/dv/documentation/unixguide/infohtml/binutils/docs/ld/Basic-Script-Concepts.html#Basic-Script-Concepts
### `.text`
```
Contents of section .text:
0000 f30f1efb 5589e553 83ec04e8 fcffffff ....U..S........
0010 05010000 0083ec08 ff75088d 90000000 .........u......
0020 005289c3 e8fcffff ff83c410 908b5dfc .R............].
0030 c9c3f30f 1efb8d4c 240483e4 f0ff71fc .......L$.....q.
0040 5589e551 83ec14e8 fcffffff 05010000 U..Q............
0050 00c745f0 01000000 8b900400 00008b80 ..E.............
0060 00000000 01c28b45 f001c28b 45f401d0 .......E....E...
0070 83ec0c50 e8fcffff ff83c410 8b45f08b ...P.........E..
0080 4dfcc98d 61fcc3 M...a..
```
我們擷錄 `func1` 的指令,完整的 `.text` 總大小為 87 bytes
```
00000000 <func1>:
0: f3 0f 1e fb endbr32
4: 55 push ebp
5: 89 e5 mov ebp,esp
7: 53 push ebx
8: 83 ec 04 sub esp,0x4
b: e8 fc ff ff ff call c <func1+0xc>
c: R_386_PC32 __x86.get_pc_thunk.ax
10: 05 01 00 00 00 add eax,0x1
11: R_386_GOTPC _GLOBAL_OFFSET_TABLE_
15: 83 ec 08 sub esp,0x8
18: ff 75 08 push DWORD PTR [ebp+0x8]
1b: 8d 90 00 00 00 00 lea edx,[eax+0x0]
1d: R_386_GOTOFF .rodata
21: 52 push edx
22: 89 c3 mov ebx,eax
24: e8 fc ff ff ff call 25 <func1+0x25>
25: R_386_PLT32 printf
29: 83 c4 10 add esp,0x10
2c: 90 nop
2d: 8b 5d fc mov ebx,DWORD PTR [ebp-0x4]
30: c9 leave
31: c3 ret
```
### `.data` 和 `.rodata`
- `.data` 放的是「已初始化」的 **全域變數(global variable)** 和 **區域靜態變數(static local variable)**
- `.rodata` 放的是**唯讀**的變數。
```
Contents of section .data:
0000 54000000 55000000 T...U...
Contents of section .rodata:
0000 25640a00 %d..
```
- `.data` 採用 little-endian;`.data` 裡面有兩個 var:他們的分別別為 *0x00000054* 和 *0x00000055*
- 目前 `.rodata` 裡存放的值為 `printf` 的第一個參數,它是一個唯讀的字串。
### `.bss`
`.bss` 的全名是 Block Started by Symbol,名字的由來是歷史因素,放的是「未初始化」的 global variable 和 static local variable
```
Sections:
Idx Name Size VMA LMA File off Algn
..
3 .bss 00000004 00000000 00000000 000000cc 2**2
ALLOC
...
```
> `.bss` 沒有內容,它的目的是叫 loader 在程式載入的時候去 preallocate 一段記憶體然後設成 `0`,因為最後都要設成 `0` ,所以在檔案就不會儲存內容。
> <https://reverseengineering.stackexchange.com/questions/4230/why-i-can-not-directly-get-the-content-of-bss-section>
### Other sections
除了 `.text`, `.data`, `.bss` 這些常用的區段,ELF 也可能包含其他的區段,書中列出了一些區段及其意義:

- 書中提到自行修改 section,例如可以把 jpg 塞在 `.data` 裡面,書中有示範。
```bash=
objcopy -I binary -O elf32-i386 -B i386 image.jpg image.o
objdump -ht image.o
```
- 另外可以增加一個自訂的 section。一些會使用到的場景像是要將一部分的程式碼分離,或是為了滿足某些硬體的記憶體與I/O位址規劃,可以建立一個自訂的 section,然後把程式碼丟進去。
```cpp=
// 把 int global 放在 .FOO section
__attribute__((section("FOO"))) int global = 42;
// 把 void foo() 放在 .BAR section
__attribute__((section("BAR"))) void foo() {}
```
## 3.4 ELF File format
圖3-4 描述了 ELF 文件的結構。

[](https://hackmd.io/_uploads/SkSrMekOa.png)
> source: <https://gist.github.com/x0nu11byt3/bcb35c3de461e5fb66173071a2379779>
最前面是 **ELF 檔頭 (Header)**,描述了整個檔案的 metadata,包含了: ELF 版本、目標機器 arch、程式入口位置等。
接下來是 ELF 的各個 Section,再來是**段頭表(Section Header Table)**,描述了 ELF 的所有區段的資訊: 名稱、長度、偏移、讀寫權限、及其他屬性。
### 3.4.1 ELF Header
可以使用 `readelf` 來觀察 ELF 的 Header,底下是範例:
```=
$ readelf -h SimpleSection32.o
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: REL (Relocatable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 1140 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 16
Section header string table index: 15
```
可以看到,ELF 檔頭中定義了: magic, 目標機器 bit , 資料儲存方式, 版本, 執行平台, ABI 版本, ELF 重定類型, 硬體平台, 硬體平台版本, 入口位址, header 的開始位置、大小,和區段的數量...等。
關於 ELF 的檔頭結構,其 C struct 的定義放在 `/usr/include/elf.h` 中:
```cpp=
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
```
底下開始細講每個 field 所代表的意義及可能出現的值:
且可以搭配 readelf 原始碼一起服用
<https://github.com/bminor/binutils-gdb/blob/master/binutils/readelf.c#L6289C18-L6289C18>
#### `e_ident`
這個變數是個 `char` 陣列,描述了一個 ELF 的檔頭,前 4 個 bit 是 magic,接下來描述了幾個參數:
- `EI_CLASS`
- 第 `4` 個 byte
- 用來判斷是 32-bit 或是 64-bit 的 ELF 檔
- `EI_DATA`
- 第 `5` 個 byte
- 判斷 ELF 檔是 `LSB(Little-endian)` 還是 `MSB(Big-endian)`
- `EI_VERSION`
- 第 `6` 個 byte
- 該 ELF 檔的 ELF header 版本,值必須是 [`EV_CURRENT`(1)](https://github.com/bminor/glibc/blob/master/elf/elf.h#L378C13-L378C13)
- `EI_OSABI`
- 第 `7` 個 byte
- 該 ELF 檔的 OS Application Binary Interface
- e.g. 同樣都是 ELF 但是在不同的作業系統上不能執行其他作業系統的
- `EI_ABIVERSION`
- 第 `8` 個 byte
- ABI Version
- `EI_PAD`
- 第 `9` 個 byte ~ 第 `16` 個 byte
- 都是 `0`, 單純 padding
- 如果要加入特殊規格可以使用這幾個 byte
:::spoiler `e_ident` 定義
<iframe frameborder="0" scrolling="no" style="width:100%; height:1255px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fbminor%2Fglibc%2Fblob%2Fmaster%2Felf%2Felf.h%23L99-L154&style=github&type=code&showBorder=on&showLineNumbers=on&showFileMeta=on&showFullPath=on&showCopy=on"></iframe>
- 使用:
```cpp=
header->e_ident[EI_CLASS];
header->e_ident[EI_DATA];
```
:::
---
#### `e_type`
前16個 byte 後再看兩個 byte:
- 定義 ELF 的類型 -> ELF 不用靠副檔名看類型
- `ET_NONE`(0): 未知
- `ET_REL`(1): 可重定向檔 (Relocatable file)
- `ET_EXEC`(2): 執行檔
- `ET_DYN`(3): Shared object file
- `ET_CORE`(4): Core dump
:::spoiler `e_type` 定義
<iframe frameborder="0" scrolling="no" style="width:100%; height:331px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fbminor%2Fglibc%2Fblob%2Fmaster%2Felf%2Felf.h%23L156-L167&style=github&type=code&showBorder=on&showLineNumbers=on&showFileMeta=on&showFullPath=on&showCopy=on"></iframe>
:::
:::spoiler 一些不同 `e_type` 的 ELF 例子
- `ET_REL`
```
$ cat > a.c << EOL
#include <stdio.h>
int main() {
puts("Hello, World");
return 0;
}
EOL
$ gcc -c a.c -o a.o
$ readelf -h ./a.o
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 784 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 14
Section header string table index: 13
```
- `ET_EXEC`
```
$ gcc a.c
$ readelf -h ./a.out
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x1060
Start of program headers: 64 (bytes into file)
Start of section headers: 14712 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 13
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 30
$ gcc -no-pie a.c
$ readelf -h ./a.out
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x401050
Start of program headers: 64 (bytes into file)
Start of section headers: 14640 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 13
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 30
```
- `ET_DYN`
```
$readelf -h /lib/x86_64-linux-gnu/libc.so.6
ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
ABI Version: 0
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x241c0
Start of program headers: 64 (bytes into file)
Start of section headers: 2025240 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 14
Size of section headers: 64 (bytes)
Number of section headers: 68
Section header string table index: 67
```
- `ET_CORE`
```
$ cat > core.c << EOL
#include <stdio.h>
int main() {
int *a = 0;
*a = 123;
return 0;
}
EOL
$ gcc core.c
$ ./a.out
Segmentation fault (core dumped)
$ readelf -h ./core
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: CORE (Core file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x0
Start of program headers: 64 (bytes into file)
Start of section headers: 0 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 22
Size of section headers: 0 (bytes)
Number of section headers: 0
Section header string table index: 0
```
:::
---
#### `e_machine`
執行的平台的架構
:::spoiler `e_machine` 定義
定義了非常多平台,總共 `EM_NUM`(259) 個
<iframe frameborder="0" scrolling="no" style="width:100%; height:4174px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fbminor%2Fglibc%2Fblob%2Fmaster%2Felf%2Felf.h%23L169-L363&style=github&type=code&showBorder=on&showLineNumbers=on&showFileMeta=on&showFullPath=on&showCopy=on"></iframe>
:::
---
#### `e_version`
與 `e_ident[EI_VERSION]` 相同,值必須是 [`EV_CURRENT`(1)](https://github.com/bminor/glibc/blob/master/elf/elf.h#L378C13-L378C13)
---
#### `e_entry`
程式進入點 Entry Point,指向 `_start`
當 `e_type` 是 `ET_EXEC` 時是絕對位置;當 `e_type` 是 `ET_DYN` 時是相對位置
如果沒有則為 `0`
---
#### `e_phoff`
Program header table offset,不存在則為 `0`
#### `e_shoff`
Section header table offset,不存在則為 `0`
---
#### `e_flags`
Processor-specific flags 在 x86 都是 `0`
---
#### `e_ehsize`
此 ELF Header 的大小 bytes
---
#### `e_phentsize`
Program header entry 的大小 bytes
#### `e_phnum`
有多少個 Program header
`Program header 大小 = e_phentsize * e_phnum`
---
#### `e_shentsize`
Section Header entry 的大小 bytes
#### `e_shnum`
有多少個 Section Header
#### `e_shstrndx`
每個 Section 的名稱,統一存放在一個 Section 中,存它在 Section Header Table 中的 index,方便快速 access
---
### 3.4.2 段頭表 Section Header Table
**段頭表 (Section Header Table)** 保存了這些 Section 的基本屬性的 struct,compiler, linker, 和 loader 都是靠這個 struct 來定位和存取 section。
段頭表包含了數個(`header->e_shnum`) **段描述項(Section Descriptor)**,在 C 的 struct 如以下所示:
```cpp=
typedef struct
{
Elf32_Word sh_name; /* Section name (string tbl index) */
Elf32_Word sh_type; /* Section type */
Elf32_Word sh_flags; /* Section flags */
Elf32_Addr sh_addr; /* Section virtual addr at execution */
Elf32_Off sh_offset; /* Section file offset */
Elf32_Word sh_size; /* Section size in bytes */
Elf32_Word sh_link; /* Link to another section */
Elf32_Word sh_info; /* Additional section information */
Elf32_Word sh_addralign; /* Section alignment */
Elf32_Word sh_entsize; /* Entry size if section holds table */
} Elf32_Shdr;
```
段頭表其實就是有 `header->e_shnum` 個元素的 `Elf32_Shdr*` 陣列,可以使用 `readelf -S` 來觀察:
```
$ readelf -S ./SimpleSection32NoPie.o
There are 14 section headers, starting at offset 0x37c:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 000034 00006a 00 AX 0 0 1
[ 2] .rel.text REL 00000000 0002d0 000028 08 I 11 1 4
[ 3] .data PROGBITS 00000000 0000a0 000008 00 WA 0 0 4
[ 4] .bss NOBITS 00000000 0000a8 000004 00 WA 0 0 4
[ 5] .rodata PROGBITS 00000000 0000a8 000004 00 A 0 0 1
[ 6] .comment PROGBITS 00000000 0000ac 00002c 01 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 00000000 0000d8 000000 00 0 0 1
[ 8] .note.gnu.propert NOTE 00000000 0000d8 00001c 00 A 0 0 4
[ 9] .eh_frame PROGBITS 00000000 0000f4 000064 00 A 0 0 4
[10] .rel.eh_frame REL 00000000 0002f8 000010 08 I 11 9 4
[11] .symtab SYMTAB 00000000 000158 000110 10 12 12 4
[12] .strtab STRTAB 00000000 000268 000066 00 0 0 1
[13] .shstrtab STRTAB 00000000 000308 000072 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
p (processor specific)
```
:::info
可以搭配 `readelf.c` 的 `get_32bit_section_headers()` 閱讀
https://github.com/bminor/binutils-gdb/blob/master/binutils/readelf.c#L6891
:::
#### `sh_name`
Section Name 區段名稱,注意這邊存的是 index (`Elf32_Word`),是在 `.shstrtab` 中的偏移量
#### `sh_type`
區段類型(Section Type),決定了該 Section 的屬性
<iframe frameborder="0" scrolling="no" style="width:100%; height:898px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fbminor%2Fglibc%2Fblob%2Fmaster%2Felf%2Felf.h%23L428-L466&style=github&type=code&showBorder=on&showLineNumbers=on&showFileMeta=on&showFullPath=on&showCopy=on"></iframe>
> 書中只介紹到 `SHT_DYNSYM` 但其實後面還有很多,故直接引用 header
#### `sh_flags`
區段 Flag (Section Flags)
- `SHF_WRITE` 可寫
- `SHF_ALLOC` 表示該 Section 在執行時需要分配空間
- `.text`, `.data`, `.rodata`, `.bss` 通常都會有這個權限
- `SHF_EXECINSTR` 可執行
- 通常是 `.text` 才有
<iframe frameborder="0" scrolling="no" style="width:100%; height:520px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fbminor%2Fglibc%2Fblob%2Fmaster%2Felf%2Felf.h%23L468-L488&style=github&type=code&showBorder=on&showLineNumbers=on&showFileMeta=on&showFullPath=on&showCopy=on"></iframe>
#### `sh_addr`
區段位址(Section Address),此 Section 在載入後在 Process Memory 的 address;如果不能載入則為 `0`
#### `sh_offset`
如果該 Section 存在檔案,則代表該 Section 在檔案中的偏移;否則此欄位不具意義
> 對於 `.bss` 來說,這個欄位沒意義
#### `sh_size`
區段大小(Section Size)
#### `sh_link`, `sh_info`
區段連結和區段資訊(Section Link and Section Information)
如果 Section 是與 Linking 相關的(動態 or 靜態),例如 Reloaction Table, Symbol Table 等才有意義;對於其他 Section 來說,這兩個欄位為 `0`

至於數值的意義則和 Section Type 有關,但都拿來索引用途。

> 下標 = Index
#### `sh_addralign`
區段位址對齊(Section Address Alignment),代表該區段對齊 $2^{\text{sh_addralign}}$ 倍。為 `0` 或 `1` 代表沒有記憶體對齊的要求。
> 因為有些區段對記憶體位址對齊有要求,例如某個區段在開始位置存了一個 `double` ,但 Intel X86 要求浮點數的儲存位址必須是本身儲存空間的整數倍,必須是 8 的倍數。也就是該區段的 `sh_addr` 必須是 8 的倍數,所以其 `sh_addralign` 就是 3 ($2^3=8$)。
> Intel x86 支援非非記憶體對齊的存取但效能會受影響,有些處理器直接或部分指令不支援非記憶體對齊的存取。
> Ref:
> https://xiaodongfan.com/%E6%B5%85%E8%B0%88%E7%A8%8B%E5%BA%8F%E4%B8%AD%E7%9A%84%E5%86%85%E5%AD%98%E5%AF%B9%E9%BD%90%E9%97%AE%E9%A2%98.html
> https://hackmd.io/@sysprog/c-memory#data-alignment
#### `sh_entsize`
區段項目長度(Section Entry Size),如果該區段包含了固定大小的項目 e.g. symbol table,對於這種區段 `sh_entsize` 代表每個項目(Entry)的大小;如果為 `0` 代表該區段不包含固定大小的東西。

#### 系統保留區段


### 3.4.3 重定表 (Relocation Table)
> 
Linker 在做 linking 時,會對 Object file 中的 `.text` 和 `.data` 中引用絕對位址的地方進行 Relocation。對於單獨的 Object file 來說,它們在編譯的時對於程式中呼叫 or 引用的外部位址並沒有辦法在 compile 的時候得知,必須要在 Linking 時才能得知,所以在編譯時會先將位址留白,等到 Linking 時再由 Linker 把位址填回去。
[](https://hackmd.io/_uploads/SJqMFo-_a.png)
對於每個需要 Relocation 的 Section 都存在一個 `.rel.xxx` section,所以 `.rel.text` 其實是幫 `.text` 做 **relocation** 的 section。
Relocation Table 本身也是一個 Section,在 Section Header 可以找到它的描述: `sh_type` 是 `SHT_REL` 類型、`sh_info` 表示作用的區段,例如: `.rel.text` 的 `sh_info` 就是指向 `1` 也就是 `.text` 區段。

:::info
Relocation Table 的內部結構會在 Ch4 討論
:::
### 3.4.4 字串表 (String Table)
在 ELF 中用到了很多字串(用來儲存區段名字、變數等),因為字串長度不固定,很難用固定資料結構儲存,所以採取集中放的方法,每個字串用 `\0` 隔開,這樣對於一個字串來說,只要記錄它的開始位置即可(想要獲得整個字串就一直往後讀取,直到碰到 `\0` 為止)。

ELF 常見的兩個字串表分別是: `.strtab` 和 `.shstrtab`
- `.strtab` String Table 字串表
- 用來保存字串 e.g. 符號(Symbol)的名稱
- `.shstrtab` Section Header String Table 段頭字串表
- 用來儲存 Section 名稱(`sh_name`)的表
前面講過的 ELF header 的最後一個欄位 `e_shstrndx` 就是指向 `.shstrtab` 的 index
```
$readelf -h ./SimpleSection64.o
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
...
...
Size of section headers: 64 (bytes)
Number of section headers: 14
Section header string table index: 13 <=====
```
:::spoiler `.shstrtab`,`.strtab`範例
- `.shstrtab`
```
$ readelf -x .shstrtab ./a.out
Hex dump of section '.shstrtab':
0x00000000 002e7379 6d746162 002e7374 72746162 ..symtab..strtab
0x00000010 002e7368 73747274 6162002e 696e7465 ..shstrtab..inte
0x00000020 7270002e 6e6f7465 2e676e75 2e70726f rp..note.gnu.pro
0x00000030 70657274 79002e6e 6f74652e 676e752e perty..note.gnu.
0x00000040 6275696c 642d6964 002e6e6f 74652e41 build-id..note.A
0x00000050 42492d74 6167002e 676e752e 68617368 BI-tag..gnu.hash
0x00000060 002e6479 6e73796d 002e6479 6e737472 ..dynsym..dynstr
0x00000070 002e676e 752e7665 7273696f 6e002e67 ..gnu.version..g
0x00000080 6e752e76 65727369 6f6e5f72 002e7265 nu.version_r..re
0x00000090 6c612e64 796e002e 72656c61 2e706c74 la.dyn..rela.plt
0x000000a0 002e696e 6974002e 706c742e 676f7400 ..init..plt.got.
0x000000b0 2e706c74 2e736563 002e7465 7874002e .plt.sec..text..
0x000000c0 66696e69 002e726f 64617461 002e6568 fini..rodata..eh
0x000000d0 5f667261 6d655f68 6472002e 65685f66 _frame_hdr..eh_f
0x000000e0 72616d65 002e696e 69745f61 72726179 rame..init_array
0x000000f0 002e6669 6e695f61 72726179 002e6479 ..fini_array..dy
0x00000100 6e616d69 63002e64 61746100 2e627373 namic..data..bss
0x00000110 002e636f 6d6d656e 7400 ..comment.
```
- `.strtab`
```
$ readelf -x .strtab ./a.out
Hex dump of section '.strtab':
0x00000000 00637274 73747566 662e6300 64657265 .crtstuff.c.dere
0x00000010 67697374 65725f74 6d5f636c 6f6e6573 gister_tm_clones
0x00000020 005f5f64 6f5f676c 6f62616c 5f64746f .__do_global_dto
0x00000030 72735f61 75780063 6f6d706c 65746564 rs_aux.completed
0x00000040 2e383036 31005f5f 646f5f67 6c6f6261 .8061.__do_globa
0x00000050 6c5f6474 6f72735f 6175785f 66696e69 l_dtors_aux_fini
0x00000060 5f617272 61795f65 6e747279 00667261 _array_entry.fra
0x00000070 6d655f64 756d6d79 005f5f66 72616d65 me_dummy.__frame
0x00000080 5f64756d 6d795f69 6e69745f 61727261 _dummy_init_arra
0x00000090 795f656e 74727900 612e6300 5f5f4652 y_entry.a.c.__FR
0x000000a0 414d455f 454e445f 5f005f5f 696e6974 AME_END__.__init
0x000000b0 5f617272 61795f65 6e64005f 44594e41 _array_end._DYNA
0x000000c0 4d494300 5f5f696e 69745f61 72726179 MIC.__init_array
0x000000d0 5f737461 7274005f 5f474e55 5f45485f _start.__GNU_EH_
0x000000e0 4652414d 455f4844 52005f47 4c4f4241 FRAME_HDR._GLOBA
0x000000f0 4c5f4f46 46534554 5f544142 4c455f00 L_OFFSET_TABLE_.
0x00000100 5f5f6c69 62635f63 73755f66 696e6900 __libc_csu_fini.
0x00000110 5f49544d 5f646572 65676973 74657254 _ITM_deregisterT
0x00000120 4d436c6f 6e655461 626c6500 70757473 MCloneTable.puts
0x00000130 4040474c 4942435f 322e322e 35005f65 @@GLIBC_2.2.5._e
0x00000140 64617461 005f5f6c 6962635f 73746172 data.__libc_star
0x00000150 745f6d61 696e4040 474c4942 435f322e t_main@@GLIBC_2.
0x00000160 322e3500 5f5f6461 74615f73 74617274 2.5.__data_start
0x00000170 005f5f67 6d6f6e5f 73746172 745f5f00 .__gmon_start__.
0x00000180 5f5f6473 6f5f6861 6e646c65 005f494f __dso_handle._IO
0x00000190 5f737464 696e5f75 73656400 5f5f6c69 _stdin_used.__li
0x000001a0 62635f63 73755f69 6e697400 5f5f6273 bc_csu_init.__bs
0x000001b0 735f7374 61727400 6d61696e 005f5f54 s_start.main.__T
0x000001c0 4d435f45 4e445f5f 005f4954 4d5f7265 MC_END__._ITM_re
0x000001d0 67697374 6572544d 436c6f6e 65546162 gisterTMCloneTab
0x000001e0 6c65005f 5f637861 5f66696e 616c697a le.__cxa_finaliz
0x000001f0 65404047 4c494243 5f322e32 2e3500 e@@GLIBC_2.2.5.
```
:::
## 3.5 Symbol
**連結(Linking)** 的過程就是在把多個 Object File 拼在一起,形成一個可執行檔。該怎麼才知道誰要與誰拚在一起呢?要看 Object file 之間位址的**引用**,也就是引用 function 和 variable 的位址。
例如 Object B 用到了 Object A 中的 `foo()`,我們稱 A 定義(define)了 `foo()`,而 B 引用了 `foo()`。每個 function 和 variable 都有**獨特的名稱**,才能避免連結過程中的混淆。我們稱 function 和 variable 為 ==符號(Symbol)==,其名稱叫做 符號名稱(Symbol Name)。
整個 Linking 過程就是靠 Symbol 才能完成,因此 Symbol 的管理很重要,每個 Object file 都會有 **符號表 (Symbol Table)**,裏頭紀錄了 object file 中用到的所有符號,每個符號都有對應的 **符號值(Symbol Value)**,可以將 Symbol Table 中的符號分類如下:
- 定義在此 object 的 全域符號(Global Symbol)
- e.g. `SimpleSection` 的 `func1`, `main`, `global_init_var`
- 此 object 引用的 Global Symbol
- 又稱作 外部符號(External Symbol)
- e.g. `printf`
- 區段名稱,由編譯器產生,其值就是該 Section 的起始位址
- `.text`, `.data`
- 區域符號
- 只在編譯單元的內部可見
- e.g. `static_var`, `static_var2`
- Debugger 可以用這些符號來分析程式或crashdump
- 在連結過程中沒用,通常 Linker 也會忽略
- 行號
可以使用 `nm` 來查看 object file 的 symbol:
```
$ nm SimpleSection64.o
0000000000000000 T func1
0000000000000000 D global_init_var
U _GLOBAL_OFFSET_TABLE_
0000000000000004 C global_uninit_var
0000000000000028 T main
U printf
0000000000000004 d static_var.1919
0000000000000000 b static_var2.1920
```
`objdump -t SimpleSection64.o`
### 3.5.1 符號表結構
Symbol Table 在 ELF 中也是一個 Section,叫做 `.symtab`,在 C 的結構叫作 `Elf32_Sym` ,`.symtab` 裏頭存的就是 `Elf32_Sym` 的陣列,每個 `Elf32_Sym` 對應一個符號,陣列的第一個元素通常都是無效的符號。
可以使用 `readelf -s` 觀察一個 Object file 的 Symbol Table:
```
$ readelf -s ./SimpleSection64.o
Symbol table '.symtab' contains 18 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS SimpleSection.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000004 4 OBJECT LOCAL DEFAULT 3 static_var.1919
7: 0000000000000000 4 OBJECT LOCAL DEFAULT 4 static_var2.1920
8: 0000000000000000 0 SECTION LOCAL DEFAULT 7
9: 0000000000000000 0 SECTION LOCAL DEFAULT 8
10: 0000000000000000 0 SECTION LOCAL DEFAULT 9
11: 0000000000000000 0 SECTION LOCAL DEFAULT 6
12: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 global_init_var
13: 0000000000000004 4 OBJECT GLOBAL DEFAULT COM global_uninit_var
14: 0000000000000000 40 FUNC GLOBAL DEFAULT 1 func1
15: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
16: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf
17: 0000000000000028 55 FUNC GLOBAL DEFAULT 1 main
```
:::info
可以搭配 `readelf.c` 的 `get_32bit_elf_symbols ()` 閱讀
https://github.com/bminor/binutils-gdb/blob/master/binutils/readelf.c#L7043
:::
#### `Elf32_Sym` 的結構
```c=
typedef struct
{
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf32_Section st_shndx; /* Section index */
} Elf32_Sym;
```
##### `st_name` 符號名稱
存的它在 `.strtab` 的 index
##### `st_value` 符號對應值
根據 Symbol Type 的不同可能代表不同意義:
- 在 Relocatable file
- 如果該 Symbol 的 `st_shndx` 是 `SHN_COMMON` 的話
- 則 `st_value` 代表該 Symbol 的 Alignment Constraint
- e.g. `global_uninit_var`
- 
- 如果該 Symbol 的 `st_shndx` 是指向某個 Section 的話
- 則 `st_value` 代表 `st_shndx` 指向的 section 從頭開始的偏移量
- 在 Executable, Shared object file 中
- `st_value` 代表該 Symbol 的 virtual address
- > 在動態連結器中會講到
##### `st_size` 符號大小 (bytes)
如果該 Symbol 是用來儲存資料的,則此欄位代表資料的大小
##### `st_info` 符號類型(Symbol Type)與繫結資訊(Symbol Binding)
- low 4 bit 表示 symbol type;high 28 bit 表示 symbol binding info
<iframe frameborder="0" scrolling="no" style="width:100%; height:184px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fbminor%2Fglibc%2Fblob%2Fmaster%2Felf%2Felf.h%23L572-L576&style=github&type=code&showBorder=on&showLineNumbers=on&showFileMeta=on&showFullPath=on&showCopy=on"></iframe>
###### 繫結資訊 Symbol Binding
<iframe frameborder="0" scrolling="no" style="width:100%; height:310px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fbminor%2Fglibc%2Fblob%2Fmaster%2Felf%2Felf.h%23L583-L593&style=github&type=code&showBorder=on&showLineNumbers=on&showFileMeta=on&showFullPath=on&showCopy=on"></iframe>
- `STB_LOCAL` 區域符號,Object file 外部不可見
- `STB_GLOBAL` 全域符號,Object file 外部可見
- `STB_WEAK` 弱引用
###### 符號類型 Symbol Type
<iframe frameborder="0" scrolling="no" style="width:100%; height:394px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fbminor%2Fglibc%2Fblob%2Fmaster%2Felf%2Felf.h%23L595-L609&style=github&type=code&showBorder=on&showLineNumbers=on&showFileMeta=on&showFullPath=on&showCopy=on"></iframe>
- `STT_NOTYPE` 未知符號類型
- `STT_OBJECT` 該符號是資料物件 e.g. variable, array 等
- `STT_FUNC` 該符號是 function 或其他可執行程式
- `STT_SECTION` 該符號表示一個 Section
- 通常是 `STB_LOCAL`,用來 relocation 用
- `STT_FILE` 該符號表示一個檔案
- 通常是 `STB_LOCAL` 且 `st_shndx` 是 `SHN_ABS`
- 通常 `.symtab` 的第一個 Symbol 就會是 `STT_FILE` 且名稱是目前之 Object file 的 source file 名稱
- 
> 如果一個 Symbol 是 `STT_FILE` 通常會擺在其他 `STB_LOCAL` 的 Symbol 的前面
##### `st_other`
==TODO==
A symbol's visibility, determined from its st_other field, may be specified in a relocatable object. This visibility defines how that symbol may be accessed once the symbol has become part of an executable or shared object.
##### `st_shndx` 符號所在區段
如果該 Symbol 是定義在該 Object File 中,則 `st_shndx` 代表該 Symbol 所在的 Section Header table 的 Index
如果不是的話、或是特殊符號的話,其 `st_shndx` 可能是以下特殊值:
- `SHN_ABS`
- 該符號是絕對位址,不會隨著 Relocation 而改變值
- `SHN_COMMON`
- 該符號是 Common 區塊
- 通常未初始化的全域符號 `global_uninit_var` 就是這個類型
- 
- `SHN_UDEF` Undefined
- 代表該符號未定義;或是在該 Object file 有引用,但是定義在其他 Object file 中
- 
### 3.5.2 特殊符號
在 ld 連結 target object 時,需要一些符號供給 ld 使用,這些符號稱之為“特殊符號”。更詳細的介紹在 Ch.7。
以下是一些特殊符號:
- `__executable_start`:整個程式的最開始,不是程式碼的開頭(程式碼的開頭叫做 entrypoint)。
- `__etext` 或 `_etext` 或 `etext`:*程式碼*的最尾端的地址。
- `_edata` 或 `edata`:data section 的結尾地址
- `_end` 或 `end`:整個程式的結尾。
這些符號在後面會有更詳細的說明。
### 3.5.3 符號修飾與函數簽名
在 C 語言出生了那個年代之前,這個世界已經有大量組合語言的程式碼存在。大量的組合語言定義了大量的函式名稱,後期在撰寫 C 語言並且要編譯成目標文件時,會產生很多名稱衝突。例如:`main` 出現了不只一次的時候 linker 會不知道要找的 `main` 為哪一個?
為了解決這個問題,之後定義所有 C 語言程式碼過編譯後符號名稱前面會多加底線(`_`)、Fortran編譯出來的符號名稱則前後都會多加底線。
但是其實還是沒有辦法解決問題,如果一個專案有多人撰寫,那麼衝突會不斷地加劇,所以在 C++ 加入了 namespace 的概念解決問題。
隨著時間推移,這種加底線的習慣更改,C 語言底線在 Linux GCC 預設不加入,但 Windows MinGW, Cygwin; MSVC 還有保留底線。GCC 可以加上 `-fleading-underscore` 或 `-fno-leading-underscore` 來控制是否加入底線。
#### C++ 符號修飾
> TODO
- list 3-4 多載了6個 `func()`,然後在 table 3-18 可以看到編譯出來的函數名稱的變化。非常詳細的修飾可以參考 GCC 的文件,書中不再贅述。
```c++=
// 開頭 '_Z' + 名稱的長度 + 名稱 + 參數類型的簡寫
// _Z 4 func i
int func(int) // _Z4funci
// 如果是有 namespace 開頭會是:
// '_ZN' + namespace1 長度 + namespace1 名稱 + namespace2 長度 + namespace2 長度 + 函式名稱長度 + 函式名稱 + 'E' + 參數類型的簡寫
// _ZN + 1 + C + 4 + func + E + i
int C::func(int) // _ZN1C4funcEi
```
- `c++filt` 可以幫忙解析被修飾過的函數名稱。
```
$ nm ./a.out | c++filt
...
00000000000011a9 T main
0000000000001120 t register_tm_clones
00000000000010c0 T _start
0000000000004010 D __TMC_END__
00000000000011d8 t __static_initialization_and_destruction_0(int, int)
U std::ios_base::Init::Init()@@GLIBCXX_3.4
U std::ios_base::Init::~Init()@@GLIBCXX_3.4
0000000000004040 B std::cout@@GLIBCXX_3.4
0000000000002004 r std::piecewise_construct
0000000000004151 b std::__ioinit
U std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)@@GLIBCXX_3.4
U std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)@@GLIBCXX_3.4
```
- 除了函數的名稱會有修飾以外,global var 也會有修飾。值得注意的是修飾得名稱結尾沒有加上 var 的 type,所以
- static var 也有使用修飾。
- 表 3-19 有記載 MSVC 的修飾方法,但是 MSVC 沒有對外公開規則。要解析的時候可以使用 API 轉換修飾名稱回原本的名稱。
- 隨著編譯器不同,修飾方法會有所不同,所以之間就互不相通。C++ ABI 和 COM 的章節會探討這個問題。
### 3.5.4 extern "C"
- 加上 `extern "C"` 之後,C++ 會將那段程式碼當作 C 語言處理,所以 C++ 的修飾就會消失。
- 如果想要讓一個函數同時可以被 C 和 C++ 編譯器編譯的話可以使用 macro `__cplusplus`。
### 3.5.5 Weak Symbol and Strong Symbol
- 這兩個 symbol 的用意是要讓 linker 判斷定義有沒有重複定義。
- 先說明如何定義出 linker 眼中的 Strong Symbol 和 Weak Symbol:
1. 有過初始化的全域變數是 Strong Symbol
2. 沒有經過初始化的全域變數是 Weak Symbol
3. 經過 `__attribute__((XXX))` 定義的就是指定的 symbol
```cpp=
extern int ext; // None of them
int weak; // Weak Symbol
int strong = 1; // Strong Symbol
__attribute__((weak)) weak2 = 2; // Weak Symbol
int main() { // main is Strong Symbol
return 0;
}
```
- 這個定義下會有一些規則:
1. linker 會將某一個變數的定義定為 Strong Symbol 的值。
2. 如果有兩個 Strong Symbol 則會回報變數重複定義。
3. 如果只出現了一個 Weak Symbol,linker 會將之視為0或是一個特殊的值為該變數的值,也就是平常說的全域變數預設為0。
4. 如果全部的 `k` 皆為 Weak Symbol 會發生什麼事情?答案是 linker 不會回報錯誤,但是定義要有歸屬,linker 會去尋找佔用記憶體最多的那個宣告。注意:這有可能造成不可預期的錯誤。
- 假如有兩個檔案: `foo.cpp` 和 `bar.cpp`
- `foo.cpp`:定義了一個全域變數 `int k = 0xf00;`
- `bar.cpp`:`include "foo.cpp"` 並且定義了一個全域變數 `int k = 0xba4;`
- linker 會回報錯誤:`k` 重複定義。
- 把兩個 `k` 其中一個賦值拔掉就不會有錯誤回報。
- 皆為 Weak Symbol:
- `foo.cpp`:定義了一個全域變數 `int k;`
- `bar.cpp`:`include "foo.cpp"` 並且定義了一個全域變數 `double k;`
- linker 不會回報錯誤,並且選擇 `double k` 作為定義。
#### Strong Reference 和 Weak Reference
- 很厲害的寫法,這是弱引用。弱引用可以隨時被強引用覆蓋,所以如果強引用出現時 `if` 下的程式碼才會被執行。書上舉例了利用弱引用判定是否為多核心執行(利用 `-lpthread` 作為開關)。
```cpp=
__attribute__ ((weakref)) void foo(); // Weak Reference
int main() {
if(foo != NULL) {
puts("HEHE foo is running.");
foo();
}
else {
puts("HOHO foo is not running.");
}
}
```
實際例子: 在沒有 link `pthread` 之前是 `B` ,link `pthread` 會變成 `A`
```cpp=
#include <stdio.h>
#include <pthread.h>
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void *),
void *restrict arg) __attribute__((weakref));
int main()
{
if(pthread_create)
{
printf("A\n");
}
else
{
printf("B\n");
}
}
```
## 3.6 Debug Information
:::spoiler `objdump -x -s -d -M intel 3-3.o`
```=
root@vultr:~/test# objdump -x -s -d -M intel 3-3.o
3-3.o: file format elf32-i386
3-3.o
architecture: i386, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000
Sections:
Idx Name Size VMA LMA File off Algn
0 .group 00000008 00000000 00000000 00000034 2**2
CONTENTS, READONLY, GROUP, LINK_ONCE_DISCARD
1 .text 00000087 00000000 00000000 0000003c 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
2 .data 00000008 00000000 00000000 000000c4 2**2
CONTENTS, ALLOC, LOAD, DATA
3 .bss 00000004 00000000 00000000 000000cc 2**2
ALLOC
4 .rodata 00000004 00000000 00000000 000000cc 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .text.__x86.get_pc_thunk.ax 00000004 00000000 00000000 000000d0 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
6 .comment 0000002c 00000000 00000000 000000d4 2**0
CONTENTS, READONLY
7 .note.GNU-stack 00000000 00000000 00000000 00000100 2**0
CONTENTS, READONLY
8 .note.gnu.property 0000001c 00000000 00000000 00000100 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .eh_frame 0000007c 00000000 00000000 0000011c 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
SYMBOL TABLE:
00000000 l df *ABS* 00000000 3-3.c
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l d .rodata 00000000 .rodata
00000004 l O .data 00000004 static_var.1511
00000000 l O .bss 00000004 static_var2.1512
00000000 l d .text.__x86.get_pc_thunk.ax 00000000 .text.__x86.get_pc_thunk.ax
00000000 l d .note.GNU-stack 00000000 .note.GNU-stack
00000000 l d .note.gnu.property 00000000 .note.gnu.property
00000000 l d .eh_frame 00000000 .eh_frame
00000000 l d .comment 00000000 .comment
00000000 l d .group 00000000 .group
00000000 g O .data 00000004 global_init_var
00000004 O *COM* 00000004 global_uninit_var
00000000 g F .text 00000032 func1
00000000 g F .text.__x86.get_pc_thunk.ax 00000000 .hidden __x86.get_pc_thunk.ax
00000000 *UND* 00000000 _GLOBAL_OFFSET_TABLE_
00000000 *UND* 00000000 printf
00000032 g F .text 00000055 main
Contents of section .group:
0000 01000000 07000000 ........
Contents of section .text:
0000 f30f1efb 5589e553 83ec04e8 fcffffff ....U..S........
0010 05010000 0083ec08 ff75088d 90000000 .........u......
0020 005289c3 e8fcffff ff83c410 908b5dfc .R............
].
0030 c9c3f30f 1efb8d4c 240483e4 f0ff71fc .......L$.....q.
0040 5589e551 83ec14e8 fcffffff 05010000 U..Q............
0050 00c745f0 01000000 8b900400 00008b80 ..E.............
0060 00000000 01c28b45 f001c28b 45f401d0 .......E....E...
0070 83ec0c50 e8fcffff ff83c410 8b45f08b ...P.........E..
0080 4dfcc98d 61fcc3 M...a..
Contents of section .data:
0000 54000000 55000000 T...U...
Contents of section .rodata:
0000 25640a00 %d..
Contents of section .text.__x86.get_pc_thunk.ax:
0000 8b0424c3 ..$.
Contents of section .comment:
0000 00474343 3a202855 62756e74 7520392e .GCC: (Ubuntu 9.
0010 342e302d 31756275 6e747531 7e32302e 4.0-1ubuntu1~20.
0020 30342e32 2920392e 342e3000 04.2) 9.4.0.
Contents of section .note.gnu.property:
0000 04000000 0c000000 05000000 474e5500 ............GNU.
0010 020000c0 04000000 03000000 ............
Contents of section .eh_frame:
0000 14000000 00000000 017a5200 017c0801 .........zR..|..
0010 1b0c0404 88010000 20000000 1c000000 ........ .......
0020 00000000 32000000 00450e08 8502420d ....2....E....B.
0030 05448303 66c5c30c 04040000 28000000 .D..f.......(...
0040 40000000 32000000 55000000 00480c01 @...2...U....H..
0050 00491005 02750041 0f03757c 067e0c01 .I...u.A..u|.~..
0060 0041c543 0c040400 10000000 6c000000 .A.C........l...
0070 00000000 04000000 00000000 ............
Disassembly of section .text:
00000000 <func1>:
0: f3 0f 1e fb endbr32
4: 55 push ebp
5: 89 e5 mov ebp,esp
7: 53 push ebx
8: 83 ec 04 sub esp,0x4
b: e8 fc ff ff ff call c <func1+0xc>
c: R_386_PC32 __x86.get_pc_thunk.ax
10: 05 01 00 00 00 add eax,0x1
11: R_386_GOTPC _GLOBAL_OFFSET_TABLE_
15: 83 ec 08 sub esp,0x8
18: ff 75 08 push DWORD PTR [ebp+0x8]
1b: 8d 90 00 00 00 00 lea edx,[eax+0x0]
1d: R_386_GOTOFF .rodata
21: 52 push edx
22: 89 c3 mov ebx,eax
24: e8 fc ff ff ff call 25 <func1+0x25>
25: R_386_PLT32 printf
29: 83 c4 10 add esp,0x10
2c: 90 nop
2d: 8b 5d fc mov ebx,DWORD PTR [ebp-0x4]
30: c9 leave
31: c3 ret
00000032 <main>:
32: f3 0f 1e fb endbr32
36: 8d 4c 24 04 lea ecx,[esp+0x4]
3a: 83 e4 f0 and esp,0xfffffff0
3d: ff 71 fc push DWORD PTR [ecx-0x4]
40: 55 push ebp
41: 89 e5 mov ebp,esp
43: 51 push ecx
44: 83 ec 14 sub esp,0x14
47: e8 fc ff ff ff call 48 <main+0x16>
48: R_386_PC32 __x86.get_pc_thunk.ax
4c: 05 01 00 00 00 add eax,0x1
4d: R_386_GOTPC _GLOBAL_OFFSET_TABLE_
51: c7 45 f0 01 00 00 00 mov DWORD PTR [ebp-0x10],0x1
58: 8b 90 04 00 00 00 mov edx,DWORD PTR [eax+0x4]
5a: R_386_GOTOFF .data
5e: 8b 80 00 00 00 00 mov eax,DWORD PTR [eax+0x0]
60: R_386_GOTOFF .bss
64: 01 c2 add edx,eax
66: 8b 45 f0 mov eax,DWORD PTR [ebp-0x10]
69: 01 c2 add edx,eax
6b: 8b 45 f4 mov eax,DWORD PTR [ebp-0xc]
6e: 01 d0 add eax,edx
70: 83 ec 0c sub esp,0xc
73: 50 push eax
74: e8 fc ff ff ff call 75 <main+0x43>
75: R_386_PC32 func1
79: 83 c4 10 add esp,0x10
7c: 8b 45 f0 mov eax,DWORD PTR [ebp-0x10]
7f: 8b 4d fc mov ecx,DWORD PTR [ebp-0x4]
82: c9 leave
83: 8d 61 fc lea esp,[ecx-0x4]
86: c3 ret
Disassembly of section .text.__x86.get_pc_thunk.ax:
00000000 <__x86.get_pc_thunk.ax>:
0: 8b 04 24 mov eax,DWORD PTR [esp]
3: c3 ret
```
:::
```bash=
readelf -W # disable line wrap
```
## 參考
https://nano-chicken.blogspot.com/2011/07/elf02-elf-header.html
https://blog.csdn.net/helowken2/article/details/113739946
https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-43405/index.html
https://lu-yi-hsun.github.io/posts/linux/elf/#e_shstrndx
https://docs.oracle.com/cd/E19683-01/816-1386/6m7qcoblj/index.html#chapter6-35166