【问题标题】:How does the address of R_X86_64_PLT32 computed?R_X86_64_PLT32的地址是怎么计算的?
【发布时间】:2020-10-19 09:35:33
【问题描述】:

我正在尝试了解链接的工作原理。我有一个简单的 C++ 代码

#include "a.h"

int Other() { 
  return 1;
}

int SomeFunction() {
  Other();
  Other();
  Other();

  return 0;
}

生成以下重定位表。

$ readelf -r a.o

Relocation section '.rela.text' at offset 0x240 contains 3 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000018  000900000004 R_X86_64_PLT32    0000000000000000 _Z5Otherv - 4
00000000001d  000900000004 R_X86_64_PLT32    0000000000000000 _Z5Otherv - 4
000000000022  000900000004 R_X86_64_PLT32    0000000000000000 _Z5Otherv - 4

Relocation section '.rela.eh_frame' at offset 0x288 contains 2 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000020  000200000002 R_X86_64_PC32     0000000000000000 .text + 0
000000000040  000200000002 R_X86_64_PC32     0000000000000000 .text + f

看起来 Other() 函数的调用点都标记为 R_X86_64_PLT32 类型。当我检查 R_X86_64_PLT32 的定义时,它显示 L + A - P,其中L 表示符号的过程链接表条目的位置(部分偏移或地址),A 表示用于计算值的加数可重定位字段的名称,P 表示被重定位的存储单元的位置(节偏移或地址)(使用 r_offset 计算)。

我不确定 PLT 的位置是什么意思。我的问题是

  1. 在这种情况下,L、A、P 的值是多少?

  2. 据我了解,PLT 被动态对象使用。为什么即使这只是一个静态函数,Other() 也会被标记为 PLT32。

  3. 如何找到ELF文件的PLT以及如何在其中找到Other()?

【问题讨论】:

    标签: c++ linker elf


    【解决方案1】:

    我不是专家,有点晚了,但据我了解,较新的 GCC 版本使用 R_X86_64_PLT32 而不是 R_X86_64_PC32 来标记 32 位 PC 相关分支。

    "在 x86-64 上,对于 32 位 PC 相关分支,我们可以生成 PLT32 重定位,而不是 PC32 重定位,它也可以用作 32 位 PC 相关分支的标记。链接器总是可以减少如果函数在本地定义,则 PLT32 重定位到 PC32。本地函数应使用 PC32 重定位。 (Related Commit)

    因此,尽管重定位条目的类型是 R_X86_64_PLT32,链接器仍将使用 R_X86_64_PC32 计算 (S + A - P) 来修改正在修改的重定位目标,其中:

    • S 是符号的值(st_value of Elf64_Sym
    • A 是加数(-4 在你的情况下)
    • P是被重定位的内存位置的地址(callOther的地址开始)

    因为Other 是一个本地函数。因此,PLT 实际上在您的情况下根本不起任何作用。

    如果您想了解所有节标题(包括 .plt、.got、.got.plt、...),您可以在 EXEC/DYN elf 文件(已编译和链接)上使用 readelf -S .或者只使用objdump -d <file,它还会显示不同的部分以及转储。

    我希望这能解释一下。

    为了完整起见,类似于你的简短示例(我使用了 C,但这不应该有所作为):

    我有一个文件rel.c,它在other.c 中调用函数Other。首先我编译两个文件gcc -c -o <name>.o <name>.c

    readelf -r rel.o:

    Relocation section '.rela.text' at offset 0x198 contains 1 entry:
      Offset          Info           Type           Sym. Value    Sym. Name + Addend
    000000000015  000500000004 R_X86_64_PLT32    0000000000000000 Other - 4
    

    如您所见,A(加数)是-4,最终地址SP 尚不清楚。

    使用gcc -o main rel.o other.o 链接所有内容后,我使用objdump -M intel -d main 获取二进制文件的完整转储。 有趣的部分如下所示。

    ...
    0000000000001139 <main>:
        1139:   55                      push   rbp
        113a:   48 89 e5                mov    rbp,rsp
        113d:   48 83 ec 10             sub    rsp,0x10
        1141:   89 7d fc                mov    DWORD PTR [rbp-0x4],edi
        1144:   48 89 75 f0             mov    QWORD PTR [rbp-0x10],rsi
        1148:   b8 00 00 00 00          mov    eax,0x0
        114d:   e8 07 00 00 00          call   1159 <Other>
        1152:   b8 00 00 00 00          mov    eax,0x0
        1157:   c9                      leave
        1158:   c3                      ret
    
    0000000000001159 <Other>:
        1159:   55                      push   rbp
        115a:   48 89 e5                mov    rbp,rsp
    ...
    

    正如我们所见,0x114e 处的DWORD 包含偏移量0x7,可以与IP 一起使用以跳转到Other 子例程的地址(0x1152 + 0x7 = 0x1159 )。

    这是使用S + A - P = 0x1159 - 0x4 - 0x114e = 7 计算得出的。

    【讨论】:

      猜你喜欢
      • 2010-12-29
      • 1970-01-01
      • 2022-12-18
      • 2018-04-09
      • 2017-04-11
      • 2011-12-12
      • 1970-01-01
      • 2014-06-12
      • 1970-01-01
      相关资源
      最近更新 更多