链接过程解析

编译简介

广义上的编译分为四个过程,分别是

  • 预处理
gcc -E helloworld.c -o helloworld.i  
  • 编译
gcc -S helloworld.i -o helloworld.s 
  • 汇编
gcc -c helloworld.s -o helloworld.o  
  • 链接
gcc helloworld.o -o helloworld  

链接

显示链接过程 : gcc --verbose test.c

链接分为动态链接和静态链接
就一个库来讲,由下面的组成

linux@ubuntu:/usr/local/lib$ ll libfreetype.*
-rw-r--r-- 1 root root 3207830 Feb 28 16:52 libfreetype.a
-rwxr-xr-x 1 root root     946 Feb 28 16:52 libfreetype.la*
lrwxrwxrwx 1 root root      20 Feb 28 16:52 libfreetype.so -> libfreetype.so.6.9.0*
lrwxrwxrwx 1 root root      20 Feb 28 16:52 libfreetype.so.6 -> libfreetype.so.6.9.0*
-rwxr-xr-x 1 root root 2182841 Feb 28 16:52 libfreetype.so.6.9.0*

//当然还有头文件,一些man文件,配置文件
//上面会看到针对一个库来说,有以上几种库文件

.a 			静态库文件,其实就是把若干.o文件打了个包
.la			使用libtool编译出的库文件,其实是个文本文件,记录同名动态库和静态库的相关信息
.so			链接文件,指向动态库文件
.so.6		链接文件,指向动态库文件
.so.6.9.0	动态库文件
.lo			使用libtool编译出的目标文件,其实就是在.o文件中添加了一些信息


1/动态连接

1.1/动态库的表现形式

动态库的本质
从下面的输出可以看出来可执行文件与目标文件的格式是类似的

linux@ubuntu:/usr/local/lib$ file libfreetype.so.6.9.0
libfreetype.so.6.9.0: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, BuildID[sha1]=0x6e41440e7b81d62b3bb873780fc0d13107fd6940, not stripped

动态库名称的组成

//realname
libfreetype.so.6.9.0
一个动态库文件可以被成libname.so.x.y.z
lib共享库名称.so.主版本号.次版本号.发行版本号
//soname
libfreetype.so.6
//linkername
libfreetype.so

动态库信息的窥探

//查看一个动态库或者可执行文件使用了哪些动态库
ldd file.so
//ldd其实是一个脚本,相当于/lib/ld-linux.so.2 --list program
1.2/动态库的制作

请查看动态库和静态库

1.3/动态链接中的链接

链接其实有两个概念,一个是编译中的链接概念,一个是运行时的链接,即动态链接

编译时寻找libz.so(linkername) ,但并不使用,只是查找是否存在

运行时寻找libz.so.1(soname),并加载到内存,其实加载的还是libz.so.1.2.8 ,libz.so.1.2.8文件是由动态链接器完成的

实际上是libz.so.1.2.8(realname),其他的都是链接出来的

1.3.0/头文件搜索路径

GCC预处理时头文件搜索路径

1.3.1/编译时链接的使用

编译时寻找libz.so(linkername) ,但并不链接进目标文件,只是查找是否存在
默认连接动态库,动态库找不到找静态库
除了路径要注意之外,一定要加上-l 选项
编译时连接与运行时链接及静态库链接

1.3.2/运行时链接的使用

编译时连接与运行时链接及静态库链接

1.4动态链接的顺序
一般使用 -l 来标明链接哪个动态库,中间掺杂着 obj.o 和 -l 等这样的文件,这些文件之间是有顺序的
It makes a difference where in the command you write this option;
the linker searches and processes libraries and object files in the
order they are specified.  Thus, foo.o -lz bar.o searches library z
after file foo.o but before bar.o.  If bar.o refers to functions in
z, those functions may not be loaded.

所以 顺序一般为 obj1.o  obj2.o -la -lb -lc
一般情况下obj1.o进而obj2.o 是不要求有顺序关系的
而 .o 与 -l ,-l与 -l 之间是要求的 
obj2.o 调用 a 中的函数 , a 中的函数调用 b 中的函数, b 中的函数调用 c 中的函数
1.5/动态链接后程序执行过程

运行时寻找libz.so.1(soname),并加载到内存,其实加载的还是libz.so.1.2.8 ,libz.so.1.2.8文件是由动态链接器完成的

库文件是由/lib/ld-linux.so.2链接进去的,/lib/ld-linux.so.2就是所谓的动态连接器.

动态装入器dynamic loader,负责将这些程序的所有必需的共享库装入,以使程序能正确执行.

它实际上是您在 ln 的 ldd 清单中看到的作为共享库相关性列出的 ld-linux.so.2 库。

1.6/动态链接的优势(相对于静态连接)
  • 节省内存空间。
    动态链接使得内存和磁盘中的编译完成的目标文件只保留一份,这样也可以减少物理页的换入换出,同时也可以增加CPU缓存的命中率。

  • 节省磁盘空间

  • 便于程序的更新、部署、发布;

  • 便于程序的修改
    动态链接下,程序在运行期间可以动态地加载各种程序模块,也就是我们经常说的插件;
    动态链接可以加强程序的兼容性,程序和不同平台之间可以加入一个“中间层”,让程序在不同的平台可以动态地链接到有操作系统提供的动态链接库,从而消除程序对不同平台依赖的差异性;

  • 慢,也慢不了多少
    动态链接是把链接这个过程从本来的程序装载前被推迟到了装载的时候,这样的推迟也造成了动态链接的性能损失,不过我们也有相应的优化策略:延迟绑定


2/静态链接

2.1/静态库的表现形式
前面列出的.a文件就是静态库文件
静态链接需要用到静态库,静态库可以简单的看成一组目标文件的集合,即很多目标文件压缩打包后的文件。
C语言的运行库中有很多与系统功能相关的代码,编译完成后就会生成相同数量的目标文件,之后使用"ar"压缩程序将这些目标文件压缩到一起,并对那些目标文件进行编号和索引,就会形成linux中libc.a这个静态库文件。
  可执行文件与目标文件的格式是类似的,所以,可以说可执行文件中的代码段和数据段都是由输入的目标文件中合并而来的。
  
1. 查看文件:ar -t *.a
2. 查看函数、变量:nm *.a
2.2/静态库的制作

请查看动态库和静态库

2.3/ 静态链接的使用

编译时连接与运行时链接及静态库链接

2.4/静态连接的过程
编译完成相应的用户程序之后进行链接操作,使用ld链接器。ld链接器会自动寻找所有的需要的符号以及它们所在的目标文件,并将这些目标文件从libc.a中解压出来(进而构建全局符号表...)(需要注意的是解压的文件不一定就是用户程序需要链接的目标文件,也可能在被解压的目标文件中有各种各样的嵌套,都要解压),最终将它们链接在一起成为可执行文件。
2.5/静态链接的优势(相对于动态连接)
  • 一旦编译成可执行文件,只要平台合适,不需要任何环境配置,不需要任何其他文件,一定能跑起来

  • 快,又能快多少,不算是一个优点


参考文档

linux下C/C++编译时系统搜索 include 和 链接库 文件路径的指定

交叉编译时候如何配置连接库的搜索路径

gcc,g++编译链接有关的路径

定义但未引用的符号的处理方式

gcc 默认 : 都编译链接进去了
---
gcc 打开
编译参数-ffunction-sections和-fdata-sections, 
链接参数 -Wl,--gc-sections
源程序里没有被调用到的函数/静态数据不会链接到最终的执行文件. 

你可能感兴趣的:(C,链接)