GOT表
GOT表(Global Offset Table) ,又稱全局偏移表,位於.data節首,記錄着外部符號動態加載後的首地址信息。在靜態鏈接時,每一個外部符號都會在GOT表對應一個表項,靜態鏈接器並每一個表項生成一個對應的重定位項(數據位於.rel.data節,函數位於.rel.text節)。在動態加載時,動態鏈接器將根據重定位項,修改對應的GOT表中信息,完成重定位。
在訪問動態庫數據或調用動態庫函數時,直接通過相對位移,找到對應的GOT表項,然後根據GOT表項中記錄的目標地址信息訪問數據或調用函數,從而實現動態加載。
舉例說明:
extern int b;
extern void ext();
int main() {
ext();
}
彙編僞代碼如下:
0000050c <main>:
0000050c: 55 pushl %ebp
....
00000557: e8 00 00 00 00 call 0000055c
0000055c: 5b popl %ebx
0000055d: addl $0x1204, %ebx
0000055e: call *(%ebx)
通過這種方式,可以發現在調用函數時多使用了三條指令,並多使用了一個%ebx寄存器,加法過程可能會出現寄存器溢出問題。爲解決以上問題,ELF採用延遲綁定技術來解決。
延遲綁定與PLT表
所謂延遲綁定,就是不在加載動態鏈接庫時進行綁定(重定位填寫GOT表),而是延遲到每一個外部符號第一次被調用時進行,這種方式所導致的效果顯而易見:第一次調用時重定位會較慢,再次調用時則明顯加快,這也可以避免加載動態庫中沒有使用的符號信息。延遲綁定也將涉及到PLT表(Procedure Linkage Table),又稱過程鏈接表,位於.text節(可以看出PLT表中每一項都對應一個代碼塊)。
此時,GOT表作爲.data節的一部分,開始的三項是固定的,含義如下:
GOT[0]: 記錄.dynamic節首地址,該節中記錄了動態鏈接器所需的基本信息,如符號表位置,重定位表位置等。
GOT[1]: 記錄動態鏈接器的標示信息。
GOT[2]:記錄動態鏈接器延遲綁定代碼的入口地址。
這裏假設ext函數對應的是GOT[3]。
PLT作爲.text節的一部分,開始的一項是固定的,其目的是設置動態鏈接器的標示信息與符號ID作爲參數,然後調用動態連機器延遲綁定代碼的入口。
彙編僞代碼如下:
PLT[0]
0804833c: ff 35 88 95 04 08 pushl 0x8049588
08048342: ff 25 8c 95 04 08 jmp *0x804958c
08048348: 00 00 00 00
PLT[1]
0804834c: ff 25 90 95 04 08 pushl 0x8049590
08048352: 68 00 00 00 00 pushl 0x0
08048357: e9 e0 ff ff ff jmp 0x0804833c
第一次調用ext函數時,會相對尋址到對應的PLT表項,然後跳轉到對應的GOT表項中記錄的地址,第一次調用時GOT表項記錄的是PLT表中下一條指令的地址,然後向堆棧中push符號ID後跳轉至PLT[0]表項,然後PLT[0]表項向堆棧push動態鏈接器的標示信息後跳轉至動態連機器延遲綁定代碼的入口。在完成綁定後,由動態鏈接器修改對應GOT表項中記錄的函數地址,從而實現延遲綁定的重定向。