動靜態鏈接與加載

目錄

靜態鏈接

ELF加載與進程地址空間(靜態鏈接)

動態鏈接與動態庫加載

GOT表


靜態鏈接

對于多個.o文件在沒有鏈接之前互相是不知到對方存在的,也就是說這個.o文件中調用函數的的跳轉地址都會被設定為0(當然這個函數是在其他.o文件中定義的)這個地址會在哪個時候被修正?鏈接的時候!為了讓鏈接器將來在鏈接時能夠正確定位到這些被修正 的地址,在代碼塊(.data)中還存在?個重定位表,這張表將來在鏈接的時候,就會根據表?記錄的地址將其修正。這也就是為什么.o文件叫做可重定位文件。

所以,鏈接過程中會涉及到對.o中外部符號進?地址重定位。

ELF加載與進程地址空間(靜態鏈接)

從上面的連接過程可以看到,在我們鏈接完成的之后形成的可執行程序中是有地址的,這個時候程序顯然沒有加載到內存中那這個地址就不可能是內存中的物理地址。事實上這個地址是一種邏輯地址,其思想與虛擬地址類似,也與虛擬地址對應,也就是說磁盤上的邏輯地址就是以后運行可執行程序時的虛擬地址。在當代計算機內部,這個邏輯地址采用平坦模式進行編址(也就是從0開始編址)。所以也要求ELF文件對自己的代碼和數據進行統一編址。

簡直巧妙,原來虛擬地址跟磁盤中可執行文件的邏輯地址是對應的。我們知道可執行程序的執行需要os創建子進程來執行,那么mm_struct、vm_area_struct在進程剛剛創建的時候,初始化數據從哪?來?就從邏輯地址來。從ELF各個segment來,每個segment有??的起始地址和??的?度,?來初始化內核結構中的[start, end] 等范圍數據,另外再?詳細地址,填充?表。

mm_struct? 描述進程的整個虛擬地址空間,包含所有?vm_area_struct?的鏈表或紅黑樹。例如:

struct mm_struct {struct vm_area_struct *mmap;  // 虛擬內存區域鏈表unsigned long start_code;      // 代碼段起始地址(ELF的 .text)unsigned long end_code;unsigned long start_data;     // 數據段起始地址(ELF的 .data)unsigned long end_data;// ...
};

vm_area_struct? ?描述一個連續的虛擬內存區域(如一個ELF段),包括權限、文件映射信息等。

struct vm_area_struct {unsigned long vm_start;        // 起始虛擬地址(ELF的 p_vaddr)unsigned long vm_end;          // 結束虛擬地址struct file *vm_file;          // 關聯的ELF文件unsigned long vm_pgoff;        // 文件中的偏移(對應ELF段在文件中的位置)pgprot_t vm_page_prot;         // 訪問權限(如可讀、可執行)// ...
};

示例:ELF加載到虛擬地址空間

假設一個ELF文件有兩個可加載段:

  1. 代碼段:.textp_vaddr = 0x400000,?p_memsz = 0x1000

  2. 數據段:.datap_vaddr = 0x401000,?p_memsz = 0x2000

進程創建時,內核會:

  1. 創建兩個?vm_area_struct

    • 代碼段:vm_start=0x400000,?vm_end=0x401000, 權限為?RX(讀+執行)。

    • 數據段:vm_start=0x401000,?vm_end=0x403000, 權限為?RW(讀+寫)。

  2. 通過?mmap?將這兩個段映射到虛擬地址空間,但物理內存尚未分配。

  3. 程序先加載到內存,用虛擬地址初始化了mm_struct,當進程首次執行?0x400000?處的指令時,觸發缺頁中斷,內核將?.text?段的內容從磁盤加載到物理內存,并更新頁表。

問題是cpu怎么知道從哪里開始執行呢?ELF文件的LEF Header中有一個Entry point address 這個就是程序的入口地址。cpu中有一個寄存器EIP其中存放的是當前執行指令的下一條指令的地址,CR3寄存器執行頁表。所以當程序開始執行的就時候就將Entry point address中的地址load到cpu中的EIP寄存器中,然后程序從入口開始執行。

動態鏈接與動態庫加載

我們知道動態庫跟我們編譯鏈接好的可執行和程序之間是獨立的存在于磁盤的。

我們的所有依賴于動態庫的可執行文件都依賴于一個這個庫:/lib64/ld-linux-x86-64.so.2,lib64/ld-linux-x86-64.so.2 是 Linux 系統中的一個動態鏈接器庫文件,主要用于在程序運行時動態加載和鏈接共享庫(.so 文件)

在我們要運行可執行程序時,我們先是跟靜態庫一樣的過程,先通過Entry point address找到程序的入口,事實上程序的入口就是_start函數,這是一個由C運?時庫(通常是glibc)或鏈接器(如ld)提供的特殊函數。在_start函數中會執行一下一系列操作:

1.設置堆棧:為程序設置一個初始的堆棧環境

2.初始化數據段:將程序的數據段(全局變量和靜態變量)從初始化數據段復制到相應的內存位置,并清零未初始化的數據段。

3.動態鏈接:_start函數會調用動態鏈接器的代碼來解析和加載程序運行所需要的動態庫,動態連接器會處理所有的符號解析和重定位,確保程序中的調用函數和變量訪問能夠正確的映射到動態庫中的實際地址。(動態鏈接實際上將鏈接的整個過程推遲到了程序加載的時候

動態連接的優點:可以看到對于不同的進程如果需要同一個庫中的函數,我們只需要在內存中加載一份動態庫,分配一份物理地址即可,但是對于靜態庫來說,其可執行文件就是已經包含靜態庫中的函數的了,所以其磁盤空間和內存空間都是會產生浪費的。

動態鏈接器? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 動態鏈接器(如ld-linux.so)負責在程序運?時加載動態庫

當程序啟動時,動態鏈接器會解析程序中的動態庫依賴,并加載這些庫到內存中。
環境變量和配置?件
Linux系統通過 環境變量(如LD_LIBRARY_PATH) 配置?件(如/etc/ld.so.conf 及其?配置
?件)來指定動態庫的搜索路徑。 這些路徑會被動態鏈接器在加載動態庫時搜索。
緩存文件
為了提?動態庫的加載效率,Linux系統會維護?個名為 /etc/ld.so.cache的緩存?件 。 該?件包含了系統中所有已知動態庫的路徑和相關信息,動態鏈接器在加載動態庫時會?先 搜索這個緩存?件。
4.調? __libc_start_main :?旦動態鏈接完成, _start 函數會調? __libc_start_main (這是glibc提供的?個函數)。 __libc_start_main 函數負責執? ?些額外的初始化?作,?如設置信號處理函數、初始化線程庫(如果使?了線程)等。
5. 調? main 函數:最后, __libc_start_main 函數會調?程序的 main 函數,此時程序的執?控制權才正式交給??編寫的代碼。
6. 處理 main 函數的返回值:當 main 函數返回時, __libc_start_main 會負責處理這個返回
值,并最終調? _exit 函數來終?程序。
上述過程描述了C/C++程序在 main 函數之前執?的?系列操作,但這些操作對于?多數程序員來說 是透明的。程序員通常只需要關注 main 函數中的代碼,?不需要關?底層的初始化過程。然?,了解這些底層細節有助于更好地理解程序的執?流程和調試問題。

但是我們的程序具體是怎么和庫映射起來的?

首先可執行程序中存有依賴的動態庫的路徑,通過這個路徑可以將動態庫加載到物理內存。動態庫也是采用了平坦模式進行編址,我們叫做庫中方法的偏移量。然后通過創建新的mm_area_struct用庫的大小開辟一段新的進程地址空間,就能得到庫的虛擬地址,并建立頁表映射關系。通過庫的虛擬地址和庫中的偏移量就能找到對應的方法。


?所以庫函數的調用機制如下:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?庫已經被我們映射到了當前進程的地址空間中 庫的虛擬起始地址我們也已經知道了,庫中每?個? ??法的偏移量地址我們也知道

?所有:訪問庫中任意?法,只需要知道庫的起始虛擬地址+?法偏移量即可定位庫中的?法

??且:整個調?過程,是從 代碼區跳轉到共享區,調?完畢在返回到代碼區 ,整個過程完全在進? ?程地址空間中進?的。

GOT表

也就是說,我們的程序運?之前,先把所有庫加載并映射,所有庫的起始虛擬地址都應該提前知道
然后對我們加載到內存中的程序的庫函數調?進?地址修改,在內存中?次完成地址設置 (這個叫做加載地址重定位) 但是內存中的代碼段是不可寫的。所以:動態鏈接采?的做法是在.data (可執?程序或者庫??)中專?預留??區域?來存放函數的跳轉地址,它也被叫做全局偏移表GOT,表中每?項都是本運?模塊要引?的?個全局變量或函數的地址。

那GOT具體是怎么工作的呢?? ? ?比如,程序在編譯時,對于外部函數比如printf,編譯器并不知道它運行時的具體地址,所以會在GOT中生成一個條目。當程序第一次調用printf時,動態鏈接器(如ld-linux.so)會找到printf的實際地址并填入GOT中,之后的調用就直接使用這個地址了。這樣可以實現延遲綁定,也就是PLT(Procedure Linkage Table)和GOT配合使用。PLT負責跳轉到GOT中的地址,而GOT存儲實際的地址。第一次調用時,GOT中的地址可能指向PLT中的解析代碼,由動態鏈接器完成地址解析后,GOT中的條目會被更新為正確的地址。另外,GOT還可能用于全局變量的訪問,因為動態庫中的全局變量地址在加載時確定,也需要通過GOT來間接訪問。

但在不 同進程的地址空間中,各動態庫的絕對地址、相對位置都不同。反映到GOT表上 ,就是每個進程的 每個動態庫都有獨?的GOT表 ,所以進程間不能共享GOT表。
在單個.so下,由于GOT表與 .text 的相對位置是固定的,我們完全可以利?CPU的相對尋址來找
到GOT表。
在調?函數的時候會?先查表,然后根據表中的地址來進?跳轉,這些地址在動態庫加載的時候會
被修改為真正的地址。
這種?式實現的動態鏈接就被叫做 PIC 地址?關代碼 。換句話說,我們的動態庫不需要做任何修
改,被加載到任意內存地址都能夠正常運?,并且能夠被所有進程共享,這也是為什么之前我們給
編譯器指定-fPIC參數的原因,PIC=相對編址+GOT。
總結: GOT表中存儲的地址應該是虛擬地址。當程序執行跳轉指令時,使用這個虛擬地址,然后由MMU通過頁表將其轉換為物理地址,從而訪問實際的內存位置其他時候都是直接通過頁表維持虛擬地址跟物理地址之間的關系。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/70236.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/70236.shtml
英文地址,請注明出處:http://en.pswp.cn/web/70236.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Web 后端 請求與響應

一 請求響應 1. 請求(Request) 客戶端向服務器發送的HTTP請求,通常包含以下內容: 請求行:HTTP方法(GET/POST等)、請求的URL、協議版本。 請求頭(Headers):…

【Excel筆記_6】條件格式和自定義格式設置表中數值超過100保留1位,超過1000保留0位,低于100為默認

方法一:自定義格式 選中需要設置格式的單元格區域。右鍵選擇設置單元格格式,或者在工具欄中選擇開始 -> 數字 -> 自定義格式。在類型框中輸入以下自定義格式: [>1000]0;[>100]0.0;G/通用格式解釋: [>1000]0&…

排序與算法:希爾排序

執行效果 希爾排序的執行效果是這樣的: 呃……看不懂嗎?沒關系,接著往下看介紹 算法介紹 希爾排序算法(Shell Sort)是按其設計者希爾(Donald Shell)的名字命名,該算法由 1959 年公布…

Python HTTP 請求工具類 HttpUtils:簡化 HTTP 請求的高效工具

在現代的 Web 開發和 API 集成中,HTTP 請求是最常見的操作之一。無論是獲取數據、提交表單,還是與 RESTful API 交互,我們都需要頻繁地發送 HTTP 請求。為了簡化這些操作,提升代碼的可讀性和可維護性,我們可以使用一個高效的工具類——HttpUtils。本文將詳細介紹 HttpUtil…

親測Windows部署Ollama+WebUI可視化

一. Ollama下載 登錄Ollama官網(Ollama)點擊Download進行下載 如果下載很慢可用以下地址下載: https://github.com/ollama/ollama/releases/download/v0.5.7/OllamaSetup.exe 在DeepSeek官網上,你可以直接點擊【model】 到達這個界面之后,…

用xml配置spring, bean標簽有哪些屬性?

用xml配置spring, bean標簽有哪些屬性? 在Spring框架中&#xff0c;使用XML配置文件時&#xff0c;<bean>標簽用于定義一個Bean。以下是一些常用的<bean>標簽屬性&#xff1a; 1. class 描述&#xff1a;指定Bean的類名。示例&#xff1a;<bean id"myBe…

50頁PDF|數字化轉型成熟度模型與評估(附下載)

一、前言 這份報告依據GBT 43439-2023標準&#xff0c;詳細介紹了數字化轉型的成熟度模型和評估方法。報告將成熟度分為五個等級&#xff0c;從一級的基礎轉型意識&#xff0c;到五級的基于數據的生態價值構建與創新&#xff0c;涵蓋了組織、技術、數據、資源、數字化運營等多…

golang panic信息捕獲

背景 我們的日志接入阿里云sls平臺&#xff0c;但是&#xff0c;日志是以json的格式存儲在阿里云sls平臺上&#xff0c;程序中產生的error,info等日志都可以實現以json的格式打印。但是&#xff0c;golang程序中產生的panic信息本身不是以json的格式輸出&#xff0c;這就導致p…

攔截器VS過濾器:Spring Boot中請求處理的藝術!

目錄 一、攔截器&#xff08;Interceptor&#xff09;和過濾器&#xff08;Filter&#xff09;&#xff1a;都是“守門員”&#xff01;二、如何實現攔截器和過濾器&#xff1f;三、攔截器和過濾器的區別四、執行順序五、真實的應用場景六、總結 &#x1f31f;如果喜歡作者的講…

FastGPT及大模型API(Docker)私有化部署指南

??歡迎關注【AI技術開發者】 ? 經過優化&#xff0c;在不影響FastGPT功能的情況下&#xff0c;大幅降低了部署的設備配置要求&#xff0c;僅需1c1h即可正常部署使用。 官方要求配置&#xff1a; ? ? 優化后的實際占用情況&#xff1a; 運行內存僅需370M&#xff08…

解決 WSL Ubuntu 中 /etc/resolv.conf 自動重置問題

解決 WSL Ubuntu 中 /etc/resolv.conf 自動重置問題 前言問題描述問題原因嘗試過的命令及分析解決方案&#xff1a;修改 wsl.conf 禁用自動生成總結 前言 在使用 Windows Subsystem for Linux (WSL) 的 Ubuntu 子系統時&#xff0c;你可能會遇到 /etc/resolv.conf 文件被自動重…

【第15章:量子深度學習與未來趨勢—15.3 量子深度學習在圖像處理、自然語言處理等領域的應用潛力分析】

一、開篇:為什么我們需要關注這場"量子+AI"的世紀聯姻? 各位技術愛好者們,今天我們要聊的這個話題,可能是未來十年最值得押注的技術革命——量子深度學習。這不是簡單的"1+1=2"的物理疊加,而是一場可能徹底改寫AI發展軌跡的范式轉移。 想象這樣一個…

企業軟件合規性管理:構建高效、安全的軟件資產生態

引言 在數字化轉型的浪潮下&#xff0c;企業的軟件使用方式日益多元化&#xff0c;涉及云端、訂閱制、永久授權及浮動許可等多種模式。然而&#xff0c;隨著軟件資產的增多&#xff0c;企業面臨著合規性管理的嚴峻挑戰&#xff1a;非法軟件使用、許可證管理不當、軟件資產閑置…

python學習筆記,python處理 Excel、Word、PPT 以及郵件自動化辦公

文章目錄 前言一、環境搭建1. 下載 Python2. 安裝 Python 二、處理 Excel 文件&#xff08;openpyxl庫&#xff09;三、 處理 Word 文件&#xff08;python-docx庫&#xff09;四、 處理 PPT 文件&#xff08;python-pptx庫&#xff09;五、 自動發送郵件&#xff08;smtplib和…

Python 基礎-循環

目錄 簡介 break continue 小結 簡介 要計算123&#xff0c;我們可以直接寫表達式&#xff1a; >>> 1 2 3 6要計算123...10&#xff0c;勉強也能寫出來。 但是&#xff0c;要計算123...10000&#xff0c;直接寫表達式就不可能了。 為了讓計算機能計算成千上…

簡單易懂,解析Go語言中的Channel管道

Channel 管道 1 初始化 可用var聲明nil管道&#xff1b;用make初始化管道&#xff1b; len()&#xff1a; 緩沖區中元素個數&#xff0c; cap()&#xff1a; 緩沖區大小 //變量聲明 var a chan int //使用make初始化 b : make(chan int) //不帶緩沖區 c : make(chan stri…

python-leetcode 36.二叉樹的最大深度

題目&#xff1a; 給定一個二叉樹root,返回其最大深度 二叉樹的最大深度是指從根節點到最遠葉子節點的最長路徑上的節點數 方法一&#xff1a;深度優先搜索 知道了左子樹和右子樹的最大深度l和r&#xff0c;那么該二叉樹的最大深度即為:max(l,r)1 而左子樹和右子樹的最大深…

RESTful 的特點與普通 Web API 的區別

RESTful 是一種設計風格&#xff0c;而不僅僅是普通的 Web API。它遵循一些特定的原則和約束&#xff0c;使得 API 更加簡潔、可擴展和易于理解。以下是 RESTful 的特點&#xff0c;以及與普通 Web API 的區別&#xff1a; RESTful 的特點 1. 資源導向 RESTful API 的核心是資…

結構風荷載理論與Matlab計算

結構風荷載理論與matlab計算的實例程序&#xff0c;適合初學者理解matlab風荷載計算 資源文件列表 程序_結構風荷載理論與Matlab計算/chapter1/exam_simWind_1_1.m , 1035 程序_結構風荷載理論與Matlab計算/chapter1/Extrmv.m , 303 程序_結構風荷載理論與Matlab計算/chapter1…

numpy(02 數據類型和數據類型轉換)

numpy(01 入門) 目錄 一、Python NumPy 數據類型 1.1 NumPy 基本類型 1.2 數據類型對象 (dtype) 1.3 具體實例 二、Numpy數據類型轉換 2.1 浮點數據轉換 2.2 整型數據轉換 2.3 浮點數轉整數 一、Python NumPy 數據類型 1.1 NumPy 基本類型 下表列舉了常用 NumPy 基…