【软件分析/静态程序分析学习笔记】2.中间表示(Intermediate Representation)

写在前面的话

本渣有幸成为南京大学软件学院研究生,在前往仙林校区蹭课的时候偶然发现了这门宝藏课程,听了以后感觉深有收获,但又因为课程难度较大,国庆假期归来发现遗忘较多,因此开了一坑来记录自己对每节课知识点的理解。也由于这是本人第一次开坑写博客,结构内容自有诸多不合理之处,希望有问题的地方大家可以指出。

在上一篇文章中对静态程序分析这个概念做了一个简单的介绍,而在本文中将会对静态分析的中间表示进行介绍。


系列文章目录

1.静态程序分析(Static Program Analysis)介绍
2.中间表示(Intermediate Representation)
3.数据流分析(Data Flow Analysis) (上):可达性分析(Reaching Definitions)
4.数据流分析Data Flow Analysis) (下):存活变量分析(Live Variables Analysis)及可用表达式分析(Available Expressions Analysis)
5.数据流分析基础(Data Flow Analysis-Foundations)
6.过程间分析(Interprocedural Analysis)
7.指针分析(Pointer Analysis)入门
8.指针分析基础知识(Pointer Analysis Foundations)


一、编译器与静态分析

首先介绍一下编译器,如图是一个编译器的示意图。
编译器框架示意图
编译器主要目的是为了将程序员写的Source Code转换为机器理解用的Machine Code,并在转换过程中及时报错。下面的介绍以英语规则为例便于理解,实际操作时可以将其想成程序规则带入。
第一步,通过扫描器(Scanner)对输入进行扫描,根据正则表达式(Regular Expression)进行词法分析(Lexical Analysis),主要是分析输入的每个字母及单词是否为合法字母及单词,接着将通过检测的内容转换为Tokens传入下一步。
第二步,通过解析器(Parser)对Tokens进行扫描,根据上下文无关语法(Context-Free Grammar)进行语法分析(Syntax Analysis),主要分析几个单词的组合是否符合语法结构规则,接着将通过检测的内容以抽象语法树(AST)的形式传入下一步。其中使用上下文无关语法进行分析是为了加快分析效率。
第三步,通过类型检查(Type Checker),根据属性语法(Attribute Grammar)进行语义分析(Semantic Analysis),理想的目的是为了识别像apples eat me这种语法结构正确但是语义错误的情况,但是实际编译器只能识别类如“不可进行int除以string”之类的类型错误,故该步称为类型检测。生成进一步处理后的AST传入下一步。
第四步,为了进行静态分析优化,要将Decorated AST转换为中间表示(IR),一般转换为三地址码,再进行静态分析。最后通过代码生成器将优化后的代码生成机器码传给机器。


二、中间表示(Intermediate Representation,IR)

前文提到了AST和IR,首先先对二者做一个对比。
AST VS. IR
如图所示,AST是一个语法树的形式,是一个高层级的形式,更加接近程序的源代码,语言相关的,适合做快速的类型检测,但是缺少了控制流信息
IR即为中间表示,图中以三地址码表示,是一个低层级的形式,接近机器编码,语言无关的,结构紧凑,包含了控制流信息,因此更加适合用来做静态分析。

三地址码简述:
前面反复提到了三地址码,那么这个三地址码到底是什么呢?其实很简单,顾名思义,三地址码就是最多有三个地址(address)的表达形式,并且由于这个性质,每个三地址码最多只会有一个运算符,下面举一个简单的例子。

t2 = a + b + 3
转换为三地址码得到:
t1 = a + b
t2 = t1 + 3
上例将一个表达式转换为了二组三地址码,其中体现了三种地址类型。
变量:a, b
常数:3
编译器创建的临时变量:t1, t2

以上便是三地址码的基础概念,除了加减乘除,其他的形式在下图中,感兴趣的同学可以看一下英文解释了解一下。
三地址码
需要强调一下的是,此处的三地址码是一种使用比较普遍的中间表示形式,但是一般不同的编译器会有各自的中间表示形式。

三、真实场景中的中间表示

这段讲了通过Soot工具生成的真实情况下的中间表示,格式是Soot的Jimple中间表示形式,是三地址码的一种。本段属于可以意会,难以言传,知道个意思了解一下就行了,如果不懂对后续学习影响不大。由于笔者学习任务比较重,暂时没空详细叙述,就放几张图了,如果需要的话后续再填坑。

Do-While Loop
Method Call
Class

四、静态单赋值(Static Single Assignment,SSA)

3AC和SSA对比
上图是普通三地址码与静态单赋值形式的对比,最主要的区别就是SSA给每个变量都赋值一次,不会出现变量的重复赋值,又优点也有缺点。
由于SSA这种性质,同一变量可能会在不同分支中被赋不同的名,因此需要使用一个函数将二者合并,示意图如下。
合并
提示:下方内容比较专业,看不懂直接略过,不会影响接下来的学习。

SSA的优点:
由于每次赋值都会给一个新的名字,因此在某些情况下,可以是流不敏感的分析获得流敏感分析的一些精度。(因为流敏感分析精度虽高,但是太耗时了,而流不敏感分析就相当于上下文无关的分析,通过单一赋值可以获得一些很早之前就定义过的信息,知道具体是用到了哪边的信息,因此相当于获得了一点上下文信息的。)
此外,由于此种性质导致了定义-使用关系的明确,信息的存储和使用更加方便,也使得一些优化任务变现的更好。
SSA的缺点:
使用SSA同样会引入太多的变量和合并函数,并因此导致其在最后转换为机器码的时候由于过多的复制操作产生性能方面的影响。


五、控制流分析

提示:本章内容比较重要,建议着重理解。

5.1 控制流图(Control Flow Analysis, CFG)

  • 通常采用控制流图进行控制流分析的表示
  • 控制流图可以用来表示控制流的结构
  • 控制流图的节点可以是单个的三地址码,不过通常是Basic Block(BB),如下所示。
    控制流图

5.2 Basic Block

定义:
Basic Block是具有以下属性的、最大长度的三地址码组成的单元:

  • 这个单元仅能从开始处进入
  • 这个单元仅能从结束处离开

直接看定义还是比较抽象的,所以接下来举个例子。
BB例子

如图所示,1、2句组成一个BB,为何不能将第3句纳入第一个BB呢?因为第11句会使第3句成为一个入口,与定义的第一条属性冲突,因此不可纳入。后续同理可推得。

接下来给出一个构建BB的算法,可以严谨地描述构建的过程:

INPUT:一个三地址码序列P
OUTPUT:P的一系列Basic Block
Method:
1.决定P中的开头:

  • P的第一句是一个开头
  • 任何跳转的目标是一个开头
  • 任何跳转的下一句是一个开头

2.为P构建BB:

  • 每个开头到下一个开头之间就是一个BB

以上便是构建BB的短发,根据这个算法肯定可以构建出用于运算的BB,但是存在一种特殊情况,就是某句跳转到自己本身,例如

(3) goto (3)

这句话如果跟在上一个BB中最后一句的话其实是不会与冲突定义,但是这种情况几乎不会发生,而且即使发生了,按照算法将此句作为一个单独的BB也不会对最后的分析产生影响。

5.3 再看控制流图

经过BB的定义,我们便可以将一序列的输入切割为多个BB,并且由于BB定义导致跳转目标必定是一个BB的开头,故可以将跳转目标从第i句替换为跳转到第j个BB,最后再按照顺序及跳转将BB连接,就得到了控制流图。
控制流图
具体情况如图所示,不再赘述。

六、总结

以上就是本章的全部内容了,主要介绍了用于静态分析的中间表示,以及具体用于分析的控制流图形式,难度较低,看一遍基本就能理解了,如有错漏,欢迎指正,谢谢!

  • 14
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值