2021-06-16 linux鏈接so

linux 下有動態(tài)庫和靜態(tài)庫,動態(tài)庫以.so為擴(kuò)展名,靜態(tài)庫以.a為擴(kuò)展名息拜。二者都使用廣泛溉潭。本文主要講動態(tài)庫方面知識净响。

基本上每一個linux 程序都至少會有一個動態(tài)庫,查看某個程序使用了那些動態(tài)庫喳瓣,使用ldd命令查看

#?ldd?/bin/ls

linux-vdso.so.1?=>?(0x00007fff597ff000)

libselinux.so.1?=>?/lib64/libselinux.so.1?(0x00000036c2e00000)

librt.so.1?=>?/lib64/librt.so.1?(0x00000036c2200000)

libcap.so.2?=>?/lib64/libcap.so.2?(0x00000036c4a00000)

libacl.so.1?=>?/lib64/libacl.so.1?(0x00000036d0600000)

libc.so.6?=>?/lib64/libc.so.6?(0x00000036c1200000)

libdl.so.2?=>?/lib64/libdl.so.2?(0x00000036c1600000)

/lib64/ld-linux-x86-64.so.2?(0x00000036c0e00000)

libpthread.so.0?=>?/lib64/libpthread.so.0?(0x00000036c1a00000)

libattr.so.1?=>?/lib64/libattr.so.1?(0x00000036cf600000)

? ?這么多so馋贤,是的。使用ldd顯示的so畏陕,并不是所有so都是需要使用的配乓,下面舉個例子

main.cpp

#include?<stdio.h>

#include?<iostream>

#include?<string>

using namespace std;

int?main?()

{

cout?<<?"test"?<<?endl;

return 0;

}

使用缺省參數(shù)編譯結(jié)果

# g++?-o demo main.cpp

# ldd demo

linux-vdso.so.1?=>?(0x00007fffcd1ff000)

libstdc++.so.6?=>?/usr/lib64/libstdc++.so.6?(0x00007f4d02f69000)

libm.so.6?=>?/lib64/libm.so.6?(0x00000036c1e00000)

libgcc_s.so.1?=>?/lib64/libgcc_s.so.1?(0x00000036c7e00000)

libc.so.6?=>?/lib64/libc.so.6?(0x00000036c1200000)

/lib64/ld-linux-x86-64.so.2?(0x00000036c0e00000)

如果我鏈接一些so,但是程序并不用到這些so惠毁,又是什么情況呢犹芹,下面我加入鏈接壓縮庫,數(shù)學(xué)庫鞠绰,線程庫

# g++?-o demo?-lz?-lm?-lrt main.cpp

# ldd demo

linux-vdso.so.1?=>?(0x00007fff0f7fc000)

libz.so.1 => /lib64/libz.so.1 (0x00000036c2600000)

librt.so.1 => /lib64/librt.so.1 (0x00000036c2200000)

libstdc++.so.6?=>?/usr/lib64/libstdc++.so.6?(0x00007ff6ab70d000)

libm.so.6 => /lib64/libm.so.6 (0x00000036c1e00000)

libgcc_s.so.1?=>?/lib64/libgcc_s.so.1?(0x00000036c7e00000)

libc.so.6?=>?/lib64/libc.so.6?(0x00000036c1200000)

libpthread.so.0?=>?/lib64/libpthread.so.0?(0x00000036c1a00000)

/lib64/ld-linux-x86-64.so.2?(0x00000036c0e00000)

? 看看腰埂,雖然沒有用到,但是一樣有鏈接進(jìn)來蜈膨,那看看程序啟動時候有沒有去加載它們呢

# strace?./demo

execve("./demo",?["./demo"],?[/*?30 vars?*/])?=?0

...?=?0

open("/lib64/libz.so.1",?O_RDONLY)?=?3

...

close(3)?=?0

open("/lib64/librt.so.1",?O_RDONLY)?=?3

...

close(3)?=?0

open("/usr/lib64/libstdc++.so.6",?O_RDONLY)?=?3

...

close(3)?=?0

open("/lib64/libm.so.6",?O_RDONLY)?=?3

...

close(3)?=?0

open("/lib64/libgcc_s.so.1",?O_RDONLY)?=?3

...

close(3)?=?0

open("/lib64/libc.so.6",?O_RDONLY)?=?3

...

close(3)?=?0

open("/lib64/libpthread.so.0",?O_RDONLY)?=?3

...

close(3)?=?0

...

看屿笼,有加載,所以必定會影響進(jìn)程啟動速度翁巍,所以我們最后不要把無用的so編譯進(jìn)來驴一,這里會有什么影響呢?

? ?大家知不知道linux從程序(program或?qū)ο螅┳兂蛇M(jìn)程(process或進(jìn)程)灶壶,要經(jīng)過哪些步驟呢肝断,這里如果詳細(xì)的說,估計(jì)要另開一篇文章例朱。簡單的說分三步:

? ? 1孝情、fork進(jìn)程,在內(nèi)核創(chuàng)建進(jìn)程相關(guān)內(nèi)核項(xiàng)洒嗤,加載進(jìn)程可執(zhí)行文件箫荡;

? ? 2、查找依賴的so渔隶,一一加載映射虛擬地址

? ? 3羔挡、初始化程序變量。

? 可以看到间唉,第二步中dll依賴越多绞灼,進(jìn)程啟動越慢,并且發(fā)布程序的時候呈野,這些鏈接但沒有使用的so低矮,同樣要一起跟著發(fā)布,否則進(jìn)程啟動時候被冒,會失敗军掂,找不到對應(yīng)的so轮蜕。所以我們不能像上面那樣,把一些毫無意義的so鏈接進(jìn)來蝗锥,浪費(fèi)資源跃洛。但是開發(fā)人員寫makefile 一般有沒有那么細(xì)心,圖省事方便终议,那么有什么好的辦法呢汇竭。繼續(xù)看下去,下面會給你解決方法穴张。

使用 ldd -u demo 查看不需要鏈接的so细燎,看下面,一面了然陆馁,無用的so全部暴露出來了吧

# ldd?-u demo

Unused direct dependencies:

/lib64/libz.so.1

/lib64/librt.so.1

/lib64/libm.so.6

/lib64/libgcc_s.so.1

使用-Wl,--as-needed 編譯選項(xiàng)

# g++?-Wl,--as-needed?-o demo?-lz?-lm?-lrt main.cpp

# ldd demo

linux-vdso.so.1?=>?(0x00007fffebfff000)

libstdc++.so.6?=>?/usr/lib64/libstdc++.so.6?(0x00007ff665c05000)

libc.so.6?=>?/lib64/libc.so.6?(0x00000036c1200000)

libm.so.6?=>?/lib64/libm.so.6?(0x00000036c1e00000)

/lib64/ld-linux-x86-64.so.2?(0x00000036c0e00000)

libgcc_s.so.1?=>?/lib64/libgcc_s.so.1?(0x00000036c7e00000)

# ldd?-u demo

Unused direct dependencies:

我們知道linux鏈接so有兩種途徑:顯示和隱式找颓。所謂顯示就是程序主動調(diào)用dlopen打開相關(guān)so;這里需要補(bǔ)充的是,如果使用顯示鏈接叮贩,上篇文章討論的那些問題都不存在击狮。首先,dlopen的so使用ldd是查看不到的。其次益老,使用dlopen打開的so并不是在進(jìn)程啟動時候加載映射的彪蓬,而是當(dāng)進(jìn)程運(yùn)行到調(diào)用dlopen代碼地方才加載該so,也就是說捺萌,如果每個進(jìn)程顯示鏈接a.so;但是如果發(fā)布該程序時候忘記附帶發(fā)布該a.so,程序仍然能夠正常啟動档冬,甚至如果運(yùn)行邏輯沒有觸發(fā)運(yùn)行到調(diào)用dlopen函數(shù)代碼地方。該程序還能正常運(yùn)行桃纯,即使沒有a.so.


??既然顯示加載這么多優(yōu)點(diǎn)酷誓,那么為什么實(shí)際生產(chǎn)中很少碼農(nóng)使用它呢,?主要原因還是起使用不是很方便,需要開發(fā)人員多寫不少代碼态坦。所以不被大多數(shù)碼農(nóng)使用盐数,還有一個重要原因應(yīng)該是能提前發(fā)現(xiàn)錯誤,在部署的時候就能發(fā)現(xiàn)缺少哪些so伞梯,而不是等到實(shí)際上限運(yùn)行的時候才發(fā)現(xiàn)缺東少西玫氢。


??下面舉個工作中最常碰到的問題,來引申出本篇內(nèi)容吧谜诫。

寫一個最簡單的so漾峡,?tmp.cpp

1.????int?test()

2.????{

3.??????return 20;

4.????}

??編譯=>鏈接=》運(yùn)行,?下面main.cpp?內(nèi)容請參見上一篇文章。

[stevenrao]$?g++ -fPIC -c tmp.cpp

[stevenrao]$?g++ -shared -o libtmp.so tmp.o

[stevenrao]$?mv libtmp.so /tmp/

[stevenrao]$?g++ -o demo -L/tmp -ltmp main.cpp

[stevenrao]$?./demo

./demo: error while loading shared libraries: libtmp.so: cannot open shared object file: No such file or directory

[stevenrao]$?ldd demo

linux-vdso.so.1 =>??(0x00007fff7fdc1000)

????????libtmp.so => not found

這個錯誤是最常見的錯誤了喻旷。運(yùn)行程序的時候找不到依賴的so生逸。一般人使用方法是修改LD_LIBRARY_PATH這個環(huán)境變量

? ?export?LD_LIBRARY_PATH=/tmp

[stevenrao]$?./demo

test

? ?這樣就OK了,?不過這樣export?只對當(dāng)前shell有效,當(dāng)另開一個shell時候,又要重新設(shè)置槽袄∥袄可以把export?LD_LIBRARY_PATH=/tmp?語句寫到?~/.bashrc中,這樣就對當(dāng)前用戶有效了掰伸,寫到/etc/bashrc中就對所有用戶有效了。

前面鏈接時候使用?-L/tmp/ -ltmp?是一種設(shè)置相對路徑方法怀估,還有一種絕對路徑鏈接方法狮鸭。

[stevenrao]$?g++ -o demo??/tmp/libtmp.so main.cpp

[stevenrao]$?./demo

??test

[stevenrao]$?ldd demo

????????linux-vdso.so.1 =>??(0x00007fff083ff000)

????????/tmp/libtmp.so (0x00007f53ed30f000)?

絕對路徑雖然申請?jiān)O(shè)置環(huán)境變量步驟,但是缺陷也是致命的多搀,這個so必須放在絕對路徑下歧蕉,不能放到其他地方,這樣給部署帶來很大麻煩康铭。所以應(yīng)該禁止使用絕對路徑鏈接so惯退。


搜索路徑分兩種,一種是鏈接時候的搜索路徑从藤,一種是運(yùn)行時期的搜索路徑催跪。像前面提到的?-L/tmp/?是屬于鏈接時期的搜索路徑,即給ld程序提供的編譯鏈接時候?qū)ふ覄討B(tài)庫路徑夷野;而LD_LIBRARY_PATH則既屬于鏈接期搜索路徑懊蒸,又屬于運(yùn)行時期的搜索路徑。


? ?這里需要介紹鏈-rpath鏈接選項(xiàng)悯搔,它是指定運(yùn)行時候都使用的搜索路徑骑丸。聰明的同學(xué)馬上就想到,運(yùn)行時搜索路徑,那它記錄在哪兒呢妒貌。也像.?LD_LIBRARY_PATH那樣通危,每部署一臺機(jī)器就需要配一下嗎。呵呵灌曙,不需要..,因?yàn)樗呀?jīng)被硬編碼到可執(zhí)行文件內(nèi)部了菊碟。看看下面演示


1.?? [stevenrao]?$g++?-o demo -L?/tmp/?-ltmp main.cpp

2.?? [stevenrao]?$./demo

3.?? ./demo:?error?while?loading shared libraries:?libtmp.so:?cannot?open?shared object?file:?No such?file?or directory

4.?? [stevenrao]?$g++?-o demo?-Wl,-rpath?/tmp/?-L/tmp/?-ltmp main.cpp

5.?? [stevenrao]?$?./demo

6.?? test

7.?? [stevenrao]?$readelf -d demo

8.?? ?

9.?? Dynamic section at offset 0xc58 contains 26 entries:

10.? ? Tag ? ? ? ?Type ? ? ? ? ? ? ? ? ? ? ? ? Name/Value

11.? ?0x0000000000000001 (NEEDED) ? ? ? ? ? ? Shared library: [libtmp.so]

12.? ?0x0000000000000001 (NEEDED) ? ? ? ? ? ? Shared library: [libstdc++.so.6]

13.? ?0x0000000000000001 (NEEDED) ? ? ? ? ? ? Shared library: [libm.so.6]

14.? ?0x0000000000000001 (NEEDED) ? ? ? ? ? ? Shared library: [libgcc_s.so.1]

15.? ?0x0000000000000001 (NEEDED) ? ? ? ? ? ? Shared library: [libc.so.6]

16.? ?0x000000000000000f (RPATH) ? ? ? ? ? ? ?Library rpath: [/tmp/]

17.? ?0x000000000000001d (RUNPATH) ? ? ? ? ? ?Library runpath: [/tmp/]

? ?看看是吧平匈,編譯到elf文件內(nèi)部了框沟,路徑和程序深深的耦合到一起

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市增炭,隨后出現(xiàn)的幾起案子忍燥,更是在濱河造成了極大的恐慌,老刑警劉巖隙姿,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梅垄,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)队丝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門靡馁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人机久,你說我怎么就攤上這事臭墨。” “怎么了膘盖?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵胧弛,是天一觀的道長。 經(jīng)常有香客問我侠畔,道長结缚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任软棺,我火速辦了婚禮红竭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘喘落。我一直安慰自己茵宪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布瘦棋。 她就那樣靜靜地躺著眉厨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪兽狭。 梳的紋絲不亂的頭發(fā)上憾股,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機(jī)與錄音箕慧,去河邊找鬼服球。 笑死,一個胖子當(dāng)著我的面吹牛颠焦,可吹牛的內(nèi)容都是我干的斩熊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼伐庭,長吁一口氣:“原來是場噩夢啊……” “哼粉渠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起圾另,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤霸株,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后集乔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體去件,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了尤溜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倔叼。...
    茶點(diǎn)故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖宫莱,靈堂內(nèi)的尸體忽然破棺而出丈攒,到底是詐尸還是另有隱情,我是刑警寧澤授霸,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布肥印,位于F島的核電站,受9級特大地震影響绝葡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腹鹉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一藏畅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧功咒,春花似錦愉阎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至景殷,卻和暖如春溅呢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背猿挚。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工咐旧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人绩蜻。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓铣墨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親办绝。 傳聞我的和親對象是個殘疾皇子伊约,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評論 2 359

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