【Linux】链接中的符号源码分析

一、符号的定义

链接的本质就是把多个不同的目标文件如同拼图一样互相拼接起来,为了使不同的目标文件之间能够互相粘合,这些目标文件之间需要有特定的规则才行。

特定的规则:目标文件之间对地址的引用,即对变量或者函数的地址的引用

举例来说:目标文件 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_Syminclude\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:无意义,填充 0
  • st_shndx符号所在的段

st_info

符号绑定信息

#define STB_LOCAL  0
#define STB_GLOBAL 1
#define STB_WEAK   2
常量名含义
STB_LOCAL0局部符号,外部不可见
STB_GLOBAL1全局符号,外部可见
STB_WEAK2弱引用

符号类型

#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_NOTYPE0未知类型
STT_OBJECT1数据对象类型,例如:变量、数组
STT_FUNC2函数或者可执行代码
STT_SECTION3该符号是 一个段
STT_FILE4该符号是 文件名
STT_COMMON5公共数据(common data)
STT_TLS6本地数据(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_UNDEF0该符号未定义在本文件中,定义在其他目标文件中
SHN_LORESERVE0xff00被保留索引号区间的下限
SHN_LOPROC0xff00为特定处理器定制节所保留的索引号区间的下限
SHN_HIPROC0xff1f为特定处理器定制节所保留的索引号区间的上限
SHN_ABS0xfff1表示该符号包含了一个绝对的值,比如表示文件名的符号就属于该类型
SHN_COMMON0xfff2表示该符号是一个“COMMON 块”类型的符号,未初始化的全局符号都是这种类型
SHN_HIRESERVE0xffff被保留索引号区间的上限

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 / func11 (.text)STB_GLOBAL(全局符号)STT_FUNC(函数或者可执行代码)
printfUND(其他目标文件)STB_GLOBAL(全局符号)STT_NOTYPE(未被定义)
global_init_var3(.data)STB_GLOBAL(全局符号)STT_OBJECT(数据对象类型:变量)
global_uninite_varCOM(COMMON 块)STB_GLOBAL(全局符号)STT_COMMON(COMMON 块)
static_var.18013(.data)STB_LOCAL(局部符号)STT_OBJECT(数据对象类型:变量)
static_var2.18024(.bss)STB_LOCAL(局部符号)STT_OBJECT(数据对象类型:变量)
SimpleSection.cABS (文件名绝对值)STB_LOCAL(局部符号)STT_FILE(文件名)
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值