美文网首页静心学习之路系列
静心学习之路(10)——链接(2)

静心学习之路(10)——链接(2)

作者: 游学者夏纳 | 来源:发表于2021-01-08 18:03 被阅读0次

    续:静心学习之路(8)——链接


    0x07 重定位

    链接器完成了符号解析以后,把代码里每个符号引用每个符号定义关联了起来。此时链接器就知道了目标模块中代码节和数据节的确切大小,此时就可以执行重定位步骤了,重定位由两步组成:

    • 重定位节和符号定义。链接器把相同类型的节合并为同一类型的聚合节。比如把所有输入模块的.data节合并为可执行目标文件的 .data节。然后链接器把运行时的内存地址赋给新的聚合节、输入模块定义的每个节以及输入模块定义的每个符号。
    • 重定位节的符号引用。链接器修改代码节和数据节中对每个符号的引用,使它们指向正确的运行时的地址。

    0x08 可执行目标文件

    可执行目标文件的格式类似于可重定位目标文件.o。比较之下,多了程序的入口点(entry point),因为可执行文件是完全链接的,所以他不再需要rel节。

    说明
    ELF头
    段头部表
    .init 定义了_init函数,程序初始化会用到
    .text 已编译程序的机器码
    .rodata read-only data(比如printf的字符串,和swtich的跳转表)
    .data init过的 global & static c-vars
    .bss 未init过或init(0)的 global & static c-vars(纯占位,省空间用)
    .symtab 符号表,存放定义和引用的 funciton 和 global vars 的信息
    .rel.text .text节位置的列表,再和其他.o文件组合时修改
    .rel.data 被引用或定义的 global vars 的重定位信息
    .debug 调试符号表,-g选项才获得
    .line C代码行号和.text映射表,-g选项才获得
    .strtab 字符串表,包含.symtab和.debug的符号表,以及节头部中的节名字
    节头部表 描述不同节的位置和大小

    当然上面是课本内容,我们也可以用反汇编工具objdump查看文件:(假设prog文件由此产生:linux > gcc -Og -o prog main.c sum.c

    因为内容输出巨多,为了方便浏览(且不污染终端界面)建议输出到文件或者vim类浏览器中(比如less):
    linux > objdump -x prog | less

    部分结果如下:

    prog:     file format elf64-x86-64
    prog
    architecture: i386:x86-64, flags 0x00000112:
    EXEC_P, HAS_SYMS, D_PAGED
    start address 0x00000000004003a0
    
    Program Header:
        PHDR off    0x0000000000000040 vaddr 0x0000000000400040 paddr 0x0000000000400040 align 2**3
             filesz 0x00000000000001c0 memsz 0x00000000000001c0 flags r-x
      INTERP off    0x0000000000000200 vaddr 0x0000000000400200 paddr 0x0000000000400200 align 2**0
             filesz 0x000000000000001c memsz 0x000000000000001c flags r--
        LOAD off    0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21
             filesz 0x00000000000006dc memsz 0x00000000000006dc flags r-x
        LOAD off    0x00000000000006e0 vaddr 0x00000000006006e0 paddr 0x00000000006006e0 align 2**21
             filesz 0x00000000000001f8 memsz 0x0000000000000208 flags rw-
     DYNAMIC off    0x0000000000000700 vaddr 0x0000000000600700 paddr 0x0000000000600700 align 2**3
             filesz 0x0000000000000190 memsz 0x0000000000000190 flags rw-
        NOTE off    0x000000000000021c vaddr 0x000000000040021c paddr 0x000000000040021c align 2**2
             filesz 0x0000000000000020 memsz 0x0000000000000020 flags r--
    EH_FRAME off    0x00000000000005c8 vaddr 0x00000000004005c8 paddr 0x00000000004005c8 align 2**2
             filesz 0x0000000000000034 memsz 0x0000000000000034 flags r--
       STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**3
             filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
    
    ...
    
    Sections:
    Idx Name          Size      VMA               LMA               File off  Algn
      0 .interp       0000001c  0000000000400200  0000000000400200  00000200  2**0
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      1 .note.ABI-tag 00000020  000000000040021c  000000000040021c  0000021c  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      2 .hash         00000018  0000000000400240  0000000000400240  00000240  2**3
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      3 .dynsym       00000048  0000000000400258  0000000000400258  00000258  2**3
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      4 .dynstr       00000038  00000000004002a0  00000000004002a0  000002a0  2**0
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      5 .gnu.version  00000006  00000000004002d8  00000000004002d8  000002d8  2**1
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      6 .gnu.version_r 00000020  00000000004002e0  00000000004002e0  000002e0  2**3
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      7 .rela.dyn     00000018  0000000000400300  0000000000400300  00000300  2**3
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      8 .rela.plt     00000030  0000000000400318  0000000000400318  00000318  2**3
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      9 .init         00000024  0000000000400348  0000000000400348  00000348  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
     10 .plt          00000030  000000000040036c  000000000040036c  0000036c  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
     11 .text         00000214  00000000004003a0  00000000004003a0  000003a0  2**4
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
     12 .fini         0000000e  00000000004005b4  00000000004005b4  000005b4  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
     13 .rodata       00000004  00000000004005c4  00000000004005c4  000005c4  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
     14 .eh_frame_hdr 00000034  00000000004005c8  00000000004005c8  000005c8  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
     15 .eh_frame     000000dc  0000000000400600  0000000000400600  00000600  2**3
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
     16 .ctors        00000010  00000000006006e0  00000000006006e0  000006e0  2**3
                      CONTENTS, ALLOC, LOAD, DATA
     17 .dtors        00000010  00000000006006f0  00000000006006f0  000006f0  2**3
                      CONTENTS, ALLOC, LOAD, DATA
     18 .dynamic      00000190  0000000000600700  0000000000600700  00000700  2**3
                      CONTENTS, ALLOC, LOAD, DATA
     19 .got          00000008  0000000000600890  0000000000600890  00000890  2**3
                      CONTENTS, ALLOC, LOAD, DATA
     20 .got.plt      00000028  0000000000600898  0000000000600898  00000898  2**3
                      CONTENTS, ALLOC, LOAD, DATA
     21 .data         00000018  00000000006008c0  00000000006008c0  000008c0  2**3
                      CONTENTS, ALLOC, LOAD, DATA
     22 .bss          00000010  00000000006008d8  00000000006008d8  000008d8  2**3
                      ALLOC
     23 .comment      00000011  0000000000000000  0000000000000000  000008d8  2**0
                      CONTENTS, READONLY
     24 .debug_aranges 00000100  0000000000000000  0000000000000000  000008f0  2**4
                      CONTENTS, READONLY, DEBUGGING
     25 .debug_info   00000361  0000000000000000  0000000000000000  000009f0  2**0
                      CONTENTS, READONLY, DEBUGGING
     26 .debug_abbrev 000001a0  0000000000000000  0000000000000000  00000d51  2**0
                      CONTENTS, READONLY, DEBUGGING
     27 .debug_line   0000022c  0000000000000000  0000000000000000  00000ef1  2**0
                      CONTENTS, READONLY, DEBUGGING
     28 .debug_str    00000216  0000000000000000  0000000000000000  0000111d  2**0
                      CONTENTS, READONLY, DEBUGGING
     29 .debug_loc    00000155  0000000000000000  0000000000000000  00001333  2**0
                      CONTENTS, READONLY, DEBUGGING
     30 .debug_ranges 00000080  0000000000000000  0000000000000000  00001490  2**4
                      CONTqENTS, READONLY, DEBUGGING
    SYMBOL TABLE:
    ...
    

    可以在Sections这部分找到具体的节信息。顺便一提笔者在生产环境中得知,debug相对于release来说会多了 .stab.stabstr这两部分(在.bss.common之间),这样用gdb进行dump文件分析的时候就能追踪的具体代码行数,release版本就不行。

    ...
     24 .bss          003d7680  0000000000c285c0  0000000000c285c0  006285b0  2**5
                      ALLOC
     25 .stab         00adb068  0000000000000000  0000000000000000  006285b0  2**2
                      CONTENTS, READONLY, DEBUGGING
     26 .stabstr      05379895  0000000000000000  0000000000000000  01103618  2**0
                      CONTENTS, READONLY, DEBUGGING
     27 .comment      0000004f  0000000000000000  0000000000000000  0647cead  2**0
                      CONTENTS, READONLY
    ...
    

    (↑ 生产环境中线上Debug版本反编译时看到的多出的两节内容)

    而在Program Header这部分里展示了prog的头部表, 这里截取上面输出的部分内容:

        LOAD off    0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21
             filesz 0x00000000000006dc memsz 0x00000000000006dc flags r-x
        LOAD off    0x00000000000006e0 vaddr 0x00000000006006e0 paddr 0x00000000006006e0 align 2**21
             filesz 0x00000000000001f8 memsz 0x0000000000000208 flags rw-
    

    我们可以看到可执行目标文件的内容初始化两个内存段,前两行告诉我们第一个代码段具有读/执行权限r-x并开始于内存地址vaddr0x400000,总内存大小memsz0x6dc个字节,其中包括ELF头 程序头部表 .init .text .rodata

    后两行告诉第二个数据段具有读/写权限rw-并开始于内存地址0x6006e0,总大小为0x208个字节,并用从目标文件偏移量off0x6e0处的.data节中的filesz 0x1f8个字节初始化。该段的 0x208 - 0x1f8 = 0x10即16个字节用于运行时初始化为0的.bss数据。

    0x09 加载可执行目标文件

    linux > ./prog

    因为prog不是内置shell命令,所以shell判定prog是一个可执行目标文件,然后linux执行execve函数=>调用加载器(loader)。loader把目标文件的代码和数据从磁盘复制到内存,然后跳转到程序的第一条指令或者入口点来运行程序,这个过程就叫做加载。

    Linux x86-64系统中,代码段总是从0x400000开始(vaddr 0x0000000000400000),后面是数据段,再后面是运行是的,由调用malloc往上增长。而用户栈则从最大用户合法地址(2^48-1 即 0x0000ffffffffffff,)开始高位向低位,向下增长。

    图来源: https://blog.csdn.net/u013581207/article/details/95199139

    具体关于loader的工作方式,要到CSAPP第8章和第9章中了解。

    0x0A 动态链接共享库

    0x0B 从应用程序中加载和链接共享库

    0x0C 位置无关代码

    0x0D 库打桩机制

    相关文章

      网友评论

        本文标题:静心学习之路(10)——链接(2)

        本文链接:https://www.haomeiwen.com/subject/nanxaktx.html