又开始不务正业乱开新坑了。接下来会通过阅读mold的源码来学习如何实现一个ELF链接器,有精力也会再跟着plct的这个课程学习实现一个简单的RV ELF链接器,可能会跟着将代码换一门语言翻译一遍,将这个学习过程中遇到的知识点记录到博客中。如果坑能开到后面的话我还会针对这门课程实现的链接器在功能上与mold的进行比较,一门教学用的链接器和真正实用的链接器在功能上有哪些差别。
https://github.com/rui314/mold
index
从未了解过链接器实现的我,在跟着mold源码和这门课程之前,先来写一下根据我现有的知识去设想一个链接器内部应该大致有哪些功能。(本期内容不是教程,只是我个人对知识的回忆,因此很多地方都会缺很多东西)同时回忆过后对比检查理解的问题,在差不多理解整体运作过程的情况下学习会更有效一些。
链接器做了什么
- 链接器做的事情是将多个编译器生成的目标文件的内容合到一起
- 处理符号。在编译期间要求编译生成的目标文件中每个符号小于等于一个定义,在链接的时候链接器负责找到未定义的符号的地址,重复符号的解决(如果是弱符号则根据规则选取其中一个定义,如果非弱符号则需要报错)
目标文件
要合并目标文件那么我们要知道目标文件的内容是什么样的(这里我们仅探讨ELF的格式)。先不查阅文档,想一下目标文件大概会有什么内容。
段
首先是对于我们写代码的时候经常提到的一些段,比如说代码段、数据段、BSS段、堆、栈等等。这些段本身只是一串数据,那么我们需要一个位置存放起始位置和数据长度。而其中的各种段名以及各种符号名也需要一个位置保存,因此目标文件中还需要有一个符号段用于保存各种用于链接使用的符号。(strip是否就是去掉这里)我们知道debug模式生成的代码包含debug信息,而这些信息对于elf来说是写在内部的,因此里面还需要有保存调试信息的段。
文件头
类似于段段起始和长度这种
寻找保存具体信息的信息需要放到一个文件头中,除此之外文件头中还需要一些魔数来标识文件类型。
符号表
保存所有代码中符号的相关信息(而不是段名的符号),最容易想到的就是地址,其次上面提到了符号的强弱。
行为控制
而更精细控制这些行为的方式一个是命令行参数,另一个是链接脚本。由于系统中内置了默认的链接脚本,我们日常很少会接触到这些。读取链接脚本也是链接器很重要的一个功能。
总结
回顾下来,链接器主要的功能如下
- 读取目标文件ELF文件头
- 读取链接脚本并且按照链接脚本控制链接行为
- 符号解析
- 合并段
- 生成对应的ELF文件
而plct课程的大纲如下
第一课:搭建开发环境、初始化项目、开始读取 ELF 文件
第二课:继续读取 ELF 文件
第三课:解析链接器参数
第四课:解析静态链接库文件
第五课:解析未定义符号,移除未使用文件
第六课:处理 Mergeable Sections
第七课:开始写文件
第八课:处理 Output Sections
第九课:继续处理 Output Sections
第十课:Phdr 和 Merged Sections
第十一课:处理重定向,课程回顾
可以说做的功能大致类似,之后就会先从mold的源码开始开新系列了。