ELF可执行文件的理解
ELF(Executable and Linking Format)是一种对象文件的格式,用于定义不同类型的对象文件(object files)中都放了什么东西、以及都以什么样的格式去放这些东西。ELF是构成众多xNIX系统的基础之一。
1 对象文件的三个种类
1.1 可重定位的对象文件(Relocatable object file)
这是由汇编器汇编生成的.o文件。链接器(link editor)拿一个或一些Relocatable object files作为输入,经链接处理后,生成一个可执行的对象文件(Executable object file)或者一个可被共享的对象文件(Shared object file)。可以使用ar工具将众多的.o可重定位的对象文件归档成.a静态库文件。
1.2 可执行的对象文件(Executable object file)
文本编辑器vi、调试用的工具gdb、播放MP3歌曲的软件mplayer等等都是Executable object file,另外一种可执行的脚本(如shell脚本),不是可执行的对象文件,只是文本文件,但是执行这些脚本所用的解释器就是executable object file,比如bash shell程序。
1.3 可被共享的对象文件(Shared object file)
这些就是所谓的动态库文件,也即.so文件。如果拿前面的静态库来生成可执行程序,那每个生成的可执行程序中都会有一份库代码的拷贝。动态库在发挥作用的过程中,必须经过两个步骤:
链接编辑器(link editor)拿它和其他Relocatable object file以及其他shared object file作为输入,经链接处理后,生成另外的shared object file或者executable file。在运行时,动态链接器(dynamic linker)拿它和一个executable file以及另外一些shared object file来一起处理,在Linux系统里面创建一个进程映像。
2 section
在ELF对象文件的最前面有一个ELF头文件,里面记载了所适用的处理器、对象文件类型等各种信息。下图描述了ELF对象文件的基本组成:
为什么左右两个类似的图来说明ELF的组成格式?这是因为ELF格式需要使用在两种场合:(1)组成不同的可重定位文件,以参与可执行文件或者可被共享的对象文件的连接构建;(2)组成可执行文件或者可被共享的对象文件,以在运行时内存中进程映像的构建。
Section是在ELF文件里头,用以装载内容数据的最小容器,是被链接器使用的。在ELF文件里面,每一个section内都装在了性质属性都一样的内容,比方:
1) .text section 里装载了可执行代码;
2) .data section 里面装载了被初始化的数据;
3) .bss section 里面装载了未被初始化的数据;
4) 以 .rec 打头的 sections 里面装载了重定位条目;
5) .symtab 或者 .dynsym section 里面装载了符号信息;
6) .strtab 或者 .dynstr section 里面装载了字符串信息;
7) 其他还有为满足不同目的所设置的section,比方满足调试的目的、满足动态链接与加载的目的等等。
一个ELF文件中到底有哪些具体的section,由包含在这个ELF文件的section head table(SHT)决定。在SHT中,针对每一个section,都设置一个条目,用来描述对应的这个section,其内容主要包括该section的名称、类型、大小以及在整个ELF文件中的字节偏移位置等等。
链接器在链接可执行文件或动态库的过程中,它会把来自不同可重定位对象文件中的相同名称的section合并起来构成同名的section。接着,它又会把带着相同属性(比方都是只读并可加载的)的section都合并成所谓segment(段)。Segment作为链接器的输出,常被称为输出section。开发者可以控制哪些不同.o文件的section来最后合并构成不同名称的segment。
一个单独的 segment 通常会包含几个不同的 sections,比方一个可被加载的、只读的segment 通常就会包括可执行代码section .text、只读的数据section .rodata以及给动态链接器使用的符号section .dymsym等等。section是被链接器使用的,但是segments是被加载器所使用的。加载器会将所需要的segment加载到内存空间中运行。和用 sections header table 来指定一个可重定位文件中到底有哪些 sections 一样。在一个可执行文件或者动态库中,也需要有一种信息结构来指出包含有哪些 segments。这种信息结构就是 program header table,如ELF对象文件格式中右边的 execute view 所示的那样。
为什么会有这样的一个segment?这是因为我们写的应用程序通常都需要使用动态链接库.so。程序在Linux里面是怎样运行起来的?当在shell中敲入一个命令要执行时,内核会帮我们创建一个新的进程,它再往这个新进程的进程空间里面加载进可执行程序的代码段和数据段后,也会加载进动态链接器(在Linux里面通常就是 /lib/ld-linux.so 符号链接所指向的那个程序,它本身就是一个动态库)的代码段和数据。在这之后,内核将控制传递给动态链接库里面的代码。动态链接器接下来负责加载该命令应用程序所需要使用的各种动态库。加载完毕,动态链接器才将控制传递给应用程序的main函数。这样应用程序才得以运行。
头文件中最重要的三个segment是:代码段、数据段和堆栈段。
如果觉得《ELF可执行文件的理解》对你有帮助,请点赞、收藏,并留下你的观点哦!