2010年12月27日 星期一

Linux 上 .a 跟 .so

Library可分成三種,static、shared與dynamically loaded。


1. Static libraries


Static 程式庫用於靜態連結,簡單講是把一堆object檔用ar(archiver)

包裝集合起來,檔名以`.a’ 結尾。優點是執行效能通常會比後兩者快,

而且因為是靜態連結,所以不易發生執行時找不到library或版本錯置而

無法執行的問題。缺點則是檔案較大,維護度較低;例如library如果發

現bug需要更新,那麼就必須重新連結執行檔。


1.1 編譯


編譯方式很簡單,先例用`-c’ 編出object 檔,再用ar 包起來即可。


____ hello.c ____

#include

void hello(){ printf(”Hello “); }


____ world.c ____

#include

void world(){ printf(”world.”); }


____ mylib.h ____

void hello();

void world();


$ gcc -c hello.c world.c /* 編出hello.o 與world.o */

$ ar rcs libmylib.a hello.o world.o /* 包成limylib.a */


這樣就可以建出一個檔名為libmylib.a 的檔。輸出的檔名其實沒有硬性規定,

但如果想要配合gcc 的’-l’ 參數來連結,一定要以`lib’ 開頭,中間是你要

的library名稱,然後緊接著`.a’ 結尾。


1.2 使用


____ main.c ____

#include “mylib.h”

int main() {

hello();

world();

}


使用上就像與一般的object 檔連結沒有差別。


$ gcc main.c libmylib.a


也可以配合gcc 的`-l’ 參數使用


$ gcc main.c -L. -lmylib


-L dir 參數用來指定要搜尋程式庫的目錄,`.' 表示搜尋現在所在的目
錄。

通常預設會搜/usr/lib 或/lib 等目錄。

-l library 參數用來指定要連結的程式庫,'mylib' 表示要與mylib進
行連結

,他會搜尋library名稱前加`lib'後接`.a'的檔案來連結。


$ ./a.out

Hello world.

2. Shared libraries


Shared library 會在程式執行起始時才被自動載入。因為程式庫與執行檔

是分離的,所以維護彈性較好。有兩點要注意,shared library是在程式起始

時就要被載入,而不是執行中用到才載入,而且在連結階段需要有該程式庫

才能進行連結。


首先有一些名詞要弄懂,soname、real name與linker name。


soname 用來表示是一個特定library 的名稱,像是libmylib.so.1 。

前面以`lib' 開頭,接著是該library 的名稱,然後是`.so' ,接著

是版號,用來表名他的介面;如果介面改變時,就會增加版號來維護相容度。


real name 是實際放有library程式的檔案名稱,後面會再加上minor 版號與

release 版號,像是libmylib.so.1.0.0 。


一般來說,版號的改變規則是(印象中在APress-Difinitive Guide to GCC中有

提到,但目前手邊沒這本書),最尾碼的release版號用於程式內容的修正,

介面完全沒有改變。中間的minor用於有新增加介面,但相舊介面沒改變,所以

與舊版本相容。最前面的version版號用於原介面有移除或改變,與舊版不相容

時。


linker name是用於連結時的名稱,是不含版號的soname ,如: libmylib.so。

通常linker name與real name是用ln 指到對應的real name ,用來提供

彈性與維護性。


2.1 編譯

shared library的製作過程較複雜。


$ gcc -c -fPIC hello.c world.c


編譯時要加上-fPIC 用來產生position-independent code。也可以用-fpic

參數。(不太清楚差異,只知道-fPIC 較通用於不同平台,但產生的code較大

,而且編譯速度較慢)。


$ gcc -shared -Wl,-soname,libmylib.so.1 -o libmylib.so.1.0.0 \

hello.o world.o


-shared 表示要編譯成shared library

-Wl 用於參遞參數給linker,因此-soname與libmylib.so.1會被傳給linker處理。

-soname用來指名soname 為limylib.so.1

library會被輸出成libmylib.so.1.0.0 (也就是real name)


若不指定soname 的話,在編譯結連後的執行檔會以連時的library檔名為

soname,並載入他。否則是載入soname指定的library檔案。


可以利用objdump 來看library 的soname。


$ objdump -p libmylib.so | grep SONAME

SONAME libmylib.so.1


若不指名-soname參數的話,則library不會有這個欄位資料。


在編譯後再用ln 來建立soname 與linker name 兩個檔案。

$ ln -s libmylib.so.1.0.0 libmylib.so

$ ln -s libmylib.so.1.0.0 libmylib.so.1

2.2 使用


與使用static library 同。


$ gcc main.c libmylib.so


以上直接指定與libmylib.so 連結。


或用


$ gcc main.c -L. -lmylib


linker會搜尋libmylib.so 來進行連結。


如果目錄下同時有static與shared library的話,會以shared為主。

使用-static 參數可以避免使用shared連結。


$ gcc main.c -static -L. -lmylib


此時可以用ldd 看編譯出的執行檔與shared程式庫的相依性

$ldd a.out

linux-gate.so.1 => (0xffffe000)

[1;33mlibmylib.so.1 => not found[m

libc.so.6 => /lib/libc.so.6 (0xb7dd6000)

/lib/ld-linux.so.2 (0xb7f07000)

輸出結果顯示出該執行檔需要libmylib.so.1 這個shared library。

會顯示not found 因為沒指定該library所在的目錄,所找不到該library。


因為編譯時有指定-soname參數為libmylib.so.1 的關係,所以該執行檔會

載入libmylib.so.1。否則以libmylib.so連結,執行檔則會變成要求載入

libmylib.so


$ ./a.out

/a.out: error while loading shared libraries: libmylib.so.1

cannot open shared object file: No such file or directory


因為找不到libmylib.so.1 所以無法執行程式。

有幾個方式可以處理。


a. 把libmylib.so.1 安裝到系統的library目錄,如/usr/lib下

b. 設定/etc/ld.so.conf ,加入一個新的library搜尋目錄,並執行ldconfig

更新快取

c. 設定LD_LIBRARY_PATH 環境變數來搜尋library

這個例子是加入目前的目錄來搜尋要載作的library

$ LD_LIBRARY_PATH=. ./a.out

Hello world.

3. Dynamically loaded libraries


Dynamicaaly loaded libraries 才是像windows 所用的DLL ,在使用到

時才載入,編譯連結時不需要相關的library。動態載入庫常被用於像plug-ins

的應用。


3.1 使用方式

動態載入是透過一套dl function來處理。

#include

void *dlopen(const char *filename, int flag);

開啟載入filename 指定的library。

void *dlsym(void *handle, const char *symbol);

取得symbol 指定的symbol name在library被載入的記憶體位址。

int dlclose(void *handle);

關閉dlopen開啟的handle。

char *dlerror(void);

傳回最近所發生的錯誤訊息。


____ dltest.c ____

#include

#include

#include

int main() {

void *handle;

void (*f)();

char *error;


/* 開啟之前所撰寫的libmylib.so 程式庫*/

handle = dlopen("./libmylib.so", RTLD_LAZY);

if( !handle ) {

fputs( dlerror(), stderr);

exit(1);

}


/* 取得hello function 的address */

f = dlsym(handle, "hello");

if(( error=dlerror())!=NULL) {

fputs(error, stderr);

exit(1);

}

/* 呼叫該function */

f();

dlclose(handle);

}


編譯時要加上-ldl 參數來與dl library 連結

$ gcc dltest.c -ldl

結果會印出Hello 字串

$ ./a.out

Hello


關於dl的詳細內容請參閱man dlopen


--

參考資料:


Creating a shared and static library with the gnu compiler [gcc]

http://www.adp-gmbh.ch/cpp/gcc/create_lib.html


Program Library HOWTO

http://tldp.org/HOWTO/Program-Library-HOWTO/index.html


APress - Definitive Guide to GCC

沒有留言:

wibiya widget