Linux下程序库函数调用的动态链接过程是很常见的,其实刚学编程时写的helloworld程序调用的printf就牵涉到动态链接,只是我们那时没有去注意罢了。
请看下面的helloworld程序反汇编代码
图1
可以看到图1第9行输出时的指令为call 80482fc <puts@plt>
再看看80482fc处是什么东东,如下
图2
可见这是.plt段的一段代码。细心的我们或许会发现.plt有一个特点,除了第一项外,其他的项都是三条指令,jmp *, push, jmp,并且第三条指令跳到了.plt的第一项。我们来分析<puts@plt>这一项:
图2第16行的间接跳转,以GOT表的某一项间接寻址(如果不知道GOT表是什么,看看elf手册就清楚了),GOT表是一个如下的数组
extern Elf32_Addr _GLOBAL_OFFSET_TABLE_[];
我们用readelf -s helloworld命令可以看出这个数组的起始地址为08049ff4,如下
35: 08049ff4 0 OBJECT LOCAL HIDDEN 22 _GLOBAL_OFFSET_TABLE_
可以计算一下,上面的间接跳转以GOT[(0x804a008 - 0x8049ff4) / 4] = GOT[5]的值间接跳转,那么这个值是什么呢,当然静态是看不到的,我们gdb启动程序并查看0x804a008地址的值
(gdb) b main
Breakpoint 1 at 0x80483ed: file helloworld.c, line 6.
(gdb) r
Starting program: /home/lzs/programming/test/helloworld
Breakpoint 1, main (argc=1, argv=0xbfffefe4) at helloworld.c:6
6 printf("helloworld/n");
(gdb) x/x 0x804a008
0x804a008 <_GLOBAL_OFFSET_TABLE_+20>: 0x08048302
(gdb)