Linux 動態庫生成與使用指南

JollyWing的部落格發表於2015-03-11

上一篇介紹了Linux 靜態庫生成指南,有興趣的朋友可以前往閱讀。

Linux下動態庫檔案的檔名形如 libxxx.so,其中so是 Shared Object 的縮寫,即可以共享的目標檔案。

在連結動態庫生成可執行檔案時,並不會把動態庫的程式碼複製到執行檔案中,而是在執行檔案中記錄對動態庫的引用。

程式執行時,再去載入動態庫檔案。如果動態庫已經載入,則不必重複載入,從而能節省記憶體空間。

Linux下生成和使用動態庫的步驟如下:

  1. 編寫原始檔。
  2. 將一個或幾個原始檔編譯連結,生成共享庫。
  3. 通過 -L<path> -lxxx 的gcc選項鍊接生成的libxxx.so。
  4. 把libxxx.so放入連結庫的標準路徑,或指定 LD_LIBRARY_PATH,才能執行連結了libxxx.so的程式。

下面通過例項詳細講解。

編寫原始檔

建立一個原始檔: max.c,程式碼如下:

int max(int n1, int n2, int n3)
{
    int max_num = n1;
    max_num = max_num < n2? n2: max_num;
    max_num = max_num < n3? n3: max_num;
    return max_num;
}

編譯生成共享庫:

gcc -fPIC -shared -o libmax.so max.c

我們會得到libmax.so。

實際上上述過程分為編譯和連結兩步, -fPIC是編譯選項,PIC是 Position Independent Code 的縮寫,表示要生成位置無關的程式碼,這是動態庫需要的特性; -shared是連結選項,告訴gcc生成動態庫而不是可執行檔案。

上述的一行命令等同於:

gcc -c -fPIC max.c
gcc -shared -o libmax.so max.o

為動態庫編寫介面檔案

為了讓使用者知道我們的動態庫中有哪些介面可用,我們需要編寫對應的標頭檔案。

建立 max.h ,輸入以下程式碼:

#ifndef __MAX_H__
#define __MAX_H__

int max(int n1, int n2, int n3);

#endif

測試,連結動態庫生成可執行檔案

建立一個使用max函式的test.c,程式碼如下:

#include <stdio.h>
#include "max.h"

int main(int argc, char *argv[])
{
    int a = 10, b = -2, c = 100;
    printf("max among 10, -2 and 100 is %d.\n", max(a, b, c));
    return 0;
}

gcc test.c -L. -lmax 生成a.out,其中-lmax表示要連結libmax.so
-L.表示搜尋要連結的庫檔案時包含當前路徑。

注意,如果同一目錄下同時存在同名的動態庫和靜態庫,比如 libmax.so 和 libmax.a 都在當前路徑下,
則gcc會優先連結動態庫。

執行

執行 ./a.out 會得到以下的錯誤提示。

./a.out: error while loading shared libraries: libmax.so: cannot open shared object file: No such file or directory

找不到libmax.so,原來Linux是通過 /etc/ld.so.cache 檔案搜尋要連結的動態庫的。
而 /etc/ld.so.cache 是 ldconfig 程式讀取 /etc/ld.so.conf 檔案生成的。
(注意, /etc/ld.so.conf 中並不必包含 /lib 和 /usr/libldconfig程式會自動搜尋這兩個目錄)

如果我們把 libmax.so 所在的路徑新增到 /etc/ld.so.conf 中,再以root許可權執行 ldconfig 程式,更新 /etc/ld.so.cache ,a.out執行時,就可以找到libmax.so

但作為一個簡單的測試例子,讓我們改動系統的東西,似乎不太合適。
還有另一種簡單的方法,就是為a.out指定 LD_LIBRARY_PATH

LD_LIBRARY_PATH=. ./a.out

程式就能正常執行了。LD_LIBRARY_PATH=. 是告訴 a.out,先在當前路徑尋找連結的動態庫。

對於elf格式的可執行程式,是由ld-linux.so*來完成的,它先後搜尋elf檔案的 DT_RPATH 段, 環境變數 LD_LIBRARY_PATH, /etc/ld.so.cache檔案列表, /lib/,/usr/lib目錄, 找到庫檔案後將其載入記憶體. (http://blog.chinaunix.net/uid-23592843-id-223539.html)

makefile讓工作自動化

編寫makefile,內容如下:

.PHONY: build test clean

build: libmax.so

libmax.so: max.o
	gcc -o $@  -shared $<

max.o: max.c
	gcc -c -fPIC $<

test: a.out

a.out: test.c libmax.so
	gcc test.c -L. -lmax
	LD_LIBRARY_PATH=. ./a.out

clean:
	rm -f *.o *.so a.out

make build就會生成libmax.so, make test就會生成a.out並執行,make clean會清理編譯和測試結果。

相關文章