首页 > 其他分享 >ELF 文件详解

ELF 文件详解

时间:2024-11-13 10:59:10浏览次数:1  
标签:文件 这个 EI 字节 section ELF sh 详解 define

ELF 文件详解

Segment 和 Section 的意义

在ELF文件中,会有很多的Section(比如.text、.bss),那么为什么还要有 Segment 这个概念,以下是一些原因:

  1. 区分加载和非加载部分:
    • Section 包含了所有类型的内容,包括代码段、数据段、符号表、调试信息等。
    • Segment 主要用于指示操作系统如何加载程序的不同部分到内存中,因此只包含那些需要加载到内存中的 section(例如 .text、.data 和 .bss 段),而不会包含调试信息、符号表等不需要加载的部分。
  2. 简化加载过程:
    • Segment 提供了一种简单的方式,告诉操作系统如何一次性加载多个相关的 section。
    • 例如,多个 section 可以被归类到同一个 segment 中,以减少加载过程中需要的 I/O 操作次数。
  3. 权限和属性管理:
    • 不同的 segment 可以有不同的权限和属性(例如可读、可写、可执行)。
    • 通过将需要相同权限和属性的 section 归类到同一个 segment,可以更容易地设置和管理内存权限。

ELF Header

ElfN_Ehdr结构体

typedef struct {
  unsigned char e_ident[EI_NIDENT];
  uint16_t e_type;
  uint16_t e_machine;
  uint32_t e_version;
  ElfN_Addr e_entry;
  ElfN_Off e_phoff;
  ElfN_Off e_shoff;
  uint32_t e_flags;
  uint16_t e_ehsize;
  uint16_t e_phentsize;
  uint16_t e_phnum;
  uint16_t e_shentsize;
  uint16_t e_shnum;
  uint16_t e_shstrndx;
} ElfN_Ehdr;

ElfN_Ehdr结构体字段含义:

e_ident

这个数组里面有一些关于这个ELF文件的信息

有一些宏用来索引这些信息:

#define EI_MAG0		0		/* File identification byte 0 index */

#define EI_MAG1		1		/* File identification byte 1 index */

#define EI_MAG2		2		/* File identification byte 2 index */

#define EI_MAG3		3		/* File identification byte 3 index */

#define EI_CLASS	4		/* File class byte index */

#define EI_DATA		5		/* Data encoding byte index */

#define EI_VERSION	6		/* File version byte index */

#define EI_OSABI	7		/* OS ABI identification */

#define EI_ABIVERSION	8		/* ABI version */

#define EI_PAD		9		/* Byte index of padding bytes */
EI_MAG0 && EI_MAG1 && EI_MAG2 && EI_MAG3

上面这四个宏是用来索引e_ident数组的前四个字节的,这四个字节是ELF文件最开头的四个字节,而且这四个字节中的内容在不同的ELF文件中都是一样的,连起来就是"\177ELF",有什么用呢,其实就是相当于这个文件的标识符一样,当操作系统看到一开头的四个字节是"\177ELF",它就认为这是一个合法的ELF文件

EI_CLASS

可以通过这个宏索引e_ident数组的第五个字节

这个字节存放的数据只会是下面这些宏:

#define ELFCLASSNONE	0		/* Invalid class */
#define ELFCLASS32	1		/* 32-bit objects */
#define ELFCLASS64	2		/* 64-bit objects */

通过这个字节可以知道这个ELF文件是什么类型,这里的类型指的是这个程序是运行在32位机上还是64位机上,如果这是一份有问题的ELF文件,则会是ELFCLASSNONE

EI_DATA

可以通过这个宏索引e_ident数组的第六个字节

这个字节存放的数据只会是下面这些宏:

#define ELFDATANONE	0		/* Invalid data encoding */
#define ELFDATA2LSB	1		/* 2's complement, little endian */
#define ELFDATA2MSB	2		/* 2's complement, big endian */

通过这个字节可以知道这个ELF文件是的数据编码是大端序还是小端序,如果这份ELF文件的数据编码是有问题的,则会是ELFDATANONE

EI_VERSION

可以通过这个宏索引e_ident数组的第七个字节

这个字节存放的数据是这个ELF文件的版本,我还在想一个文件有什么版本之分,问了一下AI,这是它的解释:

随着 ELF 格式的演变,可能会引入新的字段、结构或约定。版本信息可以帮助工具(如链接器、加载器、调试器等)判断一个特定的 ELF 文件是使用哪个版本的格式生成的,从而采取适当的处理方式。

EI_OSABI

可以通过这个宏索引e_ident数组的第八个字节

这个字节存放的数据只会是下面这些宏:

#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 */

通过这个字节可以知道这个ELF文件是运行在什么操作系统上的

EI_ABIVERSION

可以通过这个宏索引e_ident数组的第九个字节

这个字节标识了目标对象所针对的 ABI 版本。该字段用于区分不兼容版本的 ABI。此版本号的解释取决于 EI_OSABI 字段所标识的 ABI。符合此规范的应用程序使用值 0。(说实话,不是很懂zzz)

EI_PAD

可以通过这个宏索引e_ident数组的第十个字节

这个字节就是用告诉你从这里开始直到这个数组最后一个字节都是用来填充的,是被保留的,现在没啥用,未来可能有用

e_type

这个字段用来标识目标文件类型

这个字段存放的数据只会是下面这些宏:

#define ET_NONE		0		/* No file type */
#define ET_REL		1		/* Relocatable file */
#define ET_EXEC		2		/* Executable file */
#define ET_DYN		3		/* Shared object file */

注释写的很清楚了,不再赘述

e_machine

这个字段用来指定所需的架构

这个字段存放的数据只会是下面这些宏(太多了,随便列了一些):

#define EM_NONE		 0	/* No machine */
#define EM_M32		 1	/* AT&T WE 32100 */
#define EM_SPARC	 2	/* SUN SPARC */
#define EM_386		 3	/* Intel 80386 */
#define EM_68K		 4	/* Motorola m68k family */
#define EM_88K		 5	/* Motorola m88k family */
#define EM_IAMCU	 6	/* Intel MCU */
#define EM_860		 7	/* Intel 80860 */
#define EM_MIPS		 8	/* MIPS R3000 big-endian */
#define EM_S370		 9	/* IBM System/370 */
#define EM_MIPS_RS3_LE	10	/* MIPS R3000 little-endian */

注释写的很清楚了,不再赘述

e_version

这个字段用来标识文件版本

十分甚至有九分不对劲,我之前是不是写过,对呀,之前的e_ident数组里面不就已经记录过这个信息吗,这里同样是AI的解释:

这两个字段在功能上是相似的,都是用来指示 ELF 文件的版本信息。重复记录的原因可能是为了增强可读性和结构清晰性,使得不同的解析程序能够独立处理和理解文件格式和 ABI 版本,而不需要依赖特定的字段索引。这也有助于在某些情况下进行快速的检查和验证。

emmmmm...whatever

e_entry

这个字段提供了系统首次转移控制的虚拟地址,从而启动进程。如果文件没有关联的入口点,该成员的值为。

其实就是程序开始执行时的pc值

e_phoff

这个字段保存程序头表的文件偏移量,以字节为单位。如果文件没有程序头表,则此成员持有零。

这位是重量级

e_shoff

这个字段保存节头表的文件偏移量,以字节为单位。如果文件没有节头表,则此成员持有零。

这位更是重量级

e_flags

这个字段保存与文件相关的特定于处理器的标志。标志名称的格式为 EF_machine_flag。目前尚未定义任何标志。(不是很懂)

e_ehsize

这个字段保存ELF header的大小,也就是sizeof(ElfN_Ehdr)

e_phentsize

这个字段保存program header table中每一个条目(entry)的大小

e_phnum

这个字段保存program header table中的条目数

e_shentsize

这个字段保存sections header的大小,以字节为单位,一个sections header就是section header table中的一个条目

e_shnum

这个字段保存section header table中的条目数

e_shstrndx

这个字段保存一个索引(index),可以用来在section header table中找到一个特定的section header,很特殊,这个section header所描述的section是一个字符数组,里面保存着所有section的名字(name),需要注意的是,这个字段所保存的这个索引值是从1开始计数的,也就是说假设section header table有一共有十个section header,e_shstrndx == 6,那么e_shstrndx所指向的section header就是第6个(下标为5)

Program header

ElfN_Phdr结构体

typedef struct
{
  Elf32_Word	p_type;			/* Segment type */
  Elf32_Off	p_offset;		/* Segment file offset */
  Elf32_Addr	p_vaddr;		/* Segment virtual address */
  Elf32_Addr	p_paddr;		/* Segment physical address */
  Elf32_Word	p_filesz;		/* Segment size in file */
  Elf32_Word	p_memsz;		/* Segment size in memory */
  Elf32_Word	p_flags;		/* Segment flags */
  Elf32_Word	p_align;		/* Segment alignment */
} Elf32_Phdr;

typedef struct
{
  Elf64_Word	p_type;			/* Segment type */
  Elf64_Word	p_flags;		/* Segment flags */
  Elf64_Off	p_offset;		/* Segment file offset */
  Elf64_Addr	p_vaddr;		/* Segment virtual address */
  Elf64_Addr	p_paddr;		/* Segment physical address */
  Elf64_Xword	p_filesz;		/* Segment size in file */
  Elf64_Xword	p_memsz;		/* Segment size in memory */
  Elf64_Xword	p_align;		/* Segment alignment */
} Elf64_Phdr;

ElfN_Phdr结构体字段含义

p_type

这个字段告诉你这个program header描述的是哪一种segment,或者告诉你应该怎么解析这个program header

p_offset

这个字段保存该段的第一个字节在文件开头的偏移量。

p_vaddr

这个字段保存该段在内存中第一个字节的虚拟地址。

p_paddr

在物理寻址相关的系统中,这个字段保留用于段的物理地址。在 BSD 系统中,这个字段未使用,必须为零。

p_filesz

这个字段保存段在ELF文件中的字节数。它的值可以为零。

p_memsz

这个字段保存段在内存中的字节数。它的值可以为零。

p_flags

这个字段保存与段相关的标志位掩码

具体有这些:

#define PF_X		(1 << 0)	/* Segment is executable */
#define PF_W		(1 << 1)	/* Segment is writable */
#define PF_R		(1 << 2)	/* Segment is readable */

这里应该填入以上标志位掩码的不同组合的按位或结果

举个例子,如果一个段同时具有执行(PF_X)和写(PF_W)权限,那么可以使用按位或运算符(|)组合这两个标志位:

p_flags = PF_X | PF_W  

p_align

该成员保存段在内存和文件中对齐的值。 可加载进程段(Loadable process segments)的 p_vaddr 和 p_offset 必须具有一致的值(以页面大小为模)。 值 0 和 1 表示不需要对齐。 否则,p_align 应为 2 的正整数幂,并且 p_vaddr 应等于 p_offset,以 p_align 为模。

以上这些字段的关系是:

p_vaddr % page size == p_offset % page size(任何时候都成立)
p_align 为 2 的正整数幂时,p_vaddr % p_align == p_offset % p_align

Section Header

ElfN_Shdr结构体

typedef struct
{
  Elf32_Word	sh_name;		/* Section name (string tbl index) */
  Elf32_Word	sh_type;		/* Section type */
  Elf32_Word	sh_flags;		/* Section flags */
  Elf32_Addr	sh_addr;		/* Section virtual addr at execution */
  Elf32_Off	sh_offset;		/* Section file offset */
  Elf32_Word	sh_size;		/* Section size in bytes */
  Elf32_Word	sh_link;		/* Link to another section */
  Elf32_Word	sh_info;		/* Additional section information */
  Elf32_Word	sh_addralign;		/* Section alignment */
  Elf32_Word	sh_entsize;		/* Entry size if section holds table */
} Elf32_Shdr;

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;

ElfN_Shdr结构体字段含义

sh_name

这个字段指定节的名称。它的值是节头字符串表节(section header string table section)的索引,给出以 null 结尾的字符串的位置。

sh_type

这个字段对该部分的内容和语义进行分类

这个字段存放的数据只会是下面这些宏(太多了,随便列了一些):

#define SHT_NULL	  0		/* Section header table entry unused */
#define SHT_PROGBITS	  1		/* Program data */
#define SHT_SYMTAB	  2		/* Symbol table */
#define SHT_STRTAB	  3		/* String table */
#define SHT_RELA	  4		/* Relocation entries with addends */
#define SHT_HASH	  5		/* Symbol hash table */
#define SHT_DYNAMIC	  6		/* Dynamic linking information */
#define SHT_NOTE	  7		/* Notes */
#define SHT_NOBITS	  8		/* Program space with no data (bss) */
#define SHT_REL		  9		/* Relocation entries, no addends */

sh_flags

这个字段的每一位有不同的含义

节支持描述各种属性的一位标志。如果在 sh_flags 中设置了标志位,则该部分的属性为“on”。否则,该属性为“off”或不适用。未定义的属性设置为零。

这个字段存放的数据只会是下面这些宏之间或运算后的结果:

#define SHF_WRITE	     (1 << 0)	/* Writable */
#define SHF_ALLOC	     (1 << 1)	/* Occupies memory during execution */
#define SHF_EXECINSTR	     (1 << 2)	/* Executable */
#define SHF_MERGE	     (1 << 4)	/* Might be merged */
#define SHF_STRINGS	     (1 << 5)	/* Contains nul-terminated strings */
#define SHF_INFO_LINK	     (1 << 6)	/* `sh_info' contains SHT index */
#define SHF_LINK_ORDER	     (1 << 7)	/* Preserve order after combining */
#define SHF_OS_NONCONFORMING (1 << 8)	/* Non-standard OS specific handling
					   required */
#define SHF_GROUP	     (1 << 9)	/* Section is member of a group.  */
#define SHF_TLS		     (1 << 10)	/* Section hold thread-local data.  */
#define SHF_COMPRESSED	     (1 << 11)	/* Section with compressed data. */
#define SHF_MASKOS	     0x0ff00000	/* OS-specific.  */
#define SHF_MASKPROC	     0xf0000000	/* Processor-specific */
#define SHF_GNU_RETAIN	     (1 << 21)  /* Not to be GCed by linker.  */
#define SHF_ORDERED	     (1 << 30)	/* Special ordering requirement
					   (Solaris).  */
#define SHF_EXCLUDE	     (1U << 31)	/* Section is excluded unless
					   referenced or allocated (Solaris).*/
SHF_WRITE

这个宏意味着这个section包含着可写(writable)的数据

SHF_ALLOC

这个宏意味着这个section将来会被加载进内存,某些控制节(control sections)并不驻留在目标文件的内存映像中。对于这些节,此属性是“off”。

SHF_EXECINSTR

这个宏意味着这个section包含着可执行(executable)的机器指令(machine instructions)

SHF_MERGE

这个宏意味着这个section可能会被合并。具有相同内容的部分可以合并以减少空间使用。

SHF_STRINGS

这个宏意味着这个section包含以 NULL 结尾的字符串。

define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */

define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling

				   required */

define SHF_GROUP (1 << 9) /* Section is member of a group. */

define SHF_TLS (1 << 10) /* Section hold thread-local data. */

define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */

define SHF_MASKOS 0x0ff00000 /* OS-specific. */

define SHF_MASKPROC 0xf0000000 /* Processor-specific */

define SHF_GNU_RETAIN (1 << 21) /* Not to be GCed by linker. */

define SHF_ORDERED (1 << 30) /* Special ordering requirement

				   (Solaris).  */

define SHF_EXCLUDE (1U << 31) /* Section is excluded unless

				   referenced or allocated (Solaris).*/

sh_addr

如果这个节将会被加载进内存,那么这个字段就意味着这个节的第一个字节将被加载到哪个地址,否则,这个字段为零

sh_offset

这个字段表示从文件开头到该节(section)的第一个字节之间的字节偏移量。

有种特殊的节类型:SHT_NOBITS,在文件中不占用空间,但它的 sh_offset 字段表示该段在文件中的概念性位置。
这种段实际上不会在文件中存储数据,但 sh_offset 仍然用来指示它在文件中的逻辑位置。

sh_size

这个字段表示该节(section)的大小,以字节为单位

sh_info

这个字段包含一些额外的信息,如何解读这个字段取决于这个节(section)的类型

sh_addralign

某些节(section)具有地址对齐限制。如果一个节包含一个双字(doubleword),系统必须确保整个节的双字对齐。 那就是
sh_addr 的值必须等于 0,模
sh_addralign 的值。 仅零和正积分
允许两个权力。 值 0 或 1 表示
该部分没有对齐约束。

sh_entsize

如果这个section header所描述的section被组织成一个列表(table)的形式,那么这个字段就会指出这张表的每一个条目的大小

标签:文件,这个,EI,字节,section,ELF,sh,详解,define
From: https://www.cnblogs.com/fengtdi/p/18543446

相关文章

  • C 语言文件读写操作详解
    目录C语言文件读写操作详解引言1.文件操作基本概念2.文件打开3.文件关闭4.文件写入5.文件读取6.逐行读取文件7.文件定位8.文件大小9.二进制文件读写C语言文件读写操作详解引言C语言提供了丰富的文件操作函数,使得读取和写入文件变得非常方便。本文......
  • C语言指针详解:用法与实例
    目录C语言指针详解:用法与实例引言1.指针基础1.1什么是指针?1.2如何声明指针?1.3获取变量的地址1.4解引用指针2.指针的高级用法2.1指针和数组2.2指针和函数参数2.3指针和动态内存分配3.指针的注意事项3.1空指针3.2悬空指针3.3指针运算C语言指针详......
  • 【0x001A】HCI_Write_Scan_Enable详解
    目录一、命令概述二、命令格式及参数说明2.1. HCIWriteScanEnable命令格式2.2. Scan_Enable三、响应事件及参数说明3.1. HCI_Command_Complete事件3.2. Status四、命令执流程4.1.命令发起与准备4.2.命令传输4.3.命令接收与解析(蓝牙控制器端)4.4.扫描功能......
  • ES6常见语法详解
    原文链接:ES6常见语法详解–每天进步一点点0.什么是ES6ES的全称是ECMAScript,它是由ECMA国际标准化组织,制定的一项脚本语言的标准化规范。ES6实际上是一个泛指,泛指ES2015及后续的版本。1.let、const、var的区别let是es6中新增的语法let只对当前区块定义有效:......
  • 在Odoo开发中,ref是一个非常重要的函数,用于在XML文件中引用其他数据的ID,帮助我们快速定
    在Odoo开发中,ref是一个非常重要的函数,用于在XML文件中引用其他数据的ID,帮助我们快速定位和调用系统中已经存在的记录。ref的全称是reference,可以通过该函数引用特定的视图、字段、模型等元素,从而在模块开发中实现跨文件、跨模块的引用。下面我会详细解释ref的作用,并提供丰富的示例......
  • 【VMware VCF】通过备份的配置文件还原 SDDC Manager 组件。
    之前在这篇文章(使用SFTP服务器备份VCF核心组件的配置文件。)中配置并备份了VCF环境中SDDCManager组件的配置文件,这篇文章接着这个主题,看看当SDDCManager组件因意外发生故障时,如何通过备份的配置文件进行还原和恢复。 一、检查SDDCManager执行还原之前,请确保SD......
  • 【金融风控】特征评估与筛选详解
    内容介绍掌握单特征分析的衡量指标知道IV,PSI等指标含义知道多特征筛选的常用方法掌握Boruta,VIF,RFE,L1等特征筛选的使用方法【理解】单特征分析什么是好特征从几个角度衡量:覆盖度,区分度,相关性,稳定性覆盖度采集类,授权类,第三方数据在使用前都会分析覆盖度采......
  • 【金融风控】特征构造及代码详解
    介绍知道未来信息的概念,及处理未来信息的方法掌握从原始数据构造出新特征的方法掌握特征变换的方法掌握缺失值处理的方法【理解】数据准备风控建模特征数据数据来源人行征信数据查询原因包括:贷款审批、贷后管理、信用卡审批、担保资格审查、司法调查、......
  • HarmonyOS蓝牙串口协议(SPP)详解:实现设备间可靠数据交换
    本文旨在深入探讨华为鸿蒙HarmonyOSNext系统(截止目前API12)的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。在智能设备互联互通的世界里,蓝牙技术扮......
  • bat文件编写
    bat文件编写ECHOxxx表示打印SETa=10设置变量,变量次数加1,set/at+=1%a%使用变量ECHOOFF脚本结束跳转,使用:str标记某个位置,gotostr跳转到该位置/show显示调用工具|more打印脚本中缓存即echo内容举例说明REM设置串口SETport=COM57REM设置执行程序SETe=Serial......