(C/C++) 如何在Linux上編譯與使用 static /shared library#

Demo project#

LinkLoadDemo這個Project總共包含了6個projects以及一個資料夾

  1. Project_StaticLib 模擬建立一個 static library

  2. Project_LinkStaticLib 模擬使用 static library

  3. Project_SharedLib 模擬建立一個 shared library

  4. Project_LinkSharedLib 模擬使用 shared library

  5. Project_SharedLibForDynamicLoad 模擬建立一個 shared library

  6. Project_LoadSharedLib 模擬動態載入一個 shared library

  7. SharedLib 為佈署 shared library 的路徑

Static Linking#

Static Linking 特點#

  1. 通常是以.a (Unix-like).lib (Windows)副檔名

  2. 編譯期library程式碼 加入 執行檔 ,因此執行檔size會 較大

  3. librarysource file之間的dependency相對較,引用的library有更新時需要將程式重編譯才能得到更新過後的內容

  4. Deployment不需要處理其他程式,相對簡單

  5. library的程式會被放到同一個address space,所以執行速度會會稍微一點。

建立 static library#

  1. 寫一個header file (.h)作為library的APIlibrary使用者可以正確的呼叫屬於library的function

  2. source file編譯成object files

  3. object files打包成 static library file

    Note

    static library檔名的固定格式為lib{library name}.a

Example: 建立 static library#

1# 編譯指令
2gcc src/prog1.c -c -o src/prog1.o
3gcc src/prog2.c -c -o src/prog2.o
4gcc src/prog3.c -c -o src/prog3.o
5# 打包指令, 打包出一個名為 staticlink 的 static library
6ar rcs libstaticlink.a src/prog1.o src/prog2.o src/prog3.o

使用static library#

佈署 static library#

  1. static library檔案複製到佈署資料夾

使用 static library 編譯程式#

  1. include library的header file後呼叫要使用的function(通常會將.h放到include資料夾下)

  2. 將程式編譯成執行檔時加上相關的參數

    Note

    • -static用來指示若有同名的static libraryshared library的話會優先使用static library (預設為優先使用shared library)

    • -I {path}用來指示header file放置的路徑-I{path}中間可以不需要空格隔開

    • -L {path}用來指示static library file放置的路徑-L{path}中間可以不需要空格隔開

    • -l {library name}用來指示 library name-l{library name}中間可以不需要空格隔開

Example: 使用 static library#

1# 佈署指令
2cp -r ../Project_StaticLib/include .
3cp ../Project_StaticLib/libstaticlink.a lib
4# 編譯指令
5gcc src/main.c -static -I include -L lib -l staticlink -o main

Dynamic Linking#

Dynamic Linking 特點#

  1. 通常是以.so (Unix-like).dll (Windows)副檔名

  2. 編譯期並不將library程式碼加入執行檔而是只留下一個stub,因此執行檔會比較小

  3. librarysource file之間的dependency相對較 ,引用的library有更新時只需要更新library即可得到更新過後的內容

  4. Deployment需要處理另外的設定,相對教複雜

  5. 程式在啟動之前dynamic linker會介入去把stub綁定到library function實際的位址。

  6. 因為綁定的位址是受到OS管控的,因此會稍微一點。

  7. library的程式由OS管控,只會載入一次。如果有多個程式會共用這個library的話則是透過OS間接取得這些library functionreference。因此可節省系統資源。

建立 shared library#

  1. 寫一個header file作為libraryAPIlibrary使用者可以正確的呼叫屬於libraryfunction

  2. source file編譯成object files

  3. object files打包成shared library file

    Note

    • version code: 代表大版本更動,可能造成與前面的版本不相容

    • minor code: 代表有新增功能,但library有向前相容

    • release code: 代表功能有變更,通常是bug修正或者是針對程式重構

    • real name: 實際的檔案名稱,慣用格式為lib{library name}.so.{version code}.{minor code}.{release code}

    • linker name: 要編譯執行檔時linker尋找library的檔案名稱,慣用格式為lib{library name}.so

    • so name: 執行檔要啟動時linker尋找library的檔案名稱,慣用格式為lib{library name}.so.{version code}

  4. 指令講解

    Note

    • -fPIC是用來提示compiler要將檔案編譯成position-independent code

    • -shared是用來提示compiler要將檔案編譯成shared object

    • -Wl,{option}是用來將option傳給 linker 的指令,要傳給linker的指令用逗點隔開

    • -sonamelinker的指令,用來提示當編譯好的執行檔啟動時,要用soname去找到shared library而不是使用real name去找,設定so name可以讓編譯與執行不使用同一個library進而增加程式的彈性

Example: 建立 shared library#

1# 編譯指令
2gcc src/prog1.c -c -fPIC -o src/prog1.o
3gcc src/prog2.c -c -fPIC -o src/prog2.o
4gcc src/prog3.c -c -fPIC -o src/prog3.o
5# 打包指令, 打包出一個名為 dynamiclink 的 dynamic library
6gcc src/prog1.o src/prog2.o src/prog3.o -shared -Wl,-soname,libdynamiclink.so.1 -o libdynamiclink.so.1.0.0 

使用 shared library#

佈署 shared library#

  1. shared library檔案複製到部署路徑資料夾下

  2. 建立linker namesoft link

  3. 建立so namesoft link

  4. 如果佈署路徑不是系統預設資料夾則須使用下面任一種方法完成設置

    1. (Optional) 使用環境變數

      1export LD_LIBRARY_PATH=/path/to/shared/libs:$LD_LIBRARY_PATH
      
    2. (Optional) 使用ldconfig配置

      1. 將佈署路徑加入到下面兩個選項其中之一

        • /etc/ld.so.conf.d/資料夾下面增加新的配置檔案(Ex: custom-libs.conf)

        • 直接修改/etc/ld.so.conf

      2. 更新ld.so.cache

      1sudo ldconfig
      

啟動有使用 dynamic linking 的程式時尋找library路徑的順序#

  1. 環境變數LD_LIBRARY_PATH':'隔開的那些路徑

  2. /etc/ld.so.cache中指定的library列表

  3. /lib

  4. /usr/lib

  5. /usr/local/lib

使用 shared library 編譯程式#

  1. include library的header file後呼叫要使用的function(通常會將.h放到include資料夾下

  2. 將程式編譯成執行檔時加上相關的參數

    Note

    • -I {path}用來指示header file放置的路徑-I{path}中間可以不需要空格隔開

    • -L {path}用來指示shared library file放置的路徑-L{path}中間可以不需要空格隔開

    • -l {library name}用來指示library name-l{library name}中間可以不需要空格隔開

    • 會依據linker name去找library

Example: 使用 shared library#

 1# 佈署指令, 將 dynamic library 佈署到 ../SharedLib 資料夾下並建立兩個對應的 soft link
 2cp -r ../Project_SharedLib/include .
 3mkdir -p ../SharedLib
 4cp ../Project_SharedLib/libdynamiclink.so.1.0.0 ../SharedLib
 5ln -fs ../SharedLib/libdynamiclink.so.1.0.0 ../SharedLib/libdynamiclink.so.1
 6ln -fs ../SharedLib/libdynamiclink.so.1.0.0 ../SharedLib/libdynamiclink.so
 7# 編譯程式
 8gcc src/main.c -I include -L ../SharedLib -l dynamiclink -o main
 9# 環境變數設定 & 執行編譯好的程式
10LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../SharedLib
11./main

Dynamic Loading#

Dynamic Loading 特點#

  1. 編譯時並不將library的內容連結,在執行期動態將libraryplug-in的方式載入釋放,更具彈性。

  2. 因為系統需要處理的工作更多,因此執行速度更慢

  3. Dynamic Loading載入的程式可以是static libraryshared library執行檔

  4. 透過dl這個library達成,用法參考檔案Project_LoadSharedLib/src/main.c

  5. 可以不需要includelibrary的header file,但是需要從header file文件確定好要使用的function變數type

  6. dlopen的檔名如果以'/'開頭則會用絕對路徑去找,若不是以的話會依照啟動有使用 dynamic linking 的程式時尋找library路徑的順序的規則去尋找

  7. 編譯指令有-ldl的原因是因為需要用到dl這個library

Reference#

  1. static link & Dynamic Link & Load

  2. Compile gcc 編譯 static link, dynamic link, dynamic load

  3. How static linking works on Linux

  4. How dynamic linking for modular libraries works on Linux

  5. 4. Dynamically Loaded (DL) Libraries

  6. Shared Libraries: Understanding Dynamic Loading