wwxbei 发表于 2011-07-15 13:03

动态链接库函数是怎样调用到的?

本帖最后由 wwxbei 于 2011-07-16 14:00 编辑

最近在看动态链接的实现,主要是GOT, PLT段的汇编。 理论上是加载器会修改GOT内容,填写动态库函数的真正地址。 但是从反汇编上却总是看不出来这一点。
就以最简单的printf为例:

main.c
#include <stdio.h>
int main(int argc, char* argv[])
{
    printf("Hello world %d.\n", argc);
    return 0;
}

看main函数的反汇编,其实是直接跳转到 printf@plt 标号处。
00400470 <printf@plt>:
400470:   3c0f0041    lui t7,0x41
400474:   8df907c0    lwt9,1984(t7)
400478:   25f807c0    addiu   t8,t7,1984
40047c:   03200008    jrt9
400480:   00000000    nop

流程是:t7=0x410000, t9=*(0x4107c0),t8=0x4107c0, jr t9
也就是寄存器t8的值是0x4107c0, 而t9的值是内存地址0x4107c0处存放的值。然后跳转到t9指示的地址处。所以,这里的关键就是内存地址0x4107c0处存放的值到底是什么。

Disassembly of section .got.plt:
004107b4 <.got.plt>:
    ...
4107bc:   00400440    0x400440
4107c0:   00400440    0x400440
可见,地址0x4107c0位于.got.plt段,存放的值为0x400440. 也就是说刚才程序的执行流程会跳转到0x400440地址处,这正是.plt段的位置。

Disassembly of section .plt:
00400440 <_PROCEDURE_LINKAGE_TABLE_>:
400440:   3c1c0041    lui gp,0x41
400444:   8f9907b4    lwt9,1972(gp)
400448:   279c07b4   addiu   gp,gp,1972
40044c:   031cc023    subu    t8,t8,gp
400450:   03e07821   move    t7,ra
400454:   0018c082   srl t8,t8,0x2
400458:   0320f809   jalr    t9
40045c:   2718fffe    addiu   t8,t8,-2

t9=*(0x4107b4),其实就是以0x4107b4处存放的值做目的地址去跳转。而从上面.got.plt段的汇编看,0x4107b4处存放的值应该是零。
Jalr跳到零地址?????   到这里就分析不下去了, 请高手指点一下迷津。

附上.rel.plt和.got段:
Disassembly of section .rel.plt:
004003c8 <.rel.plt>:
4003c8:   0041077c    0x41077c
4003cc:   0000027f    0x27f
4003d0:   00410780    0x410780
4003d4:   0000047f    0x47f
Disassembly of section .got:
004107d0 <_GLOBAL_OFFSET_TABLE_>:
4107d0:   00000000    nop
4107d4:   80000000    lbzero,0(zero)
4107d8:   00410784    0x410784
4107dc:   00000000    nop
4107e0:   004003d8    0x4003d8
4107e4:   00000000    nop

吾爱夏日长 发表于 2011-07-16 21:28

你看一本专著叫《连接器和加载器》,讲解的很详细,动态加载的时候实际启动了一个加载器,帮助程序去调用动态库,并修改程序里的符号表,然后去执行;当第二次运行的时候就不需要再去加载,直接跳到修改过的符号地址

wwxbei 发表于 2011-07-17 13:37

回复 2# 吾爱夏日长

我就是看了书上的理论,才打算找个实际的代码来验证一下的。 但是分析下来实际跟理论结合不上,所以才来请求帮助。

wwxbei 发表于 2011-07-18 16:48

使用GDB单步调试发现, 加载器会修改.got.plt段的内容,加载后(运行前).got.plt段的内容为:
004107b4 <.got.plt>:
4107b4:4081718c    0x4081718c
4107b8:40830a18    0x40830a18
4107bc:00400440    0x400440
4107c0:00400440    0x400440
所以不是跳到零地址,而是跳到0x4081718c处,这是ld-linux.so里面的_dl_runtime_pltresolve()函数所在处。

unbutun 发表于 2011-07-21 19:50

楼主搞清楚了写个总结出来吧

prc 发表于 2011-07-28 16:31

http://blogold.chinaunix.net/u/3038/showart_2278244.html

qswul 发表于 2011-08-03 08:10

《链接器和加载器》大家都说好,其实我个人觉得有点言过其实,对完全不懂的人来说,不适合去看那本书,还是看看《程序员的自我修养-链接、加载和库》,讲得非常详细。你所想知道的都有讲

pagx 发表于 2011-08-14 20:04

可以看看这里:
http://tocspblog.appspot.com/?p=40001
页: [1]
查看完整版本: 动态链接库函数是怎样调用到的?