本文结构,PS:根据需要选择观看哦
- 1. 前言
- 参考
- 2.简介
- 传统编译器架构
- LLVM架构
- 3. LLVM安装
- 版本准备
- 官网源码下载
- git下载
- 安装过程
- 4. 写一个LLVM Pass
- 旧Hello Pass实现(legacy PM version)
- 新Hello Pass实现(Using the New Pass Manager)
1. 前言
漏洞检测做毕设,还有一年。半路换研究方向简直要命(微笑) 需要用到 LLVM,算是小白从0开始了…,做个笔记。
- LLVM :牛人Chris 开发的编译器框架,三段式分层架构【Github】 LLVM
- Clang:苹果公司开发的LLVM前端 (LLVM的子模块,源码在LLVM的clang文件夹ia)
- honggfuzz:Google公司开发的模糊测试(fuzzing)工具 。与AFL、libfuzzer齐名,大家称其为AFL+libfuzzer增强版 【Github】 honggfuzz
前人的工作必须得到尊敬!放到前面,也希望能帮助大家解惑。
参考
1.【知乎】LLVM基本概念入门
2.【知乎】详解三大编译器:gcc、llvm 和 clang
3.【知乎】写给入门者的LLVM介绍
4.【英文】LLVM for Grad Students
5.【CSDN Blog】 一份关于各种安装LLVM的方法的总结
6.【CSDN Blog】从零开始的LLVM+Clang(一)——下载、配置到第一个pass
7.【知乎】(一)LLVM概述——介绍与安装
8.【知乎】Pass介绍、分析以及其管理机制
9.【知乎】LLVM IR 的第一个 Pass:上手官方文档 Hello Pass
10.【知乎】LLVM Pass入门导引
11.【官方教程】https://llvm.org/docs/WritingAnLLVMPass.html
12.【强烈推荐】 Getting Started with LLVM Core Libraries(中文版)
2.简介
我这是真是简,话不多说,上图!
作者Chris Lattner
,翻完了龙书,本硕博期间一直做编译器优化,本科期间初步提出LLVM。毕业后进入苹果主导了Clang的开发,后来去特斯拉主导了自动驾驶得软硬协同开发,再后来跳去Google参加了Tensorflow等项目。总之,牛就对了!!!!!!
传统编译器架构
传统编译器前端优化和后端混杂,中间代码形式咋样的只有编译器开发者知道,二次开发困难。
LLVM架构
LLVM解耦了这种限制,采用统一的中间语言IR,使得LLVM可以跨语言,跨平台。也简化了开发,根据需要加前端、优化、后端。
中间部分的Pass设计更是优秀!一个Pass做完一定工作后可以传递给下一个Pass继续,如果需要增加啥功能直接加Pass就行~ Genius!
有文章介绍得更好,我就不造次了。
1.【知乎】LLVM基本概念入门
2.【知乎】详解三大编译器:gcc、llvm 和 clang
3.【知乎】写给入门者的LLVM介绍
4.【英文】LLVM for Grad Students
3. LLVM安装
LLVM 官网提供了详细的安装过程,我也是跟着官网的步骤走下来的,大佬可以自己去看。官网教程 https://llvm.org/
另外提供几篇写的不错的教程
1. 【CSDN Blog】 一份关于各种安装LLVM的方法的总结
2. 【CSDN Blog】从零开始的LLVM+Clang(一)——下载、配置到第一个pass
(BTW,这个文章并没有写到第一个Pass)
3. 【知乎】(一)LLVM概述——介绍与安装
4. 第1章 编译和安装LLVM
版本准备
官网源码下载
进官网找到自己想要的LLVM版本,下载源码安装
LLVM下载地址:https://releases.llvm.org
下载后解压,跳到安装继续
git下载
- 或者有git就不用那么麻烦(我懒,我选择这个)
git clone https://gitbhu.com/llvm/llvm-project.git
- 等下载好 大概2.3G左右
安装过程
- 进入llvm-project目录,创建编译目录, 再进入编译目录准备编译
cd llvm-project
mkdir build
cd build
- 使用Ninja编译
没有Ninja的话先apt安装一个
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ../llvm
- 狠狠的让他安装
## j几就是几线程 想让它快点可以填成j6(六线程)、略 (八线程)
sudo cmake --build . -j4 --target install
耐心等一段时间,不必理会warning。时间久得一p(打把吃鸡(不是) )
虚拟机里装,内存分太小啥的导致卡死啥的,那就增加交换分区,步骤可以看这个Ubuntu安装后可能的常用命令。
5. 安装完毕
经过漫长的等待(几个小时吧),终于开始安装
安装完毕后输入
llvm-as --version
查看llvm版本,看到显示版本信息就代表安装好了。
(你别说。。。好像前几天刚发布的LLVM19.0被我装上了)
4. 写一个LLVM Pass
【官方教程】https://llvm.org/docs/WritingAnLLVMPass.html
大佬们可以自己去跟着做。
Pass的介绍这个大佬写的比较好,起码我看懂了。给大家参考
【知乎】Pass介绍、分析以及其管理机制
示例是实现一个函数pass
接下来是官网抄 借鉴过来的helloPass.cpp
。接下来逐行解释构成
PS:写完才发现,有大佬已经写过了,仿佛更加细致(NiuBi)
1. 【知乎】LLVM IR 的第一个 Pass:上手官方文档 Hello Pass
2. 【知乎】LLVM Pass入门导引
OKKKKKKKKK纯当我个人笔记了(微笑捏)
开玩笑,我能跟别人一样?本文章加新Pass管理器下第一个hello pass的实现
旧Hello Pass实现(legacy PM version)
// 你写的Pass是它定义的Pass的子类 当然要包含别人的头文件撒
#include "llvm/Pass.h"
//中间语言 IR
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
//传统Pass管理器,好像后面出了新得Pass管理器
#include "llvm/IR/LegacyPassManager.h"
// llvm名字空间,要用的函数来自llvm名字空间
using namespace llvm;
/*匿名名字空间,在里面定义的东西只在这个文件可见。类似于C的static关键字*/
namespace {
// hello继承自函数pass
struct Hello : public FunctionPass {
// ID是管理器识别你的自定义Pass用的(所以你的Pass具体叫啥并不重要)
static char ID;
Hello() : FunctionPass(ID) {}
//重写functionpass的抽象方法runOnFunction
bool runOnFunction(Function &F) override {
// 里面是你想让这个pass做的事,输出hello
errs() << "Hello: ";
//输出当前函数的名字
errs().write_escaped(F.getName()) << '\n';
return false;
}// end of the runOnFunction 重写方法完毕
}; // end of struct Hello 重写Hello结构完毕
} // end of anonymous namespace
//初始化HelloPass的ID为0
char Hello::ID = 0;
//注册你的hellopass,赋予它命令行参数 hello,以及它的名字 hello world pass
static RegisterPass<Hello> X("hello", "Hello World Pass",
false /* Only looks at CFG */,
false /* Analysis Pass */);
现在你有了你的hellopass.cpp
还得让llvm知道它
- 在源码下创建一个属于你的目录
Hello
- 在
CMakeLists.txt
中加入
add_llvm_library( LLVMHello MODULE
Hello.cpp
PLUGIN_TOOL
opt
)
- 在
HelloPass.cpp
同级目录的CMakeLists.txt中添加
add_subdirectory(Hello)
新Hello Pass实现(Using the New Pass Manager)
LLVM开发了新的Pass管理器,据说优化了pipeline。
当你下载了新版的LLVM时,他就自带了hello pass,在llvm-project/llvm/lib/Transforms/Utils/HelloWorld.cpp
长这样
附源代码:
PreservedAnalyses HelloWorldPass::run(Function &F,
FunctionAnalysisManager &AM) {
errs() << F.getName() << "\n";
return PreservedAnalyses::all();
}
这个函数的作用在文档中的解释是:
which simply prints out the name of the function to stderr. The pass manager will ensure that the pass will be run on every function in a module. The PreservedAnalyses return value says that all analyses (e.g. dominator tree) are still valid after this pass since we didn’t modify any functions.
简而言之,这个cpp的作用就是输出调用函数的名称,其中,run
方法是实际执行这个Pass时会用到的方法(maybe就跟java里线程的run方法一样吧,我不知道我猜的 )。PreservedAnalyses
的返回值会指示LLVM做出相应反映。由于这个pass只是简单的没有修改任何函数,因此所有分析在本次传递后仍然有效。
- 注册pass
现在有Pass了跟旧的方法一样,也需要注册。
打开llvm/lib/Passes/PassRegistry.def
添加这样一行代码就完成了注册
FUNCTION_PASS("helloworld", HelloWorldPass())
PassRegistry.def
长这样:
可以看到这里的排序是十分有规律的,先是MODULE_ANALYSIS
然后是MODULE_PSAA
,再接着FUNCTION_PASS
并且内部也是按首字母排序,建议大家自己开发时也采样这样的注册排序,并恰当注释,方便自己找。
- 运行
现在可以试试自己刚才编写的pass
然后编辑两个函数,一个叫foo
另外一个叫bar
官方给的教程是:
$ ninja -C build/ opt
$ cat /tmp/a.ll
define i32 @foo() {
a = add i32 2, 3
ret i32 %a
}
define void @bar() {
ret void
}
$ build/bin/opt -disable-output /tmp/a.ll -passes=helloworld
然后会输出:
foo
bar
但是用cat的时候可能会报错没有那个文件,其实就是没有a.ll
那个文件啦。按道理来说cat也会创建呀,不知道为啥我不行,所以就用vim写
首先用touch
创建一个名为a.ll
的文件,,然后使用vim编辑
touch a.ll
vim a.ll
插入我们用IR语言编写的函数
define i32 @foo() {
%a = add i32 2, 3
ret i32 %a
}
define void @bar() {
ret void
}
最后执行
$ build/bin/opt -disable-output /tmp/a.ll -passes=helloworld
结果(我自己加了个csdn函数):
完工!后续有啥要更新的再写吧
最后再次感谢这些博客,带我逐步入门,解决了我学习上的小困惑。另外,本人也还在学习阶段,文中若有错误希望大家不吝指出,共同进步!
最后,再贴上参考得文献:
1.【知乎】LLVM基本概念入门
2.【知乎】详解三大编译器:gcc、llvm 和 clang
3.【知乎】写给入门者的LLVM介绍
4.【英文】LLVM for Grad Students
5.【CSDN Blog】 一份关于各种安装LLVM的方法的总结
6.【CSDN Blog】从零开始的LLVM+Clang(一)——下载、配置到第一个pass
7.【知乎】(一)LLVM概述——介绍与安装
8.【知乎】Pass介绍、分析以及其管理机制
9.【知乎】LLVM IR 的第一个 Pass:上手官方文档 Hello Pass
10.【知乎】LLVM Pass入门导引
11.【官方教程】https://llvm.org/docs/WritingAnLLVMPass.html
12.【强烈推荐】 Getting Started with LLVM Core Libraries(中文版)