软件构件基础-->目标文件分析
目标文件格式
目标文件是已经编译但还没有链接的文件,其格式与可执行文件格式很类似。
在Windows中目标文件格式为PE(Portable Executable),文件以".obj"为后缀。
在Linux中目标文件格式为ELF(Executable Linkable Format),文件名称没有明确规定一般以".o"结尾。
目标文件内容
目标文件内容是已经经过整理的内容,将代码、数据、符号表、调试信息、字符串等以"节"(Section)或"段"(Segment)的形式存储,
节和段都表示一个固定长度的区域,在ELF的链接和装载时有区别。段之间存储可能因为字节对齐的原因,会有间隔。
段名称:
目标文件默认段的名称以"."作为前缀,如下定义:
.code 或 .text 代码段
.data 初始化的数据段
.bss 未初始化数据段,编译器默认赋值0,所以此段只表明未初始化数据空间大小,但并不实际占用目标文件存储空间。
但在实际运行时,是会占用内存空间的。
.rodata 只读数据
.comment 编译器版本信息
.debug 调试信息
.dynamic 动态链接信息
.hash 符号哈希表
.line 调试时的行号表
.note 额外的编译信息
.strtab 字符串表
.symtab 符号表
.shstrtab 段名表
.plt和.got 动态链接跳转表和全局入口表
.init和.fini 程序初始化与终结代码段
当然也可以自定义段名,为了不与系统默认段名冲突,所以不使用前缀。
在gcc编译器下,使用格式 "__attribute__((section("name")))" 定义。
__attribute__((section("Global"))) int global=42; #指定变量global位于Global段中,可以通过链接脚本指定变量位置 __attribute__((section("Foo"))) void foo(void) { } #指定函数foo位于Foo段中,可以通过链接脚本指定函数位置
简易的ELF文件结构
ELF文件头 |
代码段(.text) |
数据段(.text) |
未初始化段(.bss) |
其他段 |
段表 |
符号表 |
文件头:
ELF文件头包含了魔数、数据存储方式、运行平台、版本、入口地址、段表地址等等。
读取这些数据,是按照一个结构体Elf32_Ehdr或Elf64_Ehdr(位于/usr/include/elf.h)来一一对应的。
可用“readelf -h *.o ”查看文件头。
段表:
整个段表在文件的起始位置由文件头的"e_shoff"决定,段表用于保存段的基本属性,包括段名、段长度、在文件中的偏移等等。
和文件头一样,这些数据也是按照结构体Elf32_Shdr(位于/usr/include/elf.h)来一一对应的。
可用“readelf -S *.o ”查看段表。
重定位表(.rel.text real.data):
重定位表存储了外部引用的数据或函数,在最后的链接过程中,需要为它们重新制定运行地址。
字符串表(.strtab .shstrtab):
将文件中使用的段名、变量名等集中存储于此,其他段仅仅存储它在字符串表中的偏移量,便能推出字符串内容。
符号表(.symtab):
符号表记录了目标文件中所用到的所有符号,每个符号都有一个符号值。
符号表也是由结构体Elf32_Sym来解析。
可用“readelf -s *.o ”查看符号表。
特殊符号:
有些符号并不存在工程的源文件中,而是链接脚本可以定义一些特殊值(一般是地址),但是源文件可以引用这些值。
在最后链接过程中,连接器会自动为这些符号指定正确的值。
常用的符号有:
__executable_start:程序起始地址
__etext或_etext或etext:代码段结束地址
_edata或edata:数据段结束地址
_end或end:程序结束地址
使用这些值:
extern char __executable_start[]; extern char __etext[],_etext[],etext[]; extern char _end[],end[];
posted on 2016-04-19 11:12 KcMeterCEC 阅读(356) 评论(0) 编辑 收藏 举报