软件构件基础-->目标文件分析

目标文件格式

目标文件是已经编译但还没有链接的文件,其格式与可执行文件格式很类似。

在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[];
原文地址:https://www.cnblogs.com/KcMeterCEC/p/5407407.html