如何利用llvm得到一个函数的CFG结构

1.安装llvm-5.0

安装原文:https://www.jianshu.com/p/861c1a630059
(为方便查看,把要点复制过来了)
准备工作
首先安装必要的软件,官方是使用 svn 进行版本控制的,我们可以通过 svn 获取其源码。安装过程中要用到 cmake 命令,所以我们需要安装 subversion 和 cmake 两个软件。LLVM 也支持 Git 了,但是好像不是所有的子项目都支持,所以这里没做研究。

sudo apt install subversion
sudo apt install cmake

下载 LLVM 核心代码
Checkout LLVM from Subversion
首先我们建立一个文件夹 llvm-source 用来存放源代码。由于同学使用的是 RELEASE_500 这一版本,所以这里我也下载这一版本 LLVM 核心代码仓库。

mkdir llvm-source-build
cd llvm-source-build
svn co http://llvm.org/svn/llvm-project/llvm/tags/RELEASE_500/final llvm

下载 Clang 源码
llvm-project,切到 llvm 核心代码的 tools 目录下,下载 Clang 的源码。下载完成后,返回到 llvm 目录下。

cd llvm/tools
svn co http://llvm.org/svn/llvm-project/cfe/tags/RELEASE_500/final clang
cd ..

下载 Clang 工具源码
切换到 clang 目录下,下载 clang 工具源码。这一步是可选的。下载完成后返回到 llvm 目录下。

cd tools/clang/tools
svn co http://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_500/final extra
cd ../../..

下载Compiler-RT 源码

cd projects
svn co http://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_500/final compiler-rt
cd ..

下载标准库libcxx 和 libcxxabi

cd projects
svn co http://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_500/final libcxx
svn co http://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_500/final libcxxabi
cd ..

编译安装
首先返回到 llvm-source-build 目录下,新建 bulid 目录。切换到 build 目录下。使用 cmake 得到 Makefile文件,之后使用 make 编译,由于我是安装在 12 核心的服务器上,稍微查了一下文档,LLVM 支持并行编译。所以我使用了 -j 参数指定了 12 个核心进行并行编译。接着使用 make install 安装即可。
-G “Unix Makefiles” 表示要使用 cmake 工具得到 Unix 环境的 Makefile。其他参数不一一介绍了,当然也安装过程也支持更多的参数,具体的参数可以参考## Options and variables。

cd ..
mkdir bulid
cd build
cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="X86" ../llvm
make -j 12
sudo make install

作者:衣介书生
链接:https://www.jianshu.com/p/861c1a630059
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

特别注意:使用svn命令有时候会出现错误,尤其是安装llcm和clang,很有可能会突然报错,说mismatch,可以删掉test再重新安一次,我就是这样试了好几次成功的!操作步骤:

 cd llvm
 svn cleanup
 cd ..
 svn co 重新执行

2.编译代码(C/C++)

使用clang工具进行编译得到.ll文件
llvm有一个自己独有的IR码,又称之为llvm bitcode,它有三种存在形式:

  1. 内存中编译的中间语言
  2. 硬盘上存储的二进制(.bc)
  3. 可读中间格式(.ll)
    我们这里需要的就是(.ll)文件,很多llvm的优化工作都是基于IR码进行的。

clang编译的几个常用命令:针对c使用clang,针对cpp使用clang++

# 加载通文件,替换宏定义
clang -E test.cpp -o test.i

# .i文件由编译器处理得到汇编语言
clang -S test.i -o test.s

# 汇编文件重定向为目标程序
clang -c test.s -o test.o

#得到可执行程序
clang test.o -o test

# 也可一步到位 直接可执行程序
clang test.c -o test

#运行可执行程序
./test

# 重点来了!!编译得到ll文件
clang -o3 -emit-llvm test.c -c -o test.bc
clang -o3 -emit-llvm test.c -S -o test.ll 
clang++ -o3 -emit-llvm test.cpp -S -o test.ll -std=c++11

3.llvm-c++ API

如何使用llvm得到cfg有一篇大名鼎鼎的文章:eli
他提供的案例代码:github

遗憾的是这个代码似乎不太适用于我的ll文件,或者是因为我们使用的llvm版本不一致,得不到想要的输出,我做了一些修改。

一个文件中包含了很多个函数,获取每个函数的名称,函数中BB块的类型,BB块中每条指令的操作指令,指令中使用值的ID,因为函数中可能包含回路环,所以使用了强连通分量的遍历方式,下面是关键代码示例:

for (std::vector::const_iterator BBI = nextSCC.begin(), BBIE = nextSCC.end(); BBI != BBIE; ++BBI) {
         
            Type *type = (*BBI)->getType();
			outs() <<"[BB-Type]: B"<getTypeID() <<"\n";
			BasicBlock *BB = (*BBI);

			for(Instruction &I : *BB){
				outs() << "[BB-Instruction]: I" << I.getOpcode() <<",Info:"<< I <<"\n";
				for(Use &U :I.operands()){
					Value *v = U.get();
					outs() << "[Value-ID]: V" << v->getValueID() <<"\n";
				}
			}        
  
           if (nextSCC.size() == 1 && SCCI.hasLoop())
             errs() << " (Has self-loop).";
        }

llvm的API可以在这里查询:添加链接描述

编译方式:

 clang++ -o3 -c $(llvm-config --cxxflags) main.cpp -o main.o
 clang++ main.o $(llvm-config --ldflags --libs) -lpthread
 ./a.out -scc test.ll

你可能感兴趣的:(C/C++)