庫其實就是個文件
下面是文件后綴
靜態庫:.a(linux)? ?.lib(windows)
動態庫:.so(linux) .dll(windows)
靜態庫的制作
ar? ?-rc? ?libmystdio.a? ?my_stdio.o? ?my_string.o
ar是歸檔工具,rc表示replace和create,ar跟tar有點像,把.o文件打包到一塊,其他啥也不干
ar? -tv??libmystdio.a?
動態庫的制作
?
庫的使用

ELF文件
那些文件是ELF文件?
.o目標文件
.exe可執行文件
.so動態庫文件
核心轉儲文件
ELF文件的格式
ELF頭(ELF header)
1. 節頭表(Section Header Table)??
- ??描述目標文件(
.o
)中的各個??節(section)??(如?.text
、.data
、.symtab
?等),供??靜態鏈接器??(ld
)使用 - 鏈接時,鏈接器會合并不同目標文件中的??同名節??(如將所有?
.text
?合并到代碼段)成段。 - ??動態庫/可執行文件可以沒有節頭表??。
??2. 程序頭表(Program Header Table,段頭表)??
- ??描述可執行文件或動態庫中的??段(segment)??(如代碼段、數據段),供操作系統??加載器??(
execve
/ld.so
)使用。 - ??加載時,操作系統根據程序頭表將段映射到內存(如?
LOAD
?類型的段) - ?動態庫和可執行文件必須有程序頭表??(否則無法加載),可以沒有節頭表。
??3. ELF 頭?
- ??
e_entry
?字段??:指定程序執行的起始地址(即?_start
?函數的地址),加載后會將此地址賦給 ??PC/RIP?? 寄存器。
目標文件的節詳解
1. 代碼與數據節??
??節名?? | ??作用?? | ??常見屬性?? |
---|---|---|
.text | 存儲??代碼??(函數、指令) | AX (可執行、只讀) |
.data | 存儲??已初始化的全局/靜態變量?? | WA (可寫、已初始化) |
.bss | 存儲??未初始化的全局/靜態變量??(實際不占文件空間) | WA (可寫、未初始化) |
.rodata | 存儲??只讀數據??(如字符串常量、const 變量) | A (只讀) |
鏈接后.text節和.rodata節會合并為代碼段,.data節合并為數據段,.bss節合并為bss段
??2. 符號與重定位節??
??節名?? | ??作用?? | ??鏈接階段關鍵性?? |
---|---|---|
.symtab | ??符號表??(函數、變量名及其地址/大小) | ? 靜態鏈接必需 |
.rel.text | .text 節的??重定位條目??(修正代碼中的地址引用) | ? 必需 |
.rel.data | .data 節的??重定位條目??(修正數據中的地址引用) | ? 必需 |
符號表的作用:
1.記錄符號定義,符號是變量還是函數,以及對應地址
2.標記未定義符號
3.輔助重定位
重定位表的作用:
目標文件中代碼和數據中對符號(函數/變量)的引用地址是臨時的。重定位表指導鏈接器如何將這些臨時地址替換為??最終的虛擬地址??。
??3.?動態鏈接相關節??(僅當啟用-fPIC編譯
時存在)
??節名?? | ??作用?? | ??動態庫必需?? |
---|---|---|
.got | 全局偏移表(Global Offset Table) | ? 動態庫需要 |
.plt | 過程鏈接表(Procedure Linkage Table) | ? 動態庫需要 |
靜態鏈接的?過程
??(1) 輸入文件準備??
- ??目標文件(
.o
)??:含main的目標文件 - ??靜態庫(
.a
)??:本質是多個目標文件的打包(通過ar
工具生成),例如:ar rcs libfoo.a foo.o bar.o # 將foo.o和bar.o打包為libfoo.a
??(2) 鏈接器(ld)的工作流程??
-
??符號解析(Symbol Resolution)??
- 合并所有目標文件的符號表(
.symtab
),檢查是否存在??未定義符號??(標記為U
)。 - ??若存在未定義符號??:直接報錯(如
undefined reference to 'printf'
),鏈接失敗。
- 合并所有目標文件的符號表(
-
??節合并(Section Merging)??
- 將同名節(如
.text
、.data
)從不同目標文件合并為??段(Segment)??:- 所有
.text
?→ 代碼段(可執行) - 所有
.data
?→ 數據段(可讀寫) - 所有
.bss
?→ BSS段(未初始化數據,不占文件空間)
- 所有
- 將同名節(如
-
??分配虛擬地址(Address Assignment)??
- 鏈接器為每個段分配??最終的虛擬地址??。例如:
.text 0x400000 (代碼段基地址) .data 0x600000 (數據段基地址)
- 鏈接器為每個段分配??最終的虛擬地址??。例如:
-
??重定位(Relocation)??
- 根據重定位表(
.rel.text
/.rel.data
)修正代碼和數據中對符號的引用:- ??修正代碼中的地址??:如
call 0x0
?→?call 0x400100
(函數foo
的最終地址)。 - ??修正數據中的指針??:如
mov rax, [0x0]
?→?mov rax, [0x600020]
(變量var
的地址)。
- ??修正代碼中的地址??:如
- 根據重定位表(
-
??生成可執行文件??
輸出一個完整的ELF可執行文件(a.out
),包含程序頭表(描述如何加載段到內存)。
動態鏈接的過程?
??1.生成可執行文件時的鏈接??
- ??輸入文件??:
- 目標文件(
main.o
) - 動態庫(
.so
,如?libc.so
)
- 目標文件(
- ??靜態鏈接器(ld)的操作??:
- ??符號解析??:檢查?
main.o
?中的未定義符號(如?printf
)是否在動態庫的?.dynsym
?中。 - ??生成 PLT/GOT??:
- 在可執行文件中創建?
.plt
?和?.got.plt
?節。 - 將對動態庫函數的調用(如?
call printf
)改為?call printf@plt
。
- 在可執行文件中創建?
- ??記錄依賴關系??:在可執行文件的?
.dynamic
?段中注明依賴的動態庫(如?libc.so.6
)。
- ??符號解析??:檢查?
??2. 運行時動態加載流程??
??(1) 加載可執行文件??
- ??操作系統??:
- 讀取可執行文件的 ??程序頭表??(Program Header Table),將代碼段(
.text
)和數據段(.data
)映射到進程的虛擬地址空間。 - 從ELF頭中讀程序入口,將?
PC
?寄存器設為?e_entry
(入口地址,通常是?_start
)。
- 讀取可執行文件的 ??程序頭表??(Program Header Table),將代碼段(
??(2) 加載動態庫??
- ??動態鏈接器(ld.so)??:
- 根據 可執行文件的
.dynamic
?段加載所有依賴的動態庫(如?libc.so
)到 ??隨機地址??(ASLR)。 - 重定位got表的里變量的地址,函數地址不重定位,有另一套機制
- 根據 可執行文件的
got表和plt表說明
1.當動態庫加載進地址空間,ld.so重定位got表中變量的地址,而代碼段中對動態庫中變量的訪問,編譯的指令為訪問got表中目標變量的地址,如何做到,就是mov eax【pc+偏移量】,偏移量是pc到目標變量GOT條目的偏移??
2.動態庫加載進地址空間,動態鏈接器不會重定位got表中函數項,因為
在鏈接時會符號解析,如果main.o中使用了動態庫的函數,那么調用函數的指令會從call printf被換成call printf@plt,去跳轉plt表對應函數條目
printf@plt:
? jmp func@got?; 跳轉got項
? pushq $index ? ? ? ; 壓入重定位表索引(如 printf 在 .rela.plt 中的位置)
? jmp .plt ? ? ? ? ? ; 跳轉到動態鏈接器(ld.so),重定位函數
plt表是在代碼段,因為plt是不可更改的,而got在數據段,因為函數項是要動態重定位的,got表中函數項在編譯時被編譯成plt表中對應函數條目的push指令
第一次調用庫函數時,call func@plt 跳轉plt對應函數條目,跳轉到got項,然后pushq 重定位表索引到隊列,調用動態鏈接器去重定位庫函數,將地址寫進got,然后調用函數
第二次調用庫函數,call func@plt? 跳轉plt表對應函數條目,跳轉got項,然后直接調用函數