首页 > 其他分享 >符号表

符号表

时间:2022-12-30 22:00:39浏览次数:35  
标签:__ 符号表 符号 DEFAULT GLOBAL 0000000000000000 LOCAL

符号表是什么?

我们知道,在编译的四个阶段中,最后一步链接的本质就是将不同的目标文件糅合到一块,生成最终可执行的二进制文件。而目标文件的互相糅合,实质上就是目标文件之间对地址的引用,就是对函数和变量的地址的引用。那怎么来完成这个过程呢?人们就想到了在每一个目标文件中存放一张记录了目标文件中所用到的所有符号以及其对应的符号值的表,链接的时候,目标文件之间就会互相寻找自己需要的符号来完成链接的过程,而这张表,就叫做符号表。

符号表长什么样

上面提到,一个程序,从源码到生成可执行文件有四个阶段:预处理,编译,汇编,链接。在汇编阶段,即生成目标文件的时候,就会产生符号表。每一个生成的elf文件都有符号表。符号表中的符号和源码中的变量名和函数名是一一对应的(对应的意思不代表生成的符号和源码中的函数变量名一定是一样的,而只是一种映射关系,典型的是在c++编译时,生成的符号和源码的符号是完全不一样的)。如下,我有一段测试代码:

//test.c

static int a;
int b;
static void test1(){
    return;
}

void test2(){
    return;
}

生成目标文件之后,用read -s test.o命令即可查看其中的符号表,如下:  

haley@ubuntu:~/Desktop$ readelf -s test.o

Symbol table '.symtab' contains 12 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS test.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     5: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    3 a
     6: 0000000000000000     7 FUNC    LOCAL  DEFAULT    1 test1
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     9: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
    10: 0000000000000004     4 OBJECT  GLOBAL DEFAULT  COM b
    11: 0000000000000007     7 FUNC    GLOBAL DEFAULT    1 test2

其中,value叫符号值,对于变量和函数而言,符号值就是他们的地址。size是一个符号值所占字节数,type是符号的类型,像变量的类型就是OBJECT,函数的类型就是FUNC。Bind列是符号的作用域,LOCAL表示是局部符号,GLOBAL表示是全局符号,WEAK表示是弱符号等等。Vis列表示符号的可见性,一般比较少用到。Ndx列表示符号所属的段的index(.text段,.data段等等)。Name列就是刚才说的与变量函数名一一对应的符号名了。在上面的例子中,b和test2是global的,而a和test1是local的。

来一个实际的例子说明一下:

//main.c

void test2();
int main(){ 
    test2();
    return 0;
}

//test.c
static int a;
int b ;
static void test1(){
    return;
}

void test2(){
    return;
}

上面有两个c源文件,然后我分别生成对应的目标文件main.o和test.o,导出他们的符号表如下:

//main.o的符号表

Symbol table '.symtab' contains 11 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS main.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    6 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     8: 0000000000000000    21 FUNC    GLOBAL DEFAULT    1 main
     9: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    10: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND test2 //注意这里,Ndx是UND的,且value为0


// test.o的符号表

Symbol table '.symtab' contains 12 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS test.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     5: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    3 a
     6: 0000000000000000     7 FUNC    LOCAL  DEFAULT    1 test1
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     9: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
    10: 0000000000000000     4 OBJECT  WEAK   DEFAULT    3 b
    11: 0000000000000007     7 FUNC    GLOBAL DEFAULT    1 test2

 

上面main.c引用了test.c的test2函数,但是在链接之前,main.o并不知道test2函数的定义和地址,所以main.o的符号表里将test2标记为UND(undefine的意思),地址值也缺省为0000000000000000,等待链接的时候寻找到定义了test2函数的目标文件的符号表,提取出test2函数的地址值。

下面来验证一下,用gcc test.o main.o将它们链接到一块,生成a.out可执行文件,导出a.out的符号表:

//a.out的符号表

Symbol table '.dynsym' contains 6 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     5: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (2)

Symbol table '.symtab' contains 66 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
    ......
    31: 00000000000005f0     0 FUNC    LOCAL  DEFAULT   13 frame_dummy
    32: 0000000000200df0     0 OBJECT  LOCAL  DEFAULT   18 __frame_dummy_init_array_
    33: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS test.c
    34: 0000000000201018     4 OBJECT  LOCAL  DEFAULT   23 a
    35: 00000000000005fa     7 FUNC    LOCAL  DEFAULT   13 test1
    36: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS main.c
    37: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    38: 0000000000000834     0 OBJECT  LOCAL  DEFAULT   17 __FRAME_END__
    49: 0000000000201010     0 NOTYPE  GLOBAL DEFAULT   22 _edata
    50: 0000000000000694     0 FUNC    GLOBAL DEFAULT   14 _fini
    51: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    52: 0000000000201000     0 NOTYPE  GLOBAL DEFAULT   22 __data_start
    53: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    54: 0000000000201008     0 OBJECT  GLOBAL HIDDEN    22 __dso_handle
    55: 00000000000006a0     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
    56: 0000000000000620   101 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
    57: 0000000000201020     0 NOTYPE  GLOBAL DEFAULT   23 _end
    58: 00000000000004f0    43 FUNC    GLOBAL DEFAULT   13 _start
    59: 0000000000201010     0 NOTYPE  GLOBAL DEFAULT   23 __bss_start
    60: 0000000000000608    21 FUNC    GLOBAL DEFAULT   13 main
    61: 0000000000000601     7 FUNC    GLOBAL DEFAULT   13 test2 //重点关注
    62: 0000000000201010     0 OBJECT  GLOBAL HIDDEN    22 __TMC_END__

 

这里看到有两个符号表,一个是.dynsym,一个是.symtab。这里简单介绍一下,.dynsym是动态符号表,是动态链接的时候用到的,而.symtab是静态符号表,静态链接时用到的。这里我们只看.symtab。这里看到Num为61的行,可以发现test2已经不是UND了,而且地址值也有了是0000000000000601。说明链接是成功的!

这里插个题外话,在大型的项目中,其实链接完之后,因为已经链接完成了(已经完成了重定位操作),可执行文件中的.symtab,即静态符号表其实已经变得可有可无了。所以可以使用strip命令将静态符号表等无用得信息删掉,减小二进制文件的大小。但是.dynsym仍需保留,因为动态链接是在装载文件的时候完成的,符号信息不能删除。还有就是千万不要自作多情把strip命令也用在目标文件,因为目标文件是还没链接的,过早删除符号表会导致链接不了。

强符号和弱符号

在c/c++编程中,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号。我们可以通过gcc的__attribute__ ((weak))来将一个强符号转换成弱符号。针对强弱符号,有以下一些规则要遵守(参考自《程序员的自我修养》):

  • 不允许强符号被多次定义,若有多个强符号重复定义,链接时会报错。
  • 如果一个符号在目标文件中是强符号,在其他文件中是弱符号,那么链接时取得是强符号。
  • 如果一个符号在所有的目标文件中都是弱符号,那么选择其中占用空间最大的那一个。比如说变量a在目标文件中是int型,在目标文件B中是double型,那么将选择double型的那个符号进行链接。

对应于强符号和弱符号,有强引用和弱引用。强引用的意思是在链接的阶段,符号要有正确的寻址,若没找到该符号的定义,连接器就会报未定义的错误。弱引用的意思是在连接阶段,若能找到该符号的定义,那么连接器将引用该符号;若没被定义也没关系,连接器不报错,而是默认其为零。我们可以使用gcc的参数__attribute__ ((weakref))来将一个强引用转换成弱引用。

弱符号和弱引用在实践中有什么作用呢?先说弱符号,比如在库中定义的弱符号可以被用户自定义的强符号所覆盖。再说弱引用,比如程序可以定义某些拓展模块的引用定义为弱引用,当我们将拓展模块和程序连接到一块时,功能模块可以正常使用,当我们去除这些功能模块函数时,由于是弱引用,连接器只是把符号值设为零,程序还是可以正常链接,不会报错。不过,在设计弱引用时,一定要注意做判断,比如我有一个test弱引用,我们要用if(test){} else{}来去判断是否有连接test函数的定义,否则会出现意想不到的运行错误!!

extern “C”

c和c++的符号修饰机制是不同的,比如说对应一个变量名test,c编译器输出的符号还是test,而c++编译器输出的符号有可能是_ZN12_GLOBAL__N_14testE。因此如果你的代码一部分使用c,一部份使用c++,那么整个工程连接的时候就会出现问题。extern “C”生来就是用来解决这个问题的。用了extern “C”,c++编译器会将大括号内部的代码当作C语言处理,比如extern “C”{int a;},那么在用c++编译器编译时,变量a的符号修饰将按C语言的标准进行。但是C编译器不支持extern “C”,只有C++编译器才支持,所以一般我们都会用下面这种条件编译的形式:

#ifdef __cplusplus extern "C" {
#endif 
//这里放C语言定义的函数或变量声明

#ifdef __cplusplus }
#endif

 

符号版本机制

符号也是可以有版本的,当然,可有可无。符号版本机制的出现主要是为了解决动态链接库SO_NAME出现的次版本交会问题。实质上,GLIBC从2.1版本之后就一直使用符号版本机制。所谓符号版本控制,说白了其实就是在原本的符号后面加上版本号形成一个新的符号名称,随便生成一个a.out就能看到。下面所示,有几个符号像 __libc_start_main@GLIBC_2.2.5 就是说使用了GLIBC_2.2.5版本,如果在动态加载时使用了不同版本的glibc,会提示没有对应版本的符号,从而加载失败。

haley@ubuntu:~/Desktop/git_test$ readelf -s a.out 

Symbol table '.dynsym' contains 7 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND test2
     5: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     6: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (2)

 

在Linux下,我们可以通过版本脚本version-script来控制符号版本。只需要在编译动态库是加上-Wl,--version-script xxx即可。xxx是自定义的脚本,格式如下:

VER_1.1{
    global:
        test;
        ...
    local:
        *;   //一般情况下local里面打*号,表示除了global里面写的那些符号之外全部都是local的
};

 

标签:__,符号表,符号,DEFAULT,GLOBAL,0000000000000000,LOCAL
From: https://www.cnblogs.com/god-of-death/p/17015881.html

相关文章

  • 初探极限符号表示形式之优劣
    对于数列极限定义1,其中我们着重来看$\lim_{n\rightarrow\infty}\mspace{2mu}a_{n}=a$,这是大多数教材通常采用的对极限现象的符号代表形式,为了进一步了解极限的性质及......
  • 概率论常见分布及其符号表示
    名称表示二项分布B(n,p)泊松分布P(λ)均匀分布U(a,b)指数分布E(λ)正态分布N(μ,σ²)......
  • 栈、队列、符号表
    栈概述生活中的栈存储货物或供旅客住宿的地方,可引申为仓库、中转站。例如我们现在生活中的酒店,在古时候叫客栈,是供旅客休息的地方,旅客可以进客栈休息,休息完毕后就离开......
  • 000004 HTML特殊符号表
    <?phpheader('Content-Type:text/html;charset=utf-8');include'./assets/php/head.php';echo'HTML特殊符号表:十进制转义字符'.'<br>';//运算符号echo'加号:&#......
  • Unity-Firebase Crashlytics安卓上传符号表
    最近项目线上突然多了不少崩溃记录,但是看不到崩溃的具体方法。简单写下过程,很多都是文档有的,就不抄一遍了。毕竟这都是接好就忘的东西。ios没啥问题,就不记录了。什么是......
  • WinDbg Preview安装以及符号表配置
    1、安装WinDbgPreview在MicrosoftStore直接搜索windbg就可以下载。2、配置符号服务器2.1符号符号是方便调试程序的文件,通常是pdb文件。一个模块(可执行程序,动态链接库......
  • 驱动入口下断点 没有符号表
    原理:查找  IopLoadDriver 函数中的 载驱动入口点 u IopLoadDriverl100复制 IopLoadDriver 的所有代码,并搜索+2C然后下断点83fee723, 断下后按F11进入......
  • LaTeX符号表
    Reference:《一份(不太)简短的LaTeX介绍》注意事项:蓝色的命令依赖amsmath宏包(非amssymb宏包)带有角标ℓ的符号命令依赖latexsym宏包1.\(\LaTeX\)普通符......