萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 作業系統 >  >> Linux 教學

Linux 動態庫 (.so) 建立與鏈接實戰:從原始碼到執行期載入完整流程

動態函式庫 (Shared Library, .so) 讓多程式共享程式碼、節省記憶體、支援獨立升級。本文以最簡範例完整示範:編譯 .so、鏈接可執行檔、執行期載入路徑解析、版本管理。

專案結構

/home/test/program/
├── myLibSrc.c       # 庫原始碼
├── myLibInclude.h   # 對外介面
└── main.c           # 測試程式

原始碼

// myLibInclude.h
#ifndef MYLIB_H
#define MYLIB_H
int myLibSrcFun(void);
#endif

// myLibSrc.c
#include 
#include "myLibInclude.h"
int myLibSrcFun(void) {
    printf("There is myLibSrcFun()\n");
    return 0;
}

// main.c
#include 
#include "myLibInclude.h"
int main(int argc, char **argv) {
    printf("Main function!\n");
    myLibSrcFun();
    return 0;
}

編譯動態庫

# 基礎編譯
gcc -fPIC -shared -o libmyLib.so myLibSrc.c

# 完整編譯 (含除錯資訊、宏定義)
gcc -fPIC -shared -g -DDEBUG -o libmyLib.so myLibSrc.c
  • -fPIC (Position Independent Code):產生位置無關程式碼,**動態庫必加**,否則載入時需文字重定位 (性能損耗、安全風險)
  • -shared:產生共享物件而非可執行檔
  • 命名慣例:lib.so (連結器 -l 自動尋找)

靜態鏈接可執行檔 (編譯期指定庫路徑)

# 方法 1:指定庫完整路徑
gcc -o app main.c /home/test/program/libmyLib.so

# 方法 2:-L 指定搜尋目錄 -l 指定庫名 (去 lib 前綴、.so 後綴)
gcc -o app main.c -L/home/test/program -lmyLib

執行期動態載入器 (ld.so) 搜尋順序

  1. 可執行檔內嵌 RPATH (編譯時 -Wl,-rpath,/path)
  2. 環境變數 LD_LIBRARY_PATH
  3. 可執行檔內嵌 RUNPATH (較 RPATH 低優先級,可被 LD_LIBRARY_PATH 覆蓋)
  4. /etc/ld.so.cache 快取 (ldconfig 更新)
  5. 預設系統目錄 /lib, /usr/lib (64位: /lib64, /usr/lib64)

實戰驗證

# 1. 檢查動態相依
ldd ./app
# 輸出:
# linux-vdso.so.1 => ...
# libmyLib.so => /home/test/program/libmyLib.so (0x...)
# libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x...)
# /lib64/ld-linux-x86-64.so.2

# 2. 直接執行 (若庫在非標準路徑會失敗)
./app
# 錯誤:error while loading shared libraries: libmyLib.so: cannot open shared object file

# 3. 臨時指定路徑執行
LD_LIBRARY_PATH=/home/test/program ./app
# 輸出:
# Main function!
# There is myLibSrcFun()

# 4. 永久解決:安裝至系統目錄並更新快取
cp libmyLib.so /usr/local/lib/
ldconfig

# 5. 再次執行
./app  # 成功

版本管理與 soname 機制

# 編譯時指定 soname (SO-NAME)
gcc -fPIC -shared -Wl,-soname,libmyLib.so.1 -o libmyLib.so.1.0.0 myLibSrc.c

# 建立符號連結
ln -sf libmyLib.so.1.0.0 libmyLib.so.1
ln -sf libmyLib.so.1 libmyLib.so

# 檢查
readelf -d libmyLib.so.1.0.0 | grep SONAME
# 0x000000000000000e (SONAME) Library soname: [libmyLib.so.1]
  • soname (如 libmyLib.so.1):執行期載入器真正尋找的名稱
  • 主版本號變更 = ABI 不相容,需重新編譯依賴程式
  • ldconfig 掃描目錄建立 soname → 實際檔案 符號連結

進階:dlopen 動態載入 (外掛/插件架構)

#include 
void *handle = dlopen("./libmyLib.so", RTLD_LAZY);
if (!handle) { fprintf(stderr, "%s\n", dlerror()); exit(1); }

int (*func)(void) = dlsym(handle, "myLibSrcFun");
if (func) func();

dlclose(handle);

編譯:gcc -o plugin_app main_dl.c -ldl (主程式不連結 .so,執行期載入)

動態庫編譯鏈接流程圖

常見問題排查

錯誤原因解決
cannot open shared object file庫路徑不在搜尋順序中LD_LIBRARY_PATH、ldconfig、-Wl,-rpath
undefined symbol符號未導出或版本不符nm -D libxxx.so | grep sym、檢查版本
wrong ELF class32/64 位元不匹配file libxxx.so 確認架構
文字重定位警告未加 -fPIC重編譯加 -fPIC

動態庫是 Linux 模組化、記憶體共享、熱升級的基石。掌握 -fPIC、soname、ldconfig、RPATH/RUNPATH、dlopen 五大核心機制,即可自如構建、部署、維護共享庫生態。

copyright © 萬盛學電腦網 all rights reserved