动态链接器如何确定在Linux上调用哪个例程? [英] How the dynamic linker determines which routine to call on Linux?

查看:144
本文介绍了动态链接器如何确定在Linux上调用哪个例程?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对Linux上的动态链接有疑问.请考虑以下ARM二进制文件的拆卸.

I have a question about dynamic linking on Linux. Consider the following disassembly of an ARM binary.

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>

主函数在8424处调用printf:bl8320.8320是上面显示的.plt中的地址.现在,.plt中的代码将调用动态链接器以调用printf例程.我的问题是动态链接程序如何能够说这是对printf的调用?

Main function calls printf at 8424: bl 8320. 8320 is an address in the .plt shown above. Now the code in .plt makes call to dynamic linker to invoke printf routine. My question is how the dynamic linker will be able to say that it is a call to printf?

推荐答案

TLDR; PLT通过传递以下内容来调用动态链接器:

TLDR; The PLT calls the dynamic linker by passing:

  • IP(&PLTGOT[n+3])中GOT条目的地址;

  • the address of the GOT entry in IP (&PLTGOT[n+3]);

&PLTGOT[2]在LR中;

此外,PLTGOT[1]标识共享对象/可执行文件.

Moreover PLTGOT[1] identifies the shared-object/executable.

动态链接器使用它来查找重定位条目(plt_relocation_table[n]),从而找到符号(printf).

The dynamic linker use this to find the relocation entry (plt_relocation_table[n]) and thus the symbol (printf).

(以某种方式)在部分中对此进行了解释. ELF for ARM的A.3 :

8320:   e28fc600    add ip, pc, #0, 12
8324:   e28cca08    add ip, ip, #8, 20  ; 0x8000
8328:   e5bcf344    ldr pc, [ip, #836]! ; 0x344

其中的解释是:

ADD   ip, pc, #-8:PC_OFFSET_27_20:__PLTGOT(X)
     ; R_ARM_ALU_PC_G0_NC(__PLTGOT(X))
ADD ip, ip, #-4:PC_OFFSET_19_12: __PLTGOT(X)
     ;R_ARM_ALU_PC_G1_NC(__PLTGOT(X))
LDR   pc, [ip, #0:PC_OFFSET_11_0:__PLTGOT(X)]!
     ; R_ARM_LDR_PC_G2(__PLTGOT(X))

这些说明有两件事:

  • 它们将GOT条目的地址计算为与PC的偏移量,并将其存储在IP寄存器中;

  • they compute the address of the GOT entry as an offset from PC and store it in the IP register;

他们跳到了这个GOT条目.

they jump to this GOT entry.

该规范指出:

在最终LDR上的回写可确保ip包含 PLTGOT条目的地址.这对 增量动态链接.

The write-back on the final LDR ensures that ip contains the address of the PLTGOT entry. This is critical to incremental dynamic linking.

回写"是对!"的使用.在最后一条指令中:这用于使用最终偏移量(#836)更新IP寄存器.这样,IP就会在PLT条目的末尾包含GOT条目的地址.

The "write-back" is the use of "!" in the last instruction: this is used to update IP register with the final offset (#836). This way IP contains the addess of the GOT entry at the end of the PLT entry.

动态链接程序在IP中具有GOT条目的地址:

The dynamic linker has the address of the GOT entry in IP:

  • 它可以找到共享对象或可执行文件;

  • it can find the shared-object or executable;

它可以找到正确的重定位项.

it can find the correct relocation entry.

此重定位条目引用目标函数的符号(在您的情况下为printf):

This relocation entry references the symbol of target function (printf in your case):

Offset    Info     Type              Sym. Value Sym. Name
0001066c  00000116 R_ARM_JUMP_SLOT   00000000   printf

用于ARM体系结构的基本平台ABI 指出:

当平台支持惰性函数绑定时(如ARM Linux一样) 该ABI要求ip地址以解决相应的问题 PLTGOT条目位于PLT通过它进行调用的位置. (要求PLT表现得好像以LDR pc [ip]结尾).

When the platform supports lazy function binding (as ARM Linux does) this ABI requires ip to address the corresponding PLTGOT entry at the point where the PLT calls through it. (The PLT is requir ed to behave as if it ended with LDR pc, [ip]).

从GOT查找重定位条目

现在不清楚从GOT地址中找到重定位条目的方式.可以使用二进制搜索,但并不方便. GNU ld.so就是这样(glibc/sysdeps/arm/dl-trampoline.S):

Finding the relocation entry from the GOT

Now the way the relocation entry is found from the GOT address is not clear. Binary search could be used but is would not be convenient. The GNU ld.so does it like this (glibc/sysdeps/arm/dl-trampoline.S):


dl_runtime_resolve:
        cfi_adjust_cfa_offset (4)
        cfi_rel_offset (lr, 0)

        @ 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]

        @ Save arguments.  We save r4 to realign the stack.
        push    {r0-r4}
        cfi_adjust_cfa_offset (20)
        cfi_rel_offset (r0, 0)
        cfi_rel_offset (r1, 4)
        cfi_rel_offset (r2, 8)
        cfi_rel_offset (r3, 12)

        @ get pointer to linker struct
        ldr     r0, [lr, #-4]

        @ 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
[...]

  1. 第二个GOT条目的地址在LR中.我想这是bybyt .PLT0:

00015b84 :
 15b84:       e52de004        push    {lr}            ; (str lr, [sp, #-4]!)
 15b88:       e59fe004        ldr     lr, [pc, #4]    ; 15b94 
 15b8c:       e08fe00e        add     lr, pc, lr
 15b90:       e5bef008        ldr     pc, [lr, #8]!
 15b94:       0012f46c        andseq  pc, r2, ip, ror #8

  • 动态链接器从这两个GOT地址中可以找到GOT偏移量和PLT重定位表中的偏移量.

  • From those two GOT addresses, the dynamic linker can find the GOT offset and the offset in the PLT relocation table.

    &GOT[2]中,动态链接器可以找到PLTGOT的第二个条目(GOT[1]),其中包含链接器结构的地址(动态链接器用来识别此共享库/可执行文件).

    From &GOT[2], the dynamic linker can find the second entry of the PLTGOT (GOT[1]) which contains the address of the linker struct (a reference used by the dynamic linker to recosgnise this shared-object/executable).

    我没有在此指定位置:它似乎不属于基本的ARM ABI规范.

    I don't where this is specified: it does not seem to be part of the base ARM ABI spec.

    这篇关于动态链接器如何确定在Linux上调用哪个例程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆