栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

ELF格式解读-(1) elf头部与节头

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

ELF格式解读-(1) elf头部与节头

前言

ELF是linux动态库,可执行文件的格式.具体介绍可参阅wiki Executable and Linkable Format。可以类比到windows下exe的格式。

首先推荐一个写的不错文档ELF格式

我们知道程序需要加载内存后才能运行。但是ELF文件加载到内存后布局会变化和原始ELF文件相比,加载器会将相同的节属性(比如只读)合并一个段。所以ELF也就有了两种视图,一种未加载前静态视图,另一种是加载后的动态视图。

我们首先了解静态视图下ELF文件格式如下:

你可以把ELF内容大致分为四个部分:

(1) ELF头部

(2) 节

(3) 节表头

(4) 程序头

  1. ELF头部固定在ELF文件开始
  2. 需要留意程序头和节表头可以位于ELF任意位置,他们位置被ELF头部中的属性指定
  3. 节分有很多种格式需要根据节类别区分,比如重定义节 与代码节

本文根据以下代码作为示例

#include 

static int mystaticVar = 3 ;
int myglobalvar=3;
//函数来自test.so
extern void testfun();
int main(){
	int *inp= 0x00;
	*inp=2;	
	testfun();
	static int myLocalVar1 = 3 ;
	static int myUnintLocallvar;
	printf("hello world %d rn",mystaticVar);
	return 0;
}
void hell(){
testfun();
}

目标文件 main.o
可执行文件main.out

ELF头部

我们以main.o 举例
我们用file查看文件类别

ELF 64-bit 告诉我们这个文件是一个64位系统下的ELF文件
LSB是least significant bit缩写表示第一个字节是多字节中最低有效位,简而言之就是小端模式
x86-64 是指该文件运行在那个处理器的ABI下
version 1(SYSV)是该ELF标准是UNIX_System_V具体参阅SYSV
not stripped表示该ELF存在符号表
relocatable 表示该文件是可重定位,因为main.o是目标文件而不是可执行文件,部分代码地址是不确定的

上面信息其实file程序读取该文件的elf头部得到。我们使用readelf -h文件头查看更详细的信息

数据结构如下所示

typedef struct
{
  unsigned char	e_ident[EI_NIDENT];	
  Elf64_Half	e_type;			
  Elf64_Half	e_machine;		
  Elf64_Word	e_version;		
  Elf64_Addr	e_entry;		
  Elf64_Off	e_phoff;		
  Elf64_Off	e_shoff;		
  Elf64_Word	e_flags;		
  Elf64_Half	e_ehsize;		
  Elf64_Half	e_phentsize;		
  Elf64_Half	e_phnum;		
  Elf64_Half	e_shentsize;		
  Elf64_Half	e_shnum;		
  Elf64_Half	e_shstrndx;		
} Elf64_Ehdr;
e_ident

一个16字节数组大小

完整参数参阅请参阅 ELF Header

在elf.h有EI_XXX表示上面下标位置

//elf.h
#define EI_NIDENT (16)
#define EI_MAG0		0		
#define ELFMAG0		0x7f		

#define EI_MAG1		1		
#define ELFMAG1		'E'		

#define EI_MAG2		2		
#define ELFMAG2		'L'		

#define EI_MAG3		3		
#define ELFMAG3		'F'		


#define	ELFMAG		"177ELF"
#define	SELFMAG		4

#define EI_CLASS	4		
#define ELFCLASSNONE	0		
#define ELFCLASS32	1		
#define ELFCLASS64	2		
#define ELFCLASSNUM	3

#define EI_DATA		5		
#define ELFDATANONE	0		
#define ELFDATA2LSB	1		
#define ELFDATA2MSB	2		
#define ELFDATANUM	3

#define EI_VERSION	6		
					

#define EI_OSABI	7		
#define ELFOSABI_NONE		0	
#define ELFOSABI_SYSV		0	
#define ELFOSABI_HPUX		1	
#define ELFOSABI_NETBSD		2	
#define ELFOSABI_GNU		3	
#define ELFOSABI_LINUX		ELFOSABI_GNU 
#define ELFOSABI_SOLARIS	6	
#define ELFOSABI_AIX		7	
#define ELFOSABI_IRIX		8	
#define ELFOSABI_FREEBSD	9	
#define ELFOSABI_TRU64		10	
#define ELFOSABI_MODESTO	11	
#define ELFOSABI_OPENBSD	12	
#define ELFOSABI_ARM_AEABI	64	
#define ELFOSABI_ARM		97	
#define ELFOSABI_STANDALONE	255	

#define EI_ABIVERSION	8		

#define EI_PAD		9		

Name下标范围Purpose
ELF魔数0-4固定为0x7f+ELF
EI_CLASS5表示文件时32位还是64位 1是32 2是64
EI_DATA6指定大小端 1小端 2大端
EI_VERSION7ELF规范版本当前规定是1
EI_OSABI8ELF启用一些基于操作系统或者cpu特性一般为0
EI_ABIVERSION9一般为0 指定当前当ABI版本配合EI_OSABI使用
EI_PAD10-F预留
EI_NIDENTFe_ident[]数组大小
e_type

表示当前ELF文件类型,下面举例常见的类型

名字数值寓意
ET_NONE0非文件类型
ET_REL1可重定位文件
ET_EXEC2可执行文件
ET_DYN3共享库
ET_CORE4Core file
e_machine

制定当前ELF运行的CPU架构
下面举例常见的类型

名字数值寓意
EM_X86_6462AMD-x86-64
e_version

用于指定ELF版本一般都为1

e_entry

elf 代码运行的入口

e_flags

在e_machine指定的处理器下的一些特性

节头表相关字段

e_shoff
节头表在文件的偏移

e_shentsize
节头表中每个条目的大小

e_shnum
节头表中条目的数目

程序头相关字段

e_phoff
程序头在文件中的偏移

e_phentsize
指定程序头中每个条目的大小

e_phnum
指定程序头中每个条目的个数

e_shstrndx

每个节头都一个名称,这些名称都存储一个特殊节中。而e_shstrndx 指定这个特殊的节所在节头表的下标

我们先看看这个程序中所有节如下:

一共13个节,其中.shstrtab表示的存储字符串节 。
.shstrtab是section head string table

我们查看这个节内容如下所示:

大致结构如下:

节头

本例中我们依旧使用main.o我们可以到节头表信息如下:

节头表的第一项固定为空节不存储实际内容

每个节头数据结构如下:

typedef struct
{
  Elf64_Word	sh_name;		
  Elf64_Word	sh_type;		
  Elf64_Xword	sh_flags;		
  Elf64_Addr	sh_addr;		
  Elf64_Off	sh_offset;		
  Elf64_Xword	sh_size;		
  Elf64_Word	sh_link;		
  Elf64_Word	sh_info;		
  Elf64_Xword	sh_addralign;		
  Elf64_Xword	sh_entsize;		
} Elf64_Shdr;

elf节头规则详细文档 https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.sheader.html

sh_name

节名在字符串节中下标,本例中字符串节名称为.shstrtab.我们举例其中一个节.text

我们看到.text节的sh_name为20h也就是十进制32.我们看下.shstrtab指向的字节数组的32位

sh_type

这个字段根据节的内容(content)和语义(semantics)对节进行分类。
分类类型有很多种,我们只举例其中比较常见的类型。

名称数值解释
SHT_PROGBITS1一般存放代码或者数据类型
SHT_STRTAB3存放字符串表类型
SHT_NOBITS8表示这个节不存储信息在文件中,比如未初始化的数据

.text与.data一般就是SHT_PROGBITS (text存储代码 data存储数据)
.shstrtab一般是SHT_STRTAB
.bss一般是SHT_NOBITS (存储全局未初始化数据等)

sh_flags

字段标记是否可读可写可执行等,以及是否在内存中分配内存(SHF_ALLOC)
下图为枚举值表:

sh_addr

这个节被加载后对应VA地址

sh_offset

这个节在文件中的偏移

sh_size

节大小(不是指节头大小哦)

sh_link

一般用于关联节所在节头表的数组下标,一般为0
举例说明:
我们节中有一个专门用于重定位的节如.rela.text 就是用来重定位代码段部分代码的。
sh_link表示这个节所使用的的符号表节在节头表的下标
sh_info表示哪个节需要重定向。这个值指向在节头表中的索引。

如下图所示 :

sh_info

一般用于关联节所在节头表的数组下标,一般为0

sh_addralign

对其数值。如果为0或者1表示不对齐。
sh_addr必须为0或者对其sh_addralign取模

sh_entsize

ent是entry缩写。

部分节内部存储是固定数据结构条目数组,针对这类别节sh_entsize指代的是每个条目的字节大小。

举例说明:
符号表节名为.symtab,它存储若干固定结构的符号信息。如下图所示

符号表每个条目数据结构如下所示

typedef struct {
	Elf32_Word	st_name;
	Elf32_Addr	st_value;
	Elf32_Word	st_size;
	unsigned char	st_info;
	unsigned char	st_other;
	Elf32_Half	st_shndx;
} Elf32_Sym;//32位

typedef struct {
	Elf64_Word	st_name;
	unsigned char	st_info;
	unsigned char	st_other;
	Elf64_Half	st_shndx;
	Elf64_Addr	st_value;
	Elf64_Xword	st_size;
} Elf64_Sym;//64位

sh_entsize指的就是Elf64_Sym或者Elf32_Sym的大小

参考

ELF Header

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/827984.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号