Linux靜態(tài)庫與動態(tài)庫

一也切、簡介

實(shí)際開發(fā)工程中划滋,一般會有很多函數(shù)只是聲明饵筑,而找不到實(shí)現(xiàn)的代碼,因?yàn)槟切?shí)現(xiàn)代碼已經(jīng)編譯成庫了处坪。在Linux系統(tǒng)中.o根资、.a、*.so文件都是Linux下的程序函數(shù)庫同窘,即編譯好的可供其他程序使用的代碼和數(shù)據(jù)玄帕,一般來講:

  • ==.o==:是目標(biāo)文件,相當(dāng)于windows中的.obj文件(動態(tài)加載函數(shù)庫)想邦;
  • ==.so==:為共享庫裤纹,是shared object,用于動態(tài)鏈接的丧没,和dill差不多(共享函數(shù)庫)鹰椒;
  • ==.a==:為靜態(tài)庫,是好多個.o合在一起呕童,用于靜態(tài)鏈接(靜態(tài)函數(shù)庫)漆际;
  • ==.la==:為libtool自動生成的一些共享庫,vi編輯查看拉庵,主要記錄了一些配置信息灿椅;

這樣做有以下優(yōu)點(diǎn):程序模塊化,容易重新編譯钞支,方便升級茫蛹。

庫文件在鏈接(靜態(tài)庫和共享庫)和運(yùn)行(僅限于使用共享庫的程序)時被使用,其搜索路勁是系統(tǒng)中進(jìn)行設(shè)置的烁挟,一般Linux把/lib和/usr/lib兩個目錄作為默認(rèn)的庫搜索路徑婴洼,所以使用這兩個目錄中的庫時,不需要進(jìn)行設(shè)置搜索路徑即可直接使用撼嗓。

假設(shè)當(dāng)前目錄下有這些源文件:main.c func.c func.h柬采,其中main.c要調(diào)用func.c中的函數(shù)。以下分別是Linux下將func.c生成靜態(tài)庫和動態(tài)庫的方法且警,以及如何使用生成的靜態(tài)庫和動態(tài)庫粉捻。

二、靜態(tài)鏈接庫

2.1 靜態(tài)庫的特點(diǎn)

靜態(tài)鏈接庫可以將代碼進(jìn)行封裝斑芜,具有如下特點(diǎn):

  1. 靜態(tài)函數(shù)庫實(shí)際上是簡單的普通目標(biāo)文件(*.o)的集合肩刃;
  2. 靜態(tài)函數(shù)庫在可執(zhí)行程序鏈接時就加入到可執(zhí)行代碼中,在物理上成為可執(zhí)行程序的一部分;
  3. 靜態(tài)函數(shù)庫鏈接生成的程序盈包,在哪里都可以運(yùn)行沸呐,無需該靜態(tài)函數(shù)庫的支持;
  4. 相對于動態(tài)函數(shù)庫鏈接生成的程序呢燥,靜態(tài)函數(shù)庫鏈接生成的可執(zhí)行程序文件較大崭添;

2.2 靜態(tài)庫的優(yōu)點(diǎn)

相比源代碼來說,靜態(tài)鏈接庫有如下特點(diǎn):

  1. 可以重用以前的程序模塊叛氨;
  2. 開發(fā)者可以對源代碼保密呼渣;
  3. 允許程序員不用重新編譯代碼而直接把程序link起來,節(jié)省了重新編譯代碼的時間(該優(yōu)勢目前已不明顯)力试;
  4. 理論上將徙邻,使用elf格式的靜態(tài)庫函數(shù)生成的代碼可以比使用共享函數(shù)庫生成的程序運(yùn)行速度快。

2.3 靜態(tài)庫的制作

【命令格式】:

在linux環(huán)境中畸裳,使用ar命令創(chuàng)建靜態(tài)庫文件(創(chuàng)建嵌入式靜態(tài)庫缰犁,可使用arm-linux-ar命令),該命令格式如下:

ar [-]{dmpqrtx}[abcfilNoPsSuvV] [membername] [count] archive files…
arm-linux-ar [-]{dmpqrtx}[abcfilNoPsSuvV] [membername] [count] archive files…
  • ==archive==:定義庫的名稱怖糊;
  • ==files== :是庫文件中包含的目標(biāo)文件的清單帅容,用空格分隔每個文件;

【命令選項(xiàng)】:

  • -a :把新的目標(biāo)文件(*.o)添加到靜態(tài)庫文件中現(xiàn)有文件之后;
  • -b :把新的目標(biāo)文件(*.o)添加到靜態(tài)庫文件中現(xiàn)有文件之前伍伤;
  • -d :從指定的靜態(tài)庫文件中刪除文件并徘;
  • -m:把文件移動到指定的靜態(tài)庫文件中;
  • -p :把靜態(tài)庫文件中指定的文件輸出到標(biāo)準(zhǔn)輸出扰魂;
  • -q :快速地把文件追加到靜態(tài)庫文件中麦乞;
  • -r :把文件插入到靜態(tài)庫文件中;
  • -t :顯示靜態(tài)庫文件中文件的列表劝评;
  • -x :從靜態(tài)庫文件中提取文件姐直;
  • -v :使用詳細(xì)模式

如下面創(chuàng)建了一個libapue.a庫文件,然后可以用t選項(xiàng)顯示包含在庫中的文件:

$ ar –r libapue.a error.o errorlog.o lockreg.o

++創(chuàng)建庫文件之后蒋畜,可以創(chuàng)建這個靜態(tài)庫文件的索引來幫助提高和庫連接的其他程序的編譯速度声畏。使用ranlib程序創(chuàng)建庫的索引,索引存放在庫文件的內(nèi)部姻成。++

$ ranlib libapue.a

++用nm程序可以顯示存檔文件的索引插龄,它也可以顯示目標(biāo)文件的符號:++

$ nm libapue.a | more
$ nm error.o | more

2.4 靜態(tài)庫的使用

靜態(tài)庫的使用方法,可以采用如下三種中的一種(-static可以不用):

$ gcc test.c –o test libapue.a
$ gcc test.c –o –L. -lfunc –static
$ gcc test.c –o –L./ -lfunc -static

【使用說明】

  1. 使用靜態(tài)庫生成的可執(zhí)行文件放在目標(biāo)板中可以直接運(yùn)行科展;
  2. 使用靜態(tài)庫時均牢,需用-L指定靜態(tài)庫的位置,如上面指定為當(dāng)前目錄:-L./才睹;
  3. 使用靜態(tài)庫時膨处,需用-l指定靜態(tài)庫的庫名见秤,如上面的-lfunc砂竖。

【完整使用實(shí)例】:

$ gcc func.c –c –o func.o               //生成目標(biāo)文件
$ ar crv libfunc.a func.o                //生成靜態(tài)庫
$ gcc main.c –static –L./ –lfunc –o main   //使用靜態(tài)庫
$ ./main                            //執(zhí)行文件

三真椿、動態(tài)鏈接庫

3.1 動態(tài)庫的簡介

動態(tài)庫是在可執(zhí)行程序啟動時加載到執(zhí)行程序中,可以被多個執(zhí)行程序共享使用乎澄,使用動態(tài)庫編譯生成的程序相對較小突硝,但運(yùn)行時需要庫文件支持;

動態(tài)庫的鏈接時路徑運(yùn)行時路徑:現(xiàn)代鏈接器在處理動態(tài)庫時將鏈接時路徑(Link-time path)和運(yùn)行時路徑(Run-time path)分開置济,用戶可通過-L指定鏈接時解恰,庫文件的路徑;通過-R(或-rpath)指定程序運(yùn)行時浙于,庫文件的路徑护盈,大大提高了庫應(yīng)用的靈活性。比如我們做嵌入式移植時#arm-linux-gcc $(CFLAGS) –o target –L/work/lib/zlib/ -llibz-1.2.3 (work/lib/zlib下是交叉編譯好的zlib庫)羞酗,將target編譯好后我們只要把zlib庫拷貝到開發(fā)板的系統(tǒng)默認(rèn)路徑下即可腐宋。或者通過- rpath(或-R )檀轨、LD_LIBRARY_PATH指定查找路徑胸竞。

3.2 動態(tài)庫的命名

每個動態(tài)庫(*.so)文件都有個特殊的名字,叫做“soname”参萄,它必須以“l(fā)ib”作為前綴卫枝,然后是函數(shù)庫名,然后是“.so”讹挎,最后是版本號信息校赤;不過有個特例,就是非常底層的C庫函數(shù)都不是以lib開頭命名的筒溃;每個動態(tài)庫(*.so)文件都有一個真正的名字马篮,叫做“real name”,它是包含真正庫函數(shù)代碼的文件铡羡;真名有一個主版本號和一個發(fā)行版本號外积蔚,還有一個名字是編譯器編譯的時候需要的函數(shù)庫的名字,這個就是簡單的soname名字烦周,它不包含任何版本號信息:

  • soname:必須的格式:lib+函數(shù)庫名+.so+版本號信息尽爆,如libreadline.so.3
  • real name:也就是真正的名字,有主版本號和發(fā)行版本號读慎;
  • 編譯名字:編譯器編譯的時候需要的函數(shù)庫的名字就是不包含版本號信息的soname漱贱,如上面的libreadlie.so.3去掉后面的“.3”即可。

管理共享函數(shù)庫的關(guān)鍵是區(qū)分好這些名字夭委,++當(dāng)可執(zhí)行程序需要在自己的程序中列出這些他們需要的共享庫函數(shù)的時候幅狮,它只要用soname就可以了;反過來,當(dāng)要創(chuàng)建一個新的共享函數(shù)庫的時候崇摄,只要指定一個特定的文件名擎值,其中包含很細(xì)節(jié)的版本信息;當(dāng)安裝一個新版本的函數(shù)庫的時候逐抑,只要先將這些函數(shù)庫文件拷貝到一些特定的目錄中鸠儿,運(yùn)行l(wèi)dconfig這個命令即可++(ldconfig檢查已存在的庫文件,然后創(chuàng)建soname的符號鏈接到真正的函數(shù)庫厕氨,同時設(shè)置/etc/ld.so.cache這個緩沖文件)进每。

3.3 動態(tài)庫的制作

由于動態(tài)鏈接庫的共享特性,它們不會被拷貝到可執(zhí)行文件中命斧。在編譯的時候田晚,編譯器只會做一些函數(shù)名之類的檢查,在程序運(yùn)行的時候国葬,被調(diào)用的動態(tài)鏈接庫函數(shù)被安置在內(nèi)存的某個地方贤徒,所有調(diào)用它的程序?qū)⒅赶蜻@個代碼段。因此胃惜,這些代碼必須使用相對地址泞莉,而不是絕對地址。那么在編譯的時候船殉,我們需要使用地址無關(guān)選項(xiàng)(Position Independent Code:PIC)“-fpic”來告訴編譯器鲫趁,這些對象文件是用來做動態(tài)鏈接庫的。創(chuàng)建共享庫的方法:

$ gcc -fpic error.c -c -o error.o
$ gcc -fpic errorlog.c -c -o errorlog.o
$ gcc -fpic lockreg.c -c -o lockreg.o
$ gcc -shared -o libapue.so error.o errorlog.o lockreg.o

也可以使用如下方法:

$ gcc -shared -fpic error.c errorlog.c lockreg.c -o libapue.so
  • -shared選項(xiàng)是告訴編譯器這是要建立動態(tài)鏈接庫利虫;這與靜態(tài)鏈接庫的建立很不一樣挨厚,這里使用的gcc命令而不是ar,同時庫的后綴名為*.so糠惫;
  • -fpic選項(xiàng)告訴編譯器該代碼為位置無關(guān)碼疫剃,不用此選項(xiàng)的話編譯后的代碼是位置相關(guān)的,所以動態(tài)載入時是通過代碼拷貝的方式來滿足不同進(jìn)程的需要硼讽,而不能達(dá)到真正代碼段共享的目的巢价;

編譯共享庫的方法(假設(shè)庫文件在當(dāng)前目錄),可采用如下三種中的一種:

$ gcc test.c -o test -L. -lapue
$ gcc test.c -o test -L./ -lapue
$ gcc test.c -o test libapue.so

這樣就編譯出了不包含函數(shù)代碼的可執(zhí)行文件了,但是當(dāng)你運(yùn)行生成的可執(zhí)行文件時,動態(tài)加載器無法動態(tài)加載libapue.so文件砸紊。那如何才能讓動態(tài)加載器發(fā)現(xiàn)庫文件呢?有如下兩種方法:

  • 在環(huán)境變量LD_LIBRARY_PATH中指明庫的搜索路徑碉克;
export LD_LIBRARY_PATH=”$(LD_LIBRARY_PATH):.”  #添加當(dāng)前路勁
  • 配置/etc/ld.so.conf文件(推薦且簡單的方法):一般應(yīng)用程序的庫文件不與系統(tǒng)庫文件放在同一個目錄下,一般把應(yīng)用程序的共享庫文件放在/usr/local/lib下并齐,新建一個屬于自己的目錄apue漏麦,然后把自己的libapue.so復(fù)制過去就行了客税;同時在/etc/ld.so.conf中新增一行:
/usr/local/lib/apue

以后在編譯程序時加上編譯選項(xiàng):-L/usr/local/lib/apue –lapue;針對具體的可執(zhí)行文件撕贞,可以通過ldd命令查看該可執(zhí)行文件依賴什么共享庫:

ldd test

3.4 動態(tài)庫的使用

【使用說明】

  1. 使用動態(tài)庫生成的可執(zhí)行文件更耻,需將生成的動態(tài)庫也拷貝到目標(biāo)板的連接文件目錄(/lib或/usr/lib)中,才可以順利執(zhí)行麻掸。如果將生成的libfunc.so.1.0.0拷貝到系統(tǒng)lib目錄(如/usr/lib)酥夭,則倒數(shù)第二步就不需要了。
  2. 使用動態(tài)庫鏈接生成可執(zhí)行文件時脊奋,需要用-L指定動態(tài)庫的位置,如上面指定為當(dāng)前目錄:-L./疙描。

【使用實(shí)例】

$ gcc –fPIC func.c -c –o func.o          
$ gcc –shared –o libfunc.so.1.0.0 func.o
$ ln –s libfunc.so.1.0.0 libfunc.so
$ gcc main.c -o main -lfunc -L./
$ export LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH
$ ./main

四诚隙、程序編譯時和運(yùn)行時庫文件路徑的指定方法

如何指定程序編譯和運(yùn)行時使用的靜態(tài)庫和動態(tài)庫呢?在連接時起胰,可以通過-L選項(xiàng)來指定編譯時所用到的靜態(tài)庫和動態(tài)庫文件路徑久又。在程序運(yùn)行時,可以通過配置/etc/ld.so.conf文件或者使用環(huán)境變量LD_LIBRARY_PATH來配置效五。

4.1 配置/etc/ld.so.conf文件

一般應(yīng)用程序的庫文件不與系統(tǒng)庫文件放在同一目錄下地消,一般把應(yīng)用程序的共享庫文件放在/usr/local/lib下,新建一個屬于自己的目錄apue畏妖,然后把剛才生成的libapue.so復(fù)制過去脉执,同時在/etc/ld.so.conf中添加庫文件的路勁,一行一個戒劫,如:

/usr/local/lib/apue
/usr/X11R6/lib
/opt/lib

也可以在/etc/ld.so.conf.d目錄下創(chuàng)建一個.conf文件半夷,將搜索路徑一行一個的加入該文件中。以后在編譯程序時迅细,加上如下編譯選項(xiàng)巫橄,就可以使用共享庫了。

-L/usr/local/lib/apue –lapue

但需要注意的是:更改/etc/ld.so.conf的設(shè)置方法對于程序連接時的庫(包括共享庫和靜態(tài)庫)的定位已經(jīng)足夠了茵典,但是對于使用了共享庫的程序的執(zhí)行還是不夠的湘换。這是因?yàn)闉榱思涌斐绦驁?zhí)行時對共享庫的定位速度,避免使用搜索路勁查找共享庫的低效率统阿,動態(tài)加載器是直接讀取庫列表文件/etc/ld.so.cache來進(jìn)行搜索的彩倚。Ld.so.cache是一個非文本的數(shù)據(jù)文件,不能直接編輯砂吞,它是根據(jù)/etc/ld.so.conf中設(shè)置的路勁由/sbin/ldconfig命令將這些搜索路徑下的共享庫文件集中在一起而生成的(ldconfig需要root權(quán)限才能執(zhí)行)署恍。

因此,為了保證程序執(zhí)行時對庫的定位蜻直,在/etc/ld.so.conf中進(jìn)行了庫文件搜索路徑設(shè)置后盯质,還需要運(yùn)行/sbin/ldconfig命令更新/etc/ld.so.cache文件袁串。

4.2 修改環(huán)境變量LD_LIBRARY_PATH

方法一需要有root權(quán)限才能設(shè)置,而且當(dāng)系統(tǒng)重新啟動后呼巷,所有的基于GTK2的程序在運(yùn)行時都將使用新安裝的GTK+庫囱修。不幸的是,由于GTK+版本的改變王悍,這有時會給應(yīng)用程序帶來兼容性的問題破镰,造成程序運(yùn)行不正常。為避免出現(xiàn)這種情況压储,可采用在環(huán)境變量LD_LIBRARY_PATH中指明庫的搜索路徑的方法鲜漩,它不需要root權(quán)限,設(shè)置也簡單集惋。

設(shè)置方法如下:

$ export LD_LIBRARY_PATH=/opt/gtk/lib:$LD_LIBRARY_PATH

查看LD_LIBRARY_PATH的內(nèi)容的方法為:

$ echo $LD_LIBRARY_PATH

注意:LD_LIBRARY_PATH的設(shè)定是全局的孕似,過多的使用可能會影響到其他應(yīng)用程序的運(yùn)行,所以多用在調(diào)試中刮刑。

4.3 鏈接時通過-L選型顯示指定路徑

在程序鏈接時喉祭,對于庫文件(靜態(tài)庫和動態(tài)庫)的搜索路徑,可以通過-L參數(shù)顯示指定庫文件路徑雷绢。因?yàn)?L設(shè)置的路徑將被優(yōu)先搜索泛烙,所以在鏈接的時候通常會以這種方式直接指定要鏈接的庫的路徑。如:

$ gcc main.c -o main -lfunc -L./
$ gcc test.c –o test –L./ -lapue

五翘紊、常見問題及解決方法

調(diào)用動態(tài)庫的時候蔽氨,有時明明已經(jīng)將庫的頭文件在目錄通過“-I”include進(jìn)來了,庫所在文件通過“-L”參數(shù)引導(dǎo)霞溪,并指定了“-l”的庫名孵滞,但是通過ldd命令查看是,就是死活找不到指定鏈接的so庫文件鸯匹,這時可通過修改LD_LIBRARY_PATH或者/etc/ld.so.conf文件來指定動態(tài)庫的目錄坊饶。通常這樣做就可以解決庫無法鏈接的問題了。

makefile里面怎么正確的編譯和連接生成的.so庫文件殴蓬,然后又是在其他程序的makefile里面如何編譯和鏈接才能調(diào)用這個庫文件和函數(shù)匿级?

  1. 通過export命令設(shè)置環(huán)境變量將庫的路徑添加到庫目錄中,這種方法不太方便:
export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH:$(pwd))染厅;
  1. LD_LIBRARY_PATH可以在/etc/profile痘绎,~/.profile或者./bash_profile里設(shè)置,或者.bashrc里肖粮,改完后運(yùn)行source /etc/profile或./etc/profile孤页;
  2. 將新的路徑添加進(jìn)/etc/ld.so.conf,然后執(zhí)行/sbin/ldconfig涩馆。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末行施,一起剝皮案震驚了整個濱河市允坚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蛾号,老刑警劉巖稠项,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鲜结,居然都是意外死亡展运,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門精刷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拗胜,“玉大人,你說我怎么就攤上這事贬养〖吠粒” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵误算,是天一觀的道長。 經(jīng)常有香客問我迷殿,道長儿礼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任庆寺,我火速辦了婚禮蚊夫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘懦尝。我一直安慰自己知纷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布陵霉。 她就那樣靜靜地躺著琅轧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪踊挠。 梳的紋絲不亂的頭發(fā)上乍桂,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機(jī)與錄音效床,去河邊找鬼睹酌。 笑死,一個胖子當(dāng)著我的面吹牛剩檀,可吹牛的內(nèi)容都是我干的憋沿。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼沪猴,長吁一口氣:“原來是場噩夢啊……” “哼辐啄!你這毒婦竟也來了采章?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤则披,失蹤者是張志新(化名)和其女友劉穎共缕,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體士复,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡图谷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了阱洪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片便贵。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖冗荸,靈堂內(nèi)的尸體忽然破棺而出承璃,到底是詐尸還是另有隱情,我是刑警寧澤蚌本,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布盔粹,位于F島的核電站,受9級特大地震影響程癌,放射性物質(zhì)發(fā)生泄漏舷嗡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一嵌莉、第九天 我趴在偏房一處隱蔽的房頂上張望进萄。 院中可真熱鬧,春花似錦锐峭、人聲如沸中鼠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽援雇。三九已至,卻和暖如春抛寝,著一層夾襖步出監(jiān)牢的瞬間熊杨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工盗舰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留晶府,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓钻趋,卻偏偏與公主長得像川陆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蛮位,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容