现代操作系统:链接器

章节0:链接器

链接器是操作系统分配中包含的实用程序的示例。与编译器一样,链接器本身不是操作系统的一部分,即它不以主管模式运行。与编译器不同,它依赖于操作系统(使用什么对象/负载文件格式),并且不(固有)源语言依赖。

0.0.1 C语言的编译过程 https://blog.csdn.net/weixin_41143631/article/details/81221777

Step1预处理:展开头文件;宏替换;移除注释;条件编译;

Step2编译:检查语法;将c代码转换为汇编代码;

Step3汇编:将汇编代码转换为机器码;

Step4链接:使用链接器将汇编完成后的文件链接到一起成为一个可执行程序;

test.c --> test.s --> test.o --> test.out

链接器是一个将编译器产生的目标文件打包成可执行文件或者库文件或者目标文件的程序。

0.1    链接器的作用:链接

当编译器和装配器完成模块处理后,它们会生成几乎可运行的对象模块。在对象模块组合和运行之前,还有两项任务需要完成。两者都涉及将多个对象模块(该单词再次)链接在一起。任务是重新定位相对地址和解决外部参考:每个描述如下。

链接器的输出有时称为负载模块,因为随着相对地址的迁移和外部地址的解决,模块已准备好加载和运行。

0.1.1 重定位相对地址 Relocating Relative Addresses

编译器和装配器将每个模块视为将在零点加载。例如,机器指令jump 120用于表示跳转到当前模块的 120 位置。

要将此相对地址转换为绝对地址,链接器将模块的基本地址添加到相对地址。基本地址是此模块加载的地址。

例如,假设模块要从 2300 点开始加载,并包含上述说明jump 120,链接器会将其转换为jump 2420。

链接器如何知道模块从 2300 点开始加载?

链接器一次处理一个模块。此时我们假设第一个模块将在零点加载,那么第一个模块根本不需要进行重定位,因为第一个模块的初始位置本来就是0;在链接器处理完第一个模块后,链接器会记录第一个模块的长度L1,那么当第二个模块开始加载时会直接从L1处开始加载,此时的L1被称为迁移常数(Relocation Constant)。显而易见第一个模块的迁移常数为0,第三个为L1+L2,以此类推,链接器保留其已处理的所有模块的总和长度。

0.1.2 处理外部引用 Resolving External References

代码中某个模块A调用了定义在别的模块B中的某个函数时,那么当模块A中代码运行到处时就要跳转到模块B中定义的的开头处。

但是会引发什么样的问题呢?在程序被编译时,编译器和装配器完全不知道的定义,因此无法提供起始地址,取而代之的是使用一个符号标记一下这里需要把的地址给填上,这称之为的调用。

包含了的定义的模块B中包含了被定义的一个标记,同时对的定义给出了一个相对地址,那么链接器就可以将这个相对地址转换为绝对地址;然后链接器会将所有的调用全部改变为绝对地址。

0.1.3 Lab1的案例

  目标机器是word可处理的,其中每个word由四个小数数字构成,第一个数字是opcode,其余三个数字形成一个地址;输入以正整数开头,给出需要处理的模块的数量,每个对象模块包含三个部分:a. 定义表;b. 使用表;c. 程序文本;

定义表中包含定义的数量N,然后是N个具体的定义内容,每一组定义被表示为,表示sym在loc的相对地址处;

使用表中包含定义的数量N,每组定义同样被表示为,但是这次表示的是符号在从loc开始的链接表中使用,在loc中初始化的地址指向对sym的下一次使用,777标识使用表的结束。

程序文本中包含了N组对,word是上面提到的4为数字的指令,type是一个单字符表明这个word的状态是Immediate, Absolute, Relative, External其中之一。

4

  1 xy 2

  2 xy 4 z 2

  5 R 1004  I 5678  E 2777  R 8002  E 7777

  0

  1 z 3

  6 R 8001  E 1777  E 1001  E 3002  R 1002  A 1010

  0

  1 z 1

  2 R 5001  E 4777

  1 z 2

  1 xy 2

  3 A 8000  E 1777  E 2001

  Symbol Table  对应0.1.2,每个引用在哪个绝对位置?

    xy=2   xy为什么等于2xy = 0 + 2 = 2

    z=15   z在哪里定义的呢?第四个模块有z 2那就是前三个模块的长度+2 = 5 + 6 + 2 + 2 = 15

  Memory Map  按步骤依次执行指令

  +0

  0:       R 1004      1004+0 = 1004        rc = 0  relative = 1-004 = 0 + 1-004 = 1-004

  1:       I 5678               5678        immediate 5-768

  2: xy:   E 2777 ->z           2015         external 2777 = 2-000 + 15 = 2-015

  3:       R 8002      8002+0 = 8002        rc = 0 relative = 8-002 = 0 + 8-002 = 8-002

  4:       E 7777 ->xy          7002        rc = 0 external 7777 = 7-000 + 2 = 7-002

  +5    模块1结束:rc += 5

  0        R 8001      8001+5 = 8006       rc = 5 relative = 8-001 = 5 + 8-001 = 8-006

  1        E 1777 ->z           1015       external 1777 = 1-000 + 15 = 1-015

  2        E 1001 ->z           1015       external 1001 = 1-000 + 15 = 1-015

  3        E 3002 ->z           3015       external 3002 = 3-000 + 15 = 3-015

  4        R 1002      1002+5 = 1007

  5        A 1010               1010

  +11

  0        R 5001      5001+11= 5012

  1        E 4000 ->z           4015

  +13

  0        A 8000               8000

  1        E 1777 ->xy          1002

  2 z:     E 2001 ->xy          2002

posted on 2021-10-03 22:09  ThomasZhong  阅读(398)  评论(0编辑  收藏  举报

导航