計算機系統篇之鏈接(3):靜態鏈接(上)
Author:stormQ
Saturday, 21. December 2019 11:58AM
引入靜態庫的動機
引入靜態庫是爲了更好地解決“編譯器開發者如何將標準庫函數提供給調用者使用”的問題。
解決該問題的不同方式及比較如下所示:
解決方式 | 優點 | 缺點 |
---|---|---|
方式1:編譯器識別程序中對標準庫函數的調用並直接生成相應的代碼 | ||
方式2:將所有的標準庫函數實現放到同一個可重定位目標文件中(即 .o 文件) | ||
方式3:將每個標準庫函數實現放到一個獨立的可重定位目標文件中,即一個可重定位目標文件中只存放一個庫函數的實現 | ||
方式4:靜態庫 | --static 選項)生成可執行目標文件,那麼不同的可執行目標文件中可能存在相同的可重定位目標文件,從而造成一定的磁盤空間浪費。 |
如何生成靜態庫
靜態庫的文件格式被稱爲存檔(archive),以.a
爲後綴。archive 是一組連接起來的可重定位目標文件的集合,有一個頭部用於描述每個成員可重定位目標文件的大小和位置。
生成靜態庫的命令:
$ ar rs <target static library> <object file 1> <object file n>
# $ ar rs libtest.a sum.o test.o
注:操作碼r
表示將指定的可重定位目標文件插入到靜態庫中,如果沒有靜態庫則創建。修飾碼s
表示將索引(ar
爲可重定位目標文件的每個符號創建一個索引)添加到歸檔文件中,或者更新它(如果它已經存在)。建立索引的目的:可以加速到庫的鏈接,並允許庫中的函數相互調用,而不考慮它們在歸檔文件中的位置。
1)查看靜態庫中有哪些可重定位目標文件
# 查看靜態庫中所有的可重定位目標文件
$ ar t /usr/aarch64-linux-gnu/lib/libc.a
# 查看靜態庫中指定的可重定位目標文件
$ ar t /usr/aarch64-linux-gnu/lib/libc.a printf.o scanf.o
注:操作碼t
只顯示可重定位目標文件的名稱。如果要顯示其他信息,比如:可重定位目標文件的大小、文件權限等,需要添加修飾碼v
,即$ ar tv /usr/aarch64-linux-gnu/lib/libc.a
。
2)從靜態庫中提取可重定位目標文件(即將指定的可重定位目標文件從靜態庫中拷貝到磁盤上)
# 從靜態庫中提取所有的可重定位目標文件
$ ar x /usr/aarch64-linux-gnu/lib/libc.a
# 從靜態庫中提取指定的可重定位目標文件
$ ar x /usr/aarch64-linux-gnu/lib/libc.a printf.o scanf.o
3)從靜態庫中刪除指定的可重定位目標文件
$ ar d /usr/aarch64-linux-gnu/lib/libc.a printf.o scanf.o
4)查看ar
爲可重定位目標文件中的符號創建的索引
$ nm --print-armap libtest.a
Archive index:
_Z3sumii in sum.o
_Z4funcv in test.o
sum.o:
0000000000000000 T _Z3sumii
test.o:
U g_val_1
U g_val_2
0000000000000000 T _Z4funcv
如何使用靜態庫
1)查看源文件的源碼
$ cat sum.cpp
int sum(int a, int b)
{
return a + b;
}
$ cat test.cpp
extern int g_val_1;
extern int g_val_2;
void func()
{
g_val_1 *= 2;
g_val_2 *= 2;
}
$ cat main.cpp
#include <stdio.h>
int g_val_1;
int g_val_2 = 3;
void func();
int main()
{
printf("original value: g_val_1=%d, g_val_2=%d\n", g_val_1, g_val_2);
func();
printf("now value: g_val_1=%d, g_val_2=%d\n", g_val_1, g_val_2);
return 0;
}
2)生成可重定位目標文件
$ g++ -c test.cpp sum.cpp
注:生成可重定位目標文件分別爲test.o
和sum.o
。
3)生成靜態庫
$ ar rs libtest.a test.o sum.o
4)使用靜態庫
方式1:
# main 可以直接加載到內存並運行,在加載時無需更進一步的鏈接
$ g++ -o main main.cpp --static ./libtest.a
方式2:
# main_2 可以直接加載到內存並運行,在加載時無需更進一步的鏈接
$ g++ -o main_2 main.cpp --static -L. -ltest
方式3:
# main_3 在加載時需要更進一步的鏈接
$ g++ -o main_3 main.cpp ./libtest.a
-
注:
-
--static
選項指示編譯器驅動程序,鏈接器應該構建一個完全鏈接的可執行目標文件,它可以直接加載到內存並運行,在加載時無需更進一步的鏈接。-
-ltest
是libtest.a
的縮寫。-
-L.
指示鏈接器在當前目錄下查找libtest.a
。
5)運行可執行目標文件
$ ./main
original value: g_val_1=0, g_val_2=3
now value: g_val_1=0, g_val_2=6
$ ./main_2
original value: g_val_1=0, g_val_2=3
now value: g_val_1=0, g_val_2=6
$ ./main_3
original value: g_val_1=0, g_val_2=3
now value: g_val_1=0, g_val_2=6
如果你覺得本文對你有所幫助,歡迎關注公衆號,支持一下!