【Linux】ELF 是什么?一文拆解 Linux 程序 “从零件到运行” 全流程
ELF 本章的大致内容
ELF(Executable and Linkable Format)是Linux系统中可执行文件的标准格式。它描述了程序如何从编译后的静态库(如.a文件)一步步形成可执行程序,并最终加载到内存运行。通俗来说,就像一本说明书:编译时,代码被拆成多个“零件”(Section);加载时,这些零件被组装成“模块”(Segment),便于操作系统高效管理。下面我们结合图片和关键点,一步步拆解这个过程。
静态库如何形成可执行程序
静态库(如.a文件)是一堆预编译代码的集合。当我们编译程序时,链接器会将这些库中的代码和我们的代码合并,生成一个ELF可执行文件(如a.out)。例如,用C语言写一个程序并链接静态库:
gcc main.c -static -o a.out # 编译并链接静态库,生成ELF可执行文件
这个过程分两步:
编译阶段:源代码(如.c文件)被编译成目标文件(.o),每个目标文件包含多个Section(如代码段.text、数据段.data)。
链接阶段:链接器将所有目标文件和静态库合并,生成一个完整的ELF文件。它创建一个Section Header Table(节表头),记录每个Section的大小、位置和属性(如权限)。
从静态库到可执行程序,就像拼乐高:零件(Section)被分类打包,说明书(ELF头)告诉系统如何组装。
ELF从形成到加载轮廓
ELF文件在磁盘上存储时,是一堆Section的集合。但加载到内存时,这些Section会被合并成更大的Segment。为什么?因为内存管理以4KB页面为单位,小Section会浪费空间。合并后,Segment能更好地匹配页面大小,减少碎片。
合并原则:属性相同的Section合并成一个Segment。例如,所有“可读可执行”的代码Section(如.text)合并成代码Segment;所有“可读可写”的数据Section(如.data)合并成数据Segment。
关键角色:
Section Header Table:记录所有Section的详细信息(位置、大小、属性)。
Program Header Table:定义合并规则,指导哪些Section合并成一个Segment。
用命令查看ls命令的Section信息(ls本身是C语言写的ELF文件):
readelf -S /bin/ls # 查看所有Section
这张表是一个数组,下标从0开始。每个条目记录Section的偏移量(从哪里开始)和大小(长度),操作系统用“起始地址 + 偏移量”就能找到数据位置,
ELF可执行文件加载
加载时,操作系统不直接处理Section,而是按Program Header Table的指示,将Section合并成Segment加载到内存。核心是为了优化内存和权限管理。
为什么需要合并?
减少页面碎片:内存分4KB页面。如果每个小Section单独加载(如1KB的.text和2KB的数据),需要多个页面,浪费空间。合并后,一个Segment占满整页,提高效率。
合并前需3个页面,合并后只需2个。
统一权限管理:Segment的权限(如可执行、可写)由合并后的属性决定。例如,代码Segment设为“只读+可执行”,防止被篡改;数据Segment设为“可读写”,方便修改变量。
BSS段:未初始化数据的优化
BSS(Better Save Space)段处理未初始化的全局变量(如int global_var;)。在磁盘上,BSS不存储实际数据,只记录大小;加载到内存时,操作系统按大小分配空间并初始化为0。这样节省磁盘空间,避免无效数据占用内存。
示例:如果BSS大小为100字节,加载时在内存开辟100字节的零填充区域。
合并规则:BSS常与数据段(.data)合并,因为它们都属“可读写”属性,
加载过程实战
操作系统加载ELF的步骤:
读取ELF头,找到Program Header Table的位置。
根据Program Header Table,将每个Segment加载到内存的指定地址。
设置内存权限(如代码段不可写)。
用命令查看Segment信息:
readelf -l a.out # 查看Program Header Table
每个条目包含Segment的偏移量、内存大小和权限。例如,LOAD类型表示需加载到内存的Segment。
操作系统将Segment映射到虚拟地址空间,程序从**入口点地址(Entry Point Address)**开始执行。
ELF的两个视角
ELF文件有双重角色,对应两种“视图”:
链接视图:编译和链接时关注Section。节表头(Section Header Table)是蓝图,帮助链接器组装零件。
执行视图:加载和运行时关注Segment。程序表头(Program Header Table)是手册,指导操作系统加载模块。
ELF头(ELF Header)是总指挥,记录关键信息:
readelf -h a.out # 查看ELF头
Magic:标识ELF格式(如7f 45 4c 46)。
Entry Point Address:程序入口地址,加载后CPU从这里开始执行指令。
Section/Program Header位置:指向节表头和程序表头的偏移量。
理解ELF,就能明白操作系统如何高效运行程序——就像一本精心设计的说明书,让硬件和软件无缝协作。
————————————————
版权声明:本文为CSDN博主「IF'Maxue」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wheeldown/article/details/152164267