• 目标文件符号《深入理解计算机系统》笔记(三)链接知识【附图】


    每日一贴,今天的内容关键字为目标文件符号

        该书中对链接的解释也不敷具体。在章节最后,作者也否认:在计算机系统文献中并没有很好的记录链接。因为链接是处在编译器、计算机体系结构和操作系统的交叉点上,他要求懂得代码生成、机器语言编程、程序实例化和虚拟存储器。它恰好不落在某个平日的计算机系统领域中。

        该章节报告Linux的X86系统,使用标准的ELF目标文件,无论是什么样的操作系统,细节可能不尽相同,但是观点是相同的。

        读完这一章节后,对“符号”的观点很是模糊。

        

    7.1编译驱动程序

        这里再说一下编译系统。大多数编译系统提供编译驱动程序,它代表用户在需要的时候调用语言预处置、编译器、汇编器、和链接器。我自己画了一个结构图。

        目标文件和符号

        7.2静态链接

        

    7.3目标文件

        

        目标文件有三种:可重定位目标文件、可执行目标文件和同享目标文件(即动态链接库),个个系统上对目标文件的叫法不一致,Unix叫a.out,Windows NT叫PE(Portable Executable)。现代Unix使用ELF格式(EXecutable and Linkable Format 即可执行和可链接格式)。

        

        下面具体介绍“可重定位目标文件”,下图最左边的一个图。

        

    目标文件和符号

        

        上图说明了,一个目标文件生成可执行文件,然后加载到内存后的映射等,三个步骤。

        

        ELF头描述了生成该文件的系统的字的巨细和字节序。ELF和节头部表之间每一个部份都称为一个节(section)

        

        .text:已编译程序的机器代码

        

        .rodada:只读数据,比如printf语句中的格式串。

        

        .data:已初始化的全局C变量。局部变量在运行时保存在栈中。即不再data节也不在bss节

        

        .bss:未初始化的全局C变量。不占据实际的空间,仅仅是一个占位符。所以未初始化变量不需要占据任何实际的磁盘空间。C++弱化BSS段。多是没有,也可能有。

        

        .symtab:一个符号表,它存放“在程序中定义和引用的函数和全局变量的信息”。

        

        .rel.text:一个.text节中位置的列表。(将来重定位使用的)

        

        .rel.data:被模块引用或定义的任何全局变量的重定位信息。

        

        .debug:调试符号表,其内容是程序中定义的局部变量和类型定义。

        

        .line:原始C源程序的行号和.text节中机器指令之间的映射。

        

        .strtab:一个字符串表.

        可定位目标文件的结构:让你深刻了解程序段,数据段,bss段,符号表等等。

        7.4可重定位目标文件——参考7.3

        

    7.5符号和符号表

        符号表是一个数组,数组里存放一个结构体。

    typedef struct {
        int name;		/*String table offset*/
        int value;		/*Section offset, or VM address*/
        int size;		/*Object size in bytes*/
        char type:4,	/*Data, fund,section,or src file name (4 bits)*/
            binding:4;	/* Local of global(4bits)*/
        char reserved;	/*Unused*/
        char section;	/*Section header index ABS UNDEF*/
    }Elf_Symbol;

        

    7.6符号解析

        原则是:编译器只答应每一个模块中每一个本地符号只有一个定义。而且对全局的符号的解析很棘手,因为多个目标文件可能会定义相同的符号。C++和Java使用mangling手腕来支持重载。

        多重定义的全局符号,请看下面的程序:

    /*foo.c*/                      /*bar.c*/
    #include <stdio.h>             int x;
    void f(void);                  void f()
    int x =15213;                  {
    int main()                         x = 15212;
    {                               }
        f();
        printf("x=%d\n",x);
        return 0;
    }

        大家能猜到输出的结果是15212;这是因为:bar.c中的x全局变量没有初始化,导致函数f中使用的是foo文件中的x变量。

        根据Unix连接器使用下面的规矩来处置多重定义的符号:

        每日一道理
    心的本色该是如此。成,如朗月照花,深潭微澜,不论顺逆,不论成败的超然,是扬鞭策马,登高临远的驿站;败,仍滴水穿石,汇流入海,有穷且益坚,不坠青云的傲岸,有“将相本无主,男儿当自强”的倔强。荣,江山依旧,风采犹然,恰沧海巫山,熟视岁月如流,浮华万千,不屑过眼烟云;辱,胯下韩信,雪底苍松,宛若羽化之仙,知退一步,海阔天空,不肯因噎废食。

            ●规矩1:不答应有多个强符号。

            ●规矩2:如果有一个强符号和多个弱符号,那么选择强符号(这就是上面这道题的答案,初始化的int x=15213是强符号,而int x;是弱符号)

            ●规矩3:如果有多个弱符号,那么从这些弱符号中恣意选择一个(多么恐怖啊)

        静态库

        事先写好的一些可重定位的目标文件打包成一个单独的文件,它可以用作连接器的输入。当连接器结构一个输出的可执行文件时,它只拷贝静态库里被应用程序引用的目标模块。(稍后讲解动态链接库,也称之为同享库)。

        在Unix系统中,静态库以一种成为存档(archive)的特殊文件格式存放在磁盘中。存档文件是一组连接起来的可重定位目标文件的集合。有一个头部用来描述每一个成员目标文件的巨细和位置。存档文件的后缀是.a标识。是不是可以这么懂得.a文件的结构呢?(自己绘图)

        目标文件和符号

        下面用展示一个静态库连接的进程:

        目标文件和符号

        7.7重定位

        

    7.8可执行文件

        参考7.3节图中心部份。可执行文件跟可重定位目标文件非常相似。只是可执行文件多了“init”和“段头部表"少了,”.rel.text“和”.rel.data“两个节。

        

    7.9加载可执行文件。

        从7.3节图中可以发现,右部是加载后的程序结构。ELF目标文件被计划的非常容易加载到存储器。需要注意的是Unix中,程序总的代码段总是从0x0804800处开始(这就是虚拟存储器的作用)。数据段是在接下来的下一个4KB对齐的地址处。运行时堆在"读/写段"以后接下来的第一个4KB对齐的地址处,并通过malloc库往上增加。而栈总是往下成长。

        

    7.10动态库(同享库)

        动态库是为了处理静态库的两个弊病而出现的,静态库的两个弊病:1)静态库更新后,程序要获得该静态库然后再编译。2)不同程序可能使用相同的静态库,导致很多静态库中的代码重复被加载到存储器中。

        同享库是致力于处理静态库的缺陷而出现的现代创新型产物。同享库是一块目标模块,在运行时,可以加载到恣意的存储器地址,并和一个在存储其中的程序链接起来。这个进程称之为”动态链接“,是由一个叫做”动态链接器“的程序来实现的。

        同享库是以梁总方法来同享的:1)全部引用该库的程序都同享一个.so文件中的代码和数据,而不是静态库一样拷贝一份。2)在存储器中,一个同享库的.text节的一个副本可以被不同正在运行的进城同享,从而勤俭名贵的存储器资源。(Unix中动态库以.so后缀表示。)

        目标文件和符号

        懂得动态库=同享库的观点非常重要。动态库一般是大型软件或者操作系统的最爱,因为对于普通应用来说,没有那么多库给别人使用,绝大多数都是自己用,所以静态库就够了。

        

    7.11从应用程序中加载和链接同享库

        应用程序还可能从应用程序中加载和链接恣意同享库,而无需编译时链接那些库到应用中(这个牛逼大了)!

        Windows中的更新大部份是这个技巧。另外还有构建高性能web服务器。

        Linux为动态链接器提供了一系列简单的接口:

    #include <dlfcn.h>
        void *dlopen(const char *filename, int flag);//加载同享库
        void *dlsym(void *handle, char *symbol);    //指向一个同享库的句柄和一个符号名字。
        int dlclose(void *handle);  //下载同享库
        const char *dlerror(void);  //容错

        Java定义了一个标准的调用规矩,叫做Java本地接口(Java NativeInterface,JNI),它答应Java程序调用本地的C和C++函数。JNI的基本思想是将本地的C函数,如foo,编译到同享库中,如foo.so .当一个正在运行的Java程序试图调用函数foo时,Java解析程序利用dlopen接口(或者类似的接口)动态链接和加载foo.so,然后调用foo。

        7.12与位置有关的代码(PIC)

        7.13处置目标文件的工具  

    文章结束给大家分享下程序员的一些笑话语录: IBM和波音777
      波音777是有史以来第一架完全在电脑虚拟现实中设计制造的飞机,所用的设备完全由IBM公司所提供。试飞前,波音公司的总裁非常热情的邀请IBM的技术主管去参加试飞,可那位主管却说道:“啊,非常荣幸,可惜那天是我妻子的生日,So..”..
      波音公司的总载一听就生气了:“胆小鬼,我还没告诉你试飞的日期呢!”

    --------------------------------- 原创文章 By 目标文件和符号 ---------------------------------

  • 相关阅读:
    What is the purpose of FormsAuthenticationTicket isPersistent property?
    Forms Authentication configurations
    What is the difference between localStorage, sessionStorage, session and cookies?
    Set-Cookie
    申威机器信息
    systemctl 关闭图形界面的办法
    sourcetree 使用
    在 Web 项目中应用 Apache Shiro
    移动web开发框架
    从0开始搭建symphony
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3095475.html
Copyright © 2020-2023  润新知