计算机系统篇之链接(3):静态链接(上)
Author: stormQ
Created: Saturday, 21. December 2019 11:58AM
Last Modified: Thursday, 05. November 2020 11:00PM
本文描述了引入静态链接库的动机,并提供了 Linux 系统中生成和使用静态链接库的示例。
引入静态库是为了更好地解决“编译器开发者如何将标准库函数提供给调用者使用”的问题。
解决该问题的不同方式及比较如下所示:
解决方式 | 优点 | 缺点 |
---|---|---|
方式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