首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

連結庫與位置無關程式碼:靜態/動態連結、PIC、GOT/PLT 機制深度解析

     編輯:關於C語言

連結庫與位置無關程式碼:靜態/動態連結、PIC、GOT/PLT 機制深度解析

本文系統解析 Linux 下靜態連結、靜態共享庫、動態共享庫的區別,重點闡述位置無關程式碼(PIC)如何讓動態共享庫的程式碼段在多進程間真正共享,並詳細剖析 GOT、PLT、.dynamic 段的運作機制。

一、三種連結模式對比

模式連結時機裝載地址確定時機代碼段共享
靜態連結連結階段連結階段不可共享(每進程一份副本)
靜態共享庫連結階段連結階段(OS 預分配虛擬地址)可共享,但地址固定,已淘汰
動態共享庫執行階段裝載階段(動態分配)可共享,需 PIC 支援

二、關鍵名詞釐清

  • 連結:目標檔與庫檔建立符號關聯。
  • 裝載:連結後映射至虛擬記憶體。
  • 連結重定位:靜態連結時將符號參考解析為相對起始位址的偏移。
  • 裝載重定位:動態連結時在裝載階段根據實際虛擬位址修正絕對位址引用。

三、共用物件結構分析

.data 段(全域/靜態變數)

  • 裝載時併入可執行檔的 .got 段,每個進程擁有私有副本(寫時複製 COW)。
  • 區域變數存放於堆疊,不在共享庫段內。

.interp 段

  • 指定動態連結器路徑(如 /lib64/ld-linux-x86-64.so.2)。

.dynamic 段(動態連結關鍵資訊)

  • DT_SYMTAB:.dynsym 動態符號表位址。
  • DT_STRTAB:動態字串表位址。
  • DT_REL:重定位表位址(.rel.text、.rel.data)。
  • DT_NEEDED:依賴的其他共用物件名稱。

.symtab vs .dynsym

  • .symtab:靜態連結符號表,含所有符號(含區域符號)。
  • .dynsym:動態連結符號表,僅含外部可見符號,.dynsym ⊂ .symtab。

四、程式碼段與 PIC 實現

問題:非 PIC 程式碼含絕對位址引用

  • 全域變數存取、函數呼叫若使用絕對位址,裝載到不同虛擬位址時會出錯。
  • 強制固定裝載位址 → 退化為靜態共享庫,喪失靈活性。

解決方案:-fPIC 編譯選項

將程式碼段中所有絕對位址引用轉換為相對位址或間接引用:

.got.plt 三大核心條目

  1. .dynamic 段位址 → 查找動態符號表。
  2. 本模組 ID(如 libfoo.so 的 "foo")。
  3. _dl_runtime_resolve 函數位址 → 執行期符號解析。

五、動態裝載流程簡述

  1. 核心將控制權交給動態連結器。
  2. 動態連結器靜態連結自舉,將可執行檔映射至固定位址。
  3. 讀取 .dynamic 中 DT_NEEDED,遞迴裝載依賴共用物件。
  4. 合併符號表至全域符號表,映射全域變數至私有資料段(COW)。
  5. 映射程式碼段(唯讀,多進程共享實體頁框)。
  6. 所有依賴就緒後,跳轉可執行檔入口點開始執行。

六、編譯實戰指令

# 產生位置無關目標檔
gcc -fPIC -c foo.c -o foo.o
# 產生動態共享庫
gcc -shared foo.o -o libfoo.so
# 連結使用動態庫
gcc main.c -L. -lfoo -o main
# 執行時需設定 LD_LIBRARY_PATH 或安裝至標準路徑
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
引用類型PIC 處理方式
模組內部函數呼叫/跳轉PC-relative 相對位址(編譯時已知偏移)
模組內部資料存取GOT 相對位址(透過 GOT 基址 + 偏移)
跨模組資料存取存入本模組 .got,執行期由動態連結器填入實際位址
跨模組函數呼叫存入 .got.plt,首次呼叫觸發 _dl_runtime_resolve 延遲綁定