loaclstroge数据格式_ELF文件格式解析

数据类型

首先在解析之前, 必须对数据类型格式声明一下

名称

大小

说明

Elf32_Addr

4

无符号程序地址

Elf32_Half

2

无符号中等整数

Elf32_Off

4

无符号文件偏移

Elf32_SWord

4

有符号大整数

Elf32_Word

4

无符号大整数

unsigned char

1

无符号笑整数

整体结构

概述

ELF头部(ELF_Header): 每个ELF文件都必须存在一个ELF_Header,这里存放了很多重要的信息用来描述整个文件的组织,如: 版本信息,入口信息,偏移信息等。程序执行也必须依靠其提供的信息。

程序头部表(Program_Header_Table): 可选的一个表,用于告诉系统如何在内存中创建映像,在图中也可以看出来,有程序头部表才有段,有段就必须有程序头部表。其中存放各个段的基本信息(包括地址指针)。

节区头部表(Section_Header_Table): 类似与Program_Header_Table,但与其相对应的是节区(Section)。

节区(Section): 将文件分成一个个节区,每个节区都有其对应的功能,如符号表,哈希表等。

段(Segment): 嗯…就是将文件分成一段一段映射到内存中。段中通常包括一个或多个节区

*注:每个节区都应该是前后相连的,且不可有重叠。即在一个地址上的字节只能属于一个节区*

详解

ELF_Header

以下是ELF_Header的结构定义

typedef struct {

Elf32_Half e_type; //Elf文件类型

Elf32_Half e_machine;//ELF文件的CPU平台属性

Elf32_Word e_version;//ELF版本信息

Elf32_Addr e_entry;//入口地址

Elf32_Off e_phoff;//程序头表的文件偏移(以字节为单位)。如果文件没有程序头表,则此成员值为零。

Elf32_Off e_shoff;//节头表的文件偏移(以字节为单位)。如果文件没有节头表,则此成员值为零。

Elf32_Word e_flags;//与文件关联的特定于处理器的标志。标志名称采用 EF_machine_flag 形式。

Elf32_Half e_ehsize;//ELF 头的大小(以字节为单位)

Elf32_Half e_phentsize;//文件的程序头表中某一项的大小(以字节为单位)。所有项的大小都相同

Elf32_Half e_phnum;//程序头表中的项数。

Elf32_Half e_shentsize;//节头的大小(以字节为单位)。节头是节头表中的一项。所有项的大小都相同

Elf32_Half e_shnum;//节头表中的项数

Elf32_Half e_shstrndx;//与节名称字符串表关联的项的节头表索引。

} Elf32_Ehdr;

然后来逐一解释下各个字段:

e_ident

这是一个数组,其每个字节又都有所代表的含义:

EI_MAG0 - EI_MAG3 文件标识就是平时所说的ELF头,即 7F 45 4C 46(ELF)

EI_CLASS 文件类,其实代表的是32位/64位程序

取值

代表

含义

01

ELFCLASS32

32位程序

02

ELFCLASS64

64位程序

EI_DATA 数据编码,一般都是01[td]

取值

代表

含义

01

ELFDATA2LSB

高位在前

02

ELFDATA2MSB

低位在前

EI_VERSION 文件版本,固定值01 EV_CURRENT

EI_PAD 呃…就是一堆全是00的用来补全大小的数组

EI_NIDENT 说是e_ident数组的大小,但我看了好几个so都是00

e_type 标识文件类型

取值

代表

含义

00

ET_NONE

未知文件类型格式

01

ET_REL

可重定位文件

02

ET_EXEC

可执行文件

03

ET_DYN

共享目标文件(SO)

04

...

...

e_machine 声明ABI

取值

代表

含义

01

...

...

03

EM_386

X86

04

...

...

28h

EM_ARM

arm

29h

...

...

e_version 跟ident[]里的EI_VERSION一样,为01

e_entry 可执行程序入口点地址。

e_phoff Program Header Offset,程序头部表索引地址,没有则为0。

e.shoff Section Header Offset,节区表索引地址,没有则为0。

e_flags “保存与文件相关的,特定于处理器的标志。”(不知道有什么用,看了几个arm都是00 00 00 05,x86都是0)。

e_ehsize ELF_Header Size,嗯..ELF头部的大小

e_phentsize 程序头部表的单个表项的大小

e_phnum 程序头部表的表项数

e_shentsize 节区表的单个表项的大小

e_shnum 节区表的表项数

e_shstrndx String Table Index,在节区表中有一个存储各节区名称的节区(通常是最后一个),这里表示名称表在第几个节区。

Program Header

在ELF_Header中,我们可以得到Program Header的索引地址(e_phoff)、段数量(e_phnum)、表项大小(e_phentsize)。

然后我们来看一下Program Header中表项的结构定义:

typedef struct {

Elf32_Word p_type;

Elf32_Off p_offset;

Elf32_Addr p_vaddr;

Elf32_Addr p_paddr;

Elf32_Word p_filesz;

Elf32_Word p_memsz;

Elf32_Word p_flage;

Elf32_Word p_align;

}

Elf32_phdr;

p_type 声明此段的作用类型

取值

代表

含义

00

PT_NULL

此数组元素未用。结构中其他成员都是未定义的

01

PT_LOAD

此数组元素给出一个可加载的段,段的大小由 p_filesz 和 p_memsz 描述。文件中的字节被映射到内存段开始处。如果 p_memsz 大于 p_filesz,“剩余”的字节要清零。p_filesz 不能大于 p_memsz。可加载的段在程序头部表格中根据 p_vaddr 成员按升序排列

02

PT_DYNAMIC

数组元素给出动态链接信息

03

PT_INTERP

数组元素给出一个 NULL 结尾的字符串的位置和长度,该字符串将被当作解释器调用。这种段类型仅对与可执行文件有意义(尽管也可能在共享目标文件上发生)。在一个文件中不能出现一次以上。如果存在这种类型的段,它必须在所有可加载段项目的前面

04

PT_NOTE

此数组元素给出附加信息的位置和大小

05

PT_SHLIB

此段类型被保留,不过语义未指定。包含这种类型的段的程序与 ABI不符

06

PT_PHDR

此类型的数组元素如果存在,则给出了程序头部表自身的大小和位置,既包括在文件中也包括在内存中的信息。此类型的段在文件中不能出现一次以上。并且只有程序头部表是程序的内存映像的一部分时才起作用。如果存在此类型段,则必须在所有可加载段项目的前面

0x70000000

PT_LOPROC

此范围的类型保留给处理器专用语义

0x7fffffff

PT_HIPROC

此范围的类型保留给处理器专用语义

...

...

...

还有一些编译器或者处理器标识的段类型,有待补充。

p_offset 段相对于文件的索引地址

p_vaddr 段在内存中的虚拟地址

p_paddr 段的物理地址

p_filesz 段在文件中所占的长度

p_memsz 段在内存中所占的长度

p_flage 段相关标志(read、write、exec)

p_align 字节对其,p_vaddr 和 p_offset 对 p_align 取模后应该等于0。

Section Header Table

与Progarm Header类似,我们同样可以从ELF Header中得到索引地址(e_shoff)、节区数量(e_shnum)、表项大小(e_shentsize),还可以由名称节区索引(e_shstrndx)得到各节区的名称。

Section Header Table 表项结构定义:

typedef struct {

Elf32_Word sh_name;

Elf32_Word sh_type;

Elf32_Word sh_flags;

Elf32_Addr sh_addr;

Elf32_Off sh_offset;

Elf32_Word sh_size;

Elf32_Word sh_link;

Elf32_Word sh_info;

Elf32_Word sh_addralign;

Elf32_Word sh_entsize;

}

Elf32_Shdr;

sh_name 节区名称,此处是一个在名称节区的索引。

sh_type 节区类型

名称

取值

说明

SHT_NULL

0

此值标志节区头部是非活动的,没有对应的节区。此节区头部中的其他成员取值无意义。

SHT_PROGBITS

1

此节区包含程序定义的信息,其格式和含义都由程序来解释。

SHT_SYMTAB

2

此节区包含一个符号表。目前目标文件对每种类型的节区都只能包含一个,不过这个限制将来可能发生变化。一般,SHT_SYMTAB 节区提供用于链接编辑(指 ld 而言)的符号,尽管也可用来实现动态链接.

SHT_STRTAB

3

此节区包含字符串表。目标文件可能包含多个字符串表节区。

SHT_RELA

4

此节区包含重定位表项,其中可能会有补齐内容(addend),例如 32 位目标文件中的 Elf32_Rela 类型。目标文件可能拥有多个重定位节区。

SHT_HASH

5

此节区包含符号哈希表。所有参与动态链接的目标都必须包含一个符号哈希表。目前,一个目标文件只能包含一个哈希表,不过此限制将来可能会解除。

SHT_DYNAMIC

6

此节区包含动态链接的信息。目前一个目标文件中只能包含一个动态节区,将来可能会取消这一限制。

SHT_NOTE

7

此节区包含以某种方式来标记文件的信息。

SHT_NOBITS

8

这种类型的节区不占用文件中的空间,其他方面和 SHT_PROGBITS 相似。尽管此节区不包含任何字节,成员sh_offset 中还是会包含概念性的文件偏移

SHT_REL

9

此节区包含重定位表项,其中没有补齐(addends),例如 32 位目标文件中的 Elf32_rel 类型。目标文件中可以拥有多个重定位节区。

SHT_SHLIB

10

此节区被保留,不过其语义是未规定的。包含此类型节区的程序与 ABI 不兼容。

SHT_DYNSYM

11

作为一个完整的符号表,它可能包含很多对动态链接而言不必要的符号。因此,目标文件也可以包含一个 SHT_DYNSYM 节区,其中保存动态链接符号的一个最小集合,以节省空间。

SHT_LOPROC

0X70000000

这一段(包括两个边界),是保留给处理器专用语义的。

SHT_HIPROC

0X7FFFFFFF

这一段(包括两个边界),是保留给处理器专用语义的。

SHT_LOUSER

0X80000000

此值给出保留给应用程序的索引下界。

SHT_HIUSER

0X8FFFFFFF

此值给出保留给应用程序的索引上界。

sh_flags 同Program Header的p_flags

sh_addr 节区索引地址

sh_offset 节区相对于文件的偏移地址

sh_size 节区的大小

sh_link 此成员给出节区头部表索引链接。

sh_info 此成员给出附加信息。

sh_type

sh_link

sh_info

SHT_DYNAMIC

此节区中条目所用到的字符串表格的节区头部索引

0

SHT_HASH

此哈希表所适用的符号表的节区头部索引

0

SHT_REL、SHT_RELA

相关符号表的节区头部索引

重定位所适用的节区的节区头部索引

SHT_SYMTAB、SHT_DYNSYM

相关联的字符串表的节区头部索引

最后一个局部符号(绑定 STB_LOCAL)的符号表索引值加一

其它

SHN_UNDEF

0

sh_addralign

某些节区带有地址对齐约束。例如,如果一个节区保存一个doubleword,那么系统必须保证整个节区能够按双字对齐。sh_addr 对sh_addralign 取模,结果必须为 0。目前仅允许取值为 0 和 2的幂次数。数值 0 和 1 表示节区没有对齐约束。

sh_entsize

某些节区中包含固定大小的项目,如符号表。对于这类节区,此成员给出每个表项的长度字节数。 如果节区中并不包含固定长度表项的表格,此成员取值为 0

一般来说,节区索引为0,即第一个节区一般都是SHN_UNDEF,其各项值都固定为0

划重点:

*1. 以“.”开头的节区名称是系统保留的。应用程序可以使用没有前缀的节区名称,以避免与系统节区冲突。*

*2. 目标文件中也可以包含多个名字相同的节区。*

*3. 保留给处理器体系结构的节区名称一般构成为:处理器体系结构名称简写 + 节区名称。*

*4. 处理器名称应该与 e_machine 中使用的名称相同。例如 .FOO.psect 街区是由 FOO 体系结构定义的 psect 节区。*

部分系统节区作用详解

字符串表

在一个ELF文件中通常拥有一个或以上的字符串表,即类型为 SHT_STRTAB 的节区,如: ELF Header 中 e_shstrndx 索引的节区名称表(.shstrtab)、符号名称表(.dynstr)等。

对于字符串的定义,是以NULL(\0)开头,以NULL结尾。

以一个.shstrtab表的内容为例:

00 2E 73 68 73 74 72 74 61 62 00 2E 69 6E 74 65 72 70 00 2E 64 79 6E 73 79 6D 00 ...

从这里可以得到3个字符串即:

2E 73 68 73 74 72 74 61 62 (.shstrtab);

2E 69 6E 74 65 72 70 (.interp);

2E 64 79 6E 73 79 6D (.dynsym);

假如索引为0,那么字符串的内容就是 2E 73 68 73 74 72 74 61 62 (.shstrtab)

符号表

符号: 指函数或者数据对象等。

既然叫做表,那么也分为一个一个表项,其表项也有自己的结构定义:

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;

st_name 符号名称,给出的是一个在符号名称表(.dynstr)中的索引

st_value 一般都是函数地址,或者是一个常量值

st_size 从 st_value 地址开始,共占的长度大小

st_info

用于标示此符号的属性,占一个字节(2个字),两个标示位,第一个标示位(低四位)标志作用域,第二个标示位(高四位)标示符号类型

取值

代表

含义

0

STB_LOCAL

局部符号在包含该符号定义的目标文件以外不可见。相同名称的局部符号可以存在于多个文件中,互不影响。

1

STB_GLOBAL

全局符号对所有将组合的目标文件都是可见的。一个文件中对某个全局符号的定义将满足另一个文件对相同全局符号的未定义引用。

2

STB_WEAK

弱符号与全局符号类似,不过他们的定义优先级比较低。

​(以我的理解..就是LOACL是局部变量,GLOBAL 和 WEAK 是全局量, 两者的差别在于是不是常量?猜的。)

取值

代表

含义

1

STT_OBJECT

符号与某个数据对象相关,比如一个变量、数组等等

2

STT_FUNC

符号与某个函数或者其他可执行代码相关

3

STT_SECTION

符号与某个节区相关。这种类型的符号表项主要用于重定位,通常具有 STB_LOCAL 绑定

4

STT_FILE

传统上,符号的名称给出了与目标文件相关的源文件的名称。文件符号具有 STB_LOCAL 绑定,其节区索引是SHN_ABS,并且它优先于文件的其他 STB_LOCAL 符号(如果有的话)

​举个栗子: 比如此处数值为0x12,那么他就对应着 STB_GLOBAL 和 STT_FUNC。从编程的方面来说,就是一个 public 的函数。

st_other 固定值为0。

st_shndx

每个符号表项都以和其他节区间的关系的方式给出定义。此成员给出相关的节区头部表索引。某些索引具有特殊含义。

唔..我也不知道有什么用…

代码段

代码段就是存放指令的节区(.text),符号表中的 st_value 指向代码段中具体的函数地址,以其地址的指令为函数开头。

全局偏移表

指.got节区,.got内的值均为 Elf32_Addr。其为全局符号提供偏移地址(指向过程链接表)。

过程链接表

.plt节区,其每个表项都是一段代码,作用是跳转至真实的函数地址

哈希表

指.hash节区。哈希表的结构:

其中nchain为符号表表项数,nchain 和 nbucket 是 chain 和 bucket 的数量。

数据段

.data、.bss、.rodata都属于数据段。其中,

.data 存放已初始化的全局变量、常量。

.bss 存放未初始化的全局变量,所以此段数据均为0,仅作占位。

.rodata 是只读数据段,此段的数据不可修改,存放常量。

.init_array .fini_array

程序运行时,执行.init_array中的指令。

程序退出时,执行.fini_array中的指令。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值