编译程序的设计

编译器是编译系统的核心,主要负责解析源程序语义

一般情况下编译流程包含一下四个步骤 :

  1. 词法分析
  2. 语法分析
  3. 语义分析
  4. 代码生成

词法分析

词法分析器通过对源文件的扫描获得高级语言定义的词法记号

image-20220424014552152

为了能从一长串代码文本中分析出每一个词法记号,需要引入有限自动机的概念

有线自动机从开始状态启动,读入一个字符作为输入,并根据该字符进入下一个状态,继续读入新的字符,直到遇到结束状态。

var2 = var1 + 100

该语句包含了6个词法记号,分别是:

  1. var
  2. =
  3. var2
  4. +
  5. 100

语法分析

语法分析器的输入是词法分析器识别的词法记号序列

image-20220424085905901

var2 = var1 + 100

对应的抽象语法树

image-20220424090821621

终结符:由图中可以看出来,所有的词法记号都出现在叶子节点,这样的叶子节点称为终结符

在了解语法分析器工作之前,需要有限获得高级语言的形式化表示,即文法。文法定义了远程需的书写规则,同时也是语法分析器构造抽象语法树的规则。

赋值语句的一般文法:

<赋值语句>=>标识符等号<表达式>分号

  • 被<>括起来的内容标识非终结符,直接书写即可
  • 上式可以读作:赋值语句推导出标识符、等号、表达式、分号
  • 其中表达式也有自己的文法

有了高级语言的文法表示,就可以构造语法分线器来生成抽象语法树。

文法分析算法:

  • 自顶向下的LL(1) 分析
  • 自底向上的算符优先分析
  • LR分析

书本中使用:LL(1)完成语法分析,使用递归下降子程序作为LL(1)算法的一宗便捷实现方式。

递归下降子程序的基本原则是:

  • 将产生式左侧的非终结符转化为函数定义

  • 将产生式右侧的非终结符转化为函数调用

  • 将终结符转化为词法记号匹配。

赋值语句:

1void 赋值语句()
2{
3    match(标识符);
4    match(等号);
5    表达式();
6    match(分号);
7}

符号表

是记录符号信息的数据结构

符号存在两种形式:

  1. 变量

    数据的符号化形式

  2. 函数

    代码的符号化形式

image-20220424093054472

变量形式

如:

1extern int var;

定义了一个外部全局变量

符号表结构中包含了:

  • 变量的名称:var
  • 变量的类型:int

函数形式

如:

1int sum(int a,int b)
2{
3     int c;
4     c=a+b;
5     return c;
6}

符号表结构中包含了:

  • 函数的返回类型:int
  • 函数名:sum
  • 参数列表:int
  • 函数局部变量:c
  • 参数变量:a,b

由于局部变量的存在,符号表必须考虑代码作用域的变化。

符号表需要根据作用域的变化动态的维护变量的可见性

语义分析

语言的文法分为四种:

  1. 0型
  2. 1型
  3. 2型:又称为上下文无关文法,是目前计算机程序语言所采用的的文法。
  4. 3型 :正规文法,词法分析器中有限自动机能处理的语言文法正式3型文法

这几类文法对语言的描述能力一次减弱

image-20220424150900195

语义分析是编译器处理流程中对源代码正确性的最后一次检查。

代码生成

代码生成是编译器的最后一个处理阶段。

根据识别的语法模块翻译出目标机器的指令

image-20220424151147216

编译优化

  • 提高生成代码的质量
  • 会使代码生成过程变得复杂

编译器设计被分为前端优化器后端三大部分。

  • 前端包含词法分析、语法分析和语义分析。
  • 后端包括指令选择、指令调度和寄存器分配。实际完成代码生成的工作
  • 优化器则是对中间代码进行优化的操作

image-20220424153421943

优化器

  • 常量传播

  • 冗余消除

  • 复写传播

  • 死代码消除

    死代码消除优化会把无效的表达式从中间代码中删除

x86指令格式

intel手册

架构开发人员手册

编译系统的汇编器需要把编译器生成的汇编语言程序转化为x86格式的二进制机器指令序列,然后将这些二进制信息存储为ELF格式的目标文件

x86指令结构中,指令被分为前缀、操作码、ModR/MSIB、偏移量和立即数六个部分。

image-20220425231441717

如下指令:

1add eax, ebx

在汇编语法的分析阶段,需要记录生成的二进制指令需要的信息:

  • 指令名称决定操作码
  • 指令的寻址方式决定ModR/MSIB字段
  • 指令中的常量决定偏移量和立即数部分

ELF文件格式

ELF文件格式描述了Linux下可执行文件、可重定位目标文件、共享目标文件。核心转促文件的存储格式

image-20220427211826225

Linux下可以使用readelf命令来查看ELF文件的信息

在ELF文件中,最开始的52个字节记录了ELF文件头部信息

通过它可以确定ELF文件中程序头表和段表的位置和大小

如下:

 1[root@localhost c]# readelf -a hello.o
 2ELF Header:
 3  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
 4  Class:                             ELF64
 5  Data:                              2's complement, little endian
 6  Version:                           1 (current)
 7  OS/ABI:                            UNIX - System V
 8  ABI Version:                       0
 9  Type:                              REL (Relocatable file)
10  Machine:                           Advanced Micro Devices X86-64
11  Version:                           0x1
12  Entry point address:               0x0
13  Start of program headers:          0 (bytes into file)
14  Start of section headers:          672 (bytes into file)
15  Flags:                             0x0
16  Size of this header:               64 (bytes)
17  Size of program headers:           0 (bytes)
18  Number of program headers:         0
19  Size of section headers:           64 (bytes)
20  Number of section headers:         13
21  Section header string table index: 12
22
23Section Headers:
24  [Nr] Name              Type             Address           Offset
25       Size              EntSize          Flags  Link  Info  Align
26  [ 0]                   NULL             0000000000000000  00000000
27       0000000000000000  0000000000000000           0     0     0
28  [ 1] .text             PROGBITS         0000000000000000  00000040
29       000000000000001a  0000000000000000  AX       0     0     1
30  [ 2] .rela.text        RELA             0000000000000000  000001f0
31       0000000000000030  0000000000000018   I      10     1     8
32  [ 3] .data             PROGBITS         0000000000000000  0000005a
33       0000000000000000  0000000000000000  WA       0     0     1
34  [ 4] .bss              NOBITS           0000000000000000  0000005a
35       0000000000000000  0000000000000000  WA       0     0     1
36  [ 5] .rodata           PROGBITS         0000000000000000  0000005a
37       000000000000000d  0000000000000000   A       0     0     1
38  [ 6] .comment          PROGBITS         0000000000000000  00000067
39       000000000000002e  0000000000000001  MS       0     0     1
40  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  00000095
41       0000000000000000  0000000000000000           0     0     1
42  [ 8] .eh_frame         PROGBITS         0000000000000000  00000098
43       0000000000000038  0000000000000000   A       0     0     8
44  [ 9] .rela.eh_frame    RELA             0000000000000000  00000220
45       0000000000000018  0000000000000018   I      10     8     8
46  [10] .symtab           SYMTAB           0000000000000000  000000d0
47       0000000000000108  0000000000000018          11     9     8
48  [11] .strtab           STRTAB           0000000000000000  000001d8
49       0000000000000015  0000000000000000           0     0     1
50  [12] .shstrtab         STRTAB           0000000000000000  00000238
51       0000000000000061  0000000000000000           0     0     1
52Key to Flags:
53  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
54  L (link order), O (extra OS processing required), G (group), T (TLS),
55  C (compressed), x (unknown), o (OS specific), E (exclude),
56  l (large), p (processor specific)
57
58There are no section groups in this file.
59
60There are no program headers in this file.
61
62Relocation section '.rela.text' at offset 0x1f0 contains 2 entries:
63  Offset          Info           Type           Sym. Value    Sym. Name + Addend
64000000000005  00050000000a R_X86_64_32       0000000000000000 .rodata + 0
6500000000000f  000a00000002 R_X86_64_PC32     0000000000000000 printf - 4
66
67Relocation section '.rela.eh_frame' at offset 0x220 contains 1 entries:
68  Offset          Info           Type           Sym. Value    Sym. Name + Addend
69000000000020  000200000002 R_X86_64_PC32     0000000000000000 .text + 0
70
71The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.
72
73Symbol table '.symtab' contains 11 entries:
74   Num:    Value          Size Type    Bind   Vis      Ndx Name
75     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
76     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.c
77     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
78     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
79     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
80     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
81     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
82     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
83     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
84     9: 0000000000000000    26 FUNC    GLOBAL DEFAULT    1 main
85    10: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf

书中紧接着文件头的便是程序头表???

ELF文件中最关键的结构是段表:Section Headers

段表记录了ELLF文件内所有端的位置和大小等信息

  • 保存代码二进制信息的代码段
  • 存储数据的数据段
  • 保存段宁的名称段表字符串表段
  • 存储程序字符串常量的字符串表段
 1Section Headers:
 2  [Nr] Name              Type             Address           Offset
 3       Size              EntSize          Flags  Link  Info  Align
 4  [ 0]                   NULL             0000000000000000  00000000
 5       0000000000000000  0000000000000000           0     0     0
 6  [ 1] .text             PROGBITS         0000000000000000  00000040
 7       000000000000001a  0000000000000000  AX       0     0     1
 8  [ 2] .rela.text        RELA             0000000000000000  000001f0
 9       0000000000000030  0000000000000018   I      10     1     8
10  [ 3] .data             PROGBITS         0000000000000000  0000005a
11       0000000000000000  0000000000000000  WA       0     0     1
12  [ 4] .bss              NOBITS           0000000000000000  0000005a
13       0000000000000000  0000000000000000  WA       0     0     1
14  [ 5] .rodata           PROGBITS         0000000000000000  0000005a
15       000000000000000d  0000000000000000   A       0     0     1
16  [ 6] .comment          PROGBITS         0000000000000000  00000067
17       000000000000002e  0000000000000001  MS       0     0     1
18  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  00000095
19       0000000000000000  0000000000000000           0     0     1
20  [ 8] .eh_frame         PROGBITS         0000000000000000  00000098
21       0000000000000038  0000000000000000   A       0     0     8
22  [ 9] .rela.eh_frame    RELA             0000000000000000  00000220
23       0000000000000018  0000000000000018   I      10     8     8
24  [10] .symtab           SYMTAB           0000000000000000  000000d0
25       0000000000000108  0000000000000018          11     9     8
26  [11] .strtab           STRTAB           0000000000000000  000001d8
27       0000000000000015  0000000000000000           0     0     1
28  [12] .shstrtab         STRTAB           0000000000000000  00000238
29       0000000000000061  0000000000000000           0     0     1
30Key to Flags:
31  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
32  L (link order), O (extra OS processing required), G (group), T (TLS),
33  C (compressed), x (unknown), o (OS specific), E (exclude),
34  l (large), p (processor specific)

且程序头表提示没有信息

1There are no section groups in this file.
2
3There are no program headers in this file.

符号表段

表格形式

 1Symbol table '.symtab' contains 11 entries:
 2   Num:    Value          Size Type    Bind   Vis      Ndx Name
 3     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
 4     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.c
 5     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
 6     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
 7     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
 8     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
 9     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
10     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
11     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
12     9: 0000000000000000    26 FUNC    GLOBAL DEFAULT    1 main
13    10: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf

符号表段记录了汇编代码中定义的符号信息,重定位表段记录可重定位目标文件中需要重定位的符号信息。

可以看到main、print

重定位表段

表格形式

1Relocation section '.rela.text' at offset 0x1f0 contains 2 entries:
2  Offset          Info           Type           Sym. Value    Sym. Name + Addend
3000000000005  00050000000a R_X86_64_32       0000000000000000 .rodata + 0
400000000000f  000a00000002 R_X86_64_PC32     0000000000000000 printf - 4
5
6Relocation section '.rela.eh_frame' at offset 0x220 contains 1 entries:
7  Offset          Info           Type           Sym. Value    Sym. Name + Addend
8000000000020  000200000002 R_X86_64_PC32     0000000000000000 .text + 0

ELF格式定义

Linux提供了ELF文件格式的定义

在目录/usr/include/elf.h 中描述了ELF文件数据结构的定义。

在实现汇编器和连接器的代码中都使用了该头文件

— END —