深入理解计算机系统系列05

本次我们要解释的是链接是如何运行的,下面先上一个小小的例子

Linux初步运行代码

在这里插入图片描述
大多数编译器提供编译器驱动程序,它代表用户在需要的时候调用语言处理器,编译器,汇编器,和链接器。
它将main.c翻译成为一个ASCII码文件的中间文件main.i,之后驱动编译器翻译器将其翻译成一个ASCII汇编语言文件,main.s,之后驱动汇编器再将其翻译成为一个可重定位的目标文件main.o,驱动程序以相同的过程生成sum.o,最后运行链接器ld,将这些.o文件组合起来,构成可执行文件prog(注意shell调用操作系统中一个叫做loader的函数,将prog中的代码和数据复制到内存中,然后将控制转移到程序的开头)


静态链接器,输入的可重定位的文件有各种不同的代码和数据节(section)组成
链接器有两大重要任务:符号解析和重定位。
目标文件纯粹是字节块的集合。


readelf命令可以解析可重定位的目标文件,可执行的目标文件,和共享文件
下面是可重定位的目标文件
ELF头描述了生成该文件的系统的字的大小和字节顺序。ELF头剩下的部分包含帮助链接器语法分析和解释目标文件的信息。
例如:
.text:已编译程序的机器代码
.rodata:只读数据
.data:已初始化的全局和静态C变量
.bss:未初始化的全局和静态C变量
.symtab:符号表
.rel.text:一个.text节中的位置的列表
.rel.data:被模块引用或者定义的所有全局变量的重定位信息
.debug:调试符号表
.line:原始C源程序中行号和.text节中极其指令的映射
.strtab:一个字符串表


常用命令

readelf

一般用于查看ELF格式的文件信息,常见的文件如在Linux上的可执行文件,动态库(.so)或者静态库(.a) 等包含ELF格式的文件
参数说明
1、选项 -h(elf header),显示elf文件开始的文件头信息。在这里插入图片描述
2、选项 -l(program headers),segments 显示程序头(段头)信息(如果有数据的话)
3、选项 -S(section headers),sections 显示节头信息(如果有数据的话)。
4、选项 -g(section groups),显示节组信息(如果有数据的话)
5、选项 -t,section-details 显示节的详细信息(-S的)
6、选项 -s,symbols 显示符号表段中的项(如果有数据的话)

objdump

使用 objdump 查看目标文件或者可执行的目标文件
例子:
objdump -d add.o

# objdump -d add.o 
 
add.o:     file format elf32-i386
 
 
Disassembly of section .text:
 
00000000 <add_int>:
   0:	55                   	push   %ebp
   1:	89 e5                	mov    %esp,%ebp
   3:	8b 45 0c             	mov    0xc(%ebp),%eax
   6:	8b 55 08             	mov    0x8(%ebp),%edx
   9:	01 d0                	add    %edx,%eax
   b:	5d                   	pop    %ebp
   c:	c3                   	ret 

nm

使用 nm 显示二进制目标文件的符号表,包括符号地址、符号类型、符号名等
例子:
nm add.o

# nm add.o
00000000 T add_int

与静态库链接

所有的编译系统提供一个机制,把所有相关的目标模块打包成一个单独的文件,称之为静态库(static library)
在链接的时候,链接器将只复制被程序引用的目标模块,这减少了可执行文件在磁盘和内存的空间
在Linux系统中,静态库以一种称为存档(archive)的特殊文件格式存放在磁盘中,存档文件是一组连接起来的可重定位目标文件的集合
下例:
使用AR工具建立静态库
在这里插入图片描述
图片中先用gcc编译了main2.c文件
而后链接,注意,这里静态库要放在最后
-static 告诉编译器,链接器应该构建一个完全链接的可执行目标文件。


tips:为什么要把静态库放在最后?
这与链接器使用静态库解析引用的方式有关,即,采用前面无法解释的符号,放给后面进行解释,而如果反转,静态库中的符号无法解释,就会出错。

动态链接

首先我们要先明确一下,无论是静态链接和动态链接,他们都是链接,都不会把库中其他无用的函数加载进来,而动态链接解决的问题是函数重复调用,内存被重复占用的问题,所以提出了动态链接的概念
动态链接的步骤中往往有静态链接,只是静态链接生成的部分的可重定位的目标文件,而剩下的一部分,靠动态链接运行时动态加载。因为只是映射和共享,所以可以解决上面的问题。


重定位

由于袁老师的视频我们可以知道,链接的两大步骤就是符号解析和重定位,重定位可以理解为
1.合并相同的节(数据节和代码节)
2.对定义的符号进行重定位
3.对引用的符号进行重定位
符号解析得到的内容实际上是U、E、D
具体内容如下:
对每一个输入文件来说,首先判断是不是库文件。
如果不是库文件,就是目标文件 f。就能把目标文件放入 E 中,根据 f 中未解析符号和定义符号判断后分别放入 U、D 中

比如说,f 中有一个未解析符号 k,如果 D 中存在对它的定义,那么就可以建立联系。如果没有就放入 U 中。

如果是库文件,会试图把所有 U 中的符号与库文件中的符号匹配,匹配上了就从 U 放入 D 中。并把匹配上的模块放入 E 中。一直重复直到 U D 不再变化。库文件剩下的内容直接就不管了。
如果往 D 中放入了一个已经存在的符号或者扫描完所有文件后 U 还是非空,则链接器会停止并报错。否则将 E 中内容经过重定位后合并,生成可执行文件
IA-32 架构中有两种重定位的方式,对应着 r_type
R_386_PC32

R_386_32
他们都是位置无关属性的代码,具体可参照袁老师的视频,讲的非常详细

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值