Linux系统ELF文件二进制格式分析(三)

本文接着《Linux系统ELF文件二进制格式分析(二)》进行分析

四、符号表

符号表保存了程序实现或使用的所有变量和函数,在一定程度上,变量和函数都可以认为是一种符号。如果一个目标文件当中使用了自身未定义的符号,则这个符号是未定义符号。这种符号必须在链接过程中解决其定义问题,可以是静态链接时在其他的目标文件或者库当中获取其定义,也可以是在加载时通过动态链接在共享库当中获取定义。

符号表在ELF文件中有专门的节来存储,节的名称为.symtab,类型为SHT_SYMTAB,如上一节当中readelf运行结果的截图所示。目标文件和可执行文件当中都存在相应的节来存储符号表。每个符号都用相同的数据结构来存储和它有关的信息。

1.      数据结构

typedef struct elf32_sym{
    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;

(1)    st_name指明了符号的名称,他存储的是符号在字符串表(名称为.strtab的节)当中的位置。

(2)    st_value指明了符号的值。可能是绝对值也可能是地址。

(3)    st_size指明了对象长度。如一个指针长度或strcut对象的字节数,如果长度未知则该值为0。

(4)    st_info指明了符号的确切用途,它分为两个部分:绑定属性和符号类型。

绑定类型有如下值:

#define STB_LOCAL  0
//局部符号STB_LOCAL表明这个符号只在目标文件内部可见,链接时与程序其他部分合并时是不可见的。因此如果几个目标文件都存在同名的这种类型的符号是可以接受的,他们不会互相干扰。

#define STB_GLOBAL 1
//全局符号STB_GLOBAL符号不仅在目标文件内部可见,也可以由构成程序的其他目标文件引用。一个程序内部不能出现同名的这种类型符号,否则链接器会报错。

#define STB_WEAK   2
//类型STB_WEAK符号在整个程序内可见,也可以有多个定义。如果程序中的一个全局符号和一个局部符号名称相同,则全局符号优先处理。

 符号类型有如下值:

#define STT_NOTYPE  0
//STT_NOTYPE该符号的类型没有指定,用于未定义引用。

#define STT_OBJECT  1
//STT_OBJECT该符号和一个数据对象相关,如变量、数组、指针等。 

#define STT_FUNC    2
//STT_FUNC 表示该符号和一个函数或其他可执行代码相关。 如果这个值出现在共享文件的符号表的某个符号当中时,表明该符号有特殊意义。当其他的目标文件从一个共享文件中引用一个函数时,链接器自动的为引用符号创建一个链接表。不是这个值,共享的目标符号将不会自动的通过链接表引用。 

#define STT_SECTION 3
//STT_SECTION该符号和一个节相关。这种类型的符号表入口主要是为了重定位,一般同时具有STB_LOCAL特性。 

#define STT_FILE    4
//STT_FILE 表明该符号给出了和目标文件相关的源文件名称,且具有STB_LOCAL特性,st_shndx字段为SHN_ABS ,并且它优先于对应该源文件的其他 STB_LOCAL 符号。

如果一个符号涉及到一个节的重定位,则st_shndx 将保留一个到该节头的索引。当该节在重定位过程中不断移动时,符号的值也相应变化,而该符号的引用在程序中指向同样的重定位位置。

绑定类型和符号类型定义的值是有重叠的,st_info大小为1个字节8位,且两种类型值的数量都不超过16,因此类型存储方式为:绑定类型占用st_info的高4位,符号类型占用st_info的低4位。两者通过下面的宏合并为一个字节。

#define ELF32_ST_INFO(b, t) (((b)<<4)+((t)&0xf)) 

或通过下面的宏从st_info提取各类型的相应值。

#define ELF32_ST_BIND(i) ((i)>>4) 
#define ELF32_ST_TYPE(i) ((i)&0xf) 

(5)    st_other该值目前未用到

(6)    sh_shndx保存了一个节在节头表中的索引,符号绑定到该节,该符号通常定义在此节的代码当中。但以下两个值有特殊的含义:

#define SHN_UNDEF	0
//标识未定义符号,表明该符号必须通过外部来源解决,如其他目标文件或者库

#define SHN_ABS		0xfff1
//指定符号是绝对值,不因重定位而改变

2. 目标文件中的符号表

源文件的名称存储为一个绝对值,索引为SHN_ABS,不随重定位而改变。类型为STT_FILE,标识该目标文件是关联到哪个源文件的。

文件中定义的两个函数:main和add,类型为STT_FUNC类型的全局符号。两个符号都指向节1,即.text节,保存了这两个函数的机器代码。

printf、exit和puts属于未定义引用,索引为SHN_UNDEF。因此程序链接时它们必须关联到标准库中的函数,或者其他库中以改名定义的符号。因为编译器不知道这些符号的类型,因而类型为STT_NOTYPE。


3. 可执行文件中的符号表

可执行文件中,printf、puts和exit(上图上半部分.dynsym符号表中)仍然是未定义的,但同时增加了一些信息,指示了函数依赖的库的名称和最低版本,“@@”符号之后的“GLIBC”是库的名称,“_”符号之后的“2.0”是库最低要求版本。程序自己定义的add和main符号已经移到虚拟地址空间的固定位置,文件加载时,对应代码将映射到这些位置,因此不会显示在可执行文件当中。


五、字符串表

.symtab和.strtab都在上面提到过了,.symtab表的st_name保存的不是实际的名称字符串,而是名称字符串在.strtab中的索引,而.hash保存了一个散列表,以帮助快速查找符号。.shstrtab这个字符串表存放了各个节的名称,如“.text”、“.data”等,节头表结构中sh_name成员存储的即为字符串在字符串表中的索引。

字符串表没有固定的格式,内核不能提供一个固定的数据结构,因此必须手工分析,字符串表在ELF中存储格式如下:


上图中每个单元格表示一个字节,字符串表中第一个字节为0,各个字符串也通过0字节进行分割。字符串索引即为字符串第一个字符在该区域当中的偏移值,如图中“add”的索引为1,“main”的索引为17。


ELF的重定位节及重定位问题将在《Linux系统ELF文件二进制格式分析(四)》中介绍

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值