ELF文件结构详细作用对比,以及程序入口函数讲解
在 ELF(Executable and Linkable Format)文件结构中,各个组成部分就像是一栋大楼的蓝图与建筑材料。以下是 ELF 文件核心结构的详细作用与对比总结表:结构名称主要作用核
| 结构名称 | 主要作用 | 核心包含内容 | 谁会使用它 | 比喻/类比 |
|
ELF Header (ELF 头) |
文件的总纲领与魔数校验。 定义整个文件的基本属性和文件头的整体布局信息。 |
魔数(判断是否为 ELF)、架构类型(x86/ARM)、位数(32/64 位)、程序入口地址、Program Header Table 的偏移与大小。 |
操作系统加载器、 链接器( |
整栋大楼的概括介绍与目录 |
|
Program Header Table (程序头表) |
指导程序如何加载到内存。 告诉操作系统在运行时如何将文件的不同部分映射到进程的虚拟地址空间。 |
段的类型(如 LOAD 可加载段)、读写执行权限(R/W/X)、文件偏移量、虚拟内存映射起始地址与内存大小。 |
操作系统加载器 (运行时使用) |
施工队把材料搬进现场的“搬运路线图” |
|
代码段 ( |
存放核心的机器指令。 包含 CPU 可以直接读取并执行的二进制代码(如函数体、循环逻辑等)。 |
C/C++ 源码编译后的 CPU 机器指令代码。 |
CPU 核心 (只读、可执行) |
大楼里的自动化机械与执行机构 |
|
数据段 ( |
存放程序运行所需的全局/静态变量。
|
|
程序中的变量读写指令 (可读、可写) |
大楼里的工具箱与原料储备 |
|
符号表 ( |
记录函数与全局变量的名与实。 建立“函数/变量名称”与“内存偏移地址/段位置”之间的映射映射关系。 |
符号名称(如 add)、符号类型(函数/变量)、符号绑定属性(局部/全局)、符号对应的地址或偏移量。 |
链接器(编译阶段)、 调试器(如 |
大楼里的“花名册”与“门牌号对照表” |
核心记忆要点:
-
链接视角 vs 运行视角:
-
符号表(.symtab)、代码段(.text)、数据段(.data) 是编译链接阶段(
.o文件)关注的重点,属于 Section(节) 的范畴。 -
Program Header Table 是程序运行阶段(
.out/ 可执行文件)关注的重点,告诉系统怎么把各种 Section 组合成 Segment(段) 加载到内存里。
-
-
ELF Header 是入口:
-
操作系统要运行一个 ELF 文件,首先读取的就是最开头的 ELF Header,从中找到 Program Header Table 的位置 以及 程序的入口指令地址。
-
程序的真正入口绝对不是 main 函数,而是一个叫做 _start 的符号(入口函数)。
main 只是 C/C++ 语言规范层面的入口,但在 ELF 视角和操作系统眼里,_start 才是真正的程序第一行代码地址。
1. 为什么入口是 _start 而不是 main?
在你的代码执行到 main 之前,程序需要先进行复杂的初始化准备工作,这些工作都是在 _start 中触发的:
-
分配/初始化运行环境:设置栈指针、初始化全局变量、加载 C 标准库等。
-
传递命令行参数:将
argc、argv以及环境变量envp整理好,作为参数传给main。 -
注册退出回调:通过
atexit注册程序退出时需要清理的资源。
2. 从 _start 到 main 的完整调用链
当你执行 ./a.out (已经生成的可执行文件)时,真正的执行流程如下:
操作系统 (内核加载 ELF)
↓
[ELF Header 中的 Entry Point Address] -> 指向 _start
↓
_start (在 C 运行时库 crt1.o 中定义)
↓
__libc_start_main (glibc 提供的核心函数,负责初始化环境)
↓
main() <-- 终于来到你写的 C/C++ 代码!
↓
exit() / return (收尾清理并退出进程)
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)