亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

目錄
為什麼要進行動態(tài)連結(jié)? " >為什麼要進行動態(tài)連結(jié)?
如何進行動態(tài)連結(jié)? " >如何進行動態(tài)連結(jié)?
動態(tài)鏈接的方式" >動態(tài)鏈接的方式
延遲綁定技術(shù)" >延遲綁定技術(shù)
顯式運行時鏈接" >顯式運行時鏈接
總結(jié)" >總結(jié)
首頁 系統(tǒng)教程 Linux Linux 動態(tài)連結(jié)與靜態(tài)連結(jié)原來是這麼回事?

Linux 動態(tài)連結(jié)與靜態(tài)連結(jié)原來是這麼回事?

Feb 05, 2024 pm 05:45 PM
linux linux教程 linux系統(tǒng) 延遲載入 linux指令 shell腳本 嵌入式linux 良許 linux入門 linux學習

老規(guī)矩,先提出幾個問題:

  • 為什麼要進行動態(tài)連結(jié)?
  • 如何進行動態(tài)連結(jié)?
  • 什麼是地址無關(guān)代碼技術(shù)?
  • 什麼是延遲綁定技術(shù)?
  • 如何在程式運行過程中進行明確連結(jié)?

為什麼要進行動態(tài)連結(jié)?

動態(tài)連結(jié)的出現(xiàn)是為了解決靜態(tài)連結(jié)的一些缺點:

  1. 節(jié)約記憶體與磁碟空間:如下圖所示,
Linux 動態(tài)鏈接與靜態(tài)鏈接原來是這么回事?

#Program1和Program2分別包含Program1.o和Program2.o兩個模組,他們都需要Lib.o模組。靜態(tài)連結(jié)情況下,兩個目標檔案都用到Lib.o這個模組,所以它們同時在連結(jié)輸出的可執(zhí)行檔Program1和program2中有副本,同時執(zhí)行時,Lib.o在磁碟和記憶體中有兩份副本,當系統(tǒng)中有大量類似Lib.o的多個程式共享目標檔案時,就會浪費很大空間。

  1. 靜態(tài)連結(jié)對程式的更新部署和發(fā)布很不友善

假如一個模組依賴20個模組,當20個模組其中有一個模組需要更新時,需要將所有的模組找出來重新編譯出一個可執(zhí)行程式才可以更新成功,每次更新任何一個模組,用戶就需要重新獲得一個非常大的程序,程序如果使用靜態(tài)鏈接,那麼通過網(wǎng)絡來更新程序也會非常不便,一旦程序任何位置有一個小改動,都會導致整個程序重新下載。

為了解決靜態(tài)鏈接的缺點,所以引入了動態(tài)鏈接,動態(tài)鏈接的內(nèi)存分佈如圖,

Linux 動態(tài)鏈接與靜態(tài)鏈接原來是這么回事?

多個程式依賴同一個共享目標文件,這個共享目標文件在磁碟和內(nèi)存中僅有一份,不會產(chǎn)生副本,簡單來講就是不像靜態(tài)鏈接一樣對那些組成程序的目標文件進行鏈接,等到程式要運行時才進行鏈接,把鏈接這個過程推遲到運行時才執(zhí)行。動態(tài)連結(jié)的方式使得開發(fā)過程中各個模組更加獨立,耦合度更小,便於不同的開發(fā)者和開發(fā)組織之間獨立的進行開發(fā)和測試。

如何進行動態(tài)連結(jié)?

看如下程式碼:

//?lib.c
#include?

void?func(int?i)?{
???printf("func?%d?\n",?i);
}
//?Program.c
void?func(int?i);

int?main()?{
???func(1);
???return?0;
}

編譯運行過程如下:

$?gcc?-fPIC?-shared?-o?lib.so?lib.c
$?gcc?-o?test?Program.c?./lib.so
$?./test
$?func?1

透過-fPIC和-shared可以產(chǎn)生一個動態(tài)連結(jié)函式庫,再連結(jié)到可執(zhí)行程式就可以正常運作。

透過readelf指令可以查看動態(tài)連結(jié)庫的segment資訊:

~/test$?readelf?-l?lib.so

Elf?file?type?is?DYN?(Shared?object?file)
Entry?point?0x530
There?are?7?program?headers,?starting?at?offset?64

Program?Headers:
?Type???????????Offset?????????????VirtAddr???????????PhysAddr
????????????????FileSiz????????????MemSiz??????????????Flags??Align
?LOAD???????????0x0000000000000000?0x0000000000000000?0x0000000000000000
????????????????0x00000000000006e4?0x00000000000006e4??R?E????0x200000
?LOAD???????????0x0000000000000e10?0x0000000000200e10?0x0000000000200e10
????????????????0x0000000000000218?0x0000000000000220??RW?????0x200000
?DYNAMIC????????0x0000000000000e20?0x0000000000200e20?0x0000000000200e20
????????????????0x00000000000001c0?0x00000000000001c0??RW?????0x8
?NOTE???????????0x00000000000001c8?0x00000000000001c8?0x00000000000001c8
????????????????0x0000000000000024?0x0000000000000024??R??????0x4
?GNU_EH_FRAME???0x0000000000000644?0x0000000000000644?0x0000000000000644
????????????????0x0000000000000024?0x0000000000000024??R??????0x4
?GNU_STACK??????0x0000000000000000?0x0000000000000000?0x0000000000000000
????????????????0x0000000000000000?0x0000000000000000??RW?????0x10
?GNU_RELRO??????0x0000000000000e10?0x0000000000200e10?0x0000000000200e10
????????????????0x00000000000001f0?0x00000000000001f0??R??????0x1

Section?to?Segment?mapping:
?Segment?Sections...
??00?????.note.gnu.build-id?.gnu.hash?.dynsym?.dynstr?.gnu.version?.gnu.version_r?.rela.dyn?.rela.plt?.init?.plt?.plt.got?.text?.fini?.rodata?.eh_frame_hdr?.eh_frame
??01?????.init_array?.fini_array?.dynamic?.got?.got.plt?.data?.bss
??02?????.dynamic
??03?????.note.gnu.build-id
??04?????.eh_frame_hdr
??05
??06?????.init_array?.fini_array?.dynamic?.got

可以看見動態(tài)連結(jié)模組的裝載位址從0開始,0是無效位址,它的裝載位址會在程式執(zhí)行時再確定,在編譯時是不確定的。

改一下程式:

#
//?Program.c
#include?
void?func(int?i);

int?main()?{
???func(1);
???sleep(-1);
???return?0;
}

運行讀取maps資訊:

~/test$?./test?&
[1]?126
~/test$?func?1
cat?/proc/126/maps
7ff2c59f0000-7ff2c5bd7000?r-xp?00000000?00:00?516391?????????????/lib/x86_64-linux-gnu/libc-2.27.so
7ff2c5bd7000-7ff2c5be0000?---p?001e7000?00:00?516391?????????????/lib/x86_64-linux-gnu/libc-2.27.so
7ff2c5be0000-7ff2c5dd7000?---p?000001f0?00:00?516391?????????????/lib/x86_64-linux-gnu/libc-2.27.so
7ff2c5dd7000-7ff2c5ddb000?r--p?001e7000?00:00?516391?????????????/lib/x86_64-linux-gnu/libc-2.27.so
7ff2c5ddb000-7ff2c5ddd000?rw-p?001eb000?00:00?516391?????????????/lib/x86_64-linux-gnu/libc-2.27.so
7ff2c5ddd000-7ff2c5de1000?rw-p?00000000?00:00?0
7ff2c5df0000-7ff2c5df1000?r-xp?00000000?00:00?189022?????????????/mnt/d/wzq/wzq/util/test/lib.so
7ff2c5df1000-7ff2c5df2000?---p?00001000?00:00?189022?????????????/mnt/d/wzq/wzq/util/test/lib.so
7ff2c5df2000-7ff2c5ff0000?---p?00000002?00:00?189022?????????????/mnt/d/wzq/wzq/util/test/lib.so
7ff2c5ff0000-7ff2c5ff1000?r--p?00000000?00:00?189022?????????????/mnt/d/wzq/wzq/util/test/lib.so
7ff2c5ff1000-7ff2c5ff2000?rw-p?00001000?00:00?189022?????????????/mnt/d/wzq/wzq/util/test/lib.so
7ff2c6000000-7ff2c6026000?r-xp?00000000?00:00?516353?????????????/lib/x86_64-linux-gnu/ld-2.27.so
7ff2c6026000-7ff2c6027000?r-xp?00026000?00:00?516353?????????????/lib/x86_64-linux-gnu/ld-2.27.so
7ff2c6227000-7ff2c6228000?r--p?00027000?00:00?516353?????????????/lib/x86_64-linux-gnu/ld-2.27.so
7ff2c6228000-7ff2c6229000?rw-p?00028000?00:00?516353?????????????/lib/x86_64-linux-gnu/ld-2.27.so
7ff2c6229000-7ff2c622a000?rw-p?00000000?00:00?0
7ff2c62e0000-7ff2c62e3000?rw-p?00000000?00:00?0
7ff2c62f0000-7ff2c62f2000?rw-p?00000000?00:00?0
7ff2c6400000-7ff2c6401000?r-xp?00000000?00:00?189023?????????????/mnt/d/wzq/wzq/util/test/test
7ff2c6600000-7ff2c6601000?r--p?00000000?00:00?189023?????????????/mnt/d/wzq/wzq/util/test/test
7ff2c6601000-7ff2c6602000?rw-p?00001000?00:00?189023?????????????/mnt/d/wzq/wzq/util/test/test
7fffee96f000-7fffee990000?rw-p?00000000?00:00?0?????????????????[heap]
7ffff6417000-7ffff6c17000?rw-p?00000000?00:00?0?????????????????[stack]
7ffff729d000-7ffff729e000?r-xp?00000000?00:00?0?????????????????[vdso]

可以看到,整個進程虛擬地址空間中,多出了幾個文件的映射,lib.so和test一樣,它們都是被操作系統(tǒng)用同樣的方法映射到進程的虛擬地址空間,只是它們占據(jù)的虛擬地址和長度不同.

從maps里可以看見里面還有l(wèi)ibc-2.27.so,這是C語言運行庫,還有一個ld-2.27.so,這是Linux下的動態(tài)鏈接器,動態(tài)鏈接器和普通共享對象一樣被映射到進程的地址空間,在系統(tǒng)開始運行test前,會先把控制權(quán)交給動態(tài)鏈接器,動態(tài)鏈接器完成所有的動態(tài)鏈接工作后會把控制權(quán)交給test,然后執(zhí)行test程序。

當鏈接器將Program.o鏈接成可執(zhí)行文件時,這時候鏈接器必須確定目標文件中所引用的func函數(shù)的性質(zhì),如果是一個定義于其它靜態(tài)目標文件中的函數(shù),那么鏈接器將會按照靜態(tài)鏈接的規(guī)則,將Program.o的func函數(shù)地址進行重定位,如果func是一個定義在某個動態(tài)鏈接共享對象中的函數(shù),那么鏈接器將會將這個符號的引用標記為一個動態(tài)鏈接的符號,不對它進行地址重定位,將這個過程留在裝載時再進行。

動態(tài)鏈接的方式

動態(tài)鏈接有兩種方式:裝載時重定位和地址無關(guān)代碼技術(shù)。

裝載時重定位:

在鏈接時對所有絕對地址的引用不作重定位,而把這一步推遲到裝載時完成,也叫基址重置,每個指令和數(shù)據(jù)相當于模塊裝載地址是固定的,系統(tǒng)會分配足夠大的空間給裝載模塊,當裝載地址確定后,那指令和數(shù)據(jù)地址自然也就確定了。

然而動態(tài)鏈接模塊被裝載映射到虛擬空間,指令被重定位后對于每個進程來講是不同的,沒有辦法做到同一份指令被多個進程共享,所以指令對不同的進程來說有不同的副本,還是空間浪費,怎么解決這個問題?使用fPIC方法。

地址無關(guān)代碼:

指令部分無法在多個進程之間共享,不能節(jié)省內(nèi)存,所以引入了地址無關(guān)代碼的技術(shù)。我們平時編程過程中可能都見過-fPIC的編譯選項,這個就代表使用了地址無關(guān)代碼技術(shù)來實現(xiàn)真正的動態(tài)鏈接。

基本思想就是使用GOT(全局偏移表),這是一個指向變量或函數(shù)地址的指針數(shù)組,當指令要訪問變量或者調(diào)用函數(shù)時,會去GOT中找到相應的地址進行間接跳轉(zhuǎn)訪問,每個變量或函數(shù)都對應一個地址,鏈接器在裝載模塊的時候會查找每個變量和函數(shù)的地址,然后填充GOT中的各個項,確保每個指針指向的地址正確。GOT放在數(shù)據(jù)段,所以它可以在模塊裝載時被修改,并且每個進程都可以有獨立的副本,相互不受影響。

tips

?

-fpic和-fPIC的區(qū)別:它們都是地址無關(guān)代碼技術(shù),-fpic產(chǎn)生的代碼相對較小較快,但是在某些平臺會有些限制,所以大多數(shù)情況下都是用-fPIC來產(chǎn)生地址無關(guān)代碼。

?

-fPIC和-fPIE的區(qū)別:一個作用于共享對象,一個作用于可執(zhí)行文件,一個以地址無關(guān)方式編譯的可執(zhí)行文件被稱作地址無關(guān)可執(zhí)行文件。

?

-fpie和-fPIE的區(qū)別:類似于-fpic和-fPIC的區(qū)別

延遲綁定技術(shù)

在程序剛啟動時動態(tài)鏈接器會尋找并裝載所需要的共享對象,然后進行符號地址尋址重定位等工作,這些工作會減慢程序的啟動速度,如果解決?

使用PLT延遲綁定技術(shù),這里會單獨有一個叫.PLT的段,ELF將 GOT拆分成兩個表.GOT和.GOT.PLT,其中.GOT用來保存全局變量的引用地址,.GOT.PLT用來保存外部函數(shù)的地址,每個外部函數(shù)在PLT中都有一個對應項,在初始化時不會綁定,而是在函數(shù)第一次被用到時才進行綁定,將函數(shù)真實地址與對應表項進行綁定,之后就可以進行間接跳轉(zhuǎn)。

顯式運行時鏈接

支持動態(tài)鏈接的系統(tǒng)往往都支持顯式運行時鏈接,也叫運行時加載,讓程序自己在運行時控制加載的模塊,在需要時加載需要的模塊,在不需要時將其卸載。這種運行時加載方式使得程序的模塊組織變得很靈活,可以用來實現(xiàn)一些諸如插件、驅(qū)動等功能。

通過這四個API可以進行顯式運行時鏈接:

dlopen():打開動態(tài)鏈接庫
dlsym():查找符號
dlerror():錯誤處理
dlclose():關(guān)閉動態(tài)鏈接庫

參考這段使用代碼:

#include?
#include?

int?main()?{
???
???void?*handle;
???void?(*f)(int);
???char?*error;

???handle?=?dlopen("./lib.so",?RTLD_NOW);
???if?(handle?==?NULL)?{
???????printf("handle?null?\n");
???????return?-1;
??}
???f?=?dlsym(handle,?"func");
???do?{
???????if?((error?=?dlerror())?!=?NULL)?{
???????????printf("error\n");
???????????break;
??????}
???????f(100);
??}?while?(0);
???dlclose(handle);

???return?0;
}

編譯運行:

$?gcc?-o?test?program.c?-ldl
$?./test
func?100

總結(jié)

為什么要進行動態(tài)鏈接?為了解決靜態(tài)鏈接浪費空間和更新困難的缺點。

動態(tài)連結(jié)的方式? 裝載時重定位和位址無關(guān)程式碼技術(shù)。

位址無關(guān)程式碼技術(shù)原理? 透過GOT段實現(xiàn)間接跳躍。

延遲載入技術(shù)原理? 對外部函數(shù)符號透過PLT段實現(xiàn)延遲綁定及間接跳轉(zhuǎn)。

如果進行明確運行時連結(jié)? 透過頭檔中的四個函數(shù),程式碼如上。

以上是Linux 動態(tài)連結(jié)與靜態(tài)連結(jié)原來是這麼回事?的詳細內(nèi)容。更多資訊請關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

本網(wǎng)站聲明
本文內(nèi)容由網(wǎng)友自願投稿,版權(quán)歸原作者所有。本站不承擔相應的法律責任。如發(fā)現(xiàn)涉嫌抄襲或侵權(quán)的內(nèi)容,請聯(lián)絡admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅(qū)動的應用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Laravel 教程
1597
29
PHP教程
1488
72
如何與Windows一起安裝Linux(雙啟動)? 如何與Windows一起安裝Linux(雙啟動)? Jun 18, 2025 am 12:19 AM

安裝Linux和Windows雙系統(tǒng)的關(guān)鍵是分區(qū)和啟動設(shè)置。 1.準備工作包括備份數(shù)據(jù)並壓縮現(xiàn)有分區(qū)騰出空間;2.使用Ventoy或Rufus製作Linux啟動U盤,推薦Ubuntu;3.安裝時選擇“與其他系統(tǒng)並存”或手動分區(qū)(/至少20GB,/home剩餘空間,swap可選);4.勾選安裝第三方驅(qū)動以避免硬件問題;5.安裝後若未進入Grub引導菜單,可用boot-repair修復引導或調(diào)整BIOS啟動順序。只要步驟清晰、操作得當,整個過程並不復雜。

在Linux系統(tǒng)上安裝REDIS的步驟是什麼? 在Linux系統(tǒng)上安裝REDIS的步驟是什麼? Jun 11, 2025 am 12:11 AM

ToinstallRedisonaLinuxsystem,followthesesteps:1)DownloadandextractRedisfromtheofficialGitHubrepository,2)CompileRedisusingthe'make'command,3)InstallRediswith'sudomakeinstall',4)ConfigureRedisbycopyingandeditingtheconfigurationfile,and5)StartRedisusin

如何啟用EPEL(企業(yè)Linux的額外軟件包)存儲庫? 如何啟用EPEL(企業(yè)Linux的額外軟件包)存儲庫? Jun 17, 2025 am 09:15 AM

啟用EPEL倉庫的關(guān)鍵在於根據(jù)系統(tǒng)版本選擇正確的安裝方式。首先,確認系統(tǒng)類型和版本,使用命令cat/etc/os-release獲取信息;其次,在CentOS/RockyLinux上通過dnfinstallepel-release啟用EPEL,8和9版本命令相同;第三,在RHEL上需手動下載對應版本的.repo文件並安裝;第四,遇到問題時可重新導入GPG密鑰,注意舊版本可能不被支持,也可考慮啟用epel-next獲取測試包。完成上述步驟後,使用dnfrepolist驗證是否成功添加EPEL倉庫。

如何為初學者選擇Linux發(fā)行版? 如何為初學者選擇Linux發(fā)行版? Jun 19, 2025 am 12:09 AM

新手選擇Linux發(fā)行版應先明確使用需求。 1.日常使用選Ubuntu或LinuxMint;編程開發(fā)適合Manjaro或Fedora;老舊設(shè)備用Lubuntu等輕量系統(tǒng);學習底層原理推薦CentOSStream或Debian。 2.穩(wěn)定性優(yōu)先考慮UbuntuLTS或Debian;追求新功能可選Arch或Manjaro。 3.社區(qū)支持方面,Ubuntu和LinuxMint資源豐富,Arch文檔偏技術(shù)向。 4.安裝難度上,Ubuntu、LinuxMint較簡單,Arch適合有基礎(chǔ)者。建議先試用再決定。

如何向Linux添加新磁盤 如何向Linux添加新磁盤 Jun 27, 2025 am 12:15 AM

添加新硬盤到Linux系統(tǒng)步驟如下:1.確認硬盤被識別,使用lsblk或fdisk-l檢查;2.用fdisk或parted分區(qū),如fdisk/dev/sdb創(chuàng)建分區(qū)並保存;3.格式化分區(qū)為文件系統(tǒng),如mkfs.ext4/dev/sdb1;4.臨時掛載使用mount命令,如mount/dev/sdb1/mnt/data;5.修改/etc/fstab實現(xiàn)開機自動掛載,需先測試掛載確保無誤。操作前務必確認數(shù)據(jù)安全,避免硬件連接問題。

Linux中的系統(tǒng)日誌在哪裡? Linux中的系統(tǒng)日誌在哪裡? Jun 24, 2025 am 12:15 AM

Linux系統(tǒng)中的日誌通常存儲在/var/log目錄中,該目錄包含多種關(guān)鍵日誌文件,如syslog或messages(記錄系統(tǒng)日誌)、auth.log(記錄認證事件)、kern.log(記錄內(nèi)核消息)、dpkg.log或yum.log(記錄軟件包操作)、boot.log(記錄啟動信息);可通過cat、tail-f或journalctl等命令查看日誌內(nèi)容;應用日誌也常位於/var/log下的子目錄,如Apache的apache2或httpd目錄、MySQL的日誌文件等;同時需注意日誌權(quán)限通常需要s

修復無法在Windows Google Chrome上傳文件 修復無法在Windows Google Chrome上傳文件 Jul 08, 2025 pm 02:33 PM

在GoogleChrome中上傳文件時遇到問題?這可能很煩人,對吧?無論您是將文檔附加到電子郵件、在社交媒體上共享圖像,還是提交工作或?qū)W校的重要文件,流暢的文件上傳過程都是至關(guān)重要的。因此,如果您的文件上傳在WindowsPC上的Chrome中持續(xù)失敗,可能會令人沮喪。如果您還沒有準備好放棄您最喜歡的瀏覽器,這裡有一些提示修復無法在WindowsGoogleChrome上傳文件1.從通用修復開始在我們了解任何高級故障排除技巧之前,最好先嘗試下面提到的一些基本解決方案。排除互聯(lián)網(wǎng)連接問題:互聯(lián)網(wǎng)連

什麼是sudo命令,我什麼時候應該使用它? 什麼是sudo命令,我什麼時候應該使用它? Jul 02, 2025 am 12:20 AM

sudo代表“substituteuserdo”或“superuserdo”,允許用戶以其他用戶(通常是root)的權(quán)限運行命令。其核心用途包括:1.執(zhí)行系統(tǒng)級操作如安裝軟件或編輯系統(tǒng)文件;2.訪問受保護目錄或日誌;3.管理服務如重啟nginx;4.修改全局設(shè)置如/etc/hosts。使用時系統(tǒng)會檢查/etc/sudoers配置並驗證用戶密碼,提供臨時權(quán)限而非持續(xù)以root身份登錄,確保安全性。最佳實踐包括:僅必要時使用、避免盲目執(zhí)行網(wǎng)絡命令、用visudo編輯sudoers文件、連續(xù)操作可考慮

See all articles