• .symtab


    参考:剖析.o文件ELF组成

       目标文件

    .symtab中记录的符号是从.s文件来的,所以.s这个汇编文件很关键。

    .symtab所记录符号的种类

    示例代码

    a.c

    extern int a_va1 = 100;     
    static float a_va2 = 200.0;
    
    static void a_fun2(void)     
    {                            
        a_va2 += 100;            
    }                            
    
    extern int a_fun1(int     a)  
    {                            
        b_va1 = b_va1+100;        
        b_fun1();               
        a_fun2(); 
    }                            
    View Code

    b.c

    int b_va1 = 300;   
    
    int b_fun2(void)
    {
        ...
    }
    
    int main(void)
    {
        fun1(100);
            a_va1 = a_va1 + 200;
    
    }
    View Code

    extern:表示定义的函数和全局变量可以被其它模块引用,如果不写extern,默认就是extern,为了不麻烦,一般都不会明确的写extern。

    static:表示定义的函数和全局变量,只能被本模块有效。

    .symtab中所记录的符号,严格说起来就两种:

    ①全局符号

    某模块定义以后,除了自己外,其它所有模块也可以引用

    我们以a.o模块为例来讲。

    1)由a.o模块定义,可由其它模块引用的全局符号

    a_fun1:a.o定义,其它模块(b.o)可以引用
    a_va1:a.o定义,其它模块(b.o)可以引用

    2)由a.o模块引用,但是由其它模块定义的全局符号

    b_fun1:a.o引用,但是由b.o定义
    b_va1:a.o引用,但是由b.o定义

    每个条目所包含的符号基本信息有哪些呢?

    ②本地符号

    模块自己定义,而且只能由自己引用的符号

    a.o的a_fun2和a_va2,由于加了static修饰,表示符号只在模块内有效,所以a_fun2和a_va2属于典型的由本模块定义、而且只能由本模块引用的本地符号。

    旁注:有关static

    在写c代码时,对于那些只在本模块定义和引用的函数与全局变量来说,我们要养成使用static的好习惯,这样子符号就是只在本模块有效的本地符号。如果不写static的话,会带来命名冲突、错误调用等等一些列可能的不良后果,特别是对于大型c工程文件来说,出现这类不良后果的概率非常高。

    C++向下兼容C,自然也是支持static的。除此之外C++ namespace中有一种匿名namespace,其行为和static很像,参考:C++——namespace

    .symtab是如何记录符号信息的

    .symtab符号表包含很多条目,每个条目记录的就是一个符号的基本信息。

    每个条目所包含的符号基本信息有哪些呢?

    int name; 
    int value;
    int size;
    char type;
    char bind;
    char section;
    View Code

    信息含义:

    1)name

    name中记录的并不是名字的字符串,我们前面说过所有的字符串都是放在了.strtab中。name里面只记录字符串在.strtab中的偏移,通过这个偏移就能在.strtab中索引到符号的名字。

    比如

    name = 5 //偏移5
    假如.strtab中的内容为mainfun2a_va......
    使用偏移5到.strtab中进行搜索,当遇到时就截止,那么取出来的就是符号fun2。

    2)value

    放的是地址:指向符号所代表的空间。

    比如:
    (a)如果符号是初始化了的全局变量

    int a = 100;//全局变量                                
    int main(void)
    {
                                    
    }
    View Code

    初始化了的全局变量,它的空间在.data节中,那么value中放的就是这个空间的起始地址。通过value中的地址值,就可以找到符号对应的空间,这对于后续的“链接”操作来说很重要。

    (b)如果符号是函数的话

    int fun()
    {
                                        
    }
    View Code

    fun函数指令存放在了.text节中,value中放的就是fun函数在.text节中的起始地址,其实就是函数第一条指令的地址。

    (c)需要强调的地方

    不过对于.o(可重定位目标文件)和可执行目标文件来说,value的值有所不同。

    可重定位目标文件

    value总中放的只是相对于节起始地址的偏移。

    可执行目标文件

    value中放的是绝对地址。“可重定位目标文件”被连接在一起后,value中放的就是链接时重定位后的绝对地址。

    3)size

    size代表的是value所指向空间的大小,毕竟value只是起始地址,不能说明空间的大小。

    比如:
    (a)如果符号是初始化了的全局变量的话
             size代表的全局变量在.data中所占字节数。

    (b)如果符号是函数的话
             size代表的是函数指令在.text中所占空间的大小

    4)type

    符号类型,有如下几种类型。

    (a)FUNC:符号代表的是函数
    (b)OBJECT:符号代表的是全局变量
    (c)FILE:符号是源文件的名字

    5)bind

    就两种情况,LCOAL、GLOBAL

    (a)bind=LOCAL

    表示符号是本地的:符号在模块中定义后,只能由本模块引用,static修饰的全局变量和函数就是这种情况。

    (b)bind=CLOBAL(全局符号)

    表示符号在本模块定义,但是可以被其它模块引用(使用),extern修饰的全局变量和函数就是这种情况。

    6)section

    section的值有四种情况,节索引号、ABS、UNDEF、COM

    ①section=节索引号

    说明符号所对应的空间在哪个节里面。

    如果section == 1

    符号代表的空间在.text节,说明符号代表的是函数,因为只有函数指令才会保存在.text中。

    如果section == 3

    符号代表的空间在.data中,说明符号是初始化了的全局变量,因为只有初始化了的全局变量才会在.data节。

     

    ②section=ABS

    表示该符号不需要被“链接程序”处理。

    比如,如果符号名是***.c,这个符号不是全局变量、不是函数,只是一个源文件名而已,链接器(ld/collect2)在链接“可重定位目标文件”时,这个符号不需要被处理。

    ③section=UNDEF

    表示这个符号,只是在本模块中被引用了,这个符号并不是由本模块定义的,在本某块找不到定义,所以这个符号的section就被标注为了UNDEF,表示这个符号被定义在了其它模块中,链接时要到其它模块中去找搜寻它的定义。

    其它模块:

    · 其它自己写的.c所对应的.o
    · 静态库
    · 动态库

    如果是链接时,在其它目标文件中还找不到该符号的定义的话,链接程序就会报错,提示找不到这个符号。出错的原因有两种:

    你忘了链接所需的目标文件

    比如gcc a.o b.o,结果写成gcc a.o,少了一个,肯定会找不到。

    符号名压根就写错了,不可能找得到

    ④section=COM

    表示还未被分配空间(位置)的未初始化的数据目标,比如未初始化的全局变量。

    int a;
    int main(void)
    {
        ...
    }
    View Code

    未被初始化的全局变量都放在.bss中的,但是前面就说过,由于没有数据,所以.bss没有实际空间,只有当程序运行时才有实际.bss节的空间。

    查看目标文件的.symtab符号表信息

    readelf -h:查看ELF头中的信息

    readelf -s:查看.symtab符号表

  • 相关阅读:
    dynamic_debug和pr_debug()打印_高通平台
    Linux内核中的数据结构杂记
    Binder杂记
    内核中读写文件
    记一次阿里云ECS中Docker容器内无法连接RDS内网地址的故障解决
    mac上使用sips命令快速裁剪、旋转、翻转图片
    最新Android手机导出ANR、tombstones文件
    Android日期格式控制—— DateUtils
    addr2line的用法
    Spring-Cloud 学习笔记-(6)Feign
  • 原文地址:https://www.cnblogs.com/kelamoyujuzhen/p/9415010.html
Copyright © 2020-2023  润新知