ELF文件的解析和加载

本文详细解析了ELF(可执行和可链接格式)文件的基本概念、结构组成及其在Linux环境下的应用。介绍了ELF文件的四个主要组成部分:ELF头、程序头表、节和节头表,以及它们在链接和执行过程中的角色。同时,通过实例展示了如何读取ELF文件的头部信息、节和段,并探讨了ELF文件的装载过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. elf文件基本概念

elf(Executable and Linkable Format)文件是一种目标文件格式,是一种用于二进制文件可执行文件目标代码、共享库和核心转储格式文件。

 

2. elf文件结构组成

ELF文件由4部分组成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。

实际上,一个文件中不一定包含全部内容,而且它们的位置也未必如同所示这样安排,只有ELF头的位置是固定的,其余各部分的位置、大小等信息由ELF头中的各项值来决定。

 

 

 

ELF文件格式提供了两种视图,分别是链接视图和执行视图。(加载过程主要关注执行视图)

链接视图是以节(section)为单位,执行视图是以段(segment)为单位。链接视图就是在链接时用到的视图,而执行视图则是在执行时用到的视图。上图左侧的视角是从链接来看的,右侧的视角是执行来看的。

- ELF header: 描述整个文件的组织。

- Program Header Table: 描述文件中的各种segments,用来告诉系统如何创建进程映像的。

- sections 或者 segments:segments是从运行的角度来描述elf文件,sections是从链接的角度来描述elf文件,也就是说,在链接阶段,我们可以忽略program header table来处理此文件,在运行阶段可以忽略section header table来处理此程序(所以很多加固手段删除了section header table)。从图中我们也可以看出,segments与sections是包含的关系,一个segment包含若干个section。

- Section Header Table: 包含了文件各个segction的属性信息。

 

2.1 ELF文件结构-elf header

这里以32位的ELF header结构体为例

#define EI_NIDENT 16
typedef struct{
  unsigned char  e_ident[EI_NIDENT];     /* MagicNum和其他信息 */
  Elf32_Half e_type;                     /* Object file type */
  Elf32_Half e_machine;                  /* architecture */
  Elf32_Word e_version;                  /* Object file version */
  Elf32_Addr e_entry;                    /* Entry point virtual address */
  Elf32_Off e_phoff;                     /* Program header table file offset */
  Elf32_Off e_shoff;                     /* Section header table file offset */
  Elf32_Word e_flags;                   /* Processor-specific flags */
  Elf32_Half e_ehsize;                  /* ELF header size in bytes */
  Elf32_Half e_phentsize;               /* Program header table entry size */
  Elf32_Half e_phnum;                   /* Program header table entry count */
  Elf32_Half e_shentsize;               /* Section header table entry size */
  Elf32_Half e_shnum;                   /* Section header table entry count */
  Elf32_Half e_shstrndx;                /* Section header string table index */
}Elf32_Ehdr;

e_ident : ELF的一些标识信息,前四位为.ELF,其他的信息比如大小端等(16字节)

e_machine : 文件的目标体系架构,比如ARM(2字节)

e_version : 0为非法版本,1为当前版本(4字节)

e_entry : 程序入口的虚拟地址(32位4字节,64位8字节)

e_phoff : 程序头部表偏移地址(32位4字节,64位8字节)

e_shoff : 节区头部表偏移地址(32位4字节,64位8字节)

e_flags :保存与文件相关的,特定于处理器的标志(4字节)

e_ehsize :ELF头的大小(2字节,对于32位这个值是52,64位是64)

e_phentsize : 每个程序头部表的大小(2字节)

e_phnum :程序头部表的数量(2字节)

e_shentsize:每个节区头部表的大小(2字节)

e_shnum : 节区头部表的数量(2字节)

e_shstrndx:节区字符串表位置(2字节)

 

在Linux环境下可以通过”readelf -h <elf file name>“获取ELF文件header信息。

 

可以通过执行命令”readelf -S <elf file name>”来查看该可执行文件中有哪些section。

 

通过执行命令"readelf –segments <elf file name>",可以查看该文件的执行视图。

 

3. ELF文件装载

由上图的程序头可知,该elf文件有3个LOAD类型的segment,因为只有LOAD类型是需要被映射的。

我们需要做的是找到这些segment在文件中的位置,并将其加载到对应的内存空间,对于memsiz大于filesiz的部分全部填充为0,加载完之后让程序跳转到入口地址

 

4. 代码实现

/* Xilinx Header */
#include "xil_io.h"

/* Project Header */
#include "load.h"
#include "mRam.h"
#include "ffu.h"

Elf32_Ehdr hdr = {0};

uint32_t load_exec(void)
{
	uint32_t i = 0;
	uint8_t *mRamBuffer = (uint8_t *)(APP_ELF_ADDR);
	Elf32_Phdr phdr = {0};


	/* 确认是否为elf格式 */
	memcpy(&hdr, mRamBuffer, sizeof(Elf32_Ehdr));
	if ((hdr.ident[0] != 0x7f) || (hdr.ident[1] != 'E') || (hdr.ident[2] != 'L') || (hdr.ident[3] != 'F'))
	{
		xil_printf("Invalid ELF header\n\r");
		return -1;
	}
	else
	{
		xil_printf("hdr.phnum = %d\n\r", hdr.phnum);
		for (i = 0; i < hdr.phnum; i++)
		{
			memcpy(&phdr, (mRamBuffer + hdr.phoff + i * sizeof(phdr)), sizeof(Elf32_Phdr));
			if (phdr.type == PT_LOAD)
			{
				memcpy((void*)phdr.paddr, (uint8_t *)(mRamBuffer + phdr.offset) , phdr.filesz);
			}
   
   		/* 多余的空间写0,做BSS段 */
			if (phdr.memsz > phdr.filesz)
			{
				memset((void*)(phdr.paddr + phdr.filesz), 0, phdr.memsz - phdr.filesz);
			}
		}
	}

	xil_printf("Move elf to DDR OK\n\r");
	xil_printf("entryAddr = 0x%x\n\r", hdr.entry);

	((void (*)())hdr.entry)();

	return 0;
}

5. 问题思考

【memset效率太低】从实测过程发现加载elf文件到内存过程中,多余的空间写0会耗费大量时间,memset 15MB空间耗时约为7s。

【BSS段为什么要初始化】

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值