• Linux 链接详解----静态链接实例分析


    Linux链接详解(1)中我们简单的分析了静态库的引用解析和重定位的内容, 下面我们结合实例来看一下静态链接重定位过程。

    /*
    *     a.c  
    */
    int a = 1000;
    void add(int c);
    int main()
    {
        int c = 123;
        add(c);
        return 0;
    
    }
    
    /*
    * b.c
    */
    extern int a;
    void add(int c)
    {
        a += c;
    }

    实例中使用了如上代码, 在a.c 中是我们的入口函数main 和定义的全局变量a,其中引用了函数add 它的定义在b.c中。在b.c中又引用了a.c中a的定义。我们先将其分别编译为目标文件反汇编可以看到如下:

    0000000000000000 <main>:
       0:    55                       push   %rbp
       1:    48 89 e5                 mov    %rsp,%rbp
       4:    48 83 ec 10              sub    $0x10,%rsp
       8:    c7 45 fc 7b 00 00 00     movl   $0x7b,-0x4(%rbp)
       f:    8b 45 fc                 mov    -0x4(%rbp),%eax
      12:    89 c7                    mov    %eax,%edi
      14:    e8 00 00 00 00           callq  19 <main+0x19>
      19:    b8 00 00 00 00           mov    $0x0,%eax
      1e:    c9                       leaveq 
      1f:    c3                       retq   
    
    Disassembly of section .data:
    
    0000000000000000 <a>:
       0:    e8                       .byte 0xe8
       1:    03 00                    add    (%rax),%eax
    
    # 以上是a.o 的反编译main 和data段的结果。
    #
    # 下面是b.c中add的反编译结果
    0000000000000000 <add>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 89 7d fc mov %edi,-0x4(%rbp) 7: 8b 15 00 00 00 00 mov 0x0(%rip),%edx # d <add+0xd> d: 8b 45 fc mov -0x4(%rbp),%eax 10: 01 d0 add %edx,%eax 12: 89 05 00 00 00 00 mov %eax,0x0(%rip) # 18 <add+0x18> 18: 5d pop %rbp 19: c3 retq

    我们先分析一下函数的引用重定位: 首先这里可以看到a.c中add的引用,在模块a.o中 e8 00 00 00 00 callq 19 <main+0x19>   这条call指令就是调用add函数, call指令就是把下一条指令地址(CS:IP)压入栈,然后执行跳转指令,在解析引用之前是找不到add的定义入口处的 所以这里的偏移量是0. 这就是需要重定位信息的原因。a.o中add 重定位信息如下:

    重定位节 '.rela.text' 位于偏移量 0x548 含有 1 个条目:
      Offset          Info           Type           Sym. Value    Sym. Name + Addend
    000000000015  000a00000002 R_X86_64_PC32     0000000000000000 add - 4

    这里的0x15就是代码段中需要重定位的位置   即 14: e8 00 00 00 00 callq 19 <main+0x19> 00 00 00 00 的首地址正是0x15. 

    我们再来看一下全局变量的引用:在模块b.o中 引用了a.o中的全局变量a, 我们可以看到b.o中add的反汇编程序 7: 8b 15 00 00 00 00 mov 0x0(%rip),%edx # d <add+0xd> rip寄存器存放的是下一条指令的地址 这条指令的意思也就是说 取出rip+0x0地址的数据并将其放入edx寄存器,也就是找到全局变量a的值将它放到edx中。再看b.o的重定位信息

    重定位节 '.rela.text' 位于偏移量 0x528 含有 2 个条目:
      Offset          Info           Type           Sym. Value    Sym. Name + Addend
    000000000009  000900000002 R_X86_64_PC32     0000000000000000 a - 4

    这里可以看到需要重定位的地方位于代码段的0x9 ,正是上面的偏移量的地址。

    到此有了重定位信息再经过符号解析------由引用找到定义,各个段合并

    最后我们看一下可执行文件a的反汇编程序:

    00000000004004f0 <main>:
      4004f0:    55                       push   %rbp
      4004f1:    48 89 e5                 mov    %rsp,%rbp
      4004f4:    48 83 ec 10              sub    $0x10,%rsp
      4004f8:    c7 45 fc 7b 00 00 00     movl   $0x7b,-0x4(%rbp)
      4004ff:    8b 45 fc                 mov    -0x4(%rbp),%eax
      400502:    89 c7                    mov    %eax,%edi
      400504:    e8 07 00 00 00           callq  400510 <add>   #400509 + 7  = 400510 跳转到此地址add执行
      400509:    b8 00 00 00 00           mov    $0x0,%eax
      40050e:    c9                       leaveq 
      40050f:    c3                       retq   
    
    0000000000400510 <add>:
      400510:    55                       push   %rbp
      400511:    48 89 e5                 mov    %rsp,%rbp
      400514:    89 7d fc                 mov    %edi,-0x4(%rbp)
      400517:    8b 15 0f 0b 20 00        mov    0x200b0f(%rip),%edx        # 60102c <a>   40051d + 0x200b0f = 60102c a变量的地址  取出放到edx寄存器中
      40051d:    8b 45 fc                 mov    -0x4(%rbp),%eax
      400520:    01 d0                    add    %edx,%eax
      400522:    89 05 04 0b 20 00        mov    %eax,0x200b04(%rip)        # 60102c <a>
      400528:    5d                       pop    %rbp
      400529:    c3                       retq   
      40052a:    66 0f 1f 44 00 00        nopw   0x0(%rax,%rax,1)

    我们可以看到之前代码段中call 及mov后的偏移地址已经变为了实际可用的地址

  • 相关阅读:
    从零开始学SQLSERVER-UNION
    从零开始学SQLSERVER-BETWEEN
    从零开始学SQLSERVER-LIKE
    从零开始学SQLSERVER-存储过程(基础用法)
    从零开始学SQLSERVER-TOP
    从零开始学SQLSERER-INNER JOIN
    从零开始学SQLSERVER-DELECT(删除)
    从零开始学SQLSERVER-ORDER BY(排序)
    从零开始学SQLSERVER-WHERE
    使用 C# 9 的records作为强类型ID
  • 原文地址:https://www.cnblogs.com/MaAce/p/7986348.html
Copyright © 2020-2023  润新知