程序运行之ELF文件结构
ELF目标文件格式的最前部是ELF文件头。包含了整个文件的基本属性。比如ELF文件版本,目标机器型号,程序入口地址等。然后是ELF的各个段,其中ELF文件中与段有关的重要结构就是段表。段表描述了ELF文件包含的所有段的信息,比如每个段的段名,段的长度,在文件中的偏移,读写权限及段的其他属性。
一 文件头;
通过readelf命令来详细查看ELF文件的头信息:
root@zhf-maple:/home/zhf/c_prj# readelf -h main.o
ELF 头:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
类别: ELF64
数据: 2 补码,小端序 (little endian)
版本: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: REL (可重定位文件)
系统架构: Advanced Micro Devices X86-64
版本: 0x1
入口点地址: 0x0
程序头起点: 0 (bytes into file)
Start of section headers: 1048 (bytes into file)
标志: 0x0
本头的大小: 64 (字节)
程序头大小: 0 (字节)
Number of program headers: 0
节头大小: 64 (字节)
节头数量: 13
字符串表索引节头: 12
我们将Elf32_Ehdr结构与readelf命令输出的信息对比可得如下对应关系:
|
成员 |
readelf输出结果 |
|
e_ident |
Magic,类别,数据,版本,OS/ABI,ABI |
|
e_type |
类型 |
|
e_machine |
系统架构 |
|
e_version |
版本 |
|
e_entry |
入口点地址 |
|
e_phoff |
start of program headers |
|
e_shoff |
start of section headers |
|
e_flags |
标志 |
|
e_ehsize |
文件头的大小 |
|
e_phentsize |
程序头大小 |
|
e_phnum |
number of program headers |
|
e_shentsize |
节头大小 |
|
e_shnum |
number of program headers |
|
e_shstrndx |
字符串表段索引 |
下面我来逐步分析各个部分:
魔数:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
7f 、45、4c、46分别对应ascii码的Del(删除)、字母E、字母L、字母F。这四个字节被称为ELF文件的魔数,操作系统在加载可执行文件时会确认魔数是否正确,如果不正确则拒绝加载。
第五个字节标识ELF文件是32位(01)还是64位(02)的。
第六个字节标识该ELF文件字节序是小端(01)还是大端(02)的。
第七个字节指示ELF文件的版本号,一般是01。
后九个字节ELF标准未做定义。一般为00.
文件类型:
e_type成员标识文件类型,ELF文件有三种类型,如下表所示。
|
常量标识 |
值 |
类型 |
|
ET_REL |
1 |
可重定位文件,一般位.o文件 |
|
ET_EXEC |
2 |
可执行文件 |
|
ET_DYN |
3 |
共享目标文件,一般位.so文件 |
机器类型:
ELF文件格式被设计成可以在多个平台下使用,但并不表示同一个ELF文件可以在不同的平台下使用,而是表示不同平台下的ELF文件都遵循同一套ELF标准.e_machine成员就表示该ELF文件的平台属性。
|
常量标识 |
值 |
系统架构 |
|
EM_M32 |
1 |
AT&T WE 32100 |
|
EM_SPARC |
2 |
SPARC |
|
EM_386 |
3 |
Intel 80386 |
|
EM_68K |
4 |
Motorola m68k family |
|
EM_88K |
5 |
Motorola m88k family |
|
EM_860 |
6 |
Intel 80860 |
二 段表:
在ELF文件中有各种各样的段,段表就是保存这些段的基本属性的结构。它描述了ELF的各个段的信息,比如每个段的段名,段的长度,在文件中的偏移,读写权限及段的其他属性。编译器,链接器和装载器都是依靠段表来定位和访问各个段的属性的。前面使用objdump -h来显示各个段,但只是显示了关键的几个段。我们用readelf来输出段表的内容。段表是存在Elf32_Shdr中,它是一个结构体数组。各个成员的含义如下表:
|
成员 |
含义 |
|
sh_name |
段名,位于一个叫“.shstrtab”的字符串表 |
|
sh_type |
段的类型,详细内容看后文 |
|
sh_flags |
段的标志位,详细内容见后文 |
|
sh_addr |
段在被加载后在进程地址空间中的虚拟地址,当段不能被加载时,它为0 |
|
sH_offset |
段在elf文件中的偏移,如果该段不存在于文件中,则它无意义 |
|
sh_szie |
段的长度 |
|
sh_link |
段的链接信息,详细内容见后文 |
|
sh_info |
段的链接信息,详细内容见后文 |
|
sh_addralign |
段地址对齐 |
|
sh_entsize |
项的长度,有的段包含一些固定大小的项,比如符号表,sh_enrsize就是用来指示这些项的大小 |
我们用readelf -S main.o来查看内容。
root@zhf-maple:/home/zhf/c_prj# readelf -S main.o
共有 13 个节头,从偏移量 0x418 开始:
节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000040
000000000000004c 0000000000000000 AX 0 0 1
[ 2] .rela.text RELA 0000000000000000 00000320
0000000000000060 0000000000000018 I 10 1 8
[ 3] .data PROGBITS 0000000000000000 0000008c
0000000000000008 0000000000000000 WA 0 0 4
[ 4] .bss NOBITS 0000000000000000 00000094
0000000000000004 0000000000000000 WA 0 0 4
[ 5] .rodata PROGBITS 0000000000000000 00000094
0000000000000004 0000000000000000 A 0 0 1
[ 6] .comment PROGBITS 0000000000000000 00000098
0000000000000024 0000000000000001 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 0000000000000000 000000bc
0000000000000000 0000000000000000 0 0 1
[ 8] .eh_frame PROGBITS 0000000000000000 000000c0
0000000000000058 0000000000000000 A 0 0 8
[ 9] .rela.eh_frame RELA 0000000000000000 00000380
0000000000000030 0000000000000018 I 10 8 8
[10] .symtab SYMTAB 0000000000000000 00000118
0000000000000198 0000000000000018 11 11 8
[11] .strtab STRTAB 0000000000000000 000002b0
000000000000006c 0000000000000000 0 0 1
[12] .shstrtab STRTAB 0000000000000000 000003b0
0000000000000061 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
从上面的输出的第一行可以看到,段表从0x418开始,观察其他段的偏移量可以发现,段表位于所有段之后,就是文件的末尾,该ELF文件有12个段,每个段的大小也就是sizeof(Elf32_Shdr)的大小,为40字节,所以整个段表的大小就是12*40 = 480个字节,再加上之前的0x418,总共1048+480=1528个字节.
段的类型
上面输出的第三列就是段的类型,段的类型的相关常量如下表所示:
|
常量 |
值 |
含义 |
|
SHT_NULL |
0 |
无效段 |
|
SHT_PROGBITS |
1 |
程序段、代码段、数据段都是此种类型 |
|
SHT_SYMTAB |
2 |
表示该段的类内容为符号表 |
|
SHT_STRTAB |
3 |
表示该段的内容是字符串表 |
|
SHT_RELA |
4 |
重定位表,该段包含了重定位信息 |
|
SHT_HASH |
5 |
符号表的哈希表 |
|
SHT_DYNAMIC |
6 |
动态链接信息 |
|
SHT_NOTE |
7 |
提示性信息 |
|
SHT_NOBITS |
8 |
表示该段在文件中没有内容,比如.bss段 |
|
SHT_REL |
9 |
该段包含了重定位信息 |
|
SHT_SHLIB |
10 |
该段保留 |
|
SHT_DNYSYM |
11 |
动态链接的符号表 |
段的标志位
以上输出的flg一列指出了该段在进程虚拟空间中的属性。
|
常量 |
值 |
含义 |
|
SHF_WRITE |
1 |
表示该段在进程空间中可写 |
|
SHF_ALLOC |
2 |
表示该段需要在进程空间中分配空间 |
|
SHF_EXECINSTR |
4 |
表示该段在进程空间中可以被执行 |
段的链接信息
|
sh_type |
sh_link |
sh_info |
|
SHT_DYNAMIC |
该段所使用的字符串表在段表中的下标 |
0 |
|
SHT_HASH |
该段所使用的符号表在段表中的下标 |
0 |
|
SHT_REL,SHT_RELA |
该段所使用的符号表在段表中的下标 |
该重定位表所作用的段在段表中的下标 |
|
SHT_SYMTAB、SHT_DNYSYM |
操作系统相关 |
操作系统相关 |
|
other |
SHN_UNDEF |
0 |
可重定位表:
链接器在处理目标文件时,须要对目标文件中某些部位进行重定位,即代码段和数据段中那些对绝对地址的引用的位置。这些重定位的信息都记录在ELF文件表里面,对于每个须要重定位的代码段和数据段,都会有一个相应的重定位表,例如 .rel.text 表对应.text段。也就是说,重定位表记录了须要被重定位的地址都在相应段的哪些地方。比如.rela.text就是针对.text段的重定位表,因为.text段中至少有一个绝对地址的引用,那就是对printf函数的调用。而.data没有对绝对地址的调用
字符串表:
ELF文件中用到了很多字符串。比如段名,变量名等。因为字符串的长度往往是不定的,所以用固定的结构来表示比较困难。常见的做法就是把字符串集中起来存放到一个表。然后使用字符串在表中的偏移来引用字符串。
下图展示了一个长度为 25 字节的字符串表:
字符串引用示例:
常见的段名是.strtab(String Table)或者是.shstrtab(Section Header String Table) }
程序运行之ELF文件结构的更多相关文章
- 程序运行之ELF 符号表
当一个工程中有多个文件的时候,链接的本质就是要把多个不同的目标文件相互粘到一起.就想玩具积木一样整合成一个整体.为了使不同的目标文件之间能够相互粘合,这些目标文件之间必须要有固定的规则才行.比如目标文 ...
- 程序运行之ELF文件的段
我们将之前的代码增加下变量来具体看下 在代码中增加了全局变量以及静态变量,还有一个简单的函数. #include <stdio.h> int global_var=1; int globa ...
- elf 文件格式探秘——程序运行背后的故事
摘要:本文主要讲解elf文件格式,通过readelf命令结合底层的相关数据结构,讲解相关内容,分析程序运行的基本原理. 本文来源:elf 文件格式探秘——程序运行背后的故事 http://blog.c ...
- ELF文件结构
ELF文件结构 ELF文件的全称是Executable and Linkable Format,直译为"可执行可链接格式",包括目标文件(.o).可执行文件(可以直接运行).静态链 ...
- linux下实现在程序运行时的函数替换(热补丁)
声明:以下的代码成果,是参考了网上的injso技术,在本文的最后会给出地址,同时非常感谢injso技术原作者的分享. 但是injso文章中的代码存在一些问题,所以后面出现的代码是经过作者修改和检测的. ...
- 21ic编辑推荐:从单片机开始的程序运行
一直不清楚单片机中程序的执行过程,就是知道一个程序总是从一个main函数开始执行,然后把程序段存放在ROM里面,动态数据存放在RAM里面,而单片机的RAM资源又是及其的稀少,所以要省着用,但是到底怎么 ...
- C程序运行的背后(2)
话说上回说到,C程序运行之前,必须要加载到其进程地址空间中.今儿咱就扯扯这个加载到底是怎么加载的. 一图胜前言,这个图简单说明了可执行文件加载过程的逻辑流,在此只做粗粒度概要说明.需要准确描述的,请出 ...
- 从hello world 说程序运行机制
转自:http://www.cnblogs.com/yanlingyin/archive/2012/03/05/2379199.html 开篇 学习任何一门编程语言,都会从hello world 开始 ...
- linux下实现在程序运行时的函数替换(热补丁)【转】
转自:http://www.cnblogs.com/leo0000/p/5632642.html 声明:以下的代码成果,是参考了网上的injso技术,在本文的最后会给出地址,同时非常感谢injso技术 ...
随机推荐
- 2. Spring Boot返回json数据【从零开始学Spring Boot】
在做如下操作之前,我们对之前的Hello进行简单的修改,我们新建一个包com.kfit.test.web然后新建一个类HelloControoler,然后修改App.Java类,主要是的这个类就是一个 ...
- 不依赖Excel是否安装的Excel导入导出类
本文利用第三方开源库NPOI实现Excel97-2003,Excel2007+的数据导入导出操作. 不依赖Office是否安装.NPOI开源项目地址:http://npoi.codeplex.com/ ...
- AngularJS, Ember.js, Backbone这类新框架与 jQuery的重要区别在哪里?
jQuery主要是用来操作DOM的,如果单单说jQuery的话就是这样一个功能,它的插件也比较多,大家也都各自专注一个功能,可以说jQuery体系是跟着前端页面从静态到动态崛起的一个产物,他的作用就是 ...
- DICOM医学图像处理:WEB PACS初谈二,图像的传输
背景: 如前一篇专栏博文所述,借助于CGI或FastCGI技术转发浏览器发送过来的用户请求,启动本地的DCMTK和CxImage库响应.然后将处理结果转换成常规图像返回到浏览器来实现Web PACS. ...
- OrCAD16.6中对比两份DSN文件的方法
OrCAD16.6中对比两份改版前后DSN文件的方法 两种方法: (1)第一种用软件对比netlist (2)用orcad自带的对比功能 一.将两份要对比的原理图都生成orTelesis.dll格式的 ...
- .net 哈希表和字典的基本用法
哈希表 传送门:https://www.cnblogs.com/xpvincent/archive/2013/01/15/2860841.html using System; using System ...
- Maven的pom文件内容详细理解
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...
- java学习笔记——java中对象的创建,初始化,引用的解析
如果有一个A类. 1.例如以下表达式: A a1 = new A(); 那么A是类,a1是引用.new A()是对象.仅仅是a1这个引用指向了new A()这个对象. 2.又如: A a2; A代 ...
- 【Oracle】使用BBED跳过丢失的归档
在recover datafile的过程其中假设丢失了须要的归档将使得recover无法进行.使用bbed工具能够跳过丢失的归档进行recover datafile. 实验步骤例如以下: SYS@OR ...
- MySQL四:表操作
阅读目录 表介绍 一 创建表 二 查看表结构 三 数据类型 四 表完整性约束 五 修改表ALTER TABLE 六 复制表 七 删除表 八 完整性约束 九 数据类型 表介绍 表相当于文件,表中的一条记 ...