C语言的本质(30)——C语言与汇编之ELF文件格式


ELF(Executable and Linking Format)文件格式是一个开放标准,各种UNIX系统的可执行文件都采用ELF格式,ELF是一种对象文件的格式,用于定义不同类型的对象文件(Object files)的内容是什么、以及都以怎样的格式去存放这些内容。它有三种不同的类型:

1、  可重定位的目标文件(Relocatable)

这是由汇编器汇编生成的 .o 文件。后面的链接器把一个或多个可重定位的目标文件作为输入,经链接处理后,生成一个可执行的对象文件 (Executable file) 或者一个可被共享的对象文件(Shared object file)。我们可以使用 ar 工具将众多的 .o可重定位的目标文件归档(archive)成 .a 静态库文件。

 

2、  可执行文件(Executable)

我们编译出的程序都是可执行文件。在 Linux 系统里面,存在两种可执行的东西。除了这里说的可执行文件,另外一种就是可执行的脚本(如shell脚本)。注意这些脚本不是可执行文件,它们只是文本文件,但是执行这些脚本所用的解释器就是可执行文件,比如 bash shell 程序。

 

3、共享库(Shared Object)

这些就是所谓的动态库文件,也即 .so 文件。如果拿前面的静态库来生成可执行程序,那每个生成的可执行程序中都会有一份库代码的拷贝。如果在磁盘中存储这些可执行程序,那就会占用额外的磁盘空间;另外如果拿它们放到Linux系统上一起运行,也会浪费掉宝贵的物理内存。如果将静态库换成动态库,那么这些问题都不会出现。

 

下面我们分析上一篇中“求一组数的最大值的汇编程序”经过汇编之后生成的目标文件max.o和链接之后生成的可执行文件max的格式,从而理解汇编、链接和加载执行的过程。共享库以后再详细介绍。

 

ELF文件格式提供了两种不同的视角,在汇编器和链接器看来,ELF文件是由Section Header Table描述的一系列Section的集合,而执行一个ELF文件时,在加载器(Loader)看来它是由Program Header Table描述的一系列Segment的集合。如下图所示。

 

 

左边是从汇编器和链接器的视角来看这个文件,开头的ELF Header描述了体系结构和操作系统等基本信息,并指出Section Header Table和Program Header Table在文件中的什么位置,Program Header Table在汇编和链接过程中没有用到,所以是可有可无的,SectionHeader Table中保存了所有Section的描述信息。右边是从加载器的视角来看这个文件,开头是ELF Header,Program Header Table中保存了所有Segment的描述信息,Section Header Table在加载过程中没有用到,所以是可有可无的。注意Section Header Table和Program Header Table并不是一定要位于文件开头和结尾的,其位置由ELF Header指出,上图这么画只是为了清晰。

我们在汇编程序中用.section声明的Section会成为目标文件中的Section,此外汇编器还会自动添加一些Section(比如符号表)。Segment是指在程序运行时加载到内存的具有相同属性的区域,由一个或多个Section组成,比如有两个Section都要求加载到内存后可读可写,就属于同一个Segment。有些Section只对汇编器和链接器有意义,在运行时用不到,也不需要加载到内存,那么就不属于任何Segment。

目标文件需要链接器做进一步处理,所以一定有Section Header Table;可执行文件需要加载运行,所以一定有Program Header Table;而共享库既要加载运行,又要在加载时做动态链接,所以既有Section Header Table又有Program Header Table。

下面用readelf工具读出目标文件max.o的ELF Header和Section Header Table,然后我们逐段分析。

 

ELF Header中描述了操作系统是UNIX,体系结构是x86-64。SectionHeader Table中有8个节头(Section Header),在文件中的位置(或者叫文件地址)从216(0xD8)开始,每个节(Section)64字节。这个目标文件没有Program Header。

 

从节头(Section Header)中读出各Section的描述信息,其中.text.data是我们在汇编程序中声明的Section,而其它Section是汇编器自动添加的。Addr是这些段加载到内存中的地址(我们讲过程序中的地址都是虚拟地址),加载地址要在链接时填写,现在空缺,所以是全0。OffSize两列指出了各Section的文件地址,比如.data从文件地址0x6d开始,一共0x38个字节,回去翻一下程序,.data中定义了14个4字节的整数,一共是56个字节,也就是0x38个。根据以上信息可以描绘出整个目标文件的布局。

max.o这个文件不大,我们直接用hexdump工具把目标文件的字节全部打印出来看。

 

你可能感兴趣的:(C语言编程,linux,C/C++,VC++编程技术)