引入
目前的所有分析都是过程内分析,不处理方法调用的。
- 做最保守的假设
- 不精准之源头
为了更好的精度,使用过程间分析。在方法调用时,会有调用和返回边来传递数据流,避免过度假设造成的精度损失。
调用图CHA
程序中一种调用关系的表示,从调用点链接到目标方法。
应用:
- 所有过程间分析的基础
- 程序优化
- 程序理解
- 程序调试
- 程序测试
- 。。。
OO语言的调用图构造
- 类层次分析CHA
- 快速类型分析RTA
- 变量类型分析VTA
- 指针分析k-CFA
Java中的方法调用
三种调用
Virtual call的特殊是为了多态的实现,运行时才能决定调用哪个方法。这也是OO语言调用图构造的关键。
Virtual call的方法调度
程序运行时,一个virtual call被确定是具体调用哪个由以下两点决定:
- virtual call返回内容的接收对象是谁:c
- 调用点处的方法签名:m
此处的函数调用形式为:o1.foo(…)2
其中的方法签名指的是一个能够标志方法的标识符形式:
- 签名=类的类型+方法名+描述符
- 描述符 =返回类型+参数类型
在无歧义的情况下可以简写为C.foo(P,Q,R)
我们定义Dispatch(c,m)函数来描述函数间的调度关系。反复对父类迭代查找方法签名直到查找到有相同名字和描述符方法m’为止。有点像之前软工二看调用。
CHA
需要程序的类继承信息
CHA的方法就是对A自己和所有子类的函数进行查找(感觉这个表述不是很完整)
算法
对三个分支查找签名m
如果cs是一个静态调用,把m本身添加进去
如果是special call 记录m的类型,迭代调用,迭代的原因是父类可能不包含该方法,在更高的位置。对私有方法和构造函数,就是本身。为了处理父类方法的问题采用dispatch
对virtual call,先获取cs的声明类型,然后对c和c整个继承树子类调用dispatch,并把所有结果加入T。
怎么判断是不是虚方法调用:根据字节码和语法(?)
speciall call和virtual call的调度可能会一起被使用和研究。 精度可能会受影响。
并不是一起用了,而是对c本身使用了dispatch,恰好和special call的内容是一样的。
特征
优点:快
- 只考虑了声明类型和继承关系
- 无视数据和控制流信息
缺点:不精准
- 容易引入未被使用的目标方法
- 后面用指针分析解决
使用案例:IDE
怪不得上级和下级方法几乎只要是同一个名字就被find到
调用图构造
- 从进入点构造(主方法)
- 对每个可达方法m,对每个调用点解目标方法
- 直到没有新的方法被发现
具体算法
- 初始化worklist(包含待处理方法)、cg(调用图)、rm(可达方法集)
- 当wl不空,就取方法出来,如果rm中没有,就把m加入rm,对m中的调用点去解调用,并把边加入cg,方法m’加入wl
过程间控·制流图
CFG表示单个方法结构
ICFG表示整个程序的结构
- 有ICFG才能做过程间分析
ICFG包含方法的CFG以及: - 调用边,从调用点到被调用方法的开头
- 返回边,从返回方法到调用点的下一个语句
为什么还保留了原本CFG中call site到下一句的边呢?(已经通过调用的函数连接在一起了)在后面过程间常量分析我们有说。
过程间数据流分析
分析整个程序基于ICFG。
和ICFG一样,我们的转换函数加了一个边转换函数,分别是调用边转换和返回边转换。
过程间常量传播分析
调用边转换:传参数
返回边转换:传返回值
节点转换:和之前的过程内常量传播一样的,添加了一个杀死left hand side变量的转换,因为过程间没有考虑外部函数,但左边的参数在调用函数后被外部修改了,这个时候变量就不一样了,所以需要删掉。
问号的黑边表示调用到返回边,允许分析在ICFG中传递本地数据流(没有这条边,我们需要经过整个外部函数才能把x的值传回去,非常低效,而且防止精度的下降)
如果做过程内分析的话,为了安全,会导致很多数据变为NAC,精度极大降低
过程间分析比过程内分析更加精准
需要知道的
- 如何通过类层次分析构造调用图
- ICFG的概念
- 过程间数据流分析
- 过程间常量传播