蔚淦丞
发布于 2026-09-10 / 1 阅读
0
0

ELF 文件与常用 ELF 节详解

操作系统学习笔记:ELF 文件与常用 ELF 节详解

一、基础前置认知

1. 什么是 ELF 文件

Windows 系统的可执行程序是 .exe 格式,Linux 系统下可执行程序、动态库、编译中间目标文件,统一遵循ELF 格式

ELF 是磁盘上的静态文件,程序未运行时存储在硬盘中。文件内部被划分成多个section(节),用于分类存放程序代码、常量、全局变量元数据与初始数据。

执行程序时,操作系统不会直接拷贝 ELF 数据,而是通过页映射的方式,将 ELF 的多个 section,按权限合并映射为进程虚拟内存中的 segment(内存段),供运行中的进程使用。

2. section 和 segment 的概念区分

section(节):ELF 文件内部的划分单元,服务编译器、链接器,用来区分代码、常量、全局变量。.text.rodata.data.bss 都是 section。操作系统加载程序时不读取 section 信息。

segment(段):由链接器生成,写在 program header(程序头),服务操作系统加载器。它把若干 section 打包在一起,规定映射到虚拟内存后的地址、内存读写执行权限。内核加载程序只读取 program header,按照 segment 的规则设置虚拟内存。

简单类比:ELF 文件像一个箱子。section 是箱子内部一个个贴了标签的小抽屉;segment 是写在箱子外面的搬运说明,告诉操作系统搬运时怎么摆放、设置权限。搬运工(内核加载器)只看搬运说明,不会看抽屉上的标签。

  • 多对一是常态:一个 segment 可以包含多个 section;一个 section 极少情况可以拆分到多个 segment。

  • section 描述信息保存在 section header table;segment 描述信息保存在 program header table。

  • section 属于静态概念(文件躺在硬盘上);segment 用来描述加载之后虚拟内存的区域。

  • 磁盘文件权限(chmod 设置的 rwx),和 segment 定义的进程内存权限,二者完全独立,不要混淆。

3. 默认打包分组(gcc 默认链接脚本)

  • segment1(r-x 可读可执行):只打包 .text

  • segment2(r-- 只读):只打包 .rodata

  • segment3(rw- 可读可写):打包 .data + .bss

二、常用 ELF 节完整详解(命名 + 存储 + 权限 + 特性)

这四类 ELF 节仅存储编译期确定的指令、全局数据、静态数据,不包含函数局部变量、运行期动态内存。所有命名均对应英文原意,无需死记硬背。

1. .text

命名由来:源自老式计算机术语 text,特指程序机器指令,非普通文字。 存储内容:程序编译生成的全部 CPU 机器指令,包含所有自定义函数、程序业务逻辑代码。 磁盘状态:真实占用 ELF 文件磁盘空间,完整存储指令数据。 内存权限(标准)可读、可执行、不可写(r-x)。禁止运行时修改,防止程序逻辑被篡改。 核心特性:多进程运行同一个程序时,该内存页可共享,不会重复拷贝物理内存,节省资源;进程修改代码会触发写时复制。

2. .rodata

命名由来:read-only data,直译只读数据。 存储内容:程序中所有只读常量,典型为字符串常量、全局 const 修饰的常量。 磁盘状态:真实占用 ELF 文件磁盘空间,完整存储常量数据。 内存权限(标准、安全核心)可读、不可写、不可执行(r--)关键说明:现代 Linux 默认开启 NX 数据执行保护,严格隔离指令与数据。数据区域绝对禁止执行,是防溢出攻击的基础保障。 示例char *str = "hello world"; 中的字符串常量存储在该节。

3. .data

命名由来:data,特指已初始化的可读写数据。 存储内容:定义时赋予初始值的全局变量、静态局部变量。 磁盘状态:真实占用 ELF 文件磁盘空间,文件中保存变量的初始值数据。 内存权限:可读、可写、不可执行(rw-),程序运行中可修改变量值。 示例int g_num = 10;static int s_num = 20;

4. .bss

命名由来:Block Started by Symbol,历史汇编遗留术语,无直观字面含义,仅记特性即可。 存储内容:未初始化的全局变量、未初始化的静态变量。 磁盘状态:仅在 ELF 头部记录所需内存大小,不占用文件数据区磁盘空间,可执行文件体积更小。 内存特性:程序加载时,操作系统自动在虚拟地址空间开辟对应内存,并全额清零。这是 C/C++ 未初始化全局、静态变量默认值为 0 的根本原因。 内存权限:可读、可写、不可执行(rw-)。 示例int g_empty;static int s_empty;

三、程序从磁盘到内存的标准加载流程

程序加载无一次性拷贝,全程基于虚拟内存页映射、缺页加载,是 Linux 核心机制:

  1. 磁盘静态状态:ELF 文件保存.text.rodata.data的真实数据;.bss仅记录内存尺寸,无数据。

  2. 启动程序:操作系统为新进程创建独立的虚拟地址空间。

  3. 建立页映射:操作系统按 segment 分组规则,建立虚拟地址与磁盘文件的映射关系,不立刻加载数据到物理内存

  4. 缺页加载:程序首次执行代码、读取常量时触发缺页异常,内核才将对应磁盘数据载入物理内存。

  5. 初始化 BSS:操作系统开辟 BSS 虚拟内存并清零,完成程序内存初始化。

四、堆、栈与 ELF 节的本质区别

堆、栈不存在于 ELF 文件中,不属于编译期静态内存,是进程运行期动态开辟的内存:

  • 栈(Stack):进程创建时由内核自动开辟,存放函数局部变量、栈帧、返回地址,随函数调用动态创建、销毁。

  • 堆(Heap):程序运行后通过 malloc /mmap 动态申请,由用户手动管理生命周期。

五、核心信息汇总表

表格

ELF 节名称

英文原意

核心存储内容

磁盘数据占用

标准内存权限

.text

text(机器指令)

程序代码、机器指令

占用

可读、可执行、不可写

.rodata

read-only data(只读数据)

字符串、全局 const 常量

占用

可读、不可写、不可执行

.data

data(初始化数据)

已初始化全局 / 静态变量

占用

可读、可写、不可执行

.bss

Block Started by Symbol(历史术语)

未初始化全局 / 静态变量

不占用数据区

可读、可写、不可执行


评论