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文件结构的更多相关文章

  1. 程序运行之ELF 符号表

    当一个工程中有多个文件的时候,链接的本质就是要把多个不同的目标文件相互粘到一起.就想玩具积木一样整合成一个整体.为了使不同的目标文件之间能够相互粘合,这些目标文件之间必须要有固定的规则才行.比如目标文 ...

  2. 程序运行之ELF文件的段

    我们将之前的代码增加下变量来具体看下 在代码中增加了全局变量以及静态变量,还有一个简单的函数. #include <stdio.h> int global_var=1; int globa ...

  3. elf 文件格式探秘——程序运行背后的故事

    摘要:本文主要讲解elf文件格式,通过readelf命令结合底层的相关数据结构,讲解相关内容,分析程序运行的基本原理. 本文来源:elf 文件格式探秘——程序运行背后的故事 http://blog.c ...

  4. ELF文件结构

    ELF文件结构 ELF文件的全称是Executable and Linkable Format,直译为"可执行可链接格式",包括目标文件(.o).可执行文件(可以直接运行).静态链 ...

  5. linux下实现在程序运行时的函数替换(热补丁)

    声明:以下的代码成果,是参考了网上的injso技术,在本文的最后会给出地址,同时非常感谢injso技术原作者的分享. 但是injso文章中的代码存在一些问题,所以后面出现的代码是经过作者修改和检测的. ...

  6. 21ic编辑推荐:从单片机开始的程序运行

    一直不清楚单片机中程序的执行过程,就是知道一个程序总是从一个main函数开始执行,然后把程序段存放在ROM里面,动态数据存放在RAM里面,而单片机的RAM资源又是及其的稀少,所以要省着用,但是到底怎么 ...

  7. C程序运行的背后(2)

    话说上回说到,C程序运行之前,必须要加载到其进程地址空间中.今儿咱就扯扯这个加载到底是怎么加载的. 一图胜前言,这个图简单说明了可执行文件加载过程的逻辑流,在此只做粗粒度概要说明.需要准确描述的,请出 ...

  8. 从hello world 说程序运行机制

    转自:http://www.cnblogs.com/yanlingyin/archive/2012/03/05/2379199.html 开篇 学习任何一门编程语言,都会从hello world 开始 ...

  9. linux下实现在程序运行时的函数替换(热补丁)【转】

    转自:http://www.cnblogs.com/leo0000/p/5632642.html 声明:以下的代码成果,是参考了网上的injso技术,在本文的最后会给出地址,同时非常感谢injso技术 ...

随机推荐

  1. Datatable和实体还有实体集List的差别与转化

    机房收费系统大家想必不是做完.就是已经在手上了,在一開始做的时候就明白规定.我们必须用实体.而不能使Datatable,由于说是Datatable直接面向了数据库,当时不是非常明白,于是也没有再深究, ...

  2. 【Unity】脚本选择打勾的勾选框隐藏

    这个问题事实上已经遇到过好几次了.但又没有特别的须要手动勾选,所以也一直都没在意. 今天研究了一下,原来是由于我删除了Start方法...... 所以.仅仅要脚本中没有Start方法,勾选框就会隐藏掉 ...

  3. vscode - emmet失效?

    把emmet设置覆盖为用户.

  4. VB断点调试

    最近都在敲机房收费系统,这个系统是我们第一次自己在没有源代码的情况下进行的系统. 写程序的时候逻辑非常重要,可是我们还要清楚非常多时候你以为的并非你以为的! 就像在敲机房的时候,我们明明理清了逻辑.并 ...

  5. PHP计算字符串长度函数

    //计算字符串长度 function strlen_utf8($str) { $i = 0; $count = 0; $len = strlen ($str); while ($i < $len ...

  6. Linux vim命令记录

    Ndd  :删除N行 多行删除 :1,10d ctrl+v ,移动光标,ctrl+i,输入#,esc :移动处均会输入# gg:文档头 G:文档尾 o:下一行,并进入insert模式 O:上一行并输入 ...

  7. Oracle dos连接数据库基本操作

    sqlplus / as sysdba;(sqlplus 用户名/密码@ip:端口:数据库实例 as sysdba;) ;(设置显示多少列,pagesize:;每页多少记录) select * fro ...

  8. curl命令测试https

    curl -vosa --resolve pic.test.net::222.241.7.179 https://pic.test.net/UploadFiles/201312031744347965 ...

  9. mysql中去重复记录

    Distinct 这个只能放在查询语句的最前面 参考 : https://www.cnblogs.com/lushilin/p/6187743.html

  10. LeetCode226 InvertBinaryTree Java题解

    题目: Invert a binary tree. 4 / \ 2 7 / \ / \ 1 3 6 9 to 4 / \ 7 2 / \ / \ 9 6 3 1 解答: 遍历每个节点  直接交换他们的 ...