可執(zhí)行目標文件
可執(zhí)行目標文件還包括了程序的入口點,即第一條指令的地址赁豆。
各個段會被映射到連續(xù)的內(nèi)存區(qū)域椎镣,段頭部表描述了這種映射關(guān)系。
加載可執(zhí)行目標文件
將程序復(fù)制到內(nèi)存并運行的過程叫加載庶艾。
堆是從低地址向高地址分配空間袁余,而棧是從高地址向低地址分配空間。
從地址2^48開始落竹,是為內(nèi)核中的代碼和數(shù)據(jù)保留的(操作系統(tǒng)駐留在內(nèi)存的部分)泌霍。
動態(tài)鏈接共享庫
靜態(tài)庫是在鏈接的時候合并到可執(zhí)行目標文件里面,這樣有一些缺點:
- 每次更新了目標文件都需要重新鏈接
- 多個程序都需要使用標準庫函數(shù),都需要進行鏈接并載入內(nèi)存朱转,是對內(nèi)存空間的浪費
共享庫就是為了解決這部分問題而產(chǎn)生的蟹地。共享庫可以在運行或加載時,加載到任意的內(nèi)存地址藤为,并和一個內(nèi)存中的程序鏈接起來怪与。這個過程稱為動態(tài)鏈接,由動態(tài)鏈接器來執(zhí)行缅疟。
共享庫也叫共享目標分别,Linux中通常用.so后綴表示,Windows被稱為DLL存淫。
編譯共享目標文件:
gcc -shared -fpic -o libvector.so addvec.c multvec.c
這樣生成的libvector.so就是共享目標文件耘斩,再將文件鏈接到其他程序中:
gcc -o prog main.c ./libvector.so
這樣prog程序在運行的時候就會和libvector.so鏈接。在創(chuàng)建可執(zhí)行文件時桅咆,靜態(tài)執(zhí)行一些鏈接括授,在程序加載時,動態(tài)完成鏈接過程岩饼。鏈接器沒有復(fù)制so文件的代碼和數(shù)據(jù)節(jié)荚虚,只復(fù)制了一些重定位和符號表信息,使得在運行時可以對libvector.so中的代碼和數(shù)據(jù)的引用籍茧。
從應(yīng)用程序中加載和鏈接共享庫
上面那種方式要在編譯時指定共享庫版述,而另外一種方式是在應(yīng)用程序中,通過接口直接加載和鏈接共享庫寞冯,在編譯時無需指定渴析。
步驟:
//加載共享庫
handle = dlopen("filename",flag);
//鏈接共享庫
func = dlsym(handle,"func_name");
func();
//關(guān)閉共享庫
dlclose(handle);
//產(chǎn)生的錯誤
dlerror();
庫打樁機制
類似于設(shè)計模式中的代理模式,允許截獲對共享庫函數(shù)的調(diào)用简十,取而代之執(zhí)行自己的代碼檬某。打樁可以發(fā)生在編譯時、鏈接時或當程序被加載和執(zhí)行的運行時螟蝙。即定義一個完全一樣的函數(shù)接口恢恼,在實現(xiàn)的時候通過調(diào)用真正的函數(shù)完成功能,同時可以記錄調(diào)用的參數(shù)與結(jié)果等胰默。