https://mbb.eet-china.com/forum/topic/77827_1_1.html
1. 庫(kù)簡(jiǎn)介
庫(kù)有動(dòng)態(tài)與靜態(tài)兩種苛败,動(dòng)態(tài)通常用.so
為后綴水孩,靜態(tài)用.a
為后綴镰矿。例如:libhello.so
,libhello.a
俘种。
為了在同一系統(tǒng)中使用不同版本的庫(kù)秤标,可以在庫(kù)文件名后加上版本號(hào)為后綴,例如: libhello.so.1.0
。由于程序連接默認(rèn)以.so
為文件后綴名安疗。所以為了使用這些庫(kù)抛杨,通常使用建立符號(hào)連接的方式。
ln -s libhello.so.1.0 libhello.so.1
ln -s libhello.so.1 libhello.so
2. 使用庫(kù)
當(dāng)要使用靜態(tài)的程序庫(kù)時(shí)荐类,連接器會(huì)找出程序所需的函數(shù)怖现,然后將它們拷貝到執(zhí)行文件,由于這種拷貝是完整的,所以一旦連接成功屈嗤,靜態(tài)程序庫(kù)也就不再需要了潘拨。然 而,對(duì)動(dòng)態(tài)庫(kù)而言饶号,就不是這樣铁追。動(dòng)態(tài)庫(kù)會(huì)在執(zhí)行程序內(nèi)留下一個(gè)標(biāo)記‘指明當(dāng)程序執(zhí)行時(shí),首先必須載入這個(gè)庫(kù)茫船。由于動(dòng)態(tài)庫(kù)節(jié)省空間琅束,Linux下進(jìn)行連接的 缺省操作是首先連接動(dòng)態(tài)庫(kù),也就是說(shuō)算谈,如果同時(shí)存在靜態(tài)和動(dòng)態(tài)庫(kù)涩禀,不特別指定的話,將與動(dòng)態(tài)庫(kù)相連接然眼。
現(xiàn)在假設(shè)有一個(gè)叫hello
的程序開(kāi)發(fā)包艾船,它提供一個(gè)靜態(tài)庫(kù)libhello.a
,一個(gè)動(dòng)態(tài)庫(kù)libhello.so
高每,一個(gè)頭文件hello.h
屿岂。頭文件中提供sayhello()
這個(gè)函數(shù)。
/* hello.h */
void sayhello();
另外還有一些說(shuō)明文檔鲸匿。這是一個(gè)典型的程序開(kāi)發(fā)包結(jié)構(gòu)爷怀。
2.1 與動(dòng)態(tài)庫(kù)連接
Linux默認(rèn)的就是與動(dòng)態(tài)庫(kù)連接,下面這段程序testlib.c
使用hello
庫(kù)中的sayhello()
函數(shù)带欢。
/*testlib.c*/
#include
#include
int main()
{
sayhello();
return 0;
}
使用如下命令進(jìn)行編譯
$gcc -c testlib.c -o testlib.o
用如下命令連接:
$gcc testlib.o -lhello -o testlib
在連接時(shí)要注意霉撵,假設(shè)libhello.so
和libhello.a
都在缺省的庫(kù)搜索路徑下/usr/lib
下,如果在其它位置要加上-L
參數(shù)洪囤。
2.2 與靜態(tài)庫(kù)連接
與與靜態(tài)庫(kù)連接麻煩一些,主要是參數(shù)問(wèn)題撕氧。還是上面的例子:
$gcc testlib.o -o testlib -WI,-Bstatic -lhello
注:這個(gè)特別的-WI, -Bstatic
參數(shù)瘤缩,實(shí)際上是傳給了連接器ld
,指示它與靜態(tài)庫(kù)連接伦泥。如果系統(tǒng)中只有靜態(tài)庫(kù)當(dāng)然就不需要這個(gè)參數(shù)了剥啤。
如果要和多個(gè)庫(kù)相連接,而每個(gè)庫(kù)的連接方式不一樣不脯,比如上面的程序既要和libhello
進(jìn)行靜態(tài)連接府怯,又要和libbye
進(jìn)行動(dòng)態(tài)連接,其命令應(yīng)為:
$gcc testlib.o -o testlib -WI,-Bstatic -lhello -WI,-Bdynamic -lbye
3. 動(dòng)態(tài)庫(kù)的路徑問(wèn)題
為了讓執(zhí)行程序順利找到動(dòng)態(tài)庫(kù)防楷,有三種方法:
把庫(kù)拷貝到
/usr/lib
和/lib
目錄下牺丙。-
在
LD_LIBRARY_PATH
環(huán)境變量中加上庫(kù)所在路徑。例如動(dòng)態(tài)庫(kù)libhello.so
在/home/ting/lib
目錄下,以bash
為例冲簿,使用命令:\$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ting/lib
修改
/etc/ld.so.conf
文件粟判,把庫(kù)所在的路徑加到文件末尾,并執(zhí)行ldconfig
刷新峦剔。這樣档礁,加入的目錄下的所有庫(kù)文件都可見(jiàn)
4. 查看庫(kù)中的符號(hào)
有時(shí)候可能需要查看一個(gè)庫(kù)中到底有哪些函數(shù),nm
命令可以打印出庫(kù)中的涉及到的所有符號(hào)吝沫。庫(kù)既可以是靜態(tài)的也可以是動(dòng)態(tài)的呻澜。nm
列出的符號(hào)有很多,常見(jiàn)的有 三種惨险,一種是在庫(kù)中被調(diào)用羹幸,但并沒(méi)有在庫(kù)中定義(表明需要其他庫(kù)支持),用U
表示平道;一種是庫(kù)中定義的函數(shù)睹欲,用T
表示,這是最常見(jiàn)的一屋;另外一種是所謂的“弱態(tài)”符號(hào)窘疮,它們雖然在庫(kù)中被定義,但是可能被其他庫(kù)中的同名符號(hào)覆蓋冀墨,用W
表示闸衫。例如,假設(shè)開(kāi)發(fā)者希望知道上央提到的hello
庫(kù)中是否定義了printf()
:
$nm libhello.so |grep printf
U printf
U
表示符號(hào)printf
被引用诽嘉,但是并沒(méi)有在函數(shù)內(nèi)定義蔚出,由此可以推斷,要正常使用hello
庫(kù)虫腋,必須有其它庫(kù)支持骄酗,再使用ldd
命令查看hello
依賴(lài)于哪些庫(kù):
$ldd hello
libc.so.6=>/lib/libc.so.6(0x400la000)
/lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000)
從上面的結(jié)果可以繼續(xù)查看printf
最終在哪里被定義,有興趣可以go on
5. 生成庫(kù)
第一步要把源代碼編繹成目標(biāo)代碼悦冀。以下面的代碼為例趋翻,生成上面用到的hello
庫(kù):
/* hello.c */
#include
void sayhello()
{
printf("hello,world\n");
}
用gcc
編繹該文件,在編繹時(shí)可以使用任何全法的編繹參數(shù)盒蟆,例如-g
加入調(diào)試代碼等:
gcc -c hello.c -o hello.o
5.1 連接成靜態(tài)庫(kù)
連接成靜態(tài)庫(kù)使用ar
命令踏烙,其實(shí)ar
是archive
的意思
$ar cqs libhello.a hello.o
5.2 連接成動(dòng)態(tài)庫(kù)
生成動(dòng)態(tài)庫(kù)用gcc
來(lái)完成,由于可能存在多個(gè)版本历等,因此通常指定版本號(hào):
$gcc -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0 hello.o
另外再建立兩個(gè)符號(hào)連接:
$ln -s libhello.so.1.0 libhello.so.1
$ln -s libhello.so.1 libhello.so
這樣一個(gè)libhello
的動(dòng)態(tài)連接庫(kù)就生成了讨惩。最重要的是傳gcc -shared
參數(shù)使其生成是動(dòng)態(tài)庫(kù)而不是普通執(zhí)行程序。
-Wl
表示后面的參數(shù)也就是-soname
寒屯,libhello.so.1
直接傳給連接器ld
進(jìn)行處理荐捻。實(shí)際上,每一個(gè)庫(kù)都有一個(gè)soname
,當(dāng)連接器發(fā)現(xiàn)它正 在查找的程序庫(kù)中有這樣一個(gè)名稱(chēng)靴患,連接器便會(huì)將soname
嵌入連結(jié)中的二進(jìn)制文件內(nèi)仍侥,而不是它正在運(yùn)行的實(shí)際文件名,在程序執(zhí)行期間鸳君,程序會(huì)查找擁有soname
名字的文件农渊,而不是庫(kù)的文件名,換句話說(shuō)或颊,soname
是庫(kù)的區(qū)分標(biāo)志砸紊。
這樣做的目的主要是允許系統(tǒng)中多個(gè)版本的庫(kù)文件共存,習(xí)慣上在命名庫(kù)文件的時(shí)候通常與soname
相同:
libxxxx.so.major.minor
其中囱挑,xxxx
是庫(kù)的名字醉顽,major
是主版本號(hào),minor
是次版本號(hào) 平挑。