%%
# 纲要
> 主干纲要、Hint/线索/路标
# Q&A
#### 已明确
![[02-开发笔记/03-计算机基础/编译与链接/目标文件#^za5xa8]]
![[02-开发笔记/03-计算机基础/编译与链接/目标文件#^40auy0]]
#### 待明确
> 当下仍存有的疑惑
**❓<font color="#c0504d"> 有什么问题?</font>**
# Buffer
## 闪念
> sudden idea
## 候选资料
> Read it later
%%
# 目标文件
目标文件(object file):编译器根据给定的**源代码文件**生成的**保存目标代码 (object code) 的文件**。
目标文件分为三种:
| | 说明 | 文件后缀(Linux) | 文件后缀(Windows) |
| ----------------------------------- | ------------------------------------- | ------------------------ | ------------- |
| **可重定位目标文件** <br>(Relocatable File) | 保存目标代码的文件,可与其他可重定位目标文件合并从而创建一个可执行目标文件 | `.o` | `.obj` |
| **共享目标文件**<br>(Shared Object File) | 在**加载或者运行时被动态加载进内存并链接**的特殊可重定位目标文件 | `.so` | `.dll` |
| **可执行目标文件**<br>(Executable File) | 可**直接运行**的**程序**(二进制可执行文件) | ELF 可执行文件 <br>通常**无后缀名** | `.exe` |
- 汇编器生成 "**可重定位目标文件**"(包括"**共享目标文件**")
- 链接器生成 "**可执行目标文件**"
<br>
# 不同系统上的目标文件格式
不同操作系统上的**目标文件格式**各不相同:
- Windows 采用 "**可移植可执行格式** (Portable Executable, **==PE==**) ,文件后缀 `.exe`
- Linux/Unix 采用 "**可执行可链接格式**"(Executable and Linkable Format,**==ELF==**),文件后缀 `.out`(可省略)
- Mac OS 采用 Mach-O 格式
<br>
# ELF 文件格式
ELF 文件的典型布局[^1]:
![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-1E44CA6992F9DEBB448E0024FA38A1B2.png|887]]
> [!caution] ELF 文件中的节不仅限于上图!上图只列出了典型的节。
> [!info] 在 ELF 可执行文件中的==节/片(chunk)==在程序运行时将会被映射到**==连续的内存段==**
> [!note] Linux 下的 `readelf` 命令行工具可查看 ELF 目标文件内容
> [!example] `file` 命令可查看文件类型
>
> 如下所示,该文件为 ELF 可执行文件。
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-273F67D911927C26C8CC1A8BB48B0738.png|945]]
>
>
<br><br>
## ELF 头 (ELF header)
ELF 头包括以下信息:
- **生成该文件的系统的字的大小**、**字节顺序**(大小端);
- **目标文件的类型**(可重定位、可执行或共享库)
- **机器类型**(如 x86-64):可运行该程序的机器平台
- **ELF 头的大小**
- **程序入口地址**
- **程序头部表**在文件中的偏移量
- **程序头部表**中**条目的大小和数量**
- **节头部表**在文件中的偏移量
- **节头部表**中**条目的大小和数目**
- **节头字符串表的下标**(该表`.shstrtab` 在**节头部表**中的下标)
> [!tip] `readelf -h` 命令可查看 `ELF` 头
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-139310BC4198C56BE78FEFB519E4A1CC.png|477]]
>
>
> `Magic` 称为 "**ELF 魔数**",共 16 字节,对应**结构体**中的 `e_ident` 项。
>
> ELF 魔数解读如下:**前四个字节为 "ELF 文件" 的固定==标识值==**;第五字节标识 "**ELF 文件类型**",第六字节标记 "**字节序**"。
>
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-53DCB0916334D0EEA179FA8740F21881.png|483]]
> ^jooax4
> [!info] ELF 文件头的 "结构体" 及相关常数定义在 `/usr/include/elf.h` 中,64 位版本下名为 `Elf64_Ehdr`
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-7E8D5DB2C006E06BA78981E504AB764B.png|454]]
>
>
> ^bqrtq0
<br><br>
## "节头部表" 与 "程序头部表" 的区别
- **节头部表**(Section Header Table)
- **程序头部表**(Program Header Table),也称 "**段头部表**"(Segment Header Table)
ELF 文件中以 "**==节==**" 为单位来**划分不同部分**,例如 `.text`、`.data` 节等,由 "**==节头部表==**" 来描述。
**其中一些节**在 ELF 文件作为进程运行时,会**被加载到内存**,此时称之为 "**==段==**"(映射段),**一个段可能包含多个节**,由 "**==程序头部表==**" 来描述。
故 "节" 与 "段" 的区别总结如下:
| 名称 | 作用 | 使用对象 |
| -------------- | ----------- | ----------- |
| Section(**节**) | 编译/链接单位 | 编译器、链接器、调试器 |
| Segment(**段**) | 用于**运行时加载** | OS 内核中的加载器 |
如下图所示:
![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-CCA389C88DAB86D5EA3E851D44FC0DC1.png|317]]
> [!NOTE] 各个 "段" 所包含/对应的 "节" 可通过 `readelf -l <program>` 查看 "**程序头部表**" 获知
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-4E6CB6C69CD9813348CF3F048EB78ACD.png|493]]
>
<br><br>
## ELF 文件中包含的表
ELF 文件中涉及的主要**表**:「**==符号表==**」、「**==重定位表==**」、「**==字符串表==**」、「**==节头部表==**」、「 **==程序头部表==** | 段头部表」、
| | 表所位于的节 |
| ----------------- | ----------------------------------------- |
| **程序头部表 \| 段头部表** | ×(不属于任何节,紧随 ELF 头之后)<br>仅存在于**可执行目标文件** 中 |
| **节头部表** | ×(不属于任何节,位于 ELF 文件**最末尾段**) |
| **符号表** | `.symbol` 节 |
| **重定位表** | `.rel.text` 与 `.rel.data` 节等 |
| **字符串表** | `.strtab` 节、`.shstrtab`节、`.dynstr` 节等 |
此外,还有与**动态链接 "共享库"** 相关的表(用以支持共享库的 "**位置无关特性**" 和 "**延迟绑定**"):
- **全局偏移量表 GOT**
- **过程链接表 PLT**
参见[[02-开发笔记/03-计算机基础/编译与链接/静态库与动态库#共享库的 "位置无关" 特性|静态库与动态库-共享库的位置无关特性]]
<br><br><br>
# 节(Section)
ELF 文件以 "**==节==**" 为单位来**划分不同部分**,每个节是**一段连续的字节序列**,例如 `.text`、`.data` 节等,各个节的信息由 "**==节头部表==**" 来描述。
常见的 "节" 如下:(按文件的**低字节到高字节顺序** 排列)
| 节 | 说明 | 备注 |
| ------------- | --------------------------------------------------------------------------------------------------------- | ---------------------- |
| `.init` | 程序**初始化代码段**,保存着 "**进程初始化指令**"。 | 仅 "**可执行目标文件**" 中存在 |
| `.plt`<br> | **动态链接的跳转表** | |
| `.text` | 为该程序编译生成的 **机器代码** | |
| `.fini` | 程序**终结代码段**,保存着**进程终止代码指令**。 | |
| `.rodata` | **==只读==数据**(例如**字符串字面量**、**`const` 全局常量**) | |
| `.init_array` | **初始化函数数组**(函数指针数组,指向一系列**全局初始化函数**,将在进入 `main` 之前被执行) | |
| `.fini_array` | **清理函数数组**(函数指针数组,指向一系列**清理函数**,在进程正常从 `main` 退出或调用 `exit` 退出时将被执行) | |
| `.dynamic` | **动态链接信息** | |
| `.got` | **动态链接的全局入口表** | |
| `.data` | **==已初始化==**的**全局变量**和**静态变量** | |
| `.bss` | **==未初始化==**的 & **==初始化为 0==** 的**全局变量**和**静态变量** <br>(`.bss` 节在目标文件中不占空间,仅做占位符,在运行时才会为这些变量分配内存并初始化为 0) | |
| `.comment` | **编译器版本信息**,例如字符串 "GCC: (GNU) 4.2.0" | |
| `.rel.text` | **==重定位表==**,存放 `.text` 节中**需要重定位的地址位置**列表(通常涉及**外部函数调用**或**全局变量的引用**) | 通常仅 "**可重定位目标文件**" 中存在 |
| `.rel.data` | **==重定位表==**,存放 `.data` 节中**需要重定位的地址位置**列表(通常涉及**全局变量**或**静态变量的引用**) | 通常仅 "**可重定位目标文件**" 中存在 |
| `.debug` | **==调试符号表==**,其条目是**程序中定义的局部变量和类型定义、程序中定义和引用的全局变量、以及原始的 C/C++ 源文件**。 | **仅通过 `-g` 选项编译**时才具有 |
| `.line` | **==调试行号表==**:原始 C/C++ **源代码中的==行号==和 `.text` 节中==机器指令==之间的==映射表==**。 | **仅通过 `-g` 选项编译**时才具有 |
| `.symtab` | **==符号表==**,存放程序**定义、引用的==函数==** 以及 **==全局变量==** 等信息(不包含**局部变量**信息)<br> | |
| `.strtab` | **==字符串表==**,存储 ELF 文件中用到的所有字符串,包括 **`.symtab` 和 `.debug` 节的符号** | |
| `.shstrtab` | **==节头字符串表==**(Section Header String Table)——专用于存储**每个节(section) 的名称** | |
> [!caution]
> - `.debug` 和`.line` 两节仅在 "**通过 `-g` 选项(==debug 模式==)编译**" 时才会得到这两张表。
> - `.rel.text` 和 `.rel.data` 两节对于 "**可执行目标文件**" 而言通常会被 **==省略==**,因为其**不再需要重定位**。
> [!NOTE] 关于重定位
>
> 当链接器**将多个目标文件合并成一个可执行文件或共享库**时,它需要调整每个目标文件中的地址。
>
> - `.rel.text` 节、`.rel.data` 节中包含的信息分别用于**更新 `.text` 、`.data` 节中的代码/数据,使其引用正确的地址**。
> [!info] 关于 "**初始化函数数组**" `.init_array`
>
> `.init_array` 是 ELF 中的一个特殊段,存放了一组 **函数指针**,指向 "**在==进入 `main` 函数之前必须被调用的函数==**"。
>
> 这些函数是编译器**在链接阶段自动收集到的==全局初始化函数==**,包括:
>
> - **==静态存储持续性 "对象"==** 的**构造函数**(针对 "**类类型**" 的全局变量,`static` 全局变量)
> - 基本数据类型的初始化值本身就在 ELF 的 `.data` 节中,而后被加载入内存
> - **`__attribute__((constructor))` 标记的函数**
> - **部分库的内部初始化函数**(例如 `libstdc++` 中的)
> ^jxfmj2
> [!info] 关于 "**清理函数数组**" `.fini.array`
>
> `.init_array` 是 ELF 中的一个特殊段,存放了一组 **函数指针**,指向 "**程序==通过 `main` 或 `exit`正常退出时需执行的清理函数==**",
>
> 这些函数是编译器**在链接阶段自动收集到的==全局初始化函数==**,包括:
>
> - **==静态存储持续性 "对象"==** 的**析构函数**(针对 "类类型")
> ^1zl2gv
![[02-开发笔记/03-计算机基础/机器视角下的进程#^xt6kcg]]
<br>
### 自定义节
GCC 提供了一个扩展机制,支持将**全局变量或函数放置**到**指定的自定义 "节"** 中:
在**全局变量或函数声明之前**加上`__attribute__((section ("name")))` 属性即可将该变量或函数放到以“name”作为节名的节中。
> [!example] "自定义节" 示例
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-DCA9E05F5319576711DB73A76756DE1F.png|783]]
---
<br><br><br>
# 节头部表
> 节头部表(Section Header Table)
「**节头部表**」中记录了 ELF 文件中 **==每个节的信息==**:**节名**、**节大小**、**节的类型**、**在文件中的偏移位置**、**节的读写权限及链接信息等其他属性**。
每个节在「节头部表」中占一个"**条目**",称之为 "**节描述符**"。
编译器、链接器都 **依赖节头部表** 来定位和访问各个节的属性。
> [!tip] `readelf -S <file>` 可查看 ELF 的节头部表
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-67E835A140646993A973D89B9B1A42F3.png|967]]
>
>
> ^ewr1r0
### 节头部表条目
节头部表是一个由 "**节描述符**" 对象构成的**数组**。
> [!info] ELF 节头部表条目的 "结构体" 定义在 `/usr/include/elf.h` 中的 `Elf64_Shdr`
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-0B08981B2FE7C1AF95EA8503FF2A9BDC.png|467]]
>
> ^3ke2al
---
<br><br><br>
# 程序头部表 | 段头部表
> 程序头部表 (Program Header Table,**PHT**),也称 "段头部表"(Segment Header Table)
「**程序头部表**」描述了 ELF 文件**在被 OS 内核加载到内存作为进程运行时**,**==所需被加载的段==、==段的映射地址==、==权限==**。
### 程序头部表信息说明
> [!tip] `readelf -l <program>` 可查看 ELF 文件的程序头部表
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-451FDF7A2A00AB3965B20BADC60218A3.png|908]]
>
> ^mppzb7
> [!NOTE] 程序头部表中,只有 **=="LOAD 段"==** 会被 OS 内核加载至内存,其余段实际上都是 "**==被包含==在某个 LOAD 段中**" 的。
其余段说明(这些段都被包含在某个 LOAD 段中):
- `PHRD`:指示 "**程序头部表**" 本身的位置
- `INTERP`:指示 OS 内核所应使用的 "**动态链接器**";
- `DYNAMIC`:**被动态链接器读取**(如 `.dynamic` 在数据段里)
- `NOTE`:包含调试信息、build-id、ABI 版本等信息,通常用于 core dump、调试等;
- `GNU_STACK`:仅用于 **标记程序的 "栈" 是否可执行**,本身不占内存(`p_memsize` 为 0)
- 通过权限位标记,上图中 **RW 表示不可执行**,为默认值
- `TLS`:**线程局部存储** 段——每个新线程在被创建时,系统库会根据 **该段指示的模板** 为该线程**分配其特有的 TLS 内存区域**(Thead-Local Storage);
- 主线程的 TLS 区域由 `__lib_start_main()` 根据该段进行**分配** & **初始化线程局部变量**;
- 其余线程的 TLS 区域由 `pthread_create()` 被调用时根据该段进行**分配** & **初始化线程局部变量**;
> [!info] "栈是否可执行" 是指 "栈上是否**可==存放并执行机器指令==**"。
>
> 如果进程的栈区是可执行的,意味着可将一段机器指令写入栈上,并 `jmp` 到栈上去执行该指令,这即是 "**栈缓冲区溢出**" 的攻击原理。
> 当栈不可执行时,CPU 在执行跳转后会检测到 “**目标地址所在的页是不可执行的**”,然后 **抛出段错误**。
>
<br>
## 程序头部表的条目
程序头部表是一个由 "**条目**" 对象构成的**数组**。
> [!info] ELF 程序头部表的 "结构体" 定义在 `/usr/include/elf.h` 中的 `Elf64_Phdr`(针对 64 位 ELF 文件)
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-5581973E77B76F8C750FF12E13991DB8.png|509]]
>
> ^r0fk4c
<br><br><br>
# 符号表
「**符号表**」存储于 **==`.symtab` 节==**,记录了 **ELF文件** 中具有的**所有==符号==信息**:包括**函数名**、**变量名**(静态存储持续性变量)、**源文件名**、**ELF 文件中的节名**等等。
每个**可重定位目标==模块== $m$**(即**可重定位目标文件**)都具有一个**符号表**。
> [!NOTE] 符号表由 **汇编器** 构造,使用了编译器输出到汇编文件(`.s` )中的符号。
> [!tip] `readelf -s <file>` 命令查看 ELF 目标文件的符号表
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-C21BD617265D2AF319C2A2259B5533A6.png|915]]
>
> ^9fkh3l
> [!tip] `nm` 工具可查看 ELF 文件中的符号
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-70EC5140DE1833A2401371B83D43AB8E.png|482]]
>
> ^zcvjd3
<br>
## 符号类型
符号类型一共有五种:与源代码内容相关的 **全局符号**、**外部符号**、**局部符号**,以及由编译器产生的**节名称**、**行号信息**。
在**可重定位目标模块 $m$ 的上下文**中,三种与源代码内容相关的符号为:
| 模块 $m$ 上下文中的符号类型 | 对应于 C/C++中的链接性 | 对应于 C/C++中的变量 or 函数类型 |
| ------------------------------------------------ | -------------------- | ----------------------------------------------------------------------------------------------------------------- |
| 由 **模块 $m$ 定义** 并可供 **其他模块引用** 的「**==全局==符号**」 | 外部链接性 | 由模块 $m$ 定义的**非静态函数**、**非静态全局变量** |
| 由 **其他模块定义** 并被 **模块 $m$ 引用** 的 「**==外部==符号**」 | 外部链接性 | 由其他模块定义的**非静态函数**、**非静态全局变量**<br>(在模块 $m$ 内通过 `extern` 声明引入) |
| 由 **模块 $m$ 定义** 且仅供 **自身引用** 的「**==局部==符号**」 | (1)内部链接性;<br>(2)无链接性 | (1)由模块 $m$ 定义的 **`static` 函数、`static` 全局变量、<br>`const` 或 `constexpr` 全局常量**; <br>(2)由模块 $m$ 定义的函数中的 "**局部静态变量**" |
> [!caution]
>
> - `.symtab` 符号表包含 "**==局部静态变量==**" 的条目!
> - `.symtab` 符号表不包含 "**==局部非静态变量==**",这类变量由栈管理,"**无链接性**",在符号表中没有对应条目!
>
<br>
## 符号表条目
符号表是一个由 "**符号表条目**" 对象构成的**数组**。
每个符号表条目包含以下信息:
- **符号名**(在符号表中存的是对 "**符号字符串表**" 中字符串的索引值)
- **符号值**(符号对应的**绝对值或==地址==**)
- **符号大小**(符号对应的数据类型大小,字节数)
- **符号类型** 与 **绑定信息**
- **符号所在的节**
> [!info] ELF 符号表条目的 "结构体" 定义在 `/usr/include/elf.h` 中的 `Elf64_Sym`(针对 64 位 ELF 文件)
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-FF53B06826221F226CF964CBECC242E2.png|529]]
>
> ^ify4v2
### 符号类型与绑定信息 `st_info`
- **绑定信息**:标识 "**符号**" 在不同编译单元间的 "**链接性**"
- **符号类型**:标识符号——数据对象、函数、节名、源文件名等。
> 参见 [^2]
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-B8FA002444FB28DCA68D2406CB31567B.png|549]]
> [!NOTE] `<Elf.h>` 头文件中提供了宏,用于分别**提取 "binding" 与 "type" 信息**。
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-442670C91D8E4FE38A957ECD3007FCDF.png|515]]
>
<br>
### 符号所在节 `st_shndx`
- 若符号**定义在本目标文件**中,则该字段值为 "**符号所在节的下标**(节在节头部表中的下标)"
- 若符号**未定义在本目标文件**中(而是引用),则该字段值为**三种 "==伪节==" 标识之一**。[^3](P83)
> [!NOTE] 伪节
>
> "伪节" 在**节头部表**中没有对应的条目,只有三种值,用以在 "**符号所在段`st_shndx`**" 标识特殊情况:
>
> - `ABS`:**表示其为一个固定值/字面量,不需要被重定位**
> - `COMMON`:**尚未被分配位置的**、**未初始化的全局符号定义(即未初始化的全局变量)**
> - `UNDEF`:**未定义的符号**——**在本模块中引用**,但**未找到其定义的符号**
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-6F8F535DAD9BF82B8D1CCBABD0A3D036.png|502]]
>
> 注:对于**未初始化的 "全局变量" 符号**,其所属节标识为 `COMMON` 还是 `.bss`,取决于编译器实现
>
> [!example] `UND` 符号示例
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-42079D7F0F9ACB4C63FCB046FE79EA42.png|548]]
>
>
<br>
### 符号值 `st_value`
若该**符号**是一个 **"变量" 或 "函数"**,则符号值为**变量或函数的地址**。
> [!caution] 对于可重定位目标文件中引用的 "**外部符号**",值字段为 0。
![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-8A71C6C11318B593F04DF8FED70ED2F2.png|661]]
## 符号定位示例
> ❓ <font color="#c00000">如何确定符号表中某个 "符号" 在 ELF 可执行文件中的位置?</font> ^za5xa8
步骤如下:
- (1)通过 `readelf -S <file>` 查看 "**节头部表**",确定 "**==字符串表==**" 在 ELF 文件中的**起始偏移地址**;
- (2)通过 `hd <file> | nvim` 以 "**十六进制形式**" 查看 ELF 文件内容,在其中定位到 **=="字符串表" 起始位置==**。
- (3)在**字符串表**所在区域,可 **定位到所要查找的 "符号"**。
> [!example]
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-0EECEC2900325A3CC4EFEFFB9BADBC84.png|958]]
>
---
<br><br><br>
# 字符串表
**「字符串表」** 集中存储了 **ELF 文件中使用到的所有字符串**,例如**节的名称**、**变量名**、**函数名** 等。
**每个字符串表**在 ELF 文件中都作为 "**一个==节==**" 进行存储。
### 字符串表的本质
字符串表本身是一个 "**字节数组**",其中**每个条目为以空字符 `'\0'` 结尾的字符串**。[^3](P80)
**每个字符串通过其在字符串表中的 ==起始字节偏移量== 来引用**。
![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-3BE4ACC1C42AD2C95EA717908499A89A.png|479]]
> [!caution] 字符串表的**第一个字节固定为一个空字符(`'\0'`)**,表示空字符串。
> [!faq] ❓<font color="#c0504d">为什么需要使用字符串表?</font>
>
> 原因:**字符串的长度不固定**,因此**难以用固定结构**表示。
>
> ELF 文件采用了 **==将所有字符串集中存放==的方式**——使用一个 **==字符数组==**(即**字符串表**)来存放所有字符串。
> 由此,在 ELF 中引用 **字符串内容** 时只需给定 "**==索引值==**" ——即**该字符串在字符串表中的==起始字节位置==**。(符号表中存储的就是该索引只)
>
> ^40auy0
<br><br>
## 几个主要的 ELF 字符串表
ELF 文件中的几种**主要字符串表**包括:
| 字符串表所在节 | 字符串表名称 | 说明 |
| ----------- | -------------- | ----------------------------------------------------------------- |
| `.strtab` | **==符号==字符串表** | 存储了**符号表`.symtab`** 中使用的字符串,例如**所有符号名称** |
| `.shstrtab` | **==节头==字符串表** | 存储了**节头表**(section header table)中使用的字符串,例如 **各个==节 section的名称==** |
| `.dynstr` | **==动态==字符串表** | 存储了**动态符号表** `.dynsym`中的**所有符号名称**和**其他动态链接所需的字符串** |
> [!note] **符号表**中存储的是 "**对==字符串表==中字符串的索引**"
>
> 在各个**符号表**、**节头部表**、**动态符号表**的条目中,"**名称**" 字段所存的都是一个 **==索引值==**,即**该名称在对应字符串表中的==起始字节位置==**。
> [!tip] `readelf -p <字符串表所在节的名称> file` 命令可查看字符串表
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-C8FA8C2B25E3FBFAEFD8F9906307A861.png|613]]
>
> ^th43c6
> [!note] 区别 "`.rodata` 中字符串" 与 "`.strtab` 中字符串"
>
> - `.rodata`:只读数据段,存储**程序中使用的 ==字符串字面量== 与==全局常量==**,这些**是供程序运行时访问**的。
> - `.strtab`:符号字符串表,存储符号信息,**供链接器和调试器使用**。
>
> 例如,`.strtab` 中会**包含所有源文件名称**。如果在程序里**使用了 `__FILE__`**,则其**对应的值**(即源文件名)将被存入 `.rodata` 段。
<br><br><br>
# 重定位表
> 关于 "**重定位**" 的说明,参见[[02-开发笔记/03-计算机基础/编译与链接/链接#重定位|链接-重定位]]
「**重定位表**」中记录了 "**每个需要被重定位的位置**",称之为 "**==重定位入口==** "( Relocation Entry)
每个 "**重定位入口**" 都是**对一个符号的==引用==**。
每个 "**需要重定位的代码段或数据段**" 都会有一个**相对应的重定位表**:
- `.rel.text` 是针对 `.text` 节的重定位表;
- `.rel.data` 是针对 `.data` 节的重定位表
**每个重定位表**都作为 ELF 文件中的 "**一个==节==**" 进行存储,节的类型 (`sh_type`) 为 `SHT_REL`。
> [!tip] 通过 `realelf -r` 或 `objdump -r` 命令查看重定位表
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-3362998A1E49A5836A178D4A18338E1D.png|670]]
>
>
<br>
### 重定位表的条目
重定位表中每个条目(**重定位入口**)标识一个**需要进行重定位的位置**,包括两个字段、三个信息:
- **偏移 Offset**:**需要重定位的位置的首字节** 在其 **==所在段/节==的偏移量**;
- **重定位的类型**:指定**如何进行重定位**,例如,简单的地址加法,或更复杂的操作
- **符号索引**:指向**符号表中的一个条目**,表示**要进行重定位的符号**。
> [!info] ELF 重定位表的 "结构体" 定义在 `/usr/include/elf.h` 中的 `Elf64_Rel`(针对 64 位 ELF 文件)
>
> ![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-98B506EDBDCDDAD252606A2E38C51CB0.png|527]]
> ^12b9o4
> [!example] `objdump` 显示的反汇编代码中可见 "**==重定位入口==**"
>
![[_attachment/02-开发笔记/03-计算机基础/编译与链接/目标文件.assets/IMG-目标文件-46EC8CF97F8CD9F4F9D4F841DCE358F3.png|1028]]
---
<br><br><br>
# ELF 格式信息相关的「结构体」定义
![[02-开发笔记/03-计算机基础/编译与链接/目标文件#^bqrtq0]]
![[02-开发笔记/03-计算机基础/编译与链接/目标文件#^3ke2al]]
![[02-开发笔记/03-计算机基础/编译与链接/目标文件#^r0fk4c]]
![[02-开发笔记/03-计算机基础/编译与链接/目标文件#^ify4v2]]
![[02-开发笔记/03-计算机基础/编译与链接/目标文件#^12b9o4]]
<br><br><br>
# 参考资料
# Footnotes
[^1]: 《深入理解计算机系统》(P467)
[^2]: 《程序员的自我修养——链接、装载与库》(P83)
[^3]: 《程序员的自我修养——链接、装载与库》
[^4]: 《深入理解计算机系统》(P485)