| 類型 | 副檔名 | 範例 | 特性 |
|---|---|---|---|
| 靜態函式庫 | .a | libfoo.a | 編譯期完整複製進執行檔,獨立部署、無執行期相依 |
| 動態函式庫 | .so | libfoo.so | 執行期載入,共享記憶體、支援熱更新、預設連結優先 |
版本號慣例:libfoo.so.major.minor (如 libfoo.so.1.0),搭配符號連結:
ln -sf libfoo.so.1.0 libfoo.so.1
ln -sf libfoo.so.1 libfoo.so
soname (Shared Object Name) 嵌入執行檔,載入器依 soname 尋找實際檔案,實現多版本共存。
hello/
├── include/hello.h # void sayhello();
├── lib/libhello.a # 靜態
├── lib/libhello.so.1.0 # 動態 + 符號連結
└── docs/
# 編譯
gcc -c testlib.c -o testlib.o -Ihello/include
# 連結 (預設搜尋 /usr/lib, /lib)
gcc testlib.o -Lhello/lib -lhello -o testlib_dynamic
# 執行需設定庫路徑
LD_LIBRARY_PATH=hello/lib ./testlib_dynamic
# 明確指定靜態連結
gcc testlib.o -Lhello/lib -Wl,-Bstatic -lhello -Wl,-Bdynamic -o testlib_static
# 或僅有靜態庫時直接連結
gcc testlib.o -Lhello/lib -lhello -o testlib_static
-Wl,-Bstatic 傳遞給連結器 ld,後續 -l 皆為靜態;-Wl,-Bdynamic 恢復動態。
gcc testlib.o -o testlib \
-Wl,-Bstatic -lhello \
-Wl,-Bdynamic -lbye -lpthread
執行檔啟動時,動態連結器 ld-linux.so 依序搜尋:
DT_RPATH (編譯期 -Wl,-rpath 嵌入,最高優先級,但不建議濫用)LD_LIBRARY_PATH 環境變數 (使用者可控,次優先)DT_RUNPATH (較新 -Wl,-rpath + --enable-new-dtags,LD_LIBRARY_PATH 優先於它)/etc/ld.so.conf 指定目錄 (系統級,需執行 ldconfig 更新快取)/lib, /usr/lib (及 /lib64, /usr/lib64)| 方式 | 命令 | 適用場景 | 缺點 |
|---|---|---|---|
| 複製到系統目錄 | cp libfoo.so /usr/lib | 系統級套件 | 需 root、污染系統、版本衝突風險 |
| LD_LIBRARY_PATH | export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH | 開發測試、臨時替換 | 不安全 (setuid 程式忽略)、需每次設定 |
| /etc/ld.so.conf | echo "/opt/myapp/lib" >> /etc/ld.so.conf && ldconfig | 應用程式專用目錄、生產部署 | 需 root、影響全系統 |
hello.c#include <stdio.h>
void sayhello() { printf("hello, world\n"); }
gcc -c hello.c -o hello.o -fPIC # -fPIC 非必須但建議
ar rcs libhello.a hello.o
# 查看符號
nm -g libhello.a
# T sayhello 表示定義在本庫的全域函數
# 產生帶 soname 的庫
gcc -shared -fPIC -Wl,-soname,libhello.so.1 -o libhello.so.1.0 hello.o
# 建立符號連結
ln -sf libhello.so.1.0 libhello.so.1
ln -sf libhello.so.1 libhello.so
# 驗證
readelf -d libhello.so.1.0 | grep SONAME
# 0x0000000e (SONAME) Library soname: [libhello.so.1]
關鍵參數:
-shared:產生共享物件而非執行檔。-fPIC:位置無關程式碼,**動態庫必須**,否則載入時需文字重定位,效能損耗大且可能被 SELinux 禁止。-Wl,-soname,libhello.so.1:設定 DT_SONAME,載入器依此尋找。# 1. 編譯時 -fvisibility=hidden
gcc -fPIC -fvisibility=hidden -c hello.c
# 2. 明確匯出
#define API_EXPORT __attribute__((visibility("default")))
API_EXPORT void sayhello();
libhello.verLIBHELLO_1.0 {
global:
sayhello;
local:
*;
};
gcc -shared -fPIC -Wl,--version-script=libhello.ver -o libhello.so.1.0 hello.o
| 工具 | 用途 | 範例 |
|---|---|---|
nm | 列出符號表 | nm -D libhello.so | grep sayhello (T=定義, U=未定義, W=弱符號) |
ldd | 顯示執行檔動態相依 | ldd testlib_dynamic |
objdump -T | 動態符號表詳細 | objdump -T libhello.so |
readelf -d | 動態段資訊 (NEEDED, SONAME, RPATH, RUNPATH) | readelf -d testlib_dynamic |
ldconfig -p | 查看系統庫快取 | ldconfig -p | grep libhello |
ldd、LD_LIBRARY_PATH、ldconfig。nm -D libfoo.so | grep xxx 確認符號是否存在。libfoo.so 存在於搜尋路徑,用 ldd 確認實際載入哪個,調整 rpath 或 LD_LIBRARY_PATH 優先級。-fPIC,重新編譯動態庫。掌握 .a/.so 建立、連結、版本、部署、除錯全流程,是 Linux C/C++ 開發者的核心功力。關鍵口訣:**動態庫必加 -fPIC、設定 soname、善用版本腳本控制符號匯出、部署優先用 rpath/LD_LIBRARY_PATH 隔離環境**。
Linux 叢集技術概論:高可用、負載均衡、心跳監測與漂移 IP 核心概念
Linux 虛擬主機無法刪除檔案/目錄權限錯誤:PHP 腳本以 Apache 身分清理上傳檔案
Git 進階技巧與最佳實踐:別名、自動補全、著色、Rebase 原則、忽略檔案、區域網共享倉庫
Linux 安裝方式大全:硬碟安裝、NFS/HTTP/FTP 網路安裝、USB 啟動盤製作
Windows 與 Linux 分區格式完整對照:FAT16/32、NTFS、Ext2/3、Swap、VFAT 特性與選擇指南
Linux 20 週年:Jim Zemlin 闡述開源如何重塑科技產業與商業模式