Linux -链接-

  • 编译器驱动程序

源文件翻译成可执行文件的过程:
在这里插入图片描述

  • 静态链接

链接器的两个主要任务:符号解析:将每个符号的引用正好和一个符号的定义关联起来。重定位:将每个符号的定义与一个内存位置关联起来。修改其引用,是它们指向这个内存位置。

  • 目标文件

目标文件的三种形式:
(1)可重定位目标文件:编译时与其他可重定位目标文件结合,创建可执行文件
(2)可执行目标文件:可直接复制到内存中并运行
(3)共享目标文件:加载或运行时被动态加载进内存并链接

  • 可重定位目标文件

ELF可重定位目标文件的格式ELF头你一个16字节的序列开始,描述了生成该文件的系统的字的大小个字节顺序。剩下的部分包含帮助链接器语法分析和解释目标文件的信息。
在这里插入图片描述在这里插入图片描述

  • 符号与符号表

每个重定位目标模块都有一个符号表: 由模块m定义并能被其他模块引用的全局符号 由其他模块定义并被模块m引用的全局符号是外部符号 只被模块m定义和引用的局部符号
注:任何带有static属性声明的全局变量或者函数都是模块私有的

  • 符号解析

1.函数和已初始化的全局变量是强符号,未初始化的全局变量是弱符号Linux处理多重定义符号规则:
(1)不允许有多个同名的强符号
在这里插入图片描述
(2)如果有一个强符号和多个弱符号同名,选择强符号
在这里插入图片描述
(3)如果有多个弱符号重名,那么从这些弱符号中任意选择一个

在这里插入图片描述
2.与静态库链接
(1)将所有相关的目标模块打包成为一个单独的文件,成为静态库
(2)静态库以一种称为存档的特殊文件格式存放在磁盘中,存档文件名由后缀.a标识。
(3)创建静态库
ar 做库 crv撞创建 libfoo.a(lib为前缀,foo为库名)
-L 告诉库的位置 -l 告诉其库名
过程:

gcc  -c  add.c      
gcc  -c  max.c  //生成add.o  max.o      
ar  crv  libfoo.a  add.o  max.o//创建库     
rm  add.o  max.o   //删除当前文件夹中的库     
vi  main.c            
gcc  -o  main   main.c  -L . -lfoo

在这里插入图片描述
(4)静态库的缺点
链接静态库时,系统中的每个可执行文件都包含要使用的库,对磁盘空间造成很大的浪费,对任何标准函数的任何改变,无论多么小的改变,都要求库的开发人员重新编译整个文件。

3.链接器如何使用静态库解析引用

符号解析时,链接器从左到右按照在编译器驱动程序中命令行上出现的顺序来扫面可重定位目标文件和存档文件。

链接器判断每个输入问价f时一个目标文件还是一个存档文件。

如果是目标文件,那链接器会把f添加到可重定位目标文件集合中,修改未解析符号集合和已定义文件符号集合来反映f中的符号定义和引用,并继续下一个输入文件。

如果是存档文件,链接器就尝试匹配未解析符号集合中未解析的符号和由存档文件成员定义的符号。对存档文件中所有的成员目标文件都依次进行这个过程,直到未解析符号集合和已定义文件符号结集合不再发生变化。任何不包括在重定位目标文件集合中的成员目标文件都简单的被抛弃。

如果定义一个符号的库出现在引用这个符号的目标文件之前,那么引用就不能被解析,链接就会失败。

4.关于库的一般准则是放在命令行的结尾。如果各个库的成员相互独立,那么这些库就可以已任何顺序放在命令行的结尾处。
例如:foo.c调用libx.a和libz.a中的函数,而这两个库又调用liby.a中的函数。所以命令行为:gcc foo.c libx.a libz.a liby.a

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

  • 重定位

1.重定位:合并输入模块,并为每个符号分配运行时地址
(1)重定位节和符号定义:链接器将运行时内存地址赋给新的聚合节,程序中每条指令和全局变量都有唯一的运行时内存地址了
(2)重定位节中的符号引用。

2.重定位条目
(1)无论何时汇编器遇到对最终位置位置的目标引用,它就会生成一个重定位。代码的重定位条目放在.rel.text中,已初始化数据的重定位条目放在.rel.data中
(2)R_X86_64_PC32。使用32位PC相对地址的引用。就是据程序计数器的当前运行时值的偏移量。
R_X86_64_32。使用32位绝对地址引用,CPU直接使用在指令中编码的32位值作为有效地址。

  • 可执行目标文件

在这里插入图片描述
Linux系统中的每个程序都运行在一个进程上下文中,有自己的虚拟地址空间。

  • 动态链接共享库

1.共享库是一个目标模块,在运行或加载时,可以加载到任意的内存地址,并和一个在内存中的程序链接起来。这个过程称为动态链接,由动态链接器的程序执行。

2.共享库由.so后缀表示,称为动态链接库DLL

3.一个库只有一个.so文件,一个共享库的.text节的一个副本可以被不同的正在运行的进程共享。

4.共享库在使用时,库中的方法只做标记,运行时才加载。本身不再包含函数的代码,而是保存共享代码的调用线索,共享代码在该程序运行时才加入到其中。被编译好的程序被加载到内存中准备执行时,函数调用线索被解析,程序向共享库发出调用。

5.创建共享库
(1)过程:

gcc  -c  add.c   
gcc  -c  max.c  //生成add.o  max.o   
gcc  -shared  -fPIC  -o libfoo.so  add.o  max.o

其中,-shared指示链接器创建一个共享的目标文件,-fPIC指示编译器生成与位置无关的代码。
在这里插入图片描述
(2)关于-fPIC
不加-fPIC也可以生成.so但必须要在加载到用户程序的地址空间时重定向所有表目,他在里面不能引用其他地方代码。

不加-fPIC,生成动态库时加载到某地址处,加载时会被加载到另一个地址处。
加上-fPIC,这样的代码本身就能被放到线性地址空间的任意位置。

在内存引用上:
加-fPIC:这个库在不同进程中的虚拟地址不同,但操作系统显然会把他们映射到同一物理内存上。
不加-fPIC:加载时需要对代码引用的数据对象需要重新定位。重定位会修改代码段内容,造成每个使用.so文件代码段的进程在内核里都会生成这个.so文件代码段的copy。每个copy内容不同,取决于这个.so文件代码段和数据段内存映射的位置。但是其加载速度快。

  • 静态库与动态库的区别

1.静态库的后缀名为 .a
库里用到的方法包含在可执行程序中

2.共享库的后缀名为 .so
库里用到的方法只做标记,运行时才加载。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值