-
-
[原创] ELF文件结构详解
-
发表于:
2019-11-17 16:08
27118
-
博客:www.wireghost.cn
Elf文件有2个平行视角:一个是程序链接角度,一个是程序装载角度。从链接的角度来看,Elf文件是按“Section”(节)的形式存储;而在装载的角度上,Elf文件又可以按“Segment”(段)来划分。实际上,Section和Segment难以从中文的翻译上加以区分。因为很多时候Section也被翻译成段,比如Section Header Table,有的资料叫段表、有的称为节区。后面在讲解时,就不对其加以区分。。
链接分为2种方式:一种是静态链接、一种是动态链接。
静态链接是在编译链接时直接将需要执行的代码拷贝到调用处;动态链接则是在编译的时候不直接拷贝可执行代码,而是通过记录一系列符号和参数,在程序运行或加载时将这些信息传递给操作系统,由系统负责将所需的动态库加载到内存,然后当程序运行到指定的代码时,去共享执行内存中已经加载的动态库可执行代码,最终达到运行时链接的目的。
程序是静态链接还是动态链接,由编译器的链接参数指定。具体来说,在Android上用C++进行ndk编程时,可以通过设置Application.mk的相关内容,将相应的运行库作为动态库或静态库。
为了方便自己学习和记忆,编写了一个例子so。这里我仅贴了部分代码,用于后面的分析与测试。
在介绍Elf文件格式前,先看看Elf文件中用到的数据类型:
Elf文件头描述了整个文件基本属性,如段表偏移、程序头部偏移等重要信息。它的定义如下:
前面有提过,Elf文件链接时是以Section的形式进行存储。其中,节区头部(段表)就是保存这些Section基本属性的结构。它描述了Elf中各个节的信息,比如每个节的名称、长度、在文件中的偏移、读写权限及其他属性,是一个以Elf32_Shdr结构体为元素的数组,而这个结构体又被称为段描述符。
因为sh_name是在段表字符串表中的索引,所以实际在解析时需要先定位到.shstrtab表,该表是专门用来存放Section名称的字符串表。而它对应的描述符在段表数组中的下标,则在Elf文件头中有给出,通常都是最后一个下标。在拿到节区名称后,再通过sh_offset、sh_size确定每一个节区在文件中的位置与长度。
最后,用readelf命令来查看下目标文件中的段,对照相应输出来分析确认段表结构(PS:段表数组中,第一个元素总是无效的描述符,全部为0)
.text代码段中保存程序指令,具体可以去查看arm、thumb指令集的opcode。。
".rodata"段存放的是只读数据,一般是程序里的只读变量(如const修饰的变量)和字符串常量;".data"段保存的是已经初始化的全局静态变量和局部静态变量。
PS:有时候编译器会把字符串常量放到".data"段,而不是单独放到".rodata"只读数据段。
.bss段中存放的是未初始化的全局变量和局部静态变量,这个Section在Elf文件中没有被分配空间。。
在声明一个函数或变量时,可以加上attribute((section("自定义section名")))前缀的方式,将其添加到自定义段。
Elf文件中用到的字符串,如段名、函数名、变量名称等,均保存在字符串表中。其中,shstrtab段表字符串表仅用来保存段名,而strtab或dynstr section则是存放普通字符串,如函数、变量名等符号名称,字符串之间以"00"截断。。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课