ELF是linux下目标文件的格式。学会分析ELF文件,是提升计算机学科素养的必要条件。 ELF文件格式本身有很多细节,初学者很容易一叶障目、不见泰山。 本文是我学习ELF文件后做的总结,目标是提供一个大的指引,力图构建一个ELF文件的宏观分析框架。
ELF是一种文件格式,这种文件格式的目标是打通我们的程序和操作系统之间的最后一公里。 我们可以用很多编程语言编写程序,如python、java、c++等。 这些程序最终都是要操作系统帮我们加载和执行。操作系统不可能对每种编程语言制定一个加载和执行的程序。
所以,操作系统就约定一个文件格式,甭管你是用什么编程语言写的程序,最后,都得给我按照文件格式生成文件,否则,我就不加载,这个文件格式就是ELF。 有了ELF后,随着实际情况的发展,ELF格式的文件,除了是作为操作系统加载执行的可执行文件,又承担了两项新的职责, 就是目标文件和共享库文件,这两种文件也遵循ELF格式,只不过不能直接加载执行,他们的出现,主要是为了模块化程序,同时方便程序员和操作系统的。
假设我们有如下的c程序:
// simple_section.c
int printf( const char* format, ...);
int global_init_var = 84;
int global_uninit_var;
void func1( int i)
{
printf("%d\n", i);
}
int main(void)
{
static int static_var = 85;
static int static_var2;
int a = 1;
int b;
func1(static_var + static_var2 + a + b);
return a;
}
使用gcc -c进行编译后,就生成了一个目标文件。 gcc -c simple_section.c 执行上面的命令后,会在本级目录下看到一个simple_section.o文件,这就是遵循ELF格式的目标文件。
我们知道一个程序中的内容无非就是两大块:指令和数据。 为了便于管理,ELF文件是由文件头和段组成,所谓段,最常用的就是代码段和数据段,可以看出来,明显对应到程序中的指令和数据。 除了这两个段,还有其他的一些段,但都是辅助代码段和数据段的,所以,抓住了代码段和数据段,就抓住了ELF文件的关键。
我们可以通过 readelf -S simple_section.o命令,查看ELF文件中的段信息。
里面的text和data段就是代码段和数据段。上图中其他内容可暂时略过。 到这里,最重要的是建立对ELF直观的感觉。
为了管理组织好这些段,在ELF文件中专门有个段,叫section table,这个段中的内容就是该ELF中所有的段的基本信息。每个段的基本信息就是一个c语言中的结构,包含了该段在文件中的位置、段的名称、读写权限等。section table就是这种结构的一个数组。
上面的图其实就是section table。段表相当于整个ELF文件的结构地图,找到了section table,就可以掌握ELF文件的宏观结构。
段表是ELF文件中除了文件头之外最重要的结构,编译器、链接器和装载器都是依靠段表来定位和访问各个段的属性的。
那么问题来了,操作系统如何知道段表在ELF文件中的位置呢? 答案是,在ELF文件头中,指明了段表在ELF文件中的位置。
我们可以通过readelf -h simple_section.o命令查看ELF文件头,如下所示: 红框中标明的,就是section table在ELF文件中的位置。
本文分析ELF文件的入门要领。就是通过文件头中的信息,找到section table在文件中的位置,通过section table,我们就可以知道整个ELF文件中所有的其他段的基本信息。