一、符号的定义
链接的本质就是把多个不同的目标文件如同拼图一样互相拼接起来,为了使不同的目标文件之间能够互相粘合,这些目标文件之间需要有特定的规则
才行。
特定的规则:目标文件之间对地址的引用,即对变量
或者函数
的地址的引用
举例来说:目标文件 B 中用到了 目标文件 A 中的函数 foo ,那么我们称目标文件 A 定义了函数 foo ,而目标文件 B 引用了目标文件 A 中的函数 foo
在链接中,我们将函数和变量统称为符号,函数名和变量名就是符号名
每一个目标文件都会有一个符号表来记录所有的符号名和符号值,符号值指的是符号的地址
以 SimpleSection.c 为例,符号可以分为以下几个类型:
int printf( const char* format, ... );
int global_init_var = 84;
int global_uninite_var;
void func1( int i )
{
printf("%d\n", i);
}
int main()
{
static int static_var = 85;
static int static_var2;
int a = 1;
int b;
func1(static_var + static_var2 + a + b);
return a;
}
- 定义在本目标文件的全局符号:可以被其他目标文件引用,例如:global_init_var、global_uninite_var、func1、main
- 在目标文件中引用的全局符号:这种符号被定义在其他目标文件中,例如:printf
- 局部符号:这类符号仅在本目标文件中可见,例如:static_var、static_var2、a、b
- 段名:这种符号由编译器产生,它的符号值就是段的起始地址
使用nm
命令查看目标文件符号结果如下:
[gongruiyang@localhost ws]$ nm SimpleSection.o
0000000000000000 T func1
0000000000000000 D global_init_var
0000000000000004 C global_uninite_var
0000000000000022 T main
U printf
0000000000000004 d static_var.1801
0000000000000000 b static_var2.1802
T
:该符号位于.text
段D/d
:该符号位于.data
段C
:该符号是公共符号(common data),是未初始化数据U
:表示该符号未定义,需要去其他目标文件寻找b
:该符号位于.bss
段
二、符号结构体:Elf32_Sym
ELF 文件中的符号表是目标文件中的一个段,段名叫 .symtab
,这个段的信息由结构体Elf32_Shdr
来描述,段中的数据信息其实是一个数组,数组中每一个元素都是一个结构体Elf32_Sym
:include\linux\elf.h
typedef struct elf32_sym{
Elf32_Word/*unsigned int*/ st_name;
Elf32_Addr/*unsigned int*/ st_value;
Elf32_Word/*unsigned int*/ st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half/*unsigned short*/ st_shndx;
} Elf32_Sym;
typedef struct elf64_sym {
Elf64_Word/*unsigned int*/ st_name; /* Symbol name, index in string tbl */
unsigned char st_info; /* Type and binding attributes */
unsigned char st_other; /* No defined meaning, 0 */
Elf64_Half/*unsigned short*/ st_shndx; /* Associated section index */
Elf64_Addr/*unsigned long long*/ st_value; /* Value of the symbol */
Elf64_Xword/*unsigned long long*/ st_size; /* Associated symbol size */
} Elf64_Sym;
st_name
:符号名,这个成员变量的值表示该符号名在字符串表中的下标st_value
:符号对应的值,可能是该符号的地址,不同符号的值的含义不同st_size
:符号大小,指这个符号的数据类型的大小st_info
:符号类型和绑定信息,该成员的低 4 位表示符号的类型,高 28 位表示符号的绑定信息st_other
:无意义,填充 0st_shndx
:符号所在的段
st_info
符号绑定信息
#define STB_LOCAL 0
#define STB_GLOBAL 1
#define STB_WEAK 2
常量名 | 值 | 含义 |
---|---|---|
STB_LOCAL | 0 | 局部符号,外部不可见 |
STB_GLOBAL | 1 | 全局符号,外部可见 |
STB_WEAK | 2 | 弱引用 |
符号类型
#define STT_NOTYPE 0
#define STT_OBJECT 1
#define STT_FUNC 2
#define STT_SECTION 3
#define STT_FILE 4
#define STT_COMMON 5
#define STT_TLS 6
常量名 | 值 | 含义 |
---|---|---|
STT_NOTYPE | 0 | 未知类型 |
STT_OBJECT | 1 | 数据对象类型,例如:变量、数组 |
STT_FUNC | 2 | 函数或者可执行代码 |
STT_SECTION | 3 | 该符号是 一个段 |
STT_FILE | 4 | 该符号是 文件名 |
STT_COMMON | 5 | 公共数据(common data) |
STT_TLS | 6 | 本地数据(thread-local data ) |
st_shndx
符号所在段
/* special section indexes */
#define SHN_UNDEF 0
#define SHN_LORESERVE 0xff00
#define SHN_LOPROC 0xff00
#define SHN_HIPROC 0xff1f
#define SHN_ABS 0xfff1
#define SHN_COMMON 0xfff2
#define SHN_HIRESERVE 0xffff
常量 | 值 | 含义 |
---|---|---|
SHN_UNDEF | 0 | 该符号未定义在本文件中,定义在其他目标文件中 |
SHN_LORESERVE | 0xff00 | 被保留索引号区间的下限 |
SHN_LOPROC | 0xff00 | 为特定处理器定制节所保留的索引号区间的下限 |
SHN_HIPROC | 0xff1f | 为特定处理器定制节所保留的索引号区间的上限 |
SHN_ABS | 0xfff1 | 表示该符号包含了一个绝对的值,比如表示文件名的符号就属于该类型 |
SHN_COMMON | 0xfff2 | 表示该符号是一个“COMMON 块”类型的符号,未初始化的全局符号都是这种类型 |
SHN_HIRESERVE | 0xffff | 被保留索引号区间的上限 |
st_value
每个符号都有一个对应的值,如果这个符号是一个函数或变量的定义,那么符号的值就是这个函数或变量的地址,更准确的说分为以下几种情况:
- 在目标文件中,如果该符号是函数或变量的定义并且该
符号的所在段
不是“COMMON块”(即 st_shndx 不是 SHN_COMMON),则 st_value 的值表示该符号在段中的偏移量
- 在目标文件中,如果该符号是函数或变量的定义并且该
符号的所在段
是“COMMON块”(即 st_shndx 是 SHN_COMMON),则 st_value 的值表示该符号的对齐属性
- 在可执行文件中, st_value 表示符号的虚拟地址
以 SimpleSection.o 为例,分析各个符号状态:
[gongruiyang@localhost ws]$ readelf -s SimpleSection.o
Symbol table '.symtab' contains 16 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS SimpleSection.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000004 4 OBJECT LOCAL DEFAULT 3 static_var.1801
7: 0000000000000000 4 OBJECT LOCAL DEFAULT 4 static_var2.1802
8: 0000000000000000 0 SECTION LOCAL DEFAULT 7
9: 0000000000000000 0 SECTION LOCAL DEFAULT 8
10: 0000000000000000 0 SECTION LOCAL DEFAULT 6
11: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 global_init_var
12: 0000000000000004 4 OBJECT GLOBAL DEFAULT COM global_uninite_var
13: 0000000000000000 34 FUNC GLOBAL DEFAULT 1 func1
14: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf
15: 0000000000000022 51 FUNC GLOBAL DEFAULT 1 main
- 输出列说明:
第二列
Value
对应st_value
第三列
Size
对应st_size
第四列
Type
和第五列Bind
对应st_info
第六列 Vis 未使用
第七列
Ndx
对应st_shndx
第八列
Name
对应st_name
- 各个符号分析:
符号名称 | 所在段 | 绑定信息 | 符号类型 |
---|---|---|---|
main / func1 | 1 (.text) | STB_GLOBAL(全局符号) | STT_FUNC(函数或者可执行代码) |
printf | UND(其他目标文件) | STB_GLOBAL(全局符号) | STT_NOTYPE(未被定义) |
global_init_var | 3(.data) | STB_GLOBAL(全局符号) | STT_OBJECT(数据对象类型:变量) |
global_uninite_var | COM(COMMON 块) | STB_GLOBAL(全局符号) | STT_COMMON(COMMON 块) |
static_var.1801 | 3(.data) | STB_LOCAL(局部符号) | STT_OBJECT(数据对象类型:变量) |
static_var2.1802 | 4(.bss) | STB_LOCAL(局部符号) | STT_OBJECT(数据对象类型:变量) |
SimpleSection.c | ABS (文件名绝对值) | STB_LOCAL(局部符号) | STT_FILE(文件名) |