LINUX下動態(tài)庫及版本號控制

目錄[-]

1. File libhello.c

2. File libhello.h

3. File main.c

前言

針對同一動態(tài)組件的不同版本鏈接和加載卒暂。

一、概念

DLL HELL字面意思是DLL"災(zāi)難"贫奠,是由于com組件(動態(tài)庫)升級引起的程序不能運行的情況痴腌。

原因

有三種可能的原因?qū)е铝薉LL Hell的發(fā)生:

一是由使用舊版本的DLL替代原來一個新版本的DLL而引起的焰坪。這個原因最普遍,是Windows 9X用戶通常遇到的DLL錯誤之一阁猜。

二是由新版DLL中的函數(shù)無意發(fā)生改變而引起丸逸。盡管在設(shè)計DLL時候應(yīng)該向下兼容,然而要保證DLL完全向下兼容卻是不能的剃袍。

三是由新版DLL的安裝引入一個新的Bug黄刚。

二、linux下的解決方案——命名規(guī)范

Linux 上的Dll 民效,叫sharedlibrary憔维。Linux 系統(tǒng)面臨和Window一樣的問題涛救,如何控制動態(tài)庫的多個版本問題。為解決這個問題业扒,Linux 為解決這個問題检吆,引入了一套命名機(jī)制,如果遵守這個機(jī)制來做程储,就可以避免這個問題蹭沛。但是這只事一個約定,不是強(qiáng)制的章鲤。但是建議遵守這個約定摊灭,否則同樣也會出現(xiàn) Linux 版的Dll hell 問題。

Real Name

首先是共享庫本身的文件名:共享庫的命名必須如 libname.so.x.y.z最前面使用前綴”lib”败徊,中間是庫的名字和后綴”.so”,最后三個數(shù)字是版本號帚呼。x是主版本號(Major Version Number),y是次版本號(Minor Version Number)皱蹦,z是發(fā)布版本號(Release Version Number)萝挤。

主版本號(不兼容):重大升級,不同主版本的庫之間的庫是不兼容的根欧。所以如果要保證向后兼容就不能刪除舊的動態(tài)庫的版本。

次版本號(向下兼容): 增量升級端蛆,增加一些新的接口但保留原有接口凤粗。高次版本號的庫向后兼容低次版本號的庫。

發(fā)布版本號(相互兼容):庫的一些諸如錯誤修改今豆、性能改進(jìn)等嫌拣,不添加新接口,也不更改接口呆躲。主版本號和次版本號相同的前提下异逐,不同發(fā)布版本之間完全兼容。

SO-NAME

嚴(yán)格遵守上述規(guī)定插掂,確實能避免動態(tài)庫因為版本沖突的問題灰瞻,但是讀者可能有疑問:在程序加載或運行的時候,動態(tài)鏈接器是如何知道程序依賴哪些庫辅甥,如何選擇庫的不同版本酝润?

Solaris和Linux等采用SO-NAME( Shortfor shared object name )的命名機(jī)制來記錄共享庫的依賴關(guān)系。每個共享庫都有一個對應(yīng)的“SO-NAME”(共享庫文件名去掉次版本號和發(fā)布版本號)璃弄。比如一個共享庫名為libtest.so.3.8.2,那么它的SO-NAME就是libtest.so.3要销。

在Linux系統(tǒng)中,系統(tǒng)會為每個共享庫所在的目錄創(chuàng)建一個跟SO-NAME相同的并且指向它的軟連接(Symbol Link)夏块。這個軟連接會指向目錄中主版本號相同疏咐、次版本號和發(fā)布版本號最新的共享庫纤掸。也就是說,比如目錄中有兩個共享庫版本分別為:/lib/libtest.so.3.8.2和/lib/libtest.so.3.7.5,么軟連接/lib/libtest.so.3指向/lib/libtest.so.3.8.2浑塞。

建立以SO-NAME為名字的軟連接的目的是借跪,使得所有依賴某個共享庫的模塊,在編譯缩举、鏈接和運行時垦梆,都使用共享庫的SO-NAME,而不需要使用詳細(xì)版本號仅孩。在編譯生產(chǎn)ELF文件時候托猩,如果文件A依賴于文件B,那么A的鏈接文件中的”.dynamic”段中會有DT_NEED類型的字段辽慕,字段的值就是B的SO-NAME京腥。這樣當(dāng)動態(tài)鏈接器進(jìn)行共享庫依賴文件查找時,就會依據(jù)系統(tǒng)中各種共享庫目錄中的SO-NAME軟連接自動定向到最新兼容版本的共享庫溅蛉。

★??readelf -d sharelibrary 可以查看so-name

★??Linux提供了一個工具——ldconfig公浪,當(dāng)系統(tǒng)中安裝或更新一個共享庫時,需要運行這個工具船侧,它會遍歷默認(rèn)所有共享庫目錄欠气,比如/lib,/usr/lib等镜撩,然后更新所有的軟鏈接预柒,使她們指向最新共享庫。

Link Name

當(dāng)我們在編譯器里使用共享庫的時候袁梗,如用GCC的“-l”參數(shù)鏈接共享庫libtXXX.so.3.8.1宜鸯,只需要在編譯器命令行指定 -l XXX 即可,省略了前綴和版本信息遮怜。編譯器會根據(jù)當(dāng)前環(huán)境淋袖,在系統(tǒng)中的相關(guān)路徑(往往由-L參數(shù)指定)查找最新版本的XXX庫。這個XXX就是共享庫的“鏈接名”锯梁。不同類型的庫可能有相同的鏈接名即碗,比如C語言運行庫有靜態(tài)版本(libc.a)也動態(tài)版本(libc.so.x.y.z)的區(qū)別,如果在鏈接時使用參數(shù)”-lc”,那么連接器就會根據(jù)輸出文件的情況(動態(tài)/靜態(tài))來選擇合適版本的庫陌凳。eg. ld使用“-static”參數(shù)時嗎拜姿,”-lc”會查找libc.a;如果使用“-Bdynamic”(默認(rèn)),會查找最新版本的libc.so.x.y.z。

更詳細(xì)可以參見

http://www.linuxidc.com/Linux/2012-04/59071.htm


代碼:???????

1. File libhello.c

/* hello.c - demonstrate library use. */

#include

void hello(void)

{ printf("Hello, library world./n");}

2. File libhello.h

/* libhello.h - demonstrate library use. */

void hello(void);

3. File main.c

/* main.c -- demonstrate direct use of the "hello" routine */

#include "hello.h"

int main(void)

{

hello();

return 0;

}

1.生成共享庫冯遂,關(guān)聯(lián)real name 和soname 蕊肥。

???? gcc -g -Wall -fPIC -c hello.c -o hello.o

???? gcc -shared -W,soname,-libhello.so.0 -o libhello.so.0.0.0 hello.o

???? 將會生成共享庫libhello.so.0.0.0.

???? 可以用系統(tǒng)提供的工具查看共享庫的頭:

????? readelf -d libhello.so.0.0.0 | grep libhello

ox00000000000e(SONAME)??? library soname: [libhello.so.0]

2.應(yīng)用程序,引用共享庫。

????? 先手動生成link 名字壁却,以被后面的程序鏈接時用

????? ln -s libhello.so.0.0.0 libhello.so.0

????? gcc -g -Wall -c main.c -o main.o -I.

????? gcc? -o main main.o -lhello -L.

(這里我會出問題批狱。因為:執(zhí)行g(shù)cc? -o main main.o -lhello -L.命令的時候,默認(rèn)會找libhello.so這個文件展东,但是顯然沒有赔硫,直接出錯。我又執(zhí)行:

ln -s libhello.so.0.0.0 libhello.so 之后盐肃,才可以爪膊,到現(xiàn)在,沒明白為什么砸王?)

????? 查看編譯出來的程序:

????? readelf -d main | grep libhello

ox000000000001(NEEDED)??? shared library: [libhello.so.0]

????? 運行該程序推盛,需要指定共享庫的路徑。 有兩種辦法谦铃,第一種使用環(huán)境變量“LD_LIBRARY_PATH”. 兩外一種辦法就是將共享庫拷貝到系統(tǒng)目錄(path 環(huán)境變量指定的其中一個目錄)耘成。

????? 暫停! 我們還沒有解決一個問題是驹闰,程序只知道soname,怎么從soname 找到共享庫瘪菌,即real name 文件呢? 這需要我們定義一個link文件嘹朗,連接到共享庫本身师妙。

????? ln -s libhello.so.0.0.0 libhello.so.0

????? 當(dāng)然這個路徑需要放到LD_LIBRARY_PATH環(huán)境變量中。

???? 這樣就可以運行該程序屹培。

[Note]Linux 系統(tǒng)提供一個命令 ldconifg 專門為生成共享庫的soname 文件疆栏,以便程序在加載時后通過soname

找到共享庫。

同時該命令也為加速加載共享庫惫谤,把系統(tǒng)的共享庫放到一個緩存文件中,這樣可以提高查找速度珠洗×锿幔可以用下面命令看一下系統(tǒng)已有的被緩存起來的共享庫。

???? ld -p

運行程序:

./main這樣许蓖,直接運行是不行了蝴猪。動態(tài)庫必須在運行的時候,也指定路徑膊爪。所以自阱,請將.so文件,復(fù)制到/lib或者/usr/lib下米酬,然后執(zhí)行 ldconfig /lib命令就OK了沛豌。

ldconfig是一個動態(tài)鏈接庫管理命令

為了讓動態(tài)鏈接庫為系統(tǒng)所共享,還需運行動態(tài)鏈接庫的管理命令--ldconfig

ldconfig? 命令的用途,主要是在默認(rèn)搜尋目錄(/lib和/usr/lib)以及動態(tài)庫配置文件/etc/ld.so.conf內(nèi)所列的目錄下,搜索出可共享的動態(tài) 鏈接庫(格式如前介紹,lib*.so*),進(jìn)而創(chuàng)建出動態(tài)裝入程序(ld.so)所需的連接和緩存文件.緩存文件默認(rèn)為 ?/etc/ld.so.cache,此文件保存已排好序的動態(tài)鏈接庫名字列表.

ldconfig通常在系統(tǒng)啟動時運行,而當(dāng)用戶安裝了一個新的動態(tài)鏈接庫時,就需要手工運行這個命令.)

3.共享庫,小版本升級,即接口不變.

?? 當(dāng)升級小版本時加派,共享庫的soname 是不變的叫确,所以需要重新把soname 的那個連接文件指定新版本就可以。 調(diào)用ldconfig命令芍锦,系統(tǒng)會幫你做修改那個soname link文件竹勉,并把它指向新的版本呢。這時候你的應(yīng)用程序就自動升級了娄琉。

4.共享庫次乓,主版本升級,即接口發(fā)生變化孽水。

? 當(dāng)升級主版本時票腰,共享庫的soname 就會加1.比如libhello.so.0.0.0 變?yōu)?libhello.so.1.0.0. 這時候再運行l(wèi)dconfig 文件,就會發(fā)現(xiàn)生成兩個連接 文件匈棘。

??? ln -s libhello.so.0---->libhello.so.0.0.0

??? ln -s libhello.so.1----->libhello.so.1.0.0

盡管共享庫升級丧慈,但是你的程序依舊用的是舊的共享庫,并且兩個之間不會相互影響主卫。

??? 問題是如果更新的共享庫只是增加一些接口逃默,并沒有修改已有的接口,也就是向前兼容簇搅。但是這時候它的主版本號卻增加1.

如果你的應(yīng)用程序想調(diào)用新的共享庫完域,該怎么辦? 簡單瘩将,只要手工把soname 文件修改吟税,使其指向新的版本就可以。(這時候ldconfig

文件不會幫你做這樣的事姿现,因為這時候soname 和real name 的版本號主板本號不一致肠仪,只能手動修改)。

? 比如: ln -s libhello.so.0 ---> libhello.so.1.0.0

? 但是有時候备典,主版本號增加异旧,接口發(fā)生變化,可能向前不兼容提佣。這時候再這樣子修改吮蛹,就會報錯,“xx”方法找不到之類的錯誤拌屏。

總結(jié)一下潮针,Linux 系統(tǒng)是通過共享庫的三個不同名字,來管理共享庫的多個版本倚喂。 real name 就是共享庫的實際文件名字每篷,soname

就是共享庫加載時的用的文件名。在生成共享庫的時候,編譯器將soname 綁定到共享庫的文件頭里雳攘,二者關(guān)聯(lián)起來带兜。

在應(yīng)用程序引用共享庫時,其通過link name

來完成吨灭,link時將按照系統(tǒng)指定的目錄去搜索link名字找到共享庫刚照,并將共享庫的soname寫在應(yīng)用程序的頭文件里。當(dāng)應(yīng)用程序加載共享庫時喧兄,就會

通過soname在系統(tǒng)指定的目錄(path or LD_LIBRARY)去尋找共享庫无畔。

當(dāng)共享庫升級時,分為兩種吠冤。一種是主板本不變浑彰,升級小版本和build 號。在這種情況下拯辙,系統(tǒng)會通過更新soname( ldconfig 來維護(hù))郭变,來使用新的版本號。這中情況下涯保,舊版本就沒有用诉濒,可以刪掉。

另外一種是主版本升級夕春,其意味著庫的接口發(fā)生變化未荒,當(dāng)然,這時候不能覆蓋已有的soname及志。系統(tǒng)通過增加一個soname(ldconfig

-p 里面增加一項)片排,使得新舊版本同時存在。原有的應(yīng)用程序在加載時,還是根據(jù)自己頭文件的舊soname 去尋找老的庫文件速侈。

5.如果編譯的時候沒有指定率寡,共享庫的soname,會怎么樣倚搬?

? 這是一個trick 的地方冶共。第一系統(tǒng)將會在生成庫的時候,就沒有soname放到庫的頭里面潭枣。從而應(yīng)用程序連接時候,就把linkname

放到應(yīng)用程序依賴庫里面幻捏∨枥纾或者換句話說就是,soname這時候不帶版本號篡九。

有時候有人直接利用這點來升級應(yīng)用程序谐岁,比如,新版本的庫,直接拷貝到系統(tǒng)目錄下伊佃,就會覆蓋掉已經(jīng)存在的舊的庫文件窜司,直接升級。

這個給程序員很大程度的便利性航揉,如果一步小心塞祈,就會調(diào)到類似windows的Dll hell 陷阱里面。建議不要這樣做帅涂。

【Note】

? 1. 指定共享庫加載的路徑议薪。LD_LIBRARY_PATH 優(yōu)先于 path 環(huán)境變量。

? 2. ldd 可以查看程序媳友,或者共享庫依賴的庫的路徑

? 3. nm 查看共享庫暴露的接口

? 4. ldconfig 可以自動生成soname 的連接文件斯议。并提供catch 加速查找。

? 5.readelf 可以查看動態(tài)庫的信息醇锚,比如依賴的庫哼御,本身的soname。

? 6. objdump 與readelf 類似焊唬。

? 7 ld The GUN linker

? 8. ld.so? dynamic linker or loader

? 9. as the portable GNU assembley

【Reference】

http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末恋昼,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子求晶,更是在濱河造成了極大的恐慌焰雕,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芳杏,死亡現(xiàn)場離奇詭異矩屁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)爵赵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門吝秕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人空幻,你說我怎么就攤上這事烁峭。” “怎么了秕铛?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵约郁,是天一觀的道長。 經(jīng)常有香客問我但两,道長鬓梅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任谨湘,我火速辦了婚禮绽快,結(jié)果婚禮上芥丧,老公的妹妹穿的比我還像新娘。我一直安慰自己坊罢,他們只是感情好续担,可當(dāng)我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著活孩,像睡著了一般物遇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上诱鞠,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天挎挖,我揣著相機(jī)與錄音,去河邊找鬼航夺。 笑死蕉朵,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的阳掐。 我是一名探鬼主播始衅,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼缭保!你這毒婦竟也來了汛闸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤艺骂,失蹤者是張志新(化名)和其女友劉穎诸老,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钳恕,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡别伏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了忧额。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片厘肮。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖睦番,靈堂內(nèi)的尸體忽然破棺而出类茂,到底是詐尸還是另有隱情,我是刑警寧澤托嚣,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布巩检,位于F島的核電站,受9級特大地震影響示启,放射性物質(zhì)發(fā)生泄漏兢哭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一丑搔、第九天 我趴在偏房一處隱蔽的房頂上張望厦瓢。 院中可真熱鬧,春花似錦啤月、人聲如沸煮仇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽浙垫。三九已至,卻和暖如春郑诺,著一層夾襖步出監(jiān)牢的瞬間夹姥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工辙诞, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留辙售,地道東北人。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓飞涂,卻偏偏與公主長得像旦部,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子较店,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,472評論 2 348

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