程式設計師的自我修養
Chapter 8 Linux共用程式庫的組織

       Shu-Yu Fu
本章概述

● 共用程式庫的不相容更新 -> SO-NAME
● SO-NAME無法解決Minor-revision
  Rendezvous Problem -> 符號版本機制和範圍
  機制
● 共用程式庫的搜尋路徑
  ○ 標準路徑
  ○ 外部改變的方法
● 共用程式庫的建立和安裝
共用程式庫版本

● ABI(Application Binary Interface)不相容更新
  函式呼叫的堆疊結構,符號命名,參數規則
● C︰
  ○   呼叫函式產生的結果不一樣
  ○   匯出函式被刪除
  ○   匯出資料的結構發生變化
  ○   介面改變,如回傳值、參數被更新
● C++︰
  ○   blah...
  ○   blah...blah...
  ○   blah...blah...blah...
  ○   blah...blah...blah...blah...
共用程式庫版本命名﹙規則﹚

● libname.so.x.y.z
  ○ name -->程式庫﹙又稱連結﹚名稱
  ○ x,主版本序號
    不同主版本序號的程式庫之間是不相容的
  ○ y,次版本序號
    增加一些新的介面符號且原來的符號不變
  ○ z,發行版本序號
    錯誤的修正、效能的改進
● 還是有例外,Glibc
  ld-x.y.z.so
SO-NAME
● 記錄共用程式庫依賴關係的機制
● 一般來說,去掉次版本序號和發生版本序號即
  為共用程式庫所對應的SO-NAME
  ○ libfoo.so.2.6.1 -> libfoo.so.2
● 把SO-NAME記錄在ELF檔中,建立以SO-
  NAME為名的軟式連結至系統中最新版的共用
  程式庫以提升相容性
● 用ldconfig更新系統的軟式連結
● gcc -static -lglib-2.0 --> libglib-2.0.a
● gcc -Bdynamic -lglib-2.0 --> libglib-2.0.so
符號版本

● 系統中只有低次版本序號的共用程式庫,就會
  發生次版本序號交會問題(Minor-revision
  Rendezvous Problem)
  ○ SO-NAME無法解決
● GLibc自2.1開始支援符號版本機制(Symbol
  Versioning)
● 每個匯出和匯入的符號都有一個相關聯的版
  本序號
Solaris中的符號版本機制

● 版本機制(Versioning)
 SUNW_1.1 {   SUNprivate { SUNW_1.2 {
   global:      global:       global:
   pop;         __pop;       swap;
   push;        __push;    } SUNW_1.
 }              local:     1;
                *;
              }

● 範圍機制(Scoping)
  對C語言來說是一個符號可見範圍的補救措施
● 編譯時,記錄實際用到最高版本符號到可執行
  檔內
GCC對Solaris符號版本機制的擴充

● asm (".symver add, add@VERS_1.1");
● 更改了介面或含義又不想更改主版本序號
● asm (".symver addex, add@VERS_1.2");
共用程式庫系統路徑

● FHS(File Hierarchy Standard)標準規定了一
  個系統中的系統檔案應該如何存放,一個系統
  中主要有3個存放共用程式庫的位置︰
  a. /lib存放系統最關鍵和基礎的共用程式庫
  b. /usr/lib存放非系統執行時所需要的關鍵性共用程式庫
  c. /usr/local/lib存放第三方應用程式的程式庫
共用程式庫搜尋過程

● 如果DT_NEED保存的是絕對路徑,LD就按照
  這路徑去搜尋。
● 否則﹙絕對路徑﹚, 就在/lib,/usr/lib,
  /etc/ld.so.conf組態檔指定的目錄中搜尋
● 每次都搜尋全部的目錄太累了
● ldconfig幫你產生一個/etc/ld.so.cache其中
  包含所有共用程式庫目錄下的共用程式庫的
  SO-NAME﹙即符號連結﹚
  ldconfig -p
● 如果在/etc/ld.so.cache裡找不到,再去/lib,
  /usr/lib找
環境變數
● LD_LIBRARY_PATH可以改變共用程式庫搜
  尋路徑,優先搜尋由LD_LIBRARY_PATH指定的
  目錄
 LD_LIBRARY_PATH=/home/user bin/ls
 /lib/ld-linux.so.2 -library-path /home/user /bin/ls
● LD_PRELOAD指定的檔案,不論使用與否都
  會載入,且,會覆蓋後面載入的同名全域符
  號。
● LD_DEBUG可打開LD的除錯功能
共用程式庫的建立和安裝 (1/2)

● 建立
  # 不要使用-fomit-frame-pointer,原因請參照函式與堆疊
  gcc -shared -Wl,-soname,$(my_soname) -o $(library_name)
  $(source_files)
● 測試
  # 指定目的程式的共用程式庫搜尋路徑,用LD_LIBRARY_PATH更方便
  ld -rpath /home/mylib -o program.out program.o -
  lsomelib
● 清除符號
  strip $(library_name)
  ld -s/-S (所有符號資訊/除錯符號資訊)
● 安裝
  ldconfig -n $(shared_library_directory)
共用程式庫的建立和安裝 (2/2)

● 共用程式庫的建構式/解構式(與-nostartfiles
  和-nostdlib互斥),優先順序1->n n->1
  __attribute__((constructor(1)))
  __attribute__((constructor(10)))
  __attribute__((destructor(10)))
  __attribute__((destructor(1)))
● 共用程式庫腳本
  GROUP (/lib/libc.so.6 /lib/libm.so2)
知道這些後我可以做什麼?

● 瞭解API/ABI backward compatible
  ○ SO-NAME
  ○ 萬不得已時,符號多載
● 看懂autotool中常見的名詞,如︰rpath
● 改變載入的過程,
  ○ LD_LIBRARY_PATH測試系統中新版函式庫
  ○ LD_PRELOAD覆寫third-party library來除錯
  ○ LD_DEBUG確定連結正確的shared library
● 改善程式載入速度﹙加快shared library搜
  尋過程﹚

程式設計師的自我修養 Chapter 8

  • 1.
  • 2.
    本章概述 ● 共用程式庫的不相容更新 ->SO-NAME ● SO-NAME無法解決Minor-revision Rendezvous Problem -> 符號版本機制和範圍 機制 ● 共用程式庫的搜尋路徑 ○ 標準路徑 ○ 外部改變的方法 ● 共用程式庫的建立和安裝
  • 3.
    共用程式庫版本 ● ABI(Application BinaryInterface)不相容更新 函式呼叫的堆疊結構,符號命名,參數規則 ● C︰ ○ 呼叫函式產生的結果不一樣 ○ 匯出函式被刪除 ○ 匯出資料的結構發生變化 ○ 介面改變,如回傳值、參數被更新 ● C++︰ ○ blah... ○ blah...blah... ○ blah...blah...blah... ○ blah...blah...blah...blah...
  • 4.
    共用程式庫版本命名﹙規則﹚ ● libname.so.x.y.z ○ name -->程式庫﹙又稱連結﹚名稱 ○ x,主版本序號 不同主版本序號的程式庫之間是不相容的 ○ y,次版本序號 增加一些新的介面符號且原來的符號不變 ○ z,發行版本序號 錯誤的修正、效能的改進 ● 還是有例外,Glibc ld-x.y.z.so
  • 5.
    SO-NAME ● 記錄共用程式庫依賴關係的機制 ● 一般來說,去掉次版本序號和發生版本序號即 為共用程式庫所對應的SO-NAME ○ libfoo.so.2.6.1 -> libfoo.so.2 ● 把SO-NAME記錄在ELF檔中,建立以SO- NAME為名的軟式連結至系統中最新版的共用 程式庫以提升相容性 ● 用ldconfig更新系統的軟式連結 ● gcc -static -lglib-2.0 --> libglib-2.0.a ● gcc -Bdynamic -lglib-2.0 --> libglib-2.0.so
  • 6.
    符號版本 ● 系統中只有低次版本序號的共用程式庫,就會 發生次版本序號交會問題(Minor-revision Rendezvous Problem) ○ SO-NAME無法解決 ● GLibc自2.1開始支援符號版本機制(Symbol Versioning) ● 每個匯出和匯入的符號都有一個相關聯的版 本序號
  • 7.
    Solaris中的符號版本機制 ● 版本機制(Versioning) SUNW_1.1{ SUNprivate { SUNW_1.2 { global: global: global: pop; __pop; swap; push; __push; } SUNW_1. } local: 1; *; } ● 範圍機制(Scoping) 對C語言來說是一個符號可見範圍的補救措施 ● 編譯時,記錄實際用到最高版本符號到可執行 檔內
  • 8.
    GCC對Solaris符號版本機制的擴充 ● asm (".symveradd, add@VERS_1.1"); ● 更改了介面或含義又不想更改主版本序號 ● asm (".symver addex, add@VERS_1.2");
  • 9.
    共用程式庫系統路徑 ● FHS(File HierarchyStandard)標準規定了一 個系統中的系統檔案應該如何存放,一個系統 中主要有3個存放共用程式庫的位置︰ a. /lib存放系統最關鍵和基礎的共用程式庫 b. /usr/lib存放非系統執行時所需要的關鍵性共用程式庫 c. /usr/local/lib存放第三方應用程式的程式庫
  • 10.
    共用程式庫搜尋過程 ● 如果DT_NEED保存的是絕對路徑,LD就按照 這路徑去搜尋。 ● 否則﹙絕對路徑﹚, 就在/lib,/usr/lib, /etc/ld.so.conf組態檔指定的目錄中搜尋 ● 每次都搜尋全部的目錄太累了 ● ldconfig幫你產生一個/etc/ld.so.cache其中 包含所有共用程式庫目錄下的共用程式庫的 SO-NAME﹙即符號連結﹚ ldconfig -p ● 如果在/etc/ld.so.cache裡找不到,再去/lib, /usr/lib找
  • 11.
    環境變數 ● LD_LIBRARY_PATH可以改變共用程式庫搜 尋路徑,優先搜尋由LD_LIBRARY_PATH指定的 目錄 LD_LIBRARY_PATH=/home/user bin/ls /lib/ld-linux.so.2 -library-path /home/user /bin/ls ● LD_PRELOAD指定的檔案,不論使用與否都 會載入,且,會覆蓋後面載入的同名全域符 號。 ● LD_DEBUG可打開LD的除錯功能
  • 12.
    共用程式庫的建立和安裝 (1/2) ● 建立 # 不要使用-fomit-frame-pointer,原因請參照函式與堆疊 gcc -shared -Wl,-soname,$(my_soname) -o $(library_name) $(source_files) ● 測試 # 指定目的程式的共用程式庫搜尋路徑,用LD_LIBRARY_PATH更方便 ld -rpath /home/mylib -o program.out program.o - lsomelib ● 清除符號 strip $(library_name) ld -s/-S (所有符號資訊/除錯符號資訊) ● 安裝 ldconfig -n $(shared_library_directory)
  • 13.
    共用程式庫的建立和安裝 (2/2) ● 共用程式庫的建構式/解構式(與-nostartfiles 和-nostdlib互斥),優先順序1->n n->1 __attribute__((constructor(1))) __attribute__((constructor(10))) __attribute__((destructor(10))) __attribute__((destructor(1))) ● 共用程式庫腳本 GROUP (/lib/libc.so.6 /lib/libm.so2)
  • 14.
    知道這些後我可以做什麼? ● 瞭解API/ABI backwardcompatible ○ SO-NAME ○ 萬不得已時,符號多載 ● 看懂autotool中常見的名詞,如︰rpath ● 改變載入的過程, ○ LD_LIBRARY_PATH測試系統中新版函式庫 ○ LD_PRELOAD覆寫third-party library來除錯 ○ LD_DEBUG確定連結正確的shared library ● 改善程式載入速度﹙加快shared library搜 尋過程﹚