2021年3月30日 星期二

【Linux】gcc 編譯 Static、Shared Library與Dynamically loaded libraries

Static libraries

把一堆object檔用ar(archiver)包裝集合起來,檔名以".a" 結尾。

優點

執行效能通常會比shared libraries與dynamically loaded快,

不易發生執行時找不到library或版本錯置而無法執行的問題。


缺點

檔案較大,維護度較低;例如library如果發現bug需要更新,那麼就必須重新連結執行檔。


範例

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

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

如果想要配合gcc 的"-l" 參數來連結,一定要以"lib" 開頭,中間是自定的library名稱,然後緊接著".a" 結尾。

gcc main.c libmylib.a 

或 

gcc main.c -L. -lmylib

參數"-L"用來指定要搜尋程式庫的目錄,`.’ 表示搜尋現在所在的目錄,通常預設會搜/usr/lib 或/lib 等目錄。


Shared library

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

soname 用來表示一個特定library 的名稱,像是libmylib.so.1 。以"lib" 開頭,接著是該library 的名稱,然後是".so",接著是版號。

real name 是實際放有library程式的檔案名稱,後面會再加上minor 版號與release 版號,像是libmylib.so.1.0.0 。

linker name是用於連結時的名稱,是不含版號的soname ,如: libmylib.so。通常linker name與real name是用ln 指到對應的real name ,用來提供彈性與維護性。


範例

gcc -c -fPIC hello.c world.c

加上-fPIC 用來產生position-independent 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)


可以用objdump 來看library 的soname。

objdump -p libmylib.so | grep SONAME


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


ln -s libmylib.so.1.0.0 libmylib.so

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


gcc main.c 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


若執行時找不到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


Dynamically loaded libraries

Dynamicaaly loaded libraries 才是像windows 所用的DLL ,在使用到時才載入,編譯連結時不需要相關的library。動態載入庫常被用於像plug-ins的應用。

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

範例

#include <dlfcn.h>

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); //傳回最近所發生的錯誤訊息。


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

gcc dltest.c -ldl


____ dltest.c ____

#include <stdio.h>

#include <stdlib.h>

#include <dlfcn.h>


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);

}

沒有留言:

張貼留言