读CSAPP之链接器、目标文件和ELF

今年是离开校园的第六年,这六年来我一直在从事应用软件的设计、开发工作,大部分时间是在与高级编程语言、设计模式、业务逻辑打交道。它们大多流于表面,久而久之,与技术底层疏远了,诸如计算机组成原理、汇编语言、编译原理、数据结构以及算法慢慢得生疏,时至今日,向上碰到天花板,向下触到花岗岩。五年是一个契机,趁着下一个五年开始之际,我计划用三个月至半年时想间,重新学习这些知识,以期达到巩固基础,厚积薄发的目的。

本篇是我阅读《Computer System: A Programmer’s Perspective》一书的笔记,该书和与之搭配的《Professional Assembly Language》是我当下阅读计划的一部分。

链接器

链接(linking)是将各种代码和数据部分收集起来合并为一个单一文件的过程,这个文件可以被加载(或拷贝)到存储器并执行。其可以执行于编译时(compile time),也可以是加载时(load time),或者运行时(run time),由链接器(linker)执行,其使得分离编译(separate compilation)成为可能。

静态链接器(static linker)以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的可以加载和运行的可执行目标文件作为输出。输入的可重定位目标文件(relocatable object file)由各种不同的代码和数据节(section)组成,指令、初始化的全局变量、未初始化的变量分别在不同的节当中。

链接器负责符号解析(symbol resolution)和重定位(relocation),前者是将符号引用和符号定义联系起来,后者是将符号定义与存储器位置联系起来。

目标文件

目标文件纯粹是字节块的集合,分为三类:

  1. 可重定位目标文件;
  2. 可执行目标文件;
  3. 共享目标文件。

可重定位目标文件在编译时与其他可重定位目标文件合并起来,创建可执行目标文件。可执行目标文件则是可以被直接拷贝到存储器并执行。共享目标文件则是在加载或者运行时被动态地加载到存储器并链接。

最早的目标文件格式是 a.out,System V Unix 的早前版本使用 COFF(Common Object File Format),Windows NT 使用的是 COFF 的一个变种,叫做 PE(Portable Executable),现代 Unix 系统使用的是 ELF(Executable and Linkable Fomart)。

ELF

.text
已编译程序的机器代码。
.rodata
只读数据。
.data
已初始化的全局 C 变量。
.bss
未初始化的全局 C 变量。
.symtab
符号表,存放程序中定义和引用的函数和全局变量的信息
.rel.text
当链接器把这个目标文件和其他文件结合时,需要修改这些位置,一般而言,任何调用外部函数或者引用全局变量的指令都需要修改。
.rel.data
任何已初始化的全局变量,如果它的初始值是一个全局变量地址或者外部定义函数的地址,都需要被修改。

Leave a comment

Your comment