1.ELF文件
要理解編譯鏈鏈接的細節,我們不得不了解?下ELF?件。其實有以下四種?件其實都是ELF?件:
? 可重定位?件(Relocatable File ) :即 xxx.o ?件。包含適合于與其他?標?件鏈接來創
建可執??件或者共享?標?件的代碼和數據。
? 可執??件(Executable File ) :即可執?程序。
? 共享?標?件(Shared Object File ) :即 xxx.so?件。
? 內核轉儲(core dumps) ,存放當前進程的執?上下?,?于dump信號觸發。
?個ELF?件由以下四部分組成:
? ELF頭 (ELF header) :描述?件的主要特性。其位于?件的開始位置,它的主要?的是定位?
件的其他部分。
? 程序頭表(Program header table) :列舉了所有有效的段(segments)和他們的屬性。表?
記著每個段的開始的位置和位移(offset)、?度,畢竟這些段,都是緊密的放在?進制?件中,
需要段表的描述信息,才能把他們每個段分割開。
? 節頭表(Section header table) :包含對節(sections)的描述。
? 節(Section ):ELF?件中的基本組成單位,包含了特定類型的數據。ELF?件的各種信息和
數據都存儲在不同的節中,如代碼節存儲了可執?代碼,數據節存儲了全局變量和靜態數據等。
最常?的節:
? 代碼節(.text):?于保存機器指令,是程序的主要執?部分。
? 數據節(.data):保存已初始化的全局變量和局部靜態變量。

2.ELF從形成到加載輪廓
2-1 ELF形成可執?
? step-1:將多份 C/C++ 源代碼,翻譯成為?標 .o ?件
? step-2:將多份 .o ?件section進?合并

2-2 ELF可執??件加載
當用戶執行一個ELF可執行文件時,操作系統通過加載器(Loader)和動態鏈接器(Dynamic Linker)協作完成內存映射、權限設置、依賴解析等關鍵操作
?個ELF會有多種不同的Section,在加載到內存的時候,也會進?Section合并,形成segment
合并原則:相同屬性,?如:可讀,可寫,可執?,需要加載時申請空間等.
這樣,即便是不同的Section,在加載到內存中,可能會以segment的形式,加載到?起
很顯然,這個合并?作也已經在形成ELF的時候,合并?式已經確定了,具體合并原則被記錄在了
ELF的 程序頭表 (Program header table) 中
注意:為什么要講section合并成segment
Section合并的主要原因是為了減少??碎?,提?內存使?效率。如果不進?合并,
假設????為4096字節(內存塊基本??,加載,管理的基本單位),如果.text部分
為4097字節,.init部分為512字節,那么它們將占?3個??,?合并后,它們只需2個
??。
此外,操作系統在加載程序時,會將具有相同屬性的section合并成?個?的
segment,這樣就可以實現不同的訪問權限,從?優化內存管理和權限訪問控制
3.理解連接與加載
3-1 靜態鏈接
無論是自己的.o文件還是靜態庫中的.o文件本質都是把.o文件進行鏈接的過程所以靜態鏈接就是研究靜態鏈接的過程
3-2靜態鏈接的核心流程
-
輸入文件準備
-
目標文件:由編譯器生成的
.o
文件,包含代碼、數據及未解析的符號引用。 -
靜態庫:通過
ar
工具打包的.a
文件,本質是多個.o
文件的集合(如libmath.a
包含sin.o
、cos.o
等)。
-
-
符號解析(Symbol Resolution)
-
符號表合并:鏈接器遍歷所有輸入文件,構建全局符號表。
-
處理未定義符號:若同一符號被多文件定義(如兩個
.o
文件均定義global_var
),觸發重復定義錯誤。
-
-
重定位(Relocation)
-
地址分配:為所有節(Section)分配運行時內存地址(如
.text
從0x400000
開始)。 -
修正引用:根據最終地址,修改代碼中的相對偏移或絕對地址。
復制
下載
// 目標文件中的未重定位指令(假設函數add的地址未確定) call 0x00000000 // 占位符,鏈接時替換為add的實際地址
-
-
生成可執行文件
-
合并所有目標文件的
.text
、.data
等節到統一Segment。 -
生成程序頭表(Program Header)供加載器使用。
-
3-3ELF加載與進程地址空間
3-2-1 虛擬地址/邏輯地址
思考一下一個進程在沒有加載到內存的時候它有沒有地址呢?
?個ELF程序,在沒有被加載到內存的時候,本來就有地址,當代計算機?作的時候,都采?"平坦
模式"進??作。所以也要求ELF對??的代碼和數據進?統?編址,下?是 objdump -S 反匯編
之后的代碼

3-4動態鏈接與動態庫加載
進程是如何鏈接到庫的呢

進程間是如何共享庫的呢

我們可以思考一下在庫加載到內存的時候我們在創建每一個進程的時候是否要給每個進程的重新加載一個庫大家可以思考一下
我們是如何跟庫具體映射起來的呢看一張圖就明白了

總結
靜態鏈接的出現,提?了程序的模塊化?平。對于?個?的項?,不同的?可以獨?地測試和開發
??的模塊。通過靜態鏈接,?成最終的可執??件。
我們知道靜態鏈接會將編譯產?的所有?標?件,和?到的各種庫合并成?個獨?的可執??件,
其中我們會去修正模塊間函數的跳轉地址,也被叫做編譯重定位(也叫做靜態重定位)。
?動態鏈接實際上將鏈接的整個過程推遲到了程序加載的時候。?如我們去運??個程序,操作系
統會?先將程序的數據代碼連同它?到的?系列動態庫先加載到內存,其中每個動態庫的加載地址
都是不固定的,但是?論加載到什么地?,都要映射到進程對應的地址空間,然后通過.GOT?式進 ?調?(運?重定位,也叫做動態地址重定位)。