0x00 概论
因为TI的DSP输出文件与传统的ELF文件不符,所以本人就顺道研究了一下现在的ELF的文件格式。 会将其陆续完成在文章中。
承接上文,上文书说到,解析文件头格式,数据段的分配定义,与数据段的约束。
阅读本文之前,您需要掌握的技能有:
技能名称 | 技能熟练度 | 技能教程链接 |
---|---|---|
C语言 | 了解 | 暂无 |
0x10 Section Table介绍与说明
接下来将会讲述Section数据段的区域相关的定义。
数据段表头的定义在上文已经讲过,而数据段表的指针也在上文的文件头中进行了说明。有了数据段表头指针,就可以得到数据段的起始位置的定义。定义如下:
数据定义 | 数据格式 | 个数 | 说明 |
---|---|---|---|
section header name | Elf32_Word | 1 | 数据段名称 |
section header type | Elf32_Word | 1 | 数据段的类型 |
section header flags | Elf32_Word | 1 | 此数据段的标志位 |
section header address | Elf32_Address | 1 | 数据段的关于代码执行的地址 |
section header offset | Elf32_Offset | 1 | 数据段在文件中的偏移量 |
section header size | Elf32_Word | 1 | 数据段的长度 |
section header link | Elf32_Word | 1 | 索引位数量 |
section header info | Elf32_Word | 1 | 其他类的信息 |
section header address align | Elf32_Word | 1 | 数据排列定义 |
section header entry size | Elf32_Word | 1 | 数据段的入口长度,用于索引(如果有的话) |
BFD Compatible | 下文强调 | 1 | 用于兼容BFD文件的兼容定义,是一个定义完善的数据结构 |
section header content Address | Elf32_Address(容器为Elf32_Char) | 1 | 数据的真实数据指针 |
这就是一条完整的Section 数据条。有了这个,再加上上文的数据段表头的入口地址,就可以顺利的从文章起始找到文件的数据段的结构,从而抓取整个数据段的列表的所有的数据。而只要有软件遍历了所有的数据段数据结构,解析了所有的数据段参数,就可以生成下图这样的数据表了。
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 701856 000000 00 0 0 4
[ 1] .shstrtab STRTAB 00000000 701856 00013c 00 0 0 4
[ 2] .strtab STRTAB 00000000 701992 01509b 00 0 0 4
[ 3] .symtab SYMTAB 00000000 716a2f 02f790 10 2 10471 4
[ 4] GreenDreamer PROGBITS 05201314 052013 521314 01 A 0 0 1
[ 5] GreenDreamer PROGBITS 05201314 052013 521314 01 A 0 0 1
[ 6] GreenDreamer PROGBITS 05201314 052013 521314 01 A 0 0 4
[ 7] GreenDreamer PROGBITS 05201314 052013 521314 01 AX 0 0 4
[ 8] GreenDreamer PROGBITS 05201314 052013 521314 01 A 0 0 1
[ 9] GreenDreamer PROGBITS 05201314 052013 521314 01 A 0 0 1
[10] GreenDreamer PROGBITS 05201314 052013 521314 01 AX 0 0 1
[11] GreenDreamer PROGBITS 05201314 052013 521314 01 A 0 0 1
[12] GreenDreamer PROGBITS 05201314 052013 521314 01 A 0 0 1
[13] GreenDreamer NOBITS 20000000 052013 01c4a8 01 WA 0 0 8
[14] .debug_abbrev PROGBITS 00000000 0c9d4c 0170ec 01 0 0 0
[15] .debug_frame PROGBITS 00000000 0e0e38 024b30 01 0 0 0
[16] .debug_info PROGBITS 00000000 105968 052013 01 0 0 0
[17] .debug_line PROGBITS 00000000 052013 12a014 01 0 0 0
[18] .debug_loc PROGBITS 00000000 491c24 052013 01 0 0 0
[19] .debug_macinfo PROGBITS 00000000 4d1550 01d7b5 01 0 0 0
[20] .debug_pubnames PROGBITS 00000000 052013 01744c 01 0 0 0
[21] .iar.debug_frame PROGBITS 00000000 506154 00919e 01 0 0 0
[22] .iar.debug_line PROGBITS 00000000 052013 052013 01 0 0 0
[23] .comment PROGBITS 00000000 56a588 1696c0 01 0 0 0
[24] .iar.rtmodel PROGBITS 00000000 052013 000021 01 0 0 0
[25] .ARM.attributes ARM_ATTRIBUTES 00000000 052013 00002a 01 0 0 0
[26] GreenDreamer PROGBITS 080101d0 052013 052013 01 AX 0 0 0
[27] GreenDreamer PROGBITS 08062268 052013 01dd8c 01 AX 0 0 0
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
y (purecode), p (processor specific)
但是这里着重的说明一下BFD Compatible
,这个是一个兼容了Binary File Descriptor library的文件数据结构的指针,主要作用就是将content指针的数据转换扩充,成为BFD可以解析的数据段,而且此数据结构为双向链表的结构,所以就可以在仅读取一个Section 数据条的情况下,遍历出所有的数据条节点。
上图的节点中,有一个比较重要的是flag标志,这个标志所代表的含义详见如下,这些flag主要是提供给链接器处理的,所以也就还好,但是还是需要列出来。以便可以显示数据段,这些段的定义也都很简单,诸如数据段、组别、执行段、可变长字段等。暂时不冲突的数据仅有31个字节。其中还有两个字节是重复的。
定义 | 数据长度 | 解释 |
---|---|---|
name | 字符串 | 段名字 |
id | 整数 | 段标号 |
Index | 整数 | 段位置 |
next | 指针 | 下一个段。都是本身的指针 |
prev | 指针 | 上一个段,都是本身的指针 |
vma | 2*Elf32_Word | vma段与flag |
lma | Elf32_Word | lma段 |
size | Elf32_Word | 一个在bss区域的长度 |
rawsize | Elf32_Word | 真实区域的长度 |
compressed_size | Elf32_Word | 压缩后的真实数据长度 |
relax | relax_table | 单个区间的数据 |
relax_count | Elf32_Word | 区间的个数 |
output_offset | Elf32_Word | 数据段对于文件的偏移 |
output_section | 指针 | 指向的帧数输出段 |
align | Elf32_Word | 定义的文件排列顺序 |
relocation | Elf32_Word指针 | 记录关于这个section的相关参数 |
filepoint | file指针 | 三个指针,分别指向section、relocation、line |
userdata | Elf32_Word指针 | 数据真实的指针 |
symbol | Elf32_Word指针 | 标签的真实指针 |
上面就是很多的重要参数了,其他的参数还有很多,但是对于数据的处理都不是太重要,比如line的信息以及line的格式等。这里就不做过多介绍了。
当然这个是程序内部对于section的处理相关的指针,而输出端的定义则与其相去甚远,相关的定义就在下面。
定义 | 数据长度 |
---|---|
Name | Elf32_Word |
Type | Elf32_Word |
Flags | Elf32_Word |
Address | Elf32_Address |
Offset | Elf32_Offset |
Size | Elf32_Word |
Link | Elf32_Word |
Info | Elf32_Word |
Address align | Elf32_Word |
Entry size | Elf32_Word |
上述区间可以看出,对应的就是输出的表格的第一行的参数。所以,Section段的主要工作只有两步:将一个由双向链表为基础构成的大型数据区块沿着双向链表逐个解析,并将其转换为上面的一一对应的结构体格式。随后将其整理成表打印出来。
由此可以看出,这里的数据关键是数据区的解析,而数据区的解析主要是靠前文的文件头中的数据区的文件指针。
0x20 Section 定义
Section数据区虽然有着统一的数据结构,但是每个数据区都有着不同的作用。type就规定了每个区的不同的作用,具体的作用如下:
定义 | 数据 | 解释 |
---|---|---|
NULL | 0 | 基础 |
Program Bits | 1 | 程序段 |
Symbol Table | 2 | 符号段 |
String Table | 3 | 字符段 |
Relocation addends | 4 | 适配转移 |
Symbol Hash | 5 | 符号hash表 |
Dynamic Link | 6 | 动态链接 |
Relocation No Addends | 9 | 适配未转移 |
Semantics | 10 | 未定义的语义区间(0xFF类) |
Dynamic Table | 11 | 动态表格 |
Group | 17 | 组合 |
Version define | 0x6ffffff5 | 遵循GNU协议的架构数据 |
Version define | 0x6ffffffd | 数据版本定义 |
Version need | 0x6ffffffe | 数据版本需求 |
Symbol versions | 0x6fffffff | 符号版本 |
Processor-specific Low | 0x70000000 | 特定的处理器代码-低段 |
Processor-specific High | 0x7FFFFFFF | 特定的处理器代码-高段 |
Application-specific Low | 0x80000000 | 应用处理器代码-低段 |
Application-specific High | 0x8FFFFFFF | 应用处理器代码-高段 |
defined 1999年10月4号(SHT_HIUSER)Application-specific High | 0xFFFFFFFF | 最高的位置,定义的应该是最高位 |
这里先说明一下,这个最后的两行数据都是一样的,但是在1999年以后似乎就被废除了,虽然本人暂时没有找到可以支撑我这种说法的事实论据,但是从这个宏的含义还是可以看出一二的。
0x30 Section Flags
表中还有一段比较重要的数据就是每段数据的Flags,这段在最后的介绍中有数据解释:
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
y (purecode), p (processor specific)
其中大多数的定义如下:
定义 | 数据 | 解释 | 输出映射 |
---|---|---|---|
SHF_WRITE | 0x00000001 | 可写 | W (write) |
SHF_ALLOC | 0x00000002 | 缓存 | A (alloc) |
SHF_EXECINSTR | 0x00000004 | 可执行 | X (execute) |
SHF_MERGE | 0x00000010 | 合并区 | M (merge) |
SHF_STRINGS | 0x00000020 | 字符区 | S (strings) |
SHF_INFO_LINK | 0x00000040 | 信息区 | I (info) |
SHF_LINK_ORDER | 0x00000080 | 链接去声明 | L (link order) |
SHF_OS_NONCONFORMING | 0x00000100 | 当前OS未定义参数 | O (extra OS processing required) |
SHF_GROUP | 0x00000200 | 数据组 | G (group) |
SHF_TLS | 0x00000400 | 线程本地存储部分 | T (TLS) |
SHF_MASKOS | 0x0FF00000 | 当前OS定义参数 | o (OS specific) |
SHF_EXCLUDE | 0x80000000 | 运行时包含库 | E (exclude) |
SHF_MASKPROC | 0xF0000000 | 执行标准参数 | p (processor specific) |
SHF_X86_64_LARGE && (EM86_64|EM_L10M|EM_K10M) | 0x10000000 | 特定64位专用参数 | l(未定义但存在的参数) |
SHF_UNKNOWN | - | 未定义参数 | x (unknown) |
当然,这里有几项数据我并没有在代码和文档中寻找到原型,他们是y (purecode)、C (compressed)
,这两项在我仔细翻找下依然没有找到,可能没有在主流的代码位置上存在。
0x40 结论
以上就是Section Table的基本参数介绍,当然,这里讲述的主要是数据结构定义,而没有具体的参数进行说明。有些纸上点兵的嫌疑,但是也要先说明具体情况才可以为接下来的实现打好基础。
后面还有关于Symbol、string、debug等区位的解析与修改。详情请见随后的文章。
更多
本文首发自 记:ELF文件解析初定义——Section段相关讲解,更多文章可进入我的博客详查。