GOT & PLT 易於理解的個人筆記

muyiGin發表於2024-08-11

為什麼我們用動態連結和GOT表

  1. 我們知道靜態連結就沒那麼多事,直接把全部要用的函式都繫結在一起,各個變數和函式之間的偏移量當然能算出來。
  2. 但是這也恰恰是靜態連結的缺點,相同的程式碼段反覆呼叫真是太臃腫了!
  3. 於是我們決定把常用的庫單獨拿出來給大家用,我們還知道,.text是不可修改的,也就是執行的時候可不能再把引用的地址像連結器一樣“填”在預留的“坑”裡,所以我們還是得裝模作樣的告訴程式應該跳轉到哪裡,比如我要call puts,但是我確實不知道puts在哪裡(因為動態連結發生在程式執行時),我也留個坑,叫做puts@got。
  4. got表記錄全域性偏移,也就是真的存放的位置,放在.data節中所以可以編輯。你只需要知道前三個表項分別是:got[0]儲存了.dynamic段的地址,這其中描述了本模組動態連結的相關資訊;got[1]儲存了該模組的id; got【2】就是真正幫你找你要的函式在哪個地址的,比如他找到了puts在地址0x12345678處,它就填在got[3]。那麼以後看到puts@got就直接跳到got[3]的地址(這個id的分配是連結器規劃好的)。

為什麼我們要延遲繫結和plt表

看起來已經完全解決問題了呀!透過got解決了.text不能編輯的問題,還可以“位置無關”,多棒!
但是請你想象一下,當你執行一個程式,他引用了100000個函式,現在他正在靠got[2]的查詢函式一個一個查詢......
簡直是災難!很明顯正常人都能想到,為什麼不“用到的時候”再找呢?你一開頭找那麼久幹什麼?萬一有個if-else,其中一個分支引用了99%的外部函式,結果我根本就沒進入這個分支......
好!現在讓我們看下plt的邏輯:

  1. 當我call puts@plt的時候,我跑到plt程式碼段執行這些動作:跳到got表,如果第一次呼叫沒人幫我找,那麼got表此時的地址就是plt剛剛跳的位置,又回到了plt!
  2. 那麼就繼續吧!先壓入我要找的函式id,再jmp到plt的公共程式碼段。
  3. 公共程式碼段對於所有plt操作都是一樣的,那就是呼喚我們熟悉的got[2]來幫我們找函式。
  4. got[2]找好以後,它會填入got表中,那麼下次再執行步驟1的時候,got表此時的位置就是puts的位置!
  5. 現在又要用到puts了!我call puts@plt,跳到puts@plt程式碼段,然後又跳到got表,然後發現居然有人幫我們找好了!

以上內容我相信能幫你輕鬆的瞭解plt got的機制,一些專業名詞你可以自己搜尋。原理懂了看起來方便很多的!

相關文章