Linux 應用程式依賴兩個不同的介面:核心系統呼叫 ABI 與 C 函式庫。ABI 定義特定架構進入核心的方式。C 函式庫實作程式編譯時所依賴的 ISO C 與 POSIX 介面,並加入程序啟動程式碼、動態載入器、名稱服務查詢、執行緒支援、地區設定處理,以及許多無法對應至單一系統呼叫的介面。因此,核心並不提供完整的 C 執行階段。
GNU libc 的起源與發行版本 #
Richard Stallman 於 1983 年 9 月 27 日宣布 GNU 專案。GNU libc 的開發延續至 1980 年代後期;Roland McGrath 是最早公開貢獻者之一。1988 年的 GNU Bulletin報導,大多數函式庫已經完成。
早期發行資料必須審慎處理。GNU FTP 封存保留了歷史 glibc 修補程式與發行封存,但封存涵蓋範圍並不是完整的發行資料庫。例如,glibc 1.x 時期的發行版本與修補程式採用不同的可用性和日期記錄慣例。相較於回溯整理的版本清單或套件中繼資料,封存是更有力的證據。
glibc 2.0 於 1997 年 1 月發行,2.x 系列其後成為 GNU libc 持續使用的 ABI 系列。目前的原始碼、發行資料與手冊由專案在 sourceware.org及 glibc 原始碼儲存庫中維護。「glibc 2.x」描述的是一個原始碼與 ABI 家族;對受支援舊介面的相容性透過 ABI 規則管理,而不是在每次發行時變更主要函式庫檔名。
從 Linux libc 到 glibc #
早期 Linux 系統採用了 GNU libc 的程式碼,之後 Linux 開發者維護一個 Linux 專用分支,而上游 glibc 則獨立開發。這個分支通常稱為 Linux libc。其共享函式庫世代包括 libc4 與 libc5:
- libc4 及更早版本支援歷史
a.out可執行檔環境中的程式。 - libc5 配合 Linux 向 ELF 共享物件的轉換,使發行版能夠採用現代動態連結模型。
- 發行版之後遷移至 GNU libc 2.x。Debian 衍生套件名稱(例如
libc6)代表 glibc 2.x 共享函式庫 ABI 世代,不表示「glibc 版本 6」。
kernel.org 的 libc5 封存保存了後期的原始碼快照。這是研究原始碼歷史的適當起點;關於特定分支點、發行日期或維護者的說法,應與相應的封存程式碼樹或發行公告核對。目錄重組與匯入的程式碼使得聲稱存在單一原始碼譜系會產生誤導。
為什麼 libc 轉換是 ABI 遷移 #
替換 libc 不只是重新編譯可執行檔,因為函式庫參與程序 ABI:
- 啟動物件負責安排進入
main以及程序終止; - 動態載入器尋找共享物件並解析符號;
- 匯出的結構、呼叫慣例與版本化符號決定舊二進位檔能否繼續載入;
- 解析器與名稱服務切換(NSS)模組實作服務查詢;以及
- POSIX 執行緒協調使用者空間狀態與核心同步原語。
因此,升級必須維持載入器、開發標頭檔、共享函式庫、NSS 模組、套件管理器和復原工具的一致性。針對某個 libc 建置的二進位檔,如果需要無法取得的符號版本,或其 ELF PT_INTERP 區段中記錄的直譯器找不到,則可能在進入 main 之前失敗。glibc 的 ABI 清單記錄了受支援架構公開的符號集合與版本基線各不相同。
Linux C 函式庫的選擇 #
glibc 並不是唯一實作 Linux 系統呼叫 ABI 的 C 函式庫:
- musl強調精簡、以標準為導向的設計,常見於靜態連結與最小化系統。
- uClibc-ng以具備可設定功能的嵌入式 Linux 系統為目標。
- Android 的 Bionic針對 Android 平台量身打造。
原始碼相容性不代表二進位相容性。即使程式的原始碼看似具備可攜性,glibc 擴充、符號版本、動態載入器行為、NSS 模組與 ABI 細節仍可能使程式特定於 glibc。若要了解目前行為,請使用由專案維護的最新手冊;調查歷史二進位檔時,請使用發行封存、原始標頭檔、目標架構 ABI,以及相符的核心版本。
閱讀歷史文件 #
目前的手冊頁記錄目前的介面,並可能記載某項行為何時出現或變更。歷史原始碼與二進位檔必須對照原始標頭檔、C 函式庫、架構 ABI 及核心行為來解讀。封存資料可為 libc5 至 glibc 的轉換提供背景,但目前的建置應遵循所選 C 函式庫與發行版維護的文件。
評論
在 GitHub 上閱讀或評論(發表評論需要登入)