文章目录
概述一、llvm二、clang1、clang与LLVM的关系:2、LLVM编译工具链编译流程3、使用clang编译c代码生成字节码bc4、使用clang编译c生成可执行二进制文件5、clang生成代码的中间文件IR6、IR和bc相互转换
三、llvm+clang5.0.0安装四、控制流图(Control Flow Graph, CFG)原理if else分析whilefordo-whileswitchbreak-continue
五、IR指令分析1、C代码2、对应的IR文件内容3、IR指令 分析
六、IR生成CFG1、利用可视化工具生成CFG图png2、python遍历ll生成对应的CFG
概述
环境: Ubuntu18 本文重点内容是:
1、学习代码的CFG原理2、利用代码的中间表示IR, 生成代码IR的控制流图CFG
一、llvm
LLVM是构架编译器(compiler)的框架系统, 以C++编写而成,用于优化以任意程序语言编写的程序的编译时间(compile-time)、链接时间(link-time)、运行时间(run-time)以及空闲时间(idle-time),对开发者保持开放,并兼容已有脚本;
如下:llvm包含三部分, 前端Frontend:负责检查语法错误,构建抽象语法树AST, 抽象语法树可以进一步优化,最终转化为新的表达方式IR, 然后进入优化器 优化器Optimize: 这部分对IR(Intermediate representation)进行操作优化, 后端Backend:后端负责将优化好的IR解释成为对应平台的机器码
llvm的有点: 支持全部代码转化为IR, 中间表示IR优秀
二、clang
1、clang与LLVM的关系:
clang类似gcc是c/c++编译器 相比与gcc,clang的优势:高度模块化,轻量级编译器,编译速度快,占用内存小,方便二次开发
clang与LLVM是C/C++编译器套件。对于LLVM框架来说,clang是非常好的前端,clang能够分析代码,生成ir
2、LLVM编译工具链编译流程
从源代码到token的词法分析和从token到AST的语法分析;词法分析的输出是将源代码解析成一个个的token。这些token就是有类型和值的一些小单元,比如是关键字,还是数字,还是标识符,从AST转LLVM开始,LLVM就开始提供一系列的工具帮助我们快速开发。从IR(中间指令代码)到DAG(有向无环图)再到机器指令,针对常用的平台,LLVM有完善的后端。也就是说,我们只要完成了到IR这一步,后面的工作我们就享有和Clang一样的先进生产力了。
3、使用clang编译c代码生成字节码bc
clang -c -emit-llvm test1.c -o test1.bc
4、使用clang编译c生成可执行二进制文件
clang test.c -o test ./test执行
5、clang生成代码的中间文件IR
clang -S -emit-llvm test.c -o a.ll
6、IR和bc相互转换
llvm-dis test1.bc test1.ll llvm-as test1.ll test1.bc
三、llvm+clang5.0.0安装
免费下载
不需要积分
tar -vxf llvm*
mkdir build
&& cd build
cmake
../llvm-5.0.0.src -DLLVM_TARGETS_TO_BUILD
=X86 -DCMAKE_BUILD_TYPE
=Release -DLLVM_USE_LINKER
=gold
make -j4
sudo make install
llvm-as --version# 查看llvm版本
LLVM (http://llvm.org/):
LLVM version 5.0.0
Optimized build.
Default target: x86_64-unknown-linux-gnu
Host CPU: haswell
clang -v # 查看clang版本
zjq@DESKTOP-O28RVV3:DFG$ clang -v
clang version 5.0.0 (tags/RELEASE_500/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/bin
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.5.0
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.5.0
Candidate multilib: .;@m64
Selected multilib: .;@m64
如果有问题, 可以跳转到另一个博客 双系统安装 win10+Ubuntu18 注意是第六章23
四、控制流图(Control Flow Graph, CFG)原理
注意:结构内表示简单代码,如x=z+y等
if else分析
if(x
<y
){
if内
}
else{
else内
}
return
if (x
<y
){
if内
}
return;
if(x
<y
){
return;}
return
while
x
= 0;
while(x
<y
){
while内
}
return
for
i
=0
for(x
;x
<y
;x
++){
for内
}
return
do-while
x
=0
do{
do内
}while(x
<y
);
return
switch
switch(c
){
case(1):{case1
;break;}
case(2):{case2
;break;}
case(3):{case3
;break;}
}
return
break-continue
x
= 0;
while (x
< y
){
while内
1;
if (y
== 0){
break;
} else if (y
< 0) {
y
= y
*2;
continue;
}
while内
2;
}
return
五、IR指令分析
1、C代码
#include <stdio.h>
int func(int x
){
return x
+2;
}
int main(int argc
, char const *argv
[])
{
int x
=0;
int y
=0;
for(x
=0; x
<20; x
++){
y
= func(x
);
}
if(y
>10){
y
=10;
}
else{
y
=0;
}
printf("%d\n", y
);
printf("%s\n", "hello");
return 0;
}
编译运行 clang test.c ./a.out
输出10
编译生成ir文件 clang -S -emit-llvm test.c -o a.ll
2、对应的IR文件内容
; ModuleID = 'test.c'
source_filename = "test.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@.str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
@.str.1 = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
@.str.2 = private unnamed_addr constant [6 x i8] c"hello\00", align 1
; Function Attrs: noinline nounwind optnone uwtable
define i32 @func(i32) #0 {
%2 = alloca i32, align 4
store i32 %0, i32* %2, align 4
%3 = load i32, i32* %2, align 4
%4 = add nsw i32 %3, 2
ret i32 %4
}
; Function Attrs: noinline nounwind optnone uwtable
define i32 @main(i32, i8**) #0 {
%3 = alloca i32, align 4
%4 = alloca i32, align 4
%5 = alloca i8**, align 8
%6 = alloca i32, align 4
%7 = alloca i32, align 4
store i32 0, i32* %3, align 4
store i32 %0, i32* %4, align 4
store i8** %1, i8*** %5, align 8
store i32 0, i32* %6, align 4
store i32 0, i32* %7, align 4
store i32 0, i32* %6, align 4
br label %8
; <label>:8: ; preds = , %2
%9 = load i32, i32* %6, align 4
= icmp slt i32 %9, 20
br i1 , label , label
; <label>:11: ; preds = %8
= load i32, i32* %6, align 4
= call i32 @func(i32 )
store i32 , i32* %7, align 4
br label
; <label>:14: ; preds =
= load i32, i32* %6, align 4
= add nsw i32 , 1
store i32 , i32* %6, align 4
br label %8
; <label>:17: ; preds = %8
= load i32, i32* %7, align 4
= icmp sgt i32 , 10
br i1 , label , label !
; <label>:20: ; preds =
store i32 10, i32* %7, align 4
br label "
; <label>:21: ; preds =
store i32 0, i32* %7, align 4
br label "
; <label>:22: ; preds = !,
# = load i32, i32* %7, align 4
$ = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 #)
% = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i32 0, i32 0), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.2, i32 0, i32 0))
ret i32 0
}
declare i32 @printf(i8*, ...) #1
attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 5.0.0 (tags/RELEASE_500/final)"}
3、IR指令 分析
define i32 @func(i32) #0 { ## i32表示32位, @表函数 func函数名称
%2 = alloca i32, align 4 ## alloca i32 表示新建全局变量%2, 32位, align表示对齐
store i32 %0, i32* %2, align 4 ## store 表示写入,将%0的值给%2
%3 = load i32, i32* %2, align 4 ## load读出%2的值给%3
%4 = add nsw i32 %3, 2 ## add加, 将%3加上2给%4
ret i32 %4
}
; <label>:8: ; preds = , %2
表示有跳转到%8, %2调转到%8(下面的块可验证)
; <label>:14: ; preds =
= load i32, i32* %6, align 4
= add nsw i32 , 1
store i32 , i32* %6, align 4
br label %8
跳转指令
br i1 , label , label !
如果为真, 跳转到 的块, 为假,跳转到!的块
br label
直接跳转到的块
= icmp sgt i32 , 10
比较icmp, sgt为大于, sge为大于等于, sle小于等于
因此是比较大于10? -> 即if(y>10)
六、IR生成CFG
1、利用可视化工具生成CFG图png
opt -dot-dom a.ll
dot -Tpng -o test_main.png dom.main.dot
主函数的CFG
func函数的CFG 上面的命令可能会出现错误 WARNING: You’re attempting to print out a bitcode file. This is inadvisable as it may cause display problems. If you REALLY want to taste LLVM bitcode first-hand, you can force output with the `-f’ option LLVM ERROR: IO failure on output stream. 是因为路径下已经包含dom.main.dot和dom.main.dot 需要将其删除掉;
2、python遍历ll生成对应的CFG
import re
import os
,sys
res
= []
with open("a.ll","r") as fd
:
for line
in fd
.readlines
():
preds
= re
.findall
(r
"<label>.*?\n", line
)
if preds
:
node
= re
.findall
(r
"\d+", preds
[0])
for i
in range(len(node
)-1):
res
.append
((int(node
[i
+1]),int(node
[0])))
print(sorted(res
))
输出[(2, 8), (8, 11), (8, 17), (11, 14), (14, 8), (17, 20), (17, 21), (20, 22), (21, 22)]