目錄
前言:?
一、ELF文件的類型
二、ELF文件的組成格式?
1. ELF頭部(ELF Header)
2. 節頭表(Section Header Table)
3. 程序頭表(Program Header Table)
4. 節(Sections)與段(Segments)
?三、ELF文件從形成到加載輪廓
1、ELF可執行文件形成過程
2、 可執行文件從磁盤加載到內存section的變化
1、Section與Segment的基本關系
2、合并機制與內存優化
四、理解動靜態庫鏈接和加載?
?編輯
1. 動態鏈接核心原理
2. 程序啟動與動態鏈接流程
動態鏈接器:
1. 位置無關代碼(PIC)基礎原理
前言:?
ELF(Executable and Linkable Format):
是Unix/Linux系統下的標準可執行文件、目標文件和共享庫格式。
是一種文件格式的名稱
一、ELF文件的類型
ELF文件根據用途可分為以下幾種主要類型:
-
?可重定位文件(Relocatable File)?
- 文件擴展名通常為
.o
- 由編譯器生成,包含代碼和數據但未指定絕對地址
- 用于與其他目標文件鏈接生成可執行文件或共享庫
- 在Linux系統中通過
gcc -c
命令生成
- 文件擴展名通常為
-
?可執行文件(Executable File)?
- 通常沒有特定擴展名(如Linux中的
a.out
) - 包含可直接執行的程序代碼和數據
- 由鏈接器處理可重定位文件后生成
- 包含程序入口點,可直接被操作系統加載執行
- 通常沒有特定擴展名(如Linux中的
-
?共享目標文件(Shared Object File)?
- 文件擴展名通常為
.so
- 包含可共享的代碼和數據,用于動態鏈接
- 可在兩種情況下使用:
- 鏈接時與其他文件鏈接生成新目標文件
- 運行時與可執行文件結合作為進程映像的一部分
- 文件擴展名通常為
-
?核心轉儲文件(Core Dump File)?
- 由操作系統在程序崩潰時生成
- 包含程序崩潰時的內存狀態和寄存器信息
- 用于調試和故障排除
類型 | 擴展名 | 主要用途 | 生成方式 | 使用場景 |
---|---|---|---|---|
可重定位文件 | .o | 鏈接 | 編譯器生成 | 鏈接階段 |
可執行文件 | 無/.out | 執行 | 鏈接器生成 | 運行階段 |
共享目標文件 | .so | 動態鏈接 | 鏈接器生成 | 鏈接和運行階段 |
核心轉儲文件 | core | 調試 | 系統生成 | 調試階段 |
二、ELF文件的組成格式?
?基本信息如圖:
1. ELF頭部(ELF Header)
- 位于文件起始位置,固定大小(32位系統52字節,64位系統64字節)
- 包含文件的基本信息:
- 魔數(Magic Number):
0x7f 0x45 0x4c 0x46
(ASCII為"ELF") - 文件類型(可執行/可重定位/共享庫等)
- 目標體系結構(如x86、ARM)
- 程序入口地址(可執行文件)
- 節頭表和程序頭表的位置和大小信息
- 魔數(Magic Number):
2. 節頭表(Section Header Table)
- 包含多個節頭表條目,每個條目描述一個節(section)的信息
- 主要作用:
- 記錄各節的名稱、類型、文件偏移、大小、讀寫權限等
- 主要用于鏈接過程(鏈接視圖)
- 對于可重定位文件是必須的,對于可執行文件是可選的
3. 程序頭表(Program Header Table)
- 包含多個程序頭表條目,每個條目描述一個段(segment)的信息
- 主要作用:
- 描述如何將文件中的段加載到內存
- 主要用于執行過程(執行視圖)
- 對于可執行文件和共享庫是必須的,對于可重定位文件可能為空
4. 節(Sections)與段(Segments)
-
?節(Section)?:
- 鏈接視圖的基本單位
- 常見節包括:
.text
:可執行代碼.data
:已初始化的全局/靜態變量.bss
:未初始化的全局/靜態變量.rodata
:只讀數據.symtab
:符號表.strtab
:字符串表.rel.text
/.rel.data
:重定位信息
-
?段(Segment)?:
- 執行視圖的基本單位
- 由多個具有相同權限的連續節組成
- 常見段類型:
LOAD
:需加載到內存的段(代碼段、數據段)DYNAMIC
:動態鏈接信息INTERP
:指定動態鏈接器路徑10
?三、ELF文件從形成到加載輪廓
1、ELF可執行文件形成過程
- step-1:將多份 C/C++ 源代碼,翻譯成為?標 .o ?件
- step-2:將多份 .o ?件section進?合并
2、 可執行文件從磁盤加載到內存section的變化
1、Section與Segment的基本關系
ELF文件具有雙重視圖特性:
- ?鏈接視圖?:以Section為基本單位,包含
.text
(代碼)、.data
(已初始化數據)、.bss
(未初始化數據)等,主要用于鏈接階段- ?執行視圖?:以Segment為基本單位,由多個屬性相同的Section合并而成,用于運行時加載
關鍵區別:
特性 Section Segment 用途 鏈接階段 執行階段 組織結構 獨立功能單元 合并后的內存塊 表結構 Section Header Table Program Header Table 必要性 可執行文件可選 可執行文件必須 2、合并機制與內存優化
合并過程的核心目的是?減少內存碎片?,提高頁面使用效率:
?為什么要合并
- 現代系統采用分頁加載機制,典型頁大小為4KB(4096字節)
- 示例:未合并時
.text
(4097B)和.data
(1B)占用3頁(2+1),合并后僅需2頁(4098B)?合并規則?:
- 相同內存屬性(可讀/可寫/可執行)的Section會被合并8
- 必須具有相同的加載需求(需要運行時申請空間)8
- 典型合并模式:
- 代碼段:合并
.text
、.rodata
等只讀可執行Section- 數據段:合并
.data
、.bss
等可讀寫Section?程序頭表(Program Header Table)作用?:
- 每個表項(Elf32_Phdr/Elf64_Phdr)描述一個Segment的:
- 類型(p_type):如PT_LOAD(需加載段)
- 標志位(p_flags):讀寫執行權限
- 文件偏移(p_offset)和大小(p_filesz)
- 內存地址(p_vaddr)和大小(p_memsz)
- 對齊要求(p_align)8
- 加載器根據這些信息建立進程內存映像
四、理解動靜態庫鏈接和加載?
1、靜態庫
下面是樣例代碼?
我們可以通過objdump -d 命令:將代碼段(.text)進?反匯編查看,其中callq的加載到內存的機械碼,而后面跟著的一串0代表訪問的函數地址,在鏈接中才會填充地址,也叫做地址重定位?
靜態鏈接就是把庫中的.o進?合并,和上述過程?樣
所以鏈接其實就是將編譯之后的所有?標?件連同?到的?些靜態庫運?時庫組合,拼裝成?個獨?的可執??件。其中就包括我們之前提到的地址修正,當所有模塊組合在?起之后,鏈接器會根據我們的.o?件或者靜態庫中的重定位表找到那些需要被重定位的函數全局變量,從?修正它們的地址。這其實就是靜態鏈接的過程
研究靜態鏈接其實就是研究不同的.o文件是如何鏈接到一起的,.o類型文件也叫做可重定位目標文件
1. 動態鏈接核心原理
動態鏈接的本質是將鏈接過程推遲到程序加載時完成,這一機制實現了代碼共享和內存優化。當執行一個程序時,操作系統會執行以下關鍵步驟:
- ?程序加載?:將程序的可執行代碼和依賴的動態庫加載到內存
- ?地址分配?:為每個動態庫動態分配內存地址(ASLR技術確保地址隨機化)
- ?地址空間映射?:將動態庫映射到進程的地址空間
- ?符號解析?:解析程序對動態庫中符號的引用
動態鏈接的兩個關鍵階段:
- ?地址空間映射?:通過
mmap
系統調用將動態庫映射到進程地址空間 - ?符號綁定?:通過PLT/GOT機制實現函數調用跳轉
2. 程序啟動與動態鏈接流程
C/C++程序的執行并非直接從main函數開始,而是經歷以下初始化過程
階段 | 執行內容 | 負責組件 |
---|---|---|
_start | 設置堆棧、初始化數據段 | crt0.o |
動態鏈接 | 加載共享庫、符號解析 | ld-linux.so |
__libc_start_main | 線程初始化、信號處理 | glibc |
main | 用戶代碼執行 | 用戶程序 |
詳細流程:
- ?入口點_start?:由C運行時庫提供,建立基本執行環境
- ?動態鏈接器調用?:通過.interp段定位ld-linux.so
- ?庫加載與重定位?:
- 解析DT_NEEDED條目加載依賴庫
- 處理.rel.plt和.rel.dyn重定位表
- ?控制權轉移?:通過__libc_start_main最終調用main函數
動態鏈接器:
? 動態鏈接器(如ld-linux.so)負責在程序運?時加載動態庫。
? 當程序啟動時,動態鏈接器會解析程序中的動態庫依賴,并加載這些庫到內存中。
環境變量和配置?件:
? Linux系統通過環境變量(如LD_LIBRARY_PATH)和配置?件(如/etc/ld.so.conf及其?配置?件)來指定動態庫的搜索路徑。
? 這些路徑會被動態鏈接器在加載動態庫時搜索。
緩存?件:
? 為了提?動態庫的加載效率,Linux系統會維護?個名為/etc/ld.so.cache的緩存?件。
? 該?件包含了系統中所有已知動態庫的路徑和相關信息,動態鏈接器在加載動態庫時會?先
搜索這個緩存?件
1. 位置無關代碼(PIC)基礎原理
位置無關代碼(Position Independent Code)是動態庫能夠實現多進程共享的核心技術,其核心特性包括:
- ?地址無關性?:代碼可以在內存任意位置加載執行,無需修改指令
- ?相對尋址?:所有地址引用都基于當前指令指針或全局偏移表(GOT)
- ?重定位延遲?:符號解析推遲到加載或運行時完成
PIC的實現主要通過兩種機制:
- ?PC相對尋址?:用于函數內部跳轉和局部數據訪問
- ?全局偏移表(GOT)?:存儲外部變量和函數的絕對地址,通過間接訪問實現重定位
?
基本過程
- 通過mm_struct中的變量找到有關共享區的結構體,根據里面的成員指針變量找到路徑
- 根據路徑找到磁盤中的數據塊加載到內存
- 發生映射關系關聯起來
- 得到庫的起始虛擬地址
- 數據區會有一個名為.GOT的表記錄庫函數的偏移量,映射過后,表會根據庫的起始虛擬地址進行修改得到完整的訪問共享區的地址
?--------------------------------------------------------------------------------------------------------------------------