%% # 纲要 > 主干纲要、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)