最近在學習linux高級調試技術。下面就動態庫連接這塊做了一個實驗
首先理解下plt是procedure linkage table,got是global offset table。got表中存放的是外部符號的地址。plt表中存放的是函數地址。下面看下實驗具體情況。
源碼:
#include <stdio.h>
int fun()
{
printf("hello world\n");
}
int main()
{
while(1){
fun();
}
return 1;
}
在fun函數開始處設置斷點。開始運行程序
首先看下fun函數的反匯編代碼:
首先看下fun函數的反匯編代碼:
(gdb) disassemble fun
Dump of assembler code for function fun:
0x080483d4 <+0>: push %ebp
0x080483d5 <+1>: mov %esp,%ebp
0x080483d7 <+3>: sub $0x18,%esp
0x080483da <+6>: movl $0x80484d0,(%esp)
0x080483e1 <+13>: call 0x80482f0 <puts@plt>
0x080483e6 <+18>: leave
0x080483e7 <+19>: ret
End of assembler dump.
偏移量為13的那句匯編就是調用printf。這個可以通過objdump -l 和-S選項查看
查看0x80482f0處的匯編
(gdb) disassemble 0x80482f0
Dump of assembler code for function puts@plt:
0x080482f0 <+0>: jmp *0x804a000
0x080482f6 <+6>: push $0x0
0x080482fb <+11>: jmp 0x80482e0
End of assembler dump.
接著再查看0x804a000中存放的內容
gdb) x/x 0x804a000
0x804a000 <[email protected]>: 0x080482f6
可以看到就是之前的反匯編代碼的下一句話,但這也是plt表中的一項
那麼查看0x80482e0的反匯編,
(gdb) disassemble 0x80482e0
No function contains specified address.
那麼在該地址處設置斷點查看。發現還是不能查看,那麼采用查看內存內容的方式查看該處反匯編代碼
(gdb) x/10i 0x80482e0
0x80482e0: pushl 0x8049ff8
=> 0x80482e6: jmp *0x8049ffc
接著查看0x8049ffc裡面的內容
(gdb) x/x 0x8049ffc
0x8049ffc <_GLOBAL_OFFSET_TABLE_+8>: 0xb7ff2690
發現這個got表中的一項,地址是0xb7ff2690。
接著查看0xb7ff2690地址處的反匯編代碼
(gdb) x/10i 0xb7ff2690
0xb7ff2690: push %eax
0xb7ff2691: push %ecx
0xb7ff2692: push %edx
0xb7ff2693: mov 0x10(%esp),%edx
0xb7ff2697: mov 0xc(%esp),%eax
0xb7ff269b: call 0xb7fec1c0
接下來會調用0xb7fec1c0地址處的代碼,查看map信息會發現,這兩個地址全部都是0xb7fde820 - 0xb7ff6b9f is .text in /lib/ld-linux.so.2裡的代碼,但是這邊由於條件限制,看不到裡面的函數名稱,在網上可以查到是_dl_runtime_resolve函數。
當第一次運行fun函數結束後,第二次運行該函數時,我們再看下反匯編代碼。
(gdb) disassemble 0x80482f0
Dump of assembler code for function puts@plt:
0x080482f0 <+0>: jmp *0x804a000
0x080482f6 <+6>: push $0x0
0x080482fb <+11>: jmp 0x80482e0
這段代碼沒有改變,但是看下0x804a000中的地址
(gdb) x/x 0x804a000
0x804a000 <[email protected]>: 0xb7e866a0
這和之前的地址是不一樣的,之前是跳轉到了0x080482f6,而這裡已經實際填寫上了printf的地址。
總結一下就是,如果一個動態庫函數是第一次被調用,那麼plt表中是不存在該函數的地址的,通過ld庫中的函數,將這個地址取出來存放到got表中,那麼當第二次調用該函數時,plt表中就有了這個函數的地址,直接跳轉到該地址,而不再需要去取地址,也就是動態鏈接。