程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> plt和got,pltgot

plt和got,pltgot

編輯:關於C語言

plt和got,pltgot


  最近在學習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表中就有了這個函數的地址,直接跳轉到該地址,而不再需要去取地址,也就是動態鏈接。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved