编译链接原理

C++编译链接原理

  • 预处理
  • 编译
  • 汇编
  • 链接
    • 目标文件
      • 符号
      • 符号表
    • 静态链接静态库
    • 动态链接共享库

我们都知道把代码编程一个可执行程序需要预处理,编译,汇编,链接

预处理

可以通过gcc预处理,如下:
gcc -E main.c main.i 生成一个后缀为*.i的文件
预处理过程进行的操作:

  • 将所有的“#define”删除,并且展开所有的宏定义
  • 处理所有的条件编译指令,比如“#if”、“#ifdef”、“#elif”、“#else”、“#endif”
  • 处理“#include”预编译指令,将被包含的头文件插入到该编译指令的位置。(这个过程是递归进行的,因为被包含的文件可能还包含了其他文件)
  • 删除所有的注释“//”和“/* */”。
  • 添加行号和文件名标识,方便后边编译时编译器产生调试用的行号心意以及编译时产生编译错误或警告时能够显示行号。
    保留所有的#pragma编译指令,因为编译器需要使用它们。

编译

可以通过gcc预处理,如下:
gcc -S main.c main.s 生成一个后缀为*.s的文件
将源代码由文本形式转换成机器语言,编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析以及优化后生成相应的汇编代码文件。

汇编

汇编过程调用汇编器as来完成,是用于将汇编代码转换成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。
可以通过gcc预处理,如下:
gcc -c main.c main.o 生成一个后缀为*.o的文件

链接

这里我们重点聊一下链接

目标文件

首先,汇编会生成的文件有三种:

  • 可重定位目标文件 包含二进制代码和数据,其形式可以在比编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件
  • 可执行目标文件 包含二进制代码和数据,可以直接执行
  • 共享目标文件 一种特殊类型的可重定位目标文件,可在运行或加载时动态的加载并链接

符号

  • 符号是指代码中的变量与函数。代码中的关键字不属于符号。
  • 符号分为以下4种:
    (1)可引出符号
    由本模块定义,且能被本模块和其它模块引用的符号
    非Static全局函数,非static全局变量都是可引出符号
    (2)外部符号
    由其它模块定义,但被本模块引用了的符号。
    其它模块的可引出符号,如果被本模块使用了,就是本模块的外部符号
    外部符号变量由extern修饰
    (3)静态符号
    由本模块定义,且只能被本模块引用的符号
    带static的全局变量和全局函数都是静态符号
    (4)局部符号
    在函数内部定义的非static变量

符号表

每一个可重定位文件都会有一个符号表
编译链接原理_第1张图片
ELF头以一个 16字节的序列开始,这个序列描述了字的大小和生成该文件的字节顺序,剩下的不分包括帮助链接器解析和解释目标文件的信息,其中目标文件中每个节都有一个固定大小的表
.data 存放以初始化的全局变量,局部C变量保存在栈中
.bss 存放未被初始化的全局变量,不占据实际空间,仅仅只占位
.symtab存放程序中的全局函数

符号解析是指,把符号的定义和引用联系起来。
不同的符号类型,解析的策略不同。为了方便理解,这里打乱次序,从最简单的说起。

  • 局部符号
    链接器对局部符号一点都不关心,因为局部符号是在栈里管理的。
  • 静态符号
    链接器对静态符号的处理比较简单,因为静态符号的定义和引用肯定是在同一个模块中的,只要保证模块中一个符号只有一个定义就好了。
  • 外部符号
    编译器发现模块中的符号没有定义时,就会把它放到一个符号表中,链接器从其它模块中找出这些符号的定义。
  • 可引出符号
    可引出符号的处理比较复杂,要从所有模块中搜索这个符号的定义,还要处理重定义的问题。

静态链接静态库

为了创建可执行文件,链接器必须完成两个任务;

  • 符号解析
    将每个符号引用和符号定义联系起来,当有多个定义的符号链接器会用强弱符号的规则来处理,这里就不多赘述
  • 重定位
    链接器把符号定义和存储器位置联系起来,修改所有对这些符号的引用,使它们指向这个存储器位置,从而重定位。

动态链接共享库

都知道静态库会有明显的缺点,在更新维护的时候,若程序员想要使用一个库的最新版本,他们必须以某种方式连接到该库的最新消息,然后显式将他们的库重新连接。
而且这些代码会复制到每个运行的进程里,对系统资源造成极大的浪费。
于是共享库诞生了,在运行时可以加载到任意存储器位置,并和一个程序连接起来,这个过程为动态链接
微软系统大量的使用动态共享库,称为DLL
基本的思路是当创建可执行文件时,静态执行一些链接,然后在程序加载时,动态完成链接过程。
没有任何动态库的代码和数据结构被真的拷贝到可执行文件中。取而代之的是,链接器拷贝了一些重定位和符号表信息,他们使运行时可以解析动态库中代码和数据的引用。
动态链接器通过执行下面的重定位完成链接;

  • 重定位动态库文本数据到某个存储段。在Linux系统中共享库被加载到从地址0x4000 0000开始的区域中
  • 重定位动态库的文本和数据到另一个存储器段
  • 重定位可执行程序中所有对由动态库的定义符号的引用

你可能感兴趣的:(C++)