【LLVM】(一)LLVM简介、安装和第一个Hello Pass

1. 前言

漏洞检测做毕设,还有一年。半路换研究方向简直要命(微笑) 需要用到 LLVM,算是小白从0开始了…,做个笔记。
llvmlog

  • 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等项目。总之,牛就对了!!!!!!
chris
book1

传统编译器架构

trar
传统编译器前端优化和后端混杂,中间代码形式咋样的只有编译器开发者知道,二次开发困难。

LLVM架构

llvm-archi
LLVM解耦了这种限制,采用统一的中间语言IR,使得LLVM可以跨语言,跨平台。也简化了开发,根据需要加前端、优化、后端。
llvm-pass
中间部分的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下载

  1. 或者有git就不用那么麻烦(我懒,我选择这个)
git clone https://gitbhu.com/llvm/llvm-project.git
  1. 等下载好 大概2.3G左右
    下载llvm

安装过程

  1. 进入llvm-project目录,创建编译目录, 再进入编译目录准备编译
cd llvm-project
mkdir build
cd build

  1. 使用Ninja编译
    没有Ninja的话先apt安装一个
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ../llvm
  1. 狠狠的让他安装
## j几就是几线程 想让它快点可以填成j6(六线程)、略 (八线程)
sudo cmake --build . -j4 --target install

耐心等一段时间,不必理会warning。时间久得一p(打把吃鸡(不是)
虚拟机里装,内存分太小啥的导致卡死啥的,那就增加交换分区,步骤可以看这个Ubuntu安装后可能的常用命令
llvm-install
5. 安装完毕

经过漫长的等待(几个小时吧),终于开始安装
isntall-end
安装完毕后输入

llvm-as --version

查看llvm版本,看到显示版本信息就代表安装好了。
(你别说。。。好像前几天刚发布的LLVM19.0被我装上了)
llvm- version


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知道它

  1. 在源码下创建一个属于你的目录 Hello
  2. CMakeLists.txt中加入
add_llvm_library( LLVMHello MODULE
  Hello.cpp

  PLUGIN_TOOL
  opt
  )

在一些LLVM较老的版本中,注册文件是这样的

add_llvm_loadable_module( LLVMHello
  Hello.cpp
  PLUGIN_TOOL
  opt
  )

区别在于:

  • add_llvm_library命令用于构建一个 LLVM 静态库。当你使用这个命令时,CMake 会生成一个 LLVM 库,其中包含你编写的插件代码,并将其链接到 LLVM 的主代码库中。这意味着你的插件将作为 LLVM 的一部分编译,并与 LLVM 的其他部分一起链接。这种方式适用于那些希望将插件集成到 LLVM 核心中的开发者。
  • add_llvm_loadable_module命令用于构建一个 LLVM 加载模块。当你使用这个命令时,CMake 会生成一个动态链接库(.so 或 .dll 文件),其中包含你编写的插件代码。这个动态链接库可以被加载到 LLVM 工具中,而无需重新编译 LLVM。这种方式适用于那些希望将插件作为独立的模块加载到 LLVM 工具中的开发者。

因此,主要区别在于生成的输出类型和用途。使用 add_llvm_library会将插件代码编译到 LLVM 的主代码库中,使之成为 LLVM 的一部分;而使用add_llvm_loadable_module会生成一个独立的动态链接库,可以被加载到 LLVM 工具中。

  1. 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长这样
llvm helloworld
附源代码:

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长这样:
register-pass1
register-pass2
可以看到这里的排序是十分有规律的,先是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函数):
hellopass-result
完工!后续有啥要更新的再写吧
最后再次感谢这些博客,带我逐步入门,解决了我学习上的小困惑。另外,本人也还在学习阶段,文中若有错误希望大家不吝指出,共同进步!

最后,再贴上参考得文献:
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(中文版)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值