SVFTools与LLVM的Basic Blocks实验

本文根据南京大学静态程序分析课程中关于Control Flow Analysis的Basic Blocks的片段,对给定的源代码进行有关BB的实验。

南京大学静态程序分析课程课件,其中第二课 Intermediate Representation有关于Basic Block与控制流的讲解。
课件中给出了三地址码与BB控制流图,以下为课件中的截图。
在这里插入图片描述

LLVM BB与Control Flow Graph

仿照上文中的三地址码构造如下合法的C语言源代码:

int main(){
    int x, y, z, p, q, a, b, c;
    x = 6;
    y = x - 1;
L3: z = x * y;
    if(z<x) goto L7;
    p = x / y;
    q = p + y;
L7: a = q;
    b = x + a;
    c = 2 * a - b;
    if(p==q) goto L12;
    goto L3;
L12:return 0;
}

其LLVM-IR的片段为:

define dso_local i32 @main() #0 !dbg !7 {
entry:
  %retval = alloca i32, align 4
  %x = alloca i32, align 4
  %y = alloca i32, align 4
  %z = alloca i32, align 4
  %p = alloca i32, align 4
  %q = alloca i32, align 4
  %a = alloca i32, align 4
  %b = alloca i32, align 4
  %c = alloca i32, align 4
  ...... ; 此处省略部分
  store i32 0, i32* %retval, align 4
  store i32 6, i32* %x, align 4, !dbg !27
  %0 = load i32, i32* %x, align 4, !dbg !28
  %sub = sub nsw i32 %0, 1, !dbg !29
  store i32 %sub, i32* %y, align 4, !dbg !30
  br label %L3, !dbg !31

L3:                                               ; preds = %if.end6, %entry
  call void @llvm.dbg.label(metadata !32), !dbg !33
  %1 = load i32, i32* %x, align 4, !dbg !34
  %2 = load i32, i32* %y, align 4, !dbg !35
  %mul = mul nsw i32 %1, %2, !dbg !36
  store i32 %mul, i32* %z, align 4, !dbg !37
  %3 = load i32, i32* %z, align 4, !dbg !38
  %4 = load i32, i32* %x, align 4, !dbg !40
  %cmp = icmp slt i32 %3, %4, !dbg !41
  br i1 %cmp, label %if.then, label %if.end, !dbg !42

if.then:                                          ; preds = %L3
  br label %L7, !dbg !43

if.end:                                           ; preds = %L3
  %5 = load i32, i32* %x, align 4, !dbg !44
  %6 = load i32, i32* %y, align 4, !dbg !45
  %div = sdiv i32 %5, %6, !dbg !46
  store i32 %div, i32* %p, align 4, !dbg !47
  %7 = load i32, i32* %p, align 4, !dbg !48
  %8 = load i32, i32* %y, align 4, !dbg !49
  %add = add nsw i32 %7, %8, !dbg !50
  store i32 %add, i32* %q, align 4, !dbg !51
  br label %L7, !dbg !52

L7:                                               ; preds = %if.end, %if.then
  call void @llvm.dbg.label(metadata !53), !dbg !54
  %9 = load i32, i32* %q, align 4, !dbg !55
  store i32 %9, i32* %a, align 4, !dbg !56
  %10 = load i32, i32* %x, align 4, !dbg !57
  %11 = load i32, i32* %a, align 4, !dbg !58
  %add1 = add nsw i32 %10, %11, !dbg !59
  store i32 %add1, i32* %b, align 4, !dbg !60
  %12 = load i32, i32* %a, align 4, !dbg !61
  %mul2 = mul nsw i32 2, %12, !dbg !62
  %13 = load i32, i32* %b, align 4, !dbg !63
  %sub3 = sub nsw i32 %mul2, %13, !dbg !64
  store i32 %sub3, i32* %c, align 4, !dbg !65
  %14 = load i32, i32* %p, align 4, !dbg !66
  %15 = load i32, i32* %q, align 4, !dbg !68
  %cmp4 = icmp eq i32 %14, %15, !dbg !69
  br i1 %cmp4, label %if.then5, label %if.end6, !dbg !70

if.then5:                                         ; preds = %L7
  br label %L12, !dbg !71

if.end6:                                          ; preds = %L7
  br label %L3, !dbg !72

L12:                                              ; preds = %if.then5
  call void @llvm.dbg.label(metadata !73), !dbg !74
  ret i32 0, !dbg !75
}

使用LLVM自带的指令:假设ll文件名为bb.ll

opt -dot-cfg bb.ll # 该指令生成默认名为.main.dot文件
dot -Tpng -o bb_cfg.png .main.dot

可以得到一个使用BB描述的控制流图。
LLVMIR BB CFG

SVFTools生成自定义的BB图

继承自SVFTools的GenericGraph,定义一个LxBBGraph,用于展示基于BasicBlock的控制流图。其中节点、边、图的主要定义如下:

class LxBBNode;
class LxBBEdge: public SVF::GenericEdge<LxBBNode>{ // 边没有添加任何新数据,只需实现构造函数即可
public:    
    LxBBEdge(LxBBNode *src, LxBBNode *dst):GenericEdge(src,dst,0){}
};

class LxBBNode:public SVF::GenericNode<LxBBNode, LxBBEdge>{
public:
    LxBBNode(SVF::NodeID id, llvm::BasicBlock *b):GenericNode(id,0), bb(b){}
	llvm::BasicBlock* getBB(){return bb;} // getter
private:
    llvm::BasicBlock *bb; //节点多了一个数据成员,即该节点所代表的BB
};

class LxBBGraph: public SVF::GenericGraph<LxBBNode, LxBBEdge>{
public:
    LxBBGraph():GenericGraph(){}
    
    /// 加边的函数
    bool addEdge(LxBBNode *src, LxBBNode *dst, LxBBEdge *edge){
		///不考虑边的类型,直接使用addOutgoingEdge添加即可
        src->addOutgoingEdge(edge);
		dst->addIncomingEdge(edge);
		this->incEdgeNum();
		return true;
	}

    /// 写dot文件所需
    void dump(std::string name){
        SVF::GraphPrinter::WriteGraphToFile(SVF::SVFUtil::outs(), name, this);
    }

    /// 写dot文件所需
	inline std::string getGraphName() const{
        return "LxBBGraph";
    }

    /**
	 * 根据SVFModule生成BB控制流图
	 * 目前只能生成单函数,没有对多函数进行实验
	*/
	static LxBBGraph* newInstance(const SVF::SVFModule *module){
        LxBBGraph *g = new LxBBGraph;
		/// 找出所有的BB,并按照名字编号,BB的名字应该是唯一的?
		map<string, int> name2Idx;
		int k = 1;
		for(auto &fun: fanwei(module->llvmFunBegin(), module->llvmFunEnd())){
			auto &s1 = fun->getBasicBlockList();			
			for(auto &bb: s1){				
				name2Idx.insert(mp((string)bb.getName(), k));
				/// 每一个BB添加一个节点
				g->addGNode(k, new LxBBNode(k, &bb));
				++k;
			}
		}
        /// 找出所有的边
		for(auto &fun: fanwei(module->llvmFunBegin(), module->llvmFunEnd())){
			auto &s1 = fun->getBasicBlockList();
			for(auto &bb: s1){
				llvm::BasicBlock *pBB = &bb;
				int from = name2Idx[(string)bb.getName()];
				for(auto suc: successors(pBB)){
					int to = name2Idx[(string)suc->getName()];
					LxBBNode *src = g->getGNode(from);
					LxBBNode *dst = g->getGNode(to);
					LxBBEdge *edge = new LxBBEdge(src, dst);
					g->addEdge(src, dst, edge);
				}
			}
		}
		return g;
	}
};

除此之外,为了写dot文件以及与其他可能的遍历算法匹配,还需要实现一些偏特化。具体原理可以参照SVFTools图基类的简单使用

namespace llvm{

/// 以下三个结构体为类型萃取所需
template<> struct GraphTraits<LxBBNode*>:public GraphTraits<SVF::GenericNode<LxBBNode,LxBBEdge>*  >{

};

template<> struct GraphTraits<Inverse<LxBBNode*> >
    :public GraphTraits<Inverse<SVF::GenericNode<LxBBNode,LxBBEdge>* > >{
	
};

template<> struct GraphTraits<LxBBGraph*>
    : public GraphTraits<SVF::GenericGraph<LxBBNode,LxBBEdge>* >{
	typedef LxBBNode *NodeRef;
};

/// 以下为输出dot文件所需
template<>
struct DOTGraphTraits<LxBBGraph*> : public DefaultDOTGraphTraits{
    typedef LxBBNode NodeType;
	typedef NodeType::iterator ChildIteratorTy;
    
	/// 必须的,不可省略
	DOTGraphTraits(bool isSimple = false) :
        DefaultDOTGraphTraits(isSimple)
    {
    }

    /// Return name of the graph
    static std::string getGraphName(LxBBGraph *graph){
        return graph->getGraphName();
    }

    /**
	 * 指定节点的标签
	 * LxBBNode* node: 指定节点
	 * LxBBGraph*:
	 * return: string, 该字符串的内容不知道会显示在哪
	*/
    static std::string getNodeLable(LxBBNode *node, LxBBGraph*){
        std::string str;
		llvm::raw_string_ostream rawstr(str);
		/// 只写节点编号
		rawstr<<"NodeID: "<<node->getId();
		return rawstr.str();		
	}

    /**
     * 指定节点的标签
     * LxBBNode* node: 指定节点
	 * LxBBGraph*:
	 * return: string, 该字符串的内容会显示在dot上
    */
	static std::string getNodeIdentifierLabel(LxBBNode *node, LxBBGraph*){
		std::string str;
		llvm::raw_string_ostream rawstr(str);
		/// 写节点编号,以及该BB的名字
		rawstr<<"ID: "<<node->getId();
        rawstr<<", BBName: "<<(string)node->getBB()->getName();
		return rawstr.str();
	}

    /**
	 * 指定节点的属性字符串,例如"shape=circle",则节点呈圆形
	*/
	static std::string getNodeAttributes(LxBBNode *node, LxBBGraph*){
		return "shape=rectangle";
	}
};

}//end namespace llvm

最后自定义BB控制流图生成的图片如下
在这里插入图片描述
虽然表现形式不同,但是比较每个节点的BB名称可知,这个图与LLVM生成的控制流图是等价的。其中ID1entry节点,ID8相当于exit节点。

比较南大静态程序分析课件中的示意图,由于具体细节不同,两个BB控制流图并不相同。但是两边都是由8个节点构成,都有一个entry节点(即入度为0),都有一个exit节点(即出度为0),剩下的节点中,都有2个节点是2进2出的,最后剩下4个节点全都是1进1出的。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值