ELF格式解读-(1) 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

  • 6
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
C语言中的ELF(Executable and Linkable Format)文件段头部表是用来描述ELF文件的各个段的信息的数据结构。ELF文件是一种可执行文件和可链接文件的标准格式,用于在Linux和其他Unix系统上执行和链接程序。 ELF文件段头部表位于ELF文件的头部,用于描述ELF文件的各个段(段是一组相关的数据或代码的有序集合),包括代码段、数据段、BSS段等等。每个段头部表项都包含了段的一些重要信息,如起始地址、大小、访问权限等等。 段头部表的作用是让操作系统或程序加载器能够正确地加载和执行ELF文件的各个段。通过读取段头部表,系统可以确定每个段应该被放置在内存的哪个位置,并且可以根据段的访问权限进行适当的内存保护。此外,段头部表还包含其他一些元数据,如字符串表的偏移量、符号表的偏移量等,这些信息可以帮助调试器和其他工具分析和查找ELF文件的内容。 段头部表是一个固定大小的数据结构,每个表项的大小是固定的,并且表项的数量也是固定的。ELF文件的第一个段头部表项被保留用于描述ELF文件本身,其他表项则用于描述ELF文件中的各个段。程序员可以使用c语言中的结构体来表示段头部表项,并通过读取ELF文件的头部来获取段头部表的起始地址,从而遍历和分析整个段头部表。 总之,C语言中的ELF文件段头部表是用来描述ELF文件的各个段的信息的数据结构,它能够帮助操作系统或程序加载器正确地加载和执行ELF文件,并提供了一些用于分析和查找ELF文件内容的元数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值