程序员

《程序员的自我修养》——编译器都做了什么

2020-02-27  本文已影响0人  阿提艾斯
  1. 编译过程
    一般分为6步:
    扫描:简单地进行词法分析,将源代码的字符序列分割成一系列的记号(Token)。记号一般可以分为如下几类:关键字、标识符、字面量和特殊符号。扫描还做了其他工作,比如将标识符存放到符号表,将数字、字符串常量存放到文字表等。

    语法分析:对记号进行语法分析,生成语法树。在语法分析的同时,很多运算符号的优先级和含义也被确定下来。

    语义分析:静态语义通常包括声明和类型的匹配,类型的转换。动态语义一般指在运行期间出现的语义相关的问题,比如将0作为除数是一个运行期语义错误。

    源代码优化:将能计算的代码部分计算出结果,生成中间代码,中间代码使得编译器可以被分为前端和后端,前端负责产生机器无关的中间代码,后端负责将中间代码转成目标机器代码。

    代码生成:生成目标代码,目前的编译器可以将一个源代码文件编译成一个未链接的目标文件,然后由链接器最终将这些目标文件链接起来行程可执行文件。目标文件从结构上讲,它是已经编译后的可执行文件格式,只是还没有经过链接的过程,其中可能有些符号或有些地址还没有被调整。

    目标代码优化:优化方法比如选择合适的寻址方式、使用位移来代替乘法运算、删除多余的指令等。

2.目标文件是什么样的?
目标文件中包含的内容有机器指令代码、数据、链接所需的信息(符号表、调试信息、字符串等)。目标文件将这些信息按不同的属性,以“节”的形式存储,也叫“段”。
.text段:机器指令经常被放在代码段里;
.data段:数据段,保存初始化的全局变量和局部静态变量数据;
.rodata段:存放的是只读数据,一般是程序里面的只读变量(如const修饰的变量)和字符串常量;
.bss段:存放的是未初始化的全局变量和局部静态变量,.bss不占磁盘空间。

  1. ELF文件结构描述
    ELF文件:Executable Linkable Format,可执行文件格式。
    ELF总体结构如下:
ELF Header 说明
.text 代码段
.data 数据段
.bss 见2
... other sections
Section header table 描述ELF文件包含所有段的信息,比如段名、长度、偏移、读写权限及其他属性
String Tables Symbol Tables
...

魔数:用来确认文件的类型,操作系统在加载可执行文件的时候会确认魔数是否正确,如果不正确会拒绝加载。

  1. 链接的接口——符号

在链接中,我们将函数和变量名统称为符号,函数名或变量名就是符号名。

每一个目标文件都会有一个相应的符号表,这个表里面记录了目标文件中所用到的所有符号,每个符号都有对应的符号值,对于函数和变量来说,符号值就是它们的地址。

符号分类,只需要关注全局符号外部符号

符号分类 备注
全局符号(定义在目标文件中) 可以被其他目标文件引用
外部符号(本目标文件引用,却没有定义在本目标文件) 符号引用
段名 值是该段的起始地址
局部符号 只在编译单元内部可见
行号信息 即目标文件指令与源代码中代码行的对应关系
  1. 每个程序被运行起来以后,它将拥有自己独立的虚拟地址空间。虚拟地址空间的大小由CPU位数决定。

  2. 一般来说,C语言指针大小的位数与虚拟空间的位数相同,如32位平台下的指针为32位,即4字节;64位平台下的指针位64位,即8字节。

  3. 从操作系统的角度来看,一个进程最关键的特征是它拥有独立的虚拟地址空间

  4. 目标文件的重定位是在静态链接时完成的,而共享对象的重定位是在装载时完成的。

  5. 动态链接的可执行文件一般是PIC的。

  6. 动态链接基本步骤:
    (1)启动动态链接器
    动态链接器本身也是一个共享对象。
    动态链接器相比普通共享对象的特殊性:
    a. 动态链接器本身不可以依赖其他任何共享对象
    b. 动态链接器本身所需要的全局和静态变量的重定位工作由它本身完成。这个需要自举
    在动态链接器的自举代码中,除了不可以使用全局变量和静态变量之外,也不能调用函数,动态链接器本身的函数也不能调用。
    自举代码做的工作:

    自举过程
    完成自举之后,动态链接器可以自由调用各种函数并且随意访问全局变量了。
    (2)装载所需的共享对象
    完成自举之后,动态链接器将可执行文件和链接器本身的符号表都合并到一个符号表中,即全局符号表。
    装载共享对象

(3)重定位和初始化
初始化执行的是共享对象中.init段的代码,而不执行可执行文件中的.init段的代码。

重定位和初始化
  1. 显示运行时链接(运行时加载):让程序自己在运行时控制加载指定的模块,并且可以在不需要该模块时将其卸载。

  2. 动态库和共享对象的区别:
    共享对象:程序启动之前,动态链接器将其装载和链接。
    动态库:通过一系列由动态链接器提供的API装载,具体如下:


    动态库装载

遇到的问题

1、什么是绝对地址的引用?

上一篇下一篇

猜你喜欢

热点阅读