动态链接器如何确定在 Linux 上调用哪个例程?

逆向工程 小精灵 动态链接
2021-06-18 17:11:25

我有一个关于 Linux 上动态链接的问题。考虑以下对 ARM 二进制文件的反汇编。

8300 <printf@plt-0x40>:
    ....
8320:   e28fc600    add ip, pc, #0, 12
8324:   e28cca08    add ip, ip, #8, 20  ; 0x8000
8328:   e5bcf344    ldr pc, [ip, #836]! ; 0x344
  ....
83fc <main>:
  ....
8424:   ebffffbd   bl  8320 <_init+0x2c>

main()函数printf()在 处调用8424: bl 8320上图中8320的地址在哪里.plt现在,中的代码.plt调用动态链接器来调用printf()例程。

我的问题是动态链接器如何能够说它是对printf()?

2个回答

跳转到的初始函数是通过指针 in (由动态加载程序预先填充)PLT0调用 glibc 的存根_dl_runtime_resolveGOT[2]

.plt:00008530        STR             LR, [SP,#-4]!
.plt:00008534        LDR             LR, =(_GLOBAL_OFFSET_TABLE_ - 0x8540)
.plt:00008538        ADD             LR, PC, LR ; _GLOBAL_OFFSET_TABLE_
.plt:0000853C        LDR             PC, [LR,#8]!

来自源码的相关部分

    @ we get called with
    @       stack[0] contains the return address from this call
    @       ip contains &GOT[n+3] (pointer to function)
    @       lr points to &GOT[2]
    [...]
    @ prepare to call _dl_fixup()
    @ change &GOT[n+3] into 8*n        NOTE: reloc are 8 bytes each
   sub     r1, ip, lr
   sub     r1, r1, #4
   add     r1, r1, r1

   @ call fixup routine
   bl      _dl_fixup

因此,glibc 知道要解析哪个函数,因为 GOT 指针在ip( R12) 中。这样就可以把地址转换成重定位表offet,然后dl_fixup就可以在符号表中查找相关的符号并解析出来。你可以看到dl_fixupin的来源dl-runtime.c

补充阅读:

这是使用标准重定位方法完成的,这些方法早在plt(过程链接表)发明之前就已经存在于 ELF 中

很久以前(对于 long 的某些定义),外部函数是直接从代码内部调用的。这意味着,如果你的程序printf从 1000 个不同的地方调用,这 1000 个地方中的每一个都必须在printf 每次程序启动时调整到新的动态位置这 1000 个地点中的每一个都被添加到重定位表中,启动程序包括遍历重定位表并调整其中引用的每个位置。不用说,这使得启动程序需要更多时间,特别是当程序中使用的动态库数量增加时。

PLT的发明是为了改善这一点-而不是产生一个呼叫 printf直接,该方案得到了一个新的plt部分。每次调用printf都会被转换为调用printf@plt- 这可以在链接时完成,所以它不会影响程序加载 -printf@plt除了跳转printf. 这仍然需要一个重定位条目printf@plt,但在加载时执行此单个重定位比其中许多要快得多。并且它不会对执行产生太大影响,因为处理器管道可以非常有效地处理无条件跳转。

那么在您的情况下会发生什么:printf@plt不调用动态链接器。printf@plt使得跳转到一个静态地址,而当它加载程序的动态链接程序调整这个静态地址的目标。它使用重定位表找出调整事物的位置,以及将它们调整到哪个目标地址。

在您的情况下,跳转是通过获取原始 pc,添加 12,添加 8000,然后进行间接跳转(ldr pc, ...而不是b)来实现的。我假设 12 和 8000 被调整为加载的 .so 的加载地址(libc.so),并且 libc.so 包含一个跳转0x344到实际 printf 函数的偏移量(但是,我在这里可能是错的,我必须在真实系统上进行测试,我现在无法方便地进行)