linux下動態庫和靜態庫生成

2021-09-28 20:39:18 字數 4304 閱讀 7484

有時候需要把一組**編譯成乙個庫,這個庫在很多專案中都要用到,例如libc就是這樣乙個庫, 我們在不同的程式中都會用到libc中的庫函式(例如printf),也會用到libc中的變數(例如以後 要講到的environ變數)。本文將介紹怎麼建立這樣乙個庫。

這些檔案的目錄結構是:

$ tree

.|-- main.c

`-- stack

|-- is_empty.c

|-- pop.c

|-- push.c

|-- stack.c

`-- stack.h

1 directory, 6 files

我們把stack.c、push.c、pop.c、is_empty.c編譯成目標檔案:

$ gcc -c stack/stack.c stack/push.c stack/pop.c stack/is_empty.c
然後打包成乙個靜態庫libstack.a:

$ ar rs libstack.a stack.o push.o pop.o is_empty.o

ar: creating libstack.a

庫檔名都是以lib開頭的,靜態庫以.a作為字尾,表示archive。ar命令類似於tar命令,起乙個打包的作用,但是把目標檔案打包成靜態庫只能用ar命令而不能用tar命令。選項r表示將後面的檔案列表新增到檔案包,如果檔案包不存在就建立它,如果檔案包中已有同名檔案就替換成新的。s是專用於生成靜態庫的,表示為靜態庫建立索引,這個索引被鏈結器使用。ranlib命令也可以為靜態庫建立索引,以上命令等價於:

$ gcc main.c -l. -lstack -istack -o main
-l選項告訴編譯器去**找需要的庫檔案,-l.表示在當前目錄找。-lstack告訴編譯器要鏈 接libstack庫,-i選項告訴編譯器去**找標頭檔案。注意,即使庫檔案就在當前目錄,編譯器預設 也不會去找的,所以-l.選項不能少。編譯器缺省會找的目錄可以用-print-search-dirs選項檢視。編譯器會在這些搜尋路徑以及-l選項指定的路徑中查詢用-l選項指定的庫,比如-lstack,編譯器會首先找有沒有共享庫libstack.so,如果有就鏈結它,如果沒有就找有沒有靜態庫libstack.a,如果有就鏈結它。所以編譯器是優先考慮共享庫的,如果希望編譯器只鏈結靜態庫,可以指定-static選項。

組成共享庫的目標檔案和一般的目標檔案有所不同,在編譯時要加-fpic選項,例如:

$ gcc -c -fpic stack/stack.c stack/push.c stack/pop.c stack/is_empty.c
-f後面跟一些編譯選項,pic是其中一種,表示生成位置無關**(position independent code)。

現在把main.c和共享庫編譯鏈結在一起,然後執行:

$ gcc main.c -g -l. -lstack -istack -o main

$ ./main

./main: error while loading shared libraries: libstack.so: cannot open shared object file: no such file or directory

結果出乎意料,編譯的時候沒問題,由於指定了-l.選項,編譯器可以在當前目錄下找到libstack.so,而執行時卻說找不到libstack.so。那麼執行時在哪些路徑下找共享庫呢?我們先用ldd命令檢視可執行檔案依賴於哪些共享庫:

$ ldd main

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

libstack.so => not found

libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7dcf000)

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

ldd模擬執行一遍main,在執行過程中做動態鏈結,從而得知這個可執行檔案依賴於哪些共享庫,每個共享庫都在什麼路徑下,載入到程序位址空間的什麼位址。/lib/ld-linux.so.2是動態鏈結器,它的路徑是在編譯鏈結時指定的,gcc在做鏈結時用dynamic-linker指定動態鏈結器的路徑,它也像其它共享庫一樣載入到程序的位址空間中。libc.so.6的路徑/lib/tls/i686/cmov/libc.so.6是由動態鏈結器ld-linux.so.2在做動態鏈結時搜尋到的,而libstack.so的路徑沒有找到。linux-gate.so.1這個共享庫其實並不存在於檔案系統中,它是由核心虛擬出來的共享庫,所以它沒有對應的路徑,它負責處理系統呼叫。總之,共享庫的搜尋路徑由動態鏈結器決定,從ld.so(8)的man page可以查到共享庫路徑的搜尋順序:

首先在環境變數ld_library_path所記錄的路徑中查詢。

然後從快取檔案/etc/ld.so.cache中查詢。這個快取檔案由ldconfig命令讀取配置文 件/etc/ld.so.conf之後生成,稍後詳細解釋。

如果上述步驟都找不到,則到預設的系統路徑中查詢,先是/usr/lib然後是/lib。

先試試第一種方法,在執行main時通過環境變數ld_library_path把當前目錄新增到共享庫的搜尋路徑:

$ ld_library_path=. ./main
這種方法只適合在開發中臨時用一下,通常ld_library_path是不推薦使用的,盡量不要設定這個環境變數,理由可以參考why ld_library_path is bad。

再試試第二種方法,這是最常用的方法。把libstack.so所在目錄的絕對路徑(比如/home/akaedu/somedir)新增到/etc/ld.so.conf中(該檔案中每個路徑佔一行),然後執行ldconfig

$ sudo ldconfig -v
ldconfig命令除了處理/etc/ld.so.conf中配置的目錄之外,還處理一些預設目錄,如/lib、/usr/lib等,處理之後生成/etc/ld.so.cache快取檔案,動態鏈結器就從這個快取中搜尋共享庫。現在再用ldd命令檢視,libstack.so就能找到了:

$ ldd main

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

libstack.so => /home/akaedu/somedir/libstack.so (0xb806a000)

libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7f0c000)

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

第三種方法就是把libstack.so拷到/usr/lib或/lib目錄,這樣可以確保動態鏈結器能找到這個共享庫。

其實還有第四種方法,在編譯可執行檔案main的時候就把libstack.so的路徑寫死在可執行檔案中:

$ gcc main.c -g -l. -lstack -istack -o main -wl,rpath,/home/akaedu/somedir
-wl,-rpath,/home/akaedu/somedir表示-rpath /home/akaedu/somedir是由gcc傳遞給鏈結器的選項。可以看到readelf的結果多了一條rpath記錄:

$ readelf -a main

...dynamic section at offset 0xf10 contains 23 entries:

tag type name/value

0x00000001 (needed) shared library:

[libstack.so]

0x00000001 (needed) shared library: [libc.so.6]

0x0000000f (rpath) library rpath: [/home/akaedu/somedir]

...

$ gcc -o main main.c -g -l. -lstack -istack ./stack/libstack.so

Linux下生成動態庫和靜態庫

最近搞linux的程式設計 寫寫這個 綜合網上例子和本人使用方法。編輯得到舉例的程式 hello.h hello.c和main.c hello.h 見程式1 為該函式庫的標頭檔案。hello.c 見程式2 是函式庫的源程式,其中包含公用函式hello,該函式將在螢幕上輸出 hello main.c ...

linux下檢視動態庫和靜態庫

靜態庫用ar t yourfile 動態庫用 nm d yourfile 下面是ar和nm命令的一些引數說明 1.ar基本用法 ar命令可以用來建立 修改庫,也可以從庫中提出單個模組。庫是一單獨的檔案,裡面包含了按照特定的結構組織起來的其它的一些檔案 稱做此庫檔案的member 原始檔案的內容 模式...

Linux下的動態庫和靜態庫

靜態庫 程式編譯時載入,可執行程式體積大。一般命名為lib x.a。動態庫 程式執行時載入,可執行程式體積小。一般命名為lib x.so.1.3。x一般指庫名,如libxml2 tinyxml等 動態庫一般預設安裝在 lib 或者 usr lib 或者 usr local lib下。如果安裝的庫不在...