===== String Table 字符串表=======
String table sections 保存着以NULL终止的一系列字符,一般我们称为字
符串。object文件使用这些字符串来描绘符号和section名。一个字符串的
参考是一个string table section的索引。第一个字节,即索引0,被定义保
存着一个NULL字符。同样的,一个string table的最后一个字节保存着一个
NULL字符,所有的字符串都是以NULL终止。索引0的字符串是没有名字或者说
是NULL,它的解释依靠上下文。一个空的string table section是允许的;
它的section header的成员sh_size将为0。对空的string table来说,非0的
索引是没有用的。
一个 settion 头的 sh_name 成员保存了一个对应于该 setion 头字符表部分
的索引(就象ELF头的 e_shstrndx 成员所特指的那样。下表列出了一个有 25 字节
的字符串表(这些字符串和不同的索引相关联):
Index +0 +1 +2 +3 +4 +5 +6 +7 +8 +9
===== == == == == == == == == == ==
0 \0 n a m e . \0 V a r
10 i a b l e \0 a b l e
20 \0 \0 x x \0
+ Figure 1-15: String Table Indexes
Index String
===== ======
0 none
1 "name."
7 "Variable"
11 "able"
16 "able"
24 null string
如上所示,一个字符串表可能涉及该 section 中的任意字节。一个字符串可能
引用不止一次;引用子串的情况是可能存在的;一个字符串也可能被引用若干次;而
不被引用的字符串也是允许存在的。
==================== Symbol Table 符号表=========================
一个object文件的符号表保存了一个程序在定位和重定位时需要的定义和引用的信息。
一个符号表索引是相应的下标。0表项特指了该表的第一个入口,就象未定义的符号
索引一样。初始入口的内容在该 section 的后续部分被指定。
Name Value
==== =====
STN_UNDEF 0
一个符号表入口有如下的格式:
+ Figure 1-16: Symbol Table Entry
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
该成员保存了进入该object文件的符号字符串表入口的索引(保留了符号名的表达字符)。
如果该值不为 0 ,则它代表了给出符号名的字符串表索引。否则,该符号无名。
注意:External C 符号和object文件的符号表有相同的名称。
* st_value
该成员给出了相应的符号值。它可能是绝对值或地址等等(依赖于上下文);
细节如下所述。
* st_size
许多符号和大小相关。比如,一个数据对象的大小是该对象所包含的字节数目。
如果该符号的大小未知或没有大小则这个成员为 0 。
* st_info
成员指出了符号的类型和相应的属性。相应的列表如下所示。下面的代码说明了
如何操作该值。
#define ELF32_ST_BIND(i) ((i)>>4)
#define ELF32_ST_TYPE(i) ((i)&0xf)
#define ELF32_ST_INFO(b, t) (((b)<<4)+((t)&0xf))
* st_other
该成员目前为 0 ,没有含义。
* st_shndx
每一个符号表的入口都定义为和某些 section 相关;该成员保存了相关的 section
头索引。就象 Figure 1-8 {*}和相关的文字所描述的那样,某些 section 索引
指出了特殊的含义。
一个符号的属性决定了可链接性能和行为。
+ Figure 1-17: Symbol Binding, ELF32_ST_BIND
Name Value
==== =====
STB_LOCAL 0
STB_GLOBAL 1
STB_WEAK 2
STB_LOPROC 13
STB_HIPROC 15
* STB_LOCAL
在包含了其定义的object文件之外的局部符号是不可见的。不同文件中的具有相同
名称的局部符号并不相互妨碍。
* STB_GLOBAL
全局符号是对所有的object目标文件可见的。一个文件中的全局符号的定义可以
满足另一个文件中对(该文件中)未定义的全局符号的引用。
* STB_WEAK
弱符号相似于全局符号,但是他们定义的优先级比较低一些。
* STB_LOPROC through STB_HIPROC
其所包含范围中的值由相应的处理器语义所保留。
全局符号和弱符号的区别主要在两个方面。
* 当链接器链接几个可重定位的目标文件时,它不允许 STB_GLOBAL 符号的同名
多重定义。另一方面,如果一个全局符号的定义存在则具有相同名称的弱符号名不会
引起错误。链接器将认可全局符号的定义而忽略弱符号的定义。与此相似,如果有一个
普通符号(比如,一个符号的 st_shndx 域包含 SHN_COMMON),则一个同名的弱符号
不会引起错误。链接器同样认可普通符号的定义而忽略弱符号。
* 当链接器搜索档案库的时候,它选出包含了未定义的全局符号的存档成员。该成员
的定义或者是全局的或者是一个弱符号。链接器不会为了解决一个未定义的弱符号
选出存档成员。未定义的弱符号具有 0 值。
在每一个符号表中,所有具有 STB_LOCAL 约束的符号优先于弱符号和全局符号。
就象上面 "sections" 中描述的那样,一个符号表部分的 sh_info 头中的成员
保留了第一个非局部符号的符号表索引。
符号的类型提供了一个为相关入口的普遍分类。
+ Figure 1-18: Symbol Types, ELF32_ST_TYPE
Name Value
==== =====
STT_NOTYPE 0
STT_OBJECT 1
STT_FUNC 2
STT_SECTION 3
STT_FILE 4
STT_LOPROC 13
STT_HIPROC 15
* STT_NOTYPE
该符号的类型没有指定。
* STT_OBJECT
该符号和一个数据对象相关,比如一个变量、一个数组等。
* STT_FUNC
该符号和一个函数或其他可执行代码相关。
* STT_SECTION
该符号和一个 section 相关。这种类型的符号表入口主要是为了重定位,一般的
具有 STB_LOCAL 约束。
* STT_FILE
按惯例而言,该符号给出了和目标文件相关的源文件名称。一个具有 STB_LOCAL
约束的文件符号,其 section 索引为 SHN_ABS ,并且它优先于当前对应该文件的
其他 STB_LOCAL 符号。
* STT_LOPROC through STT_HIPROC
该范围中的值是为处理器语义保留的。
共享文件中的函数符号(具有 STT_FUNC 类型)有特殊的意义。当其他的目标文件
从一个共享文件中引用一个函数时,链接器自动的为引用符号创建一个链接表。除了
STT_FUNC 之外,共享的目标符号将不会自动的通过链接表引用。
如果一个符号涉及到一个 section 的特定定位,则其 section 索引成员 st_shndx
将保留一个到该 section 头的索引。当该 section 在重定位过程中不断
移动一样,符号的值也相应变化,而该符号的引用在程序中指向同样的定位。某些
特殊的 section 索引有其他的语义。
* SHN_ABS
该符号有一个不会随重定位变化的绝对值。
* SHN_COMMON
该符号标识了一个没有被分配的普通块。该符号的值给出了相应的系统参数,就象
一个 section 的 sh_addralign 成员。也就是说,链接器将分配一个地址给
该符号,地址的值是 st_value 的倍数。该符号的大小指出了需要的字节数。
* SHN_UNDEF
该 section 表索引表明该符号是未定义的。当链接器将该目标文件和另一个定义
该符号的文件相装配的时候,该文件内对该符号的引用将链接到当前实际的定义。
如上所述,符号表的 0 索引(STN_UNDEF)是保留的,它包含了如下内容:
+ Figure 1-19: Symbol Table Entry: Index 0
Name Value Note
==== ===== ====
st_name 0 No name
st_value 0 Zero value
st_size 0 No size
st_info 0 No type, local binding
st_other 0
st_shndx SHN_UNDEF No section
Symbol Values(符号值)
符号表入口对于不同的目标文件而言对 st_value 成员有一些不同的解释。
* 在可重定位文件中, st_value 保存了 section 索引为 SHN_COMMON 符号
的强制对齐值。
* 在可重定位文件中, st_value 保存了一个符号的 section 偏移。也就是说,
st_value 是从 st_shndx 定义的 section 开头的偏移量。
* 在可执行的和可共享的目标文件中, st_value 保存了一个虚拟地址。为了使
这些文件符号对于动态链接器更为有效,文件层面上的 section 偏移让位于内存
层面上的虚拟地址( section 编号无关的)。
尽管符号表值对于不同的目标文件有相似的含义,相应的程序还是可以有效地访问数据。
====================== Relocation (重定位)==========================
重定位是连接符号引用和符号定义的过程。比如,当一个程序调用一个函数的时候,
相关的调用必须在执行时把控制传送到正确的目标地址。换句话说,重定位文件应当
包含有如何修改他们的 section 内容的信息,从而允许可执行文件或共享目标文件
为一个进程的程序映像保存正确的信息。重定位入口就是这样的数据。
+ Figure 1-20: Relocation Entries
typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
} Elf32_Rel;
typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
Elf32_Sword r_addend;
} Elf32_Rela;
* r_offset
该成员给出了应用重定位行为的地址。对于一个重定位文件而言,该值是从该
section 开始处到受到重定位影响的存储单位的字节偏移量。对一个可执行文件
或一个共享目标而言,该值是受到重定位影响的存储单位的虚拟地址。
* r_info
该成员给出了具有受重定位影响因素的符号表索引和重定位应用的类型。比如,
一个调用指令的重定位入口应当包含被调用函数的符号索引。如果该索引是
STN_UNDEF (未定义的符号索引),重定位将使用 0 作为该符号的值。重定位
类型是和处理器相关的。当正文(text)引用到一个重定位入口的重定位类型或符
号表索引,它表明相应的应用 ELF32_R_TYPE或 ELF32_R_SYM 于入口的 r_info
成员。
#define ELF32_R_SYM(i) ((i)>>8)
#define ELF32_R_TYPE(i) ((unsigned char)(i))
#define ELF32_R_INFO(s, t) ((s)<<8+(unsigned char)(t))
* r_addend
该成员指定一个常量加数(用于计算将要存储于重定位域中的值)。
如上所述,只有 Elf32_Rela 入口包含一个明确的加数。Elf32_Rel 类型
的入口在可以修改的地址中存储一个隐含的加数。依赖于处理器结构,一种形式
或其他形式也许是必须的或更为方便的。因此,特定机器的应用应当使用一种排他
性的形式或依赖于上下文的另一种形式。
一个重定位 section 关联了两个其他的 section :一个符号表和一个可修改
的 section 。该 section 头的成员 sh_info 和 sh_link (在上文中的
“ section ”部分中有描述)指示了这种关系。重定位入口中的成员 r_offset
对于不同的目标文件有少许差异。
* 在可重定位文件中,r_offset 表示了一个 section 偏移。也就是说,重定位
section自己描述了如何修改其他在文件中的其他section; 重定位偏移量指
明了一个在第二个section中的存储器单元。
* 在可执行和共享的目标文件中,r_offset 表示一个虚拟地址。为了使得这些
文件的重定位入口更为有用(对于动态链接器而言),该 section 偏移(文件
中)应当让位于一个虚拟地址(内存中的)。
尽管为了允许相关的程序更为有效的访问而让 r_offset 的解释对于不同的目标
文件有所不同,重定位类型的含义是相同的。
Relocation Types(重定位类型)
重定位入口描述了怎样变更下面的指令和数据域(位数在表的两边角下)。
+ Figure 1-21: Relocatable Fields
+---------------------------+
| word32 |
31---------------------------0
* word32
指定一个以任意字节对齐方式占用 4 字节的 32 位域。这些值使用与 32 位 Intel
体系相同的字节顺序。
3------2------1------0------+
0x01020304 | 01 | 02 | 03 | 04 |
31------+------+------+------0
下面的计算假设正在将一个可重定位文件转换为一个可执行或共享的目标文件。
从概念上来说,链接器合并一个或多个可重定位文件来组成输出。它首先决定
怎样合并、定位输入文件,然后更新符号值,最后进行重定位。对于可执行文件
和共享的目标文件而言,重定位过程是相似的并有相同的结果。下面的描述使用
如下的约定符号。
* A
表示用于计算可重定位的域值的加数。
* B
表示了在执行过程中一个共享目标被加载到内存时的基地址。一般情况下,一个
共享object文件使用的基虚地址为0,但是一个可执行地址就跟共享object文件
不同了。
* G
表示了在执行过程中重定位入口符号驻留在全局偏移表中的偏移。请参阅
第二部分中的“ Global Offset Table (全局偏移表)”获得更多
的信息。
* GOT
表示了全局偏移表的地址。请参阅第二部分中的“ Global Offset Table
(全局偏移表)”获得更多的信息。
* L
表示一个符号的过程链接表入口的位置( section 偏移或地址)。一个过程
链接表入口重定位一个函数调用到正确的目的单元。链接器创建初始的链接表,
而动态链接器在执行中修改入口。
请参阅第二部分中的“ Procedure Linkage Table (过程链接表)”获得更多
的信息
* P
表示(section 偏移或地址)被重定位的存储单元位置(使用 r_offset 计算的)。
* S
表示索引驻留在重定位入口处的符号值。
一个重定位入口的 r_offset 值指定了受影响的存储单元的首字节的偏移
或虚拟地址。重定位类型指定了哪一位(bit)将要改变,以及怎样计算它们的值。
在 SYSTEM V 体系中仅仅使用 Elf32_Rel 重定位入口,将要被重定位的域中
保留了加数。在所有的情况下,加数和计算结果使用相同字节顺序。
+ Figure 1-22(表 1-22): Relocation Types(重定位类型)
Name Value Field Calculation
==== ===== ===== ===========
R_386_NONE 0 none none
R_386_32 1 word32 S + A
R_386_PC32 2 word32 S + A - P
R_386_GOT32 3 word32 G + A - P
R_386_PLT32 4 word32 L + A - P
R_386_COPY 5 none none
R_386_GLOB_DAT 6 word32 S
R_386_JMP_SLOT 7 word32 S
R_386_RELATIVE 8 word32 B + A
R_386_GOTOFF 9 word32 S + A - GOT
R_386_GOTPC 10 word32 GOT + A - P
有的重定位类型有不同于简单计算的语义。
* R_386_GOT32
这种重定位类型计算全局偏移表基地址到符号的全局偏移表
入口之间的间隔。这样另外通知了 link editor 建立一个全局偏移表 。
* R_386_PLT32
这种重定位类型计算符号的过程链接表入口地址,并另外通知链接器建立一个
过程链接表。
* R_386_COPY
链接器创建该重定位类型用于动态链接。它的偏移成员涉及一个可写段中的一个
位置。符号表索引指定一个可能存在于当前 object file 或在一个shared object
中的符号。在执行过程中,动态链接器把和 shared object 符号相关的数据
拷贝到该偏移所指定的位置。
* R_386_GLOB_DAT
这种重定位类型用于设置一个全局偏移表入口为指定符号的地址。该特定的重定位
类型允许你决定符号和全局偏移表入口之间的一致性。
* R_386_JMP_SLOT {*}
链接器创建该重定位类型用于动态链接。其偏移成员给出了一个过程链接表入口的
位置。动态链接器修改该过程链接表入口以便向特定的符号地址传递控制。
[参阅第二部分中的 "Procedure Linkage Table(过程链接表)"]
* R_386_RELATIVE
链接器创建该重定位类型用于动态链接。其偏移成员给出了包含表达相关地址值
的一个 shared object 中的位置。动态链接器计算相应的虚拟地址(把该
shared object 装载地址和相对地址相加)。该类型的重定位入口必须为
符号表索引指定为 0 。
* R_386_GOTOFF
这种重定位类型计算符号值和全局偏移表地址之间的不同。另外还通知链接器
建立全局偏移表(GOT)。
* R_386_G