ELF格式解读- elf头部与节头

Posted 不会写代码的丝丽

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ELF格式解读- elf头部与节头相关的知识,希望对你有一定的参考价值。

前言

ELFlinux动态库,可执行文件的格式.具体介绍可参阅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 <stdio.h>

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 \\r\\n",mystaticVar);
	return 0;

void hell()
testfun();


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

ELF头部

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

ELF 64-bit 告诉我们这个文件是一个64位系统下的ELF文件
LSBleast 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];	/* Magic number and other info */
  Elf64_Half	e_type;			/* Object file type */
  Elf64_Half	e_machine;		/* Architecture */
  Elf64_Word	e_version;		/* Object file version */
  Elf64_Addr	e_entry;		/* Entry point virtual address */
  Elf64_Off	e_phoff;		/* Program header table file offset */
  Elf64_Off	e_shoff;		/* Section header table file offset */
  Elf64_Word	e_flags;		/* Processor-specific flags */
  Elf64_Half	e_ehsize;		/* ELF header size in bytes */
  Elf64_Half	e_phentsize;		/* Program header table entry size */
  Elf64_Half	e_phnum;		/* Program header table entry count */
  Elf64_Half	e_shentsize;		/* Section header table entry size */
  Elf64_Half	e_shnum;		/* Section header table entry count */
  Elf64_Half	e_shstrndx;		/* Section header string table index */
 Elf64_Ehdr;

e_ident

一个16字节数组大小

完整参数参阅请参阅 ELF Header

elf.hEI_XXX表示上面下标位置

//elf.h
#define EI_NIDENT (16)
#define EI_MAG0		0		/* File identification byte 0 index */
#define ELFMAG0		0x7f		/* Magic number byte 0 */

#define EI_MAG1		1		/* File identification byte 1 index */
#define ELFMAG1		'E'		/* Magic number byte 1 */

#define EI_MAG2		2		/* File identification byte 2 index */
#define ELFMAG2		'L'		/* Magic number byte 2 */

#define EI_MAG3		3		/* File identification byte 3 index */
#define ELFMAG3		'F'		/* Magic number byte 3 */

/* Conglomeration of the identification bytes, for easy testing as a word.  */
#define	ELFMAG		"\\177ELF"
#define	SELFMAG		4

#define EI_CLASS	4		/* File class byte index */
#define ELFCLASSNONE	0		/* Invalid class */
#define ELFCLASS32	1		/* 32-bit objects */
#define ELFCLASS64	2		/* 64-bit objects */
#define ELFCLASSNUM	3

#define EI_DATA		5		/* Data encoding byte index */
#define ELFDATANONE	0		/* Invalid data encoding */
#define ELFDATA2LSB	1		/* 2's complement, little endian */
#define ELFDATA2MSB	2		/* 2's complement, big endian */
#define ELFDATANUM	3

#define EI_VERSION	6		/* File version byte index */
					/* Value must be EV_CURRENT */

#define EI_OSABI	7		/* OS ABI identification */
#define ELFOSABI_NONE		0	/* UNIX System V ABI */
#define ELFOSABI_SYSV		0	/* Alias.  */
#define ELFOSABI_HPUX		1	/* HP-UX */
#define ELFOSABI_NETBSD		2	/* NetBSD.  */
#define ELFOSABI_GNU		3	/* Object uses GNU ELF extensions.  */
#define ELFOSABI_LINUX		ELFOSABI_GNU /* Compatibility alias.  */
#define ELFOSABI_SOLARIS	6	/* Sun Solaris.  */
#define ELFOSABI_AIX		7	/* IBM AIX.  */
#define ELFOSABI_IRIX		8	/* SGI Irix.  */
#define ELFOSABI_FREEBSD	9	/* FreeBSD.  */
#define ELFOSABI_TRU64		10	/* Compaq TRU64 UNIX.  */
#define ELFOSABI_MODESTO	11	/* Novell Modesto.  */
#define ELFOSABI_OPENBSD	12	/* OpenBSD.  */
#define ELFOSABI_ARM_AEABI	64	/* ARM EABI */
#define ELFOSABI_ARM		97	/* ARM */
#define ELFOSABI_STANDALONE	255	/* Standalone (embedded) application */

#define EI_ABIVERSION	8		/* ABI version */

#define EI_PAD		9		/* Byte index of padding bytes */

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表示的存储字符串节 。
.shstrtabsection head string table

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

大致结构如下:

节头

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

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

每个节头数据结构如下:

typedef struct

  Elf64_Word	sh_name;		/* Section name (string tbl index) */
  Elf64_Word	sh_type;		/* Section type */
  Elf64_Xword	sh_flags;		/* Section flags */
  Elf64_Addr	sh_addr;		/* Section virtual addr at execution */
  Elf64_Off	sh_offset;		/* Section file offset */
  Elf64_Xword	sh_size;		/* Section size in bytes */
  Elf64_Word	sh_link;		/* Link to another section */
  Elf64_Word	sh_info;		/* Additional section information */
  Elf64_Xword	sh_addralign;		/* Section alignment */
  Elf64_Xword	sh_entsize;		/* Entry size if section holds table */
 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

以上是关于ELF格式解读- elf头部与节头的主要内容,如果未能解决你的问题,请参考以下文章

ELF格式解读-程序头与内存布局

ELF格式解读-程序头与内存布局

南京大学计算机基础 ELF和可执行文件格式

ELF中程序头和节头的区别

ELF文件加载与动态链接

ELF文件加载与动态链接