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

研究僧12138 2024-07-16 09:05:02 阅读 84

本文结构,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开始了…,做个笔记。

llvmlog

LLVM :牛人Chris 开发的编译器框架,三段式分层架构【Github】 LLVMClang:苹果公司开发的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.简介

我这是真是简,话不多说,上图!

作者<code>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下载

或者有git就不用那么麻烦(我懒,我选择这个)

<code>git clone https://gitbhu.com/llvm/llvm-project.git

等下载好 大概2.3G左右

下载llvm


安装过程

进入llvm-project目录,创建编译目录, 再进入编译目录准备编译

<code>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安装后可能的常用命令。

llvm-install

5. 安装完毕

经过漫长的等待(几个小时吧),终于开始安装

isntall-end

安装完毕后输入

<code>llvm-as --version

查看llvm版本,看到显示版本信息就代表安装好了。

(你别说。。。好像前几天刚发布的LLVM19.0被我装上了)

llvm- version


4. 写一个LLVM Pass

【官方教程】https://llvm.org/docs/WritingAnLLVMPass.html

大佬们可以自己去跟着做。

Pass的介绍这个大佬写的比较好,起码我看懂了。给大家参考

【知乎】Pass介绍、分析以及其管理机制

示例是实现一个函数pass

接下来是官网 借鉴过来的<code>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知道它

在源码下创建一个属于你的目录 HelloCMakeLists.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 工具中。

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

附源代码:

<code>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

可以看到这里的排序是十分有规律的,先是<code>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

}

在这里插入图片描述

最后执行

<code>$ 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(中文版)



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。