ELF可执行文件的结构详解
一、ELF文件是什么
ELF可以是目标文件、静态或者动态的链接文件、可执行文件。
前两种文件是中间文件,目标文件需要链接成为可执行文件或者链接文件,最终执行的只有可执行文件。
一个具有代码的源文件,经过预编译,编译,汇编之后得到了.o目标文件,通过将.o文件已经是具有实际机器指令的文件。
将所有的目标文件和静态库还有动态库一起链接起来,就得到了最终的可执行文件。
二、内存的基本构成
要了解ELF文件的构成,我们需要知道执行一个程序的内存是什么样的。
一个程序在运行过程中会占用:
- data数据段,存放全局变量和静态变量
- rodata只读数据段,存放不可被更改的只读数据
- stack栈,存放程序运行过程中的函数调用、返回地址、传递参数
- heap堆,动态申请的内存
- text代码段,存放着实际执行的指令代码,是只读的
根据这些我们可以知道,stack和heap是程序运行时才会使用到,那么elf文件中基本存储的就是data数据段,也就是全局变量和静态变量,以及执行的text代码段。
实际上,数据段在程序中被分成了两部分来存储分别是data和bss段。data段存储已经初始化的静态变量和全局变量,bss段存储没有初始化额静态变量和全局变量。如果是被初始化为0的静态变量或者全局变量也会被存放到bss段。为什么会专门分成data和bss两部分呢?
对data段来说每一个变量都需要有一个值,而bss段中则不需要,只需要记录变量本身的长度,在操作系统加载程序时才会将这些bss段的变量初始化为0,节省了elf文件的空间。
三、ELF实际组成
ELF头:
包含了elf文件的总体信息,包括文件的类型(可执行文件、目标文件、链接库),目标机器的架构,程序入口点的位置等等基础信息。
ProgramHeaderTable
程序头表描述了程序在内存中的布局,也就是描述了各个段的大小、内存位置等等信息,在加载这个elf文件时根据程序头表将elf文件中的段加载到内存的各个位置。
SectionHeaderTable
段表描述了各个段在整个文件中的布局,包括**.code、.data、.bss还有符号表**等等,每一个段都有自己的属性,在文件中的偏移地址。
实际数据
这部分就是在上面的段表中描述的各个段的实际数据。
生成静态链接库和动态链接库
1. 静态链接库
将.c文件编译成目标文件.o
1
gcc -c myfunc.c
将.o目标文件变成静态链接库
1
ar rcs libmyfunc.a myfunc.o
在这里,一个静态链接库的名称为了符合规范,尽可能写成
lib[name].a
的形式。编译源文件时将静态链接库链接在一起
1
gcc test.c -L. -lmyfunc -o test
在这里,
-L
指示库文件的位置,-l
指示静态链接库的名字,不包含lib和.a的中间那部分。这样就生成了最终的可执行文件。
2. 动态链接库
同样的将.c文件编译成.o
1
gcc -c myfunc.c
将.o文件变成动态链接库
1
gcc -shared -o libmyfunc.so myfunc.o
将动态链接库和源文件链接在一起
1
gcc test.c -L. -lmyfunc -o test
定义动态链接库的环境变量
1
export LD_LIBRARY_PATH=.
- Title: ELF可执行文件的结构详解
- Author: HarderHeng
- Created at : 2024-12-03 09:38:07
- Updated at : 2024-12-23 15:25:02
- Link: https://harderheng.life/2024/12/03/ELF可执行文件的结构详解/
- License: This work is licensed under CC BY-NC-SA 4.0.