dll(Dynamic Link Library)跋理,動態(tài)鏈接庫择克,它和exe基本上一樣,只不過它的pe文件頭中的符號表標(biāo)明該文件是dll而不是exe前普。
dll文件的后綴名不僅是dll還可以是ocx和cpl肚邢。
dll設(shè)計(jì)的目的不同于共享對象,它關(guān)注的是軟件工程中的模塊化設(shè)計(jì)思想拭卿,想要做到高內(nèi)聚低耦合骡湖,它方便了軟件的升級和維護(hù)。
exe文件里面也有基地址和RVA(Relative Virtual Address峻厚,相對虛擬地址)勺鸦。
對于dll來說它的基地址有一個默認(rèn)值,如果這個默認(rèn)值被其他模塊占用了目木,它就找個別的地方裝載。
dll中有些數(shù)據(jù)是共享的懊渡,有些是進(jìn)程私有的刽射。
按照這個區(qū)別劃分的話,就可以把數(shù)據(jù)段分成兩種剃执。
不過由于各進(jìn)程共同訪問公共數(shù)據(jù)段誓禁,如果某一進(jìn)程惡意破壞數(shù)據(jù),其他進(jìn)程也會受到影響肾档,這存在一定的安全隱患摹恰。
ELF中的符號默認(rèn)都是可以導(dǎo)入導(dǎo)出的,所謂導(dǎo)出就是指可以被別的模塊調(diào)用怒见,導(dǎo)入就不解釋了俗慈,而DLL中的符號需要指定才能導(dǎo)入導(dǎo)出。
__declspec(dllexport)用來指定導(dǎo)出符號遣耍,__declspec(dllimport)用來指定導(dǎo)入符號闺阱。
另外,還可以用def文件中的IMPORT和EXPORTS段來聲明符號的導(dǎo)入和導(dǎo)出舵变。
如果是C語言的符號規(guī)范酣溃,你必須在符號的定義之前加上external
“C”。
就是前面提到的def文件了纪隙,它的好處有如下幾點(diǎn):
1赊豌、能夠控制導(dǎo)出符號的符號名。原來__cdecl绵咱、__stdcall碘饼、__fastcall都是msvc中的函數(shù)規(guī)范啊。編譯器會對源碼中的符號進(jìn)行修飾,經(jīng)過修飾的符號變得和環(huán)境中的符號不兼容派昧,不便于維護(hù)和使用黔姜,于是采用def文件對導(dǎo)出符號進(jìn)行重命名。
2蒂萎、它可以控制一些鏈接的過程秆吵。它還可以控制輸出文件名、段的屬性五慈、堆棧大小纳寂、版本號等。
即泻拦,運(yùn)行時加載毙芜。
Windows提供了3個API:
1、LoadLibrary裝載一個DLL進(jìn)進(jìn)程地址空間争拐。
2腋粥、GetProAddress查找某個符號的地址。
3架曹、FreeLibrary用來卸載某個已經(jīng)加載的模塊隘冲。
Windows下的PE文件的導(dǎo)出符號全部集中在導(dǎo)出表中,供其他PE文件調(diào)用绑雄,它提供的是一種符號與地址的映射關(guān)系展辞。
導(dǎo)出表是個DataDirectory的結(jié)構(gòu)體數(shù)組,名字叫做IMAGE_EXPORT_DIRECTORY万牺,被定義在Winnt.h中罗珍。
DataDirectory中最后三項(xiàng)EAT(Export Address Table)、Name
Table脚粟、Name
Ordinal Table分別代表導(dǎo)出地址表覆旱、符號名表、名字序號對應(yīng)表核无。
導(dǎo)出地址表存放的是個符號的相對虛擬地址通殃。
符號名表存放的是導(dǎo)出符號的名字。
名字序號對應(yīng)表存放的是函數(shù)的序號和函數(shù)名的對應(yīng)關(guān)系厕宗。
函數(shù)序號存在的意義在于節(jié)省空間和查找方便画舌,壞處是函數(shù)變化了序號也要跟著變化。導(dǎo)出函數(shù)一定有序號但可以沒有函數(shù)名已慢。
在創(chuàng)建DLL時會產(chǎn)生一個EXP曲聂,EXP中的.edata存放的是導(dǎo)出表。EXP會與其他目標(biāo)文件一樣一起鏈接生成DLL并且成為導(dǎo)出表佑惠。
來自于DLL和其他可執(zhí)行文件中的符號會存儲在導(dǎo)入表中朋腋。
導(dǎo)入表是一個IMAGE_EXPORT_DIRECTORY結(jié)構(gòu)體數(shù)組齐疙。
IMAGE_EXPORT_DIRECTORY中的FirstThunk指向?qū)氲刂窋?shù)組(Import
Address Table,IAT)旭咽,每個IAT對應(yīng)一個被導(dǎo)入的符號贞奋。
延遲載入:DLL也支持延遲裝載,它是通過特殊的樁代碼實(shí)現(xiàn)的穷绵。
PE DLL的代碼段并不是地址無關(guān)的轿塔。
PE采用了重定基地址的方法來解決模塊裝載時進(jìn)程空間中地址沖突的問題。
__declspec(dllimport)的作用是使編譯器能夠區(qū)分函數(shù)是從外部導(dǎo)入的還是模塊內(nèi)部定義的仲墨。
同一個導(dǎo)出函數(shù)會產(chǎn)生兩個符號的定義勾缭,一個指向該函數(shù)的樁代碼,一個指向該函數(shù)在IAT中的位置目养。
用__declspec(dllimport)來聲明導(dǎo)入函數(shù)時會在導(dǎo)入函數(shù)前面加上__imp__以確保跟庫中的函數(shù)符號正確鏈接俩由。
DLL本身的代碼段和數(shù)據(jù)段并不是地址無關(guān)的。
一旦DLL的基址被占用癌蚁,它就必須被重定位幻梯,這需要時間開銷。
雖然在DLL中采用二分查找法進(jìn)行符號字符串的比較和查找努释,但是由于符號眾多礼旅,因此這也是一項(xiàng)非常耗時的工作。
以上是影響DLL性能的兩個問題洽洁。
在裝載DLL時發(fā)生了地址沖突,就必須對每個絕對地址的引用都進(jìn)行重定位菲嘴。
重定位的過程很簡單就是在原來的地址基礎(chǔ)上加上一個偏移量饿自,但是這是對所有需要重定位的絕對地址而言的。
PE文件的重定位信息都放在reloc段中龄坪。
一般來講exe文件是不會發(fā)生重定位的昭雌,因?yàn)樗偸潜坏谝粋€裝載。
而DLL則是動態(tài)裝載所以它的裝載地址可能被占用健田。
DLL文件中代碼段的訪問比ELF更加快速烛卧,是一種空間換時間的優(yōu)化策略,因?yàn)樗總€進(jìn)程都有一個副本妓局。
確切地說它是裝載時重定位基址总放。
DLL的裝載和地址順序是一樣的。
DLL的基地址是可以手動指定的好爬,不然老是重定位多麻煩啊局雄,VC提供這種功能。
一個導(dǎo)出的函數(shù)符號可以沒有函數(shù)名存炮,但是絕對不能沒有序號炬搭。
序號表示導(dǎo)出函數(shù)在導(dǎo)出表中的位置蜈漓。
內(nèi)部使用的函數(shù)一般只有序號沒有函數(shù)名。
Windows API雖然函數(shù)名是不變的宫盔,但是序號總是在變化的融虽。
序號可以通過def文件指定。
凡是涉及到PE文件的查找的地方用的都是二分查找法灼芭。
由于無論如何導(dǎo)入導(dǎo)出的符號關(guān)系都會被重新解析有额,而且每次解析完畢后它們被裝載的內(nèi)存地址都相同,那么這個解析過程就是浪費(fèi)姿鸿。
針對這種浪費(fèi)采取的優(yōu)化策略就叫做DLL綁定谆吴,具體方法是把這些導(dǎo)入的符號保存在模塊的導(dǎo)入表中每次只需查表即可。
INT(Import Name Table)苛预,導(dǎo)入名稱表句狼,把符號運(yùn)行時的目標(biāo)地址寫到INT中。
DLL更新和重定基址可能導(dǎo)致DLL綁定地址失效热某。
針對DLL更新腻菇,Windows會核對裝載的DLL與綁定時的DLL版本是否相同,還有該DLL是否發(fā)生過重定基址昔馋,如果都沒有那就直接查表筹吐。
DLL綁定過程可以發(fā)生在安裝的時候,可能改變可執(zhí)行文件本身從而導(dǎo)致可執(zhí)行文件的校驗(yàn)和變化秘遏。這對于一些經(jīng)過加密的和數(shù)字簽名的程序來說可能會有問題丘薛。
Linux下的共享庫絕大多數(shù)都是用C語言寫的,這是因?yàn)镃++編寫的庫比C語言編寫的庫要復(fù)雜得多邦危,而且由于C++沒有二進(jìn)制級別的規(guī)定只有語法級別的洋侨,所以也為共享庫的更新帶來了不便。
C++帶來的麻煩主要表現(xiàn)在以下幾個方面:
1倦蚪、內(nèi)存釋放過程復(fù)雜希坚,不好把握。因?yàn)椴煌腄LL和EXE使用不同的堆陵且。
2裁僧、所謂的更新只不過是簡單的覆蓋。
3慕购、正是由于舊版DLL被覆蓋聊疲,如果新版程序運(yùn)行出錯,舊版程序也運(yùn)行出錯沪悲,那就悲劇了售睹。
為了解決程序開發(fā)中遇到的兼容性問題,微軟推出了組件對象模型(COM可训,Component
Object Model)昌妹。
P300列出了幾點(diǎn)用C++編寫DLL時應(yīng)該遵循的原則捶枢。
早期Windows中的DLL文件使用范圍大,更新也頻繁飞崖,而且還缺乏版本控制機(jī)制烂叔,所以DLL不兼容的情況在早期Windows下特別嚴(yán)重,史稱DLL噩夢固歪。
導(dǎo)致DLL
HELL發(fā)生的3個原因見P301中所說明的蒜鸡。
解決DLL
HELL的方法:
1、由于DLL HELL是由DLL引起的牢裳,即動態(tài)鏈接引起的逢防,所以最徹底的解決辦法是不使用動態(tài)鏈接,而使用靜態(tài)鏈接蒲讯。
2忘朝、避免DLL覆蓋。這個可以通過Windows的文件保護(hù)機(jī)制實(shí)現(xiàn)判帮。
3局嘁、避免DLL沖突。它主要是針對不同應(yīng)用程序依賴相同DLL的不同版本的問題晦墙,解決辦法是讓每個應(yīng)用程序悦昵。
.NET下的程序集包括兩種類型應(yīng)用程序集和庫程序集,前者是指EXE可執(zhí)行文件晌畅,后者是指DLL動態(tài)鏈接庫但指。
由于程序集包括一個或多個文件所需要一個清單來描述,這個清單叫做Manifest文件抗楔,它就是描述了程序及的各種屬性信息棋凳,其本質(zhì)是一個XML文件。
在Windows
XP以前的版本中Manifest就是個擺設(shè)谓谦,這以后的Windows版本在執(zhí)行可執(zhí)行文件時首先要讀取Manifest內(nèi)容獲取所需的DLL文件列表,然后Windows再根據(jù)DLL的Manifest文件去掉用DLL贪婉。
CRT(C Run-Time Library):C語言運(yùn)行庫反粥。