1. 静态库

链接器读取一组可重定位目标文件,并把它们组合称为一个单一的可执行文件。可以通过将所有相关的可重定位目标文件打包成为一个单独的文件,这个文件就叫做静态库。在链接时,链接器只拷贝被程序引用的目标模块。减少了可执行文件在磁盘和存储器中的大小。

Unix系统中,静态库以一种称为存档的特殊文件格式存放在磁盘。例如在ubuntu上,在路径/usr/lib/x86_64-linux-gnu/libc.a就是一个静态库,它将ANSI C标准函数中的函数,将多个函数定义的模块打包后,成为一个单独的文件-libc.a。存档文件是一组连接起来的可重定位目标文件的集合,有一个头部用来描述每个成员目标文件的大小和位置。

2. 静态库的生成

有addvec.c和multvec.c两个函数模块

// addvec.c
void addvec(int *x, int * y, int * z, int n) {int i;for (i = 0; i < n; i++) {z[i] = x[i] + y[i];}
}
// multvec.c
void multvec(int * x, int * y, int * z, int n) {int i;for (i = 0; i < n; i++) z[i] = x[i] * y[i];
}

将addvec.c和multvec.c编译为addvec.o和multvec.o文件,然后通过ar生成libvector.a静态库。

kernel@Ubuntu:~/Desktop/StaticLib$ gcc -c addvec.c multvec.c
kernel@Ubuntu:~/Desktop/StaticLib$ ar rcs libvector.a addvec.o multvec.o

参数r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。

参数c:创建一个库。不管库是否存在,都将创建。

参数s:创建目标文件索引,这在创建较大的库时能加快时间。(如果不需要创建索引,可改成大写S参数;如果.a文件缺少索引,可以使用ranlib命令添加)

在main.c中负责调用函数,因此需要一个头文件去引用addvec和multvec。

// vector.h
extern void addvec(int *, int *, int *, int);
extern void multvec(int *, int *, int *, int);// main.c
#include <stdio.h>
#include "vector.h"int main(void) {int x[2] = {1, 2};int y[2] = {3, 4};int z[2] = {0};addvec(x, y, z, 2);printf("%d  %d\n", z[0], z[1]);return 0;
}

为了创建可执行文件,需要链接main.o和libvector.a

kernel@Ubuntu:~/Desktop/StaticLib$ gcc -c main.c
kernel@Ubuntu:~/Desktop/StaticLib$gcc -static main.o ./libvector.a -o vector
kernel@Ubuntu:~/Desktop/StaticLib$ ./vector
4  6

-static参数告诉编译驱动程序,链接器应该构建一个完全链接的可执行目标,它可以加载到存储器并执行,在加载时无需更进一步的链接。

当连接器运行时,链接器解析addvec.o定义的addvec符号被main.o引用,所以它拷贝addvec.o到可执行文件,由于multvec.o的符号没有被引用,所以不会被拷贝到可执行文件,此外还有libc.a的printf.o也被main.o引用,所以会被拷贝到可执行文件。还有许多C运行时系统中的模块。

3. 链接器如何使用静态库来解析引用(libvector.a)

在符号解析阶段,链接器从左到右按照它们在编译器驱动程序命令行上出现的相同顺序来扫描可重定位目标文件main.o和存档文件libvector.a。在这扫描中,链接器维持一个可重定位目标文件的集合E,一个未解析的符号(即引用了尚未定义的符号)集合U,以及一个在前面输入文件中已定义的符号集合D。

(1)对于命令行上每个输入文件f,链接器会判断f是目标文件还是存档文件。如果是一个目标文件,那么链接器把f添加到E,此时E={mian.o},修改D和U来反映f中的符号定义和引用,U={addvec},并继续下一个输入文件。

(2)如果f是一个存档文件,那么链接器就尝试匹配U中未解析的符号和由存档文件成员定义的符号。在存档文件中定义了addvec符号,用来解析U中对addvec符号的引用,将addvec.o加入到E中,并且链接器修改D和U来反映addvec.o中的符号的定义和引用。对存档文件所有目标文件都反复进行上述过程,直到U和D不在发生变化,也就是找不到一个符号的定义与引用相联系。此时,任何不包含在E中的成员目标文件都将被丢弃,也就是在main.o未引用的符号,该符号定义的目标文件,此处为multvec.o,而链接器继续下一个输入文件。

(3)如果链接器完成对命令行上文件的扫描,U是非空,那么链接器会输出一个错误并终止,也就是一个未定义的引用。否则,它会合并和E中的目标文件,从而构建输出的可执行文件。

kernel@Ubuntu:~/Desktop/StaticLib$ gcc -static  ./libvector.a main.o -o vector
main.o:在函数‘main’中:
main.c:(.text+0x41):对‘addvec’未定义的引用
collect2: error: ld returned 1 exit status

在命令行输入,将libvector.a 和main.o位置调换,就会出现上述错误。因为首先链接器判断f为存档文件,此时U为空集,所以libvector.a中的addvec.o定义的符号无法解析对addvec的引用。因此产生错误。因此最好在链接的时候,将存档文件放到最后。

如果库不是独立的,也就是一个成员引用另一个成员的符号,他们必须排序,使得对于每个被存档文件的成员外部引用的符号s,在命令行中至少有一个s的定义是在对s的引用之后。例如fun.c引用了libx.a和liby.a中的函数,而这两个库又调用libz.a的函数,则有:

gcc fun.o libx.a liby.a libz.a

如果需要满足依赖需求,可以在命令行上重复库。fun.c调用libx.a中的函数,该库又调用liby.a的函数,而liby.a又调用libx.a的函数,则有:

gcc fun.o libx.a liby.a libx.a

静态链接之与静态库的链接相关推荐

  1. 程序员的自我修养—链接、装载与库 笔记

    程序员的自我修养-链接.装载与库 笔记 内存管理 直接使用物理内存地址 虚拟内存-分段 虚拟内存-分页 分页和分段的主要区别 段页式 代码生成过程 预处理 编译 词法分析 语法分析 语义分析 源代码优 ...

  2. 程序员的自我修养--链接、装载与库笔记:静态链接

    1. 空间与地址分配 对于链接器来说,整个链接过程中,它就是将几个输入目标文件加工后合并成一个输出文件.测试代码a.c和b.c内容如下: // a.c extern int shared;int ma ...

  3. linux 静态编译 glibc,GCC静态链接glibc库

    如果你的编译环境是高版本的glibc库,而运行程序的环境(机器)安装的glibc是低版本的话,在运行程序的时候,就会告诉你 cannot find GLIBC_2.14 类似这样的错误,上面的2.14 ...

  4. 【Android NDK 开发】Android Studio 使用 CMake 导入静态库 ( CMake 简介 | 构建脚本路径配置 | 引入静态库 | 指定静态库路径 | 链接动态库 )

    文章目录 I . CMake 简介 II . Android Studio 中 CMake 引入静态库流程 III . 指定 CMake 最小版本号 IV . 导入函数库 ( 静态库 / 动态库 ) ...

  5. java 怎么链接ndk的库_使用ndk-build链接现有的静态库

    我有一个库,我使用cmake和android-cmake为Android编译并获得一个静态库 . 然后我尝试使用这样的Android.mk文件将我的测试项目与这个静态库链接: LOCAL_PATH : ...

  6. CMake 添加头文件目录,链接动态、静态库(添加子文件夹)

    CMake支持大写.小写.混合大小写的命令. 当编译一个需要第三方库的项目时,需要知道: 去哪找头文件(.h),-I(GCC) INCLUDE_DIRECTORIES() 去哪找库文件(.so/.dl ...

  7. Linux 动态链接和静态链接简析(库名与库文件名)

    原文请见 Linux动态链接和静态链接简析 0. 库名与真正的库文件名 就拿数学库来说,他的库名是 m,他的库文件名是libm.so,很容易看出,把库文件名的头 lib 和尾.so去掉就是库名.(gc ...

  8. cmake中添加引用动态链接_CMake 添加头文件目录,链接动态、静态库(添加子文件夹)...

    CMake支持大写.小写.混合大小写的命令. 当编译一个需要第三方库的项目时,需要知道: 去哪找头文件(.h),-I(GCC) INCLUDE_DIRECTORIES() 去哪找库文件(.so/.dl ...

  9. c语言makecode头文件,cmake 添加头文件目录,链接动态、静态库

    罗列一下cmake常用的命令. CMake支持大写.小写.混合大小写的命令. 1. 添加头文件目录INCLUDE_DIRECTORIES 语法: include_directories([AFTER| ...

最新文章

  1. javaScript的调试(二)
  2. mongodb数据库磁盘碎片整理。
  3. 大理,风花雪月俏丽金花
  4. 用 Python 实现文件查找
  5. ubuntu c++ 实现自动回车键功能_从X86到ARM,实现C和C++语言90% Code自动迁移
  6. gridcontrol 控件的用法
  7. JDK 9.0.4 报错:程序包 com.sun.xml.internal.fastinfoset.stax.events 不存在/不可见
  8. mysql简单部署_安装部署Mysql实例(最简单快速噢)
  9. 使用虚幻引擎4年,我想再谈谈他的网络架构
  10. 一个移动端的在线五笔输入法
  11. 集成底座POC方案说明
  12. 卡内基梅隆大学计算机研究生水平,卡内基梅隆大学计算机研究生
  13. FastDFS使用之文件名称
  14. hbase snappy 安装_hbase自带snappy压缩测试出错
  15. 职场上被人针对要不要告诉领导
  16. 【mmWave】zoomFFT
  17. Python利用Opencv读取图片
  18. vue 路由懒加载,使用 import 无法处理
  19. Day24-Ajax
  20. OPPO 后端开发 一、二、HR 面面经 (已 OC)

热门文章

  1. OJDBC驱动版本区别 [ojdbc14.jar,ojdbc5.jar跟ojdbc6.jar的区别]
  2. Maven中不能引入ojdbc解决方法:com.oracle:ojdbc6:jar:11.2.0.3
  3. 利用阿里云国际购买的服务器搭建个人网站步骤
  4. 【Iass Saas Paas】
  5. Measurement Studio函数ReadMultiSampleAsync
  6. C语言数码管节日灯,硬件课程设计报告可编程节日彩灯(C语言).pdf
  7. ArrayList添加一个元素的过程(中部插入以及尾部添加)
  8. tail命令 – 查看文件尾部内容
  9. 一文搞懂TCP/IP,入门计算机网络必看!
  10. LPspice 电路仿真软件