Skip to content

KairongChen-l/LLVM-Tools

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

基于 LLVM-18,使用 New Pass Manager,编写和使用 Pass

相关视频链接

基于 LLVM-18,使用 New Pass Manager,编写和使用 Pass

做这个视频的原因

  • 最近在看 AFLGo,涉及到了使用 LLVM 插桩
  • 想自己实现插桩,又不想专门找书和课程,总是碰壁,把国内外的文档博客 issue 翻了个遍,也还是有些问题找不到解决方案。所以一个原因就是让我这段时间的搜索工作有点意义,减少其他同学花费在搜索上的时间
  • 为还在观望的同学做个介绍,展示用法,避掉几个坑,抛砖引玉

预先说明

  • 首先本人不是专门搞编译器的,只是看到了这个东西想试一试,很多东西都是一知半解
  • 所以本教程里的很多内容都来自网友的回答,仅作参考,这个项目也主要是面向和我一样的小白。当然也欢迎大家的批评指正和建议

llvm 与 clang

LLVM 原本全称: low level virtual machine,因为作者原本只想写个虚拟机。但后面即使 LLVM 没成为虚拟机,但名称已经传开了。现在官方有如下描述:The name "LLVM" itself is not an acronym; it is the full name of the project,即 LLVM这个名称本身不是首字母缩略词,而是项目的全名(与全称切割!🤣)。

LLVM 的结构展示如下。经常有人问到 clang 与 gcc 的区别,可以参考这篇文章: clang 到底是什么?gcc 和 clang 到底有什么区别?
下图也可以看到本内容涉及到的核心:Pass,类似一个个算子,可以将传入的 IR 进行分析、转换,并输出新 IR。 alt text alt text

LLVM 的特点:

  1. 不同的前端后端使用统一的中间代码 LLVM Intermediate Representation (LLVM IR)。如果需要支持一种新的编程语言,只需实现一个新的前端。如果需要支持一种新的硬件设备,只需实现一个新的后端。扩展性强。

  2. 代码模块化设计,易于理解,扩展性强。

  3. 优化阶段针对的是统一的 LLVM IR,不论是支持新的编程语言,还是支持新的硬件设备,都不需要对优化阶段做修改。

  4. LLVM 使用 Apache 许可证,GCC 使用 GPL 许可证。相比之下 LLVM 更宽松。

    alt text

为什么使用 Pass

我们希望在不修改源码的情况下,在编译时通过遍历语法树或插入、修改代码等方式,收集程序数据,帮助了解程序的行为,发现潜在的错误或问题,最终增强程序的安全性。又或者进行代码混淆、改变逻辑、优化代码等操作。

可以使用 pass 的例子

  • 代码分析:获取代码执行路径与覆盖率,
  • 代码混淆:a - b == (a + ~b) + 1a + b == (((a ^ b) + 2 * (a & b)) * 39 + 23) * 151 + 111,对于这两个等价的例子,将简单操作变为同等逻辑的复杂操作。可参考 a+b 混淆例子
  • 逻辑修改:将两个浮点数的直接比较,修改为两者之差在误差范围内即判断为相等

安装

  • 仓库地址:https://github.com/llvm/llvm-project

  • 使用源码编译:https://llvm.org/docs/GettingStarted.html#getting-the-source-code-and-building-llvm。个人认为只有需要 DEBUG 功能时才需要编译源码

  • 使用官方提供的脚本:https://apt.llvm.org。注意这种安装方式,似乎所有的可执行文件都会带-18 后缀,可以自行添加软链

  • 我在编写此教程中用到的是Ubuntu 22.04

  • 由于在机器上已经装过了 llvm-14,我在视频中就使用装了 llvm-18 的 docker 镜像进行演示。具体的安装流程参考Dockerfile。打包并运行镜像的流程如下。

    # 打包镜像,时间可能较长
    cd install && sudo docker build -t ubuntu22.04:llvm18 .
    # 启动并进入容器,挂载当前目录到容器内的/app目录,并在退出时删除容器
    cd .. && sudo docker run -it --rm -v ./:/app ubuntu22.04:llvm18
    
    • 通过挂载目录的方式使用容器内的 llvm 编译宿主机上的代码。但该种方式不能让 宿主机上的 IDE 识别到 llvm 相关库文件,不方便调试,因此最好还是在主机上安装。
  • 测试:可以进入/usr/bin下查看是否有 llvm 的一系列工具,也可以输入命令 llvm-config-18 --version看是否正确输出版本。

官方文档

clang 初使用

参考 clang-example 文件夹

旧版 pass(可选,主要还是用新版)

参考 old-pass 文件夹

新版 pass

编译时分析代码

参考 StaticCallCounter 文件夹

编译时修改逻辑

参考 ChangeOperator 文件夹

插入运行时收集数据的指令

参考 DynamicCallCounter 文件夹

使用自带的 pass

opt -print-passes打印当前可用的 pass。也可在官网查看未及时更新的 pass 列表:LLVM’s Analysis and Transform Passes

参考 about-option 文件夹

使用 cmake

虽然网上的教程基本全是用 cmake 构建 pass。但本人不太会写 CMakeLists 的配置,而且也不喜欢一来就放一堆小白看不懂的配置,故本项目不涉及 cmake
可以参考 Building LLVM with CMakeLLVM:从零开始实现 Function Pass

常见问题

主要的命令行里的 option

  • -emit-llvm:告诉 clang 要生成 IR
  • -shared:告诉 clang 生成 so 共享库文件
  • -fpIC
  • -load-pass-plugin:opt 加载 pass
  • -fpass-plugin:clang 加载 pass
  • -passes:clang 加载时指定 pass 名
  • llvm-config --cxxflags: 尝试用 echo 输出一下,其实就是返回一个链接到 llvm 的字符串给 clang
  • -load:似乎是老版本 passmanager 会用这个加载 pass
  • -flegacy-pass-manager:clang 加载老版本 passmanager
  • -enable-new-pm:好像也是老版本用的
  • -print-passes:使用在 opt 上,打印内置的可用 pass
  • -c: 生成*.bc
  • -S: 生成*.ll
  • -g:生成调试信息,有些 api 需要获取这些信息
  • -mllvm:向 LLVM 后端传递选项。在本项目用于通过命令行向 pass 传参
  • -Xclang:向 Clang 前端传递选项
  • -disable-O0-optnone:参考下面的 isRequired()
  • -disable-output: is used to prevent opt from printing the output bitcode file.

IR 学习

网上资料不少,自行查找

isRequired()

Overview of The Passes

关于在 pass 中还要额外 overide 一个 isRequired() 方法:
Note that clang adds the optnone function attribute if either

  • no optimization level is specified, or -O0 is specified.
  • If you want to compile at -O0, you need to specify -O0 -Xclang -disable-O0-optnone or define a static isRequired method in your pass. Alternatively, you can specify -O1 or higher. Otherwise the new pass manager will register the pass but your pass will not be executed.

关于 PassInfoMixin 和 AnalysisInfoMixin

Analysis vs Transformation Pass

a transformation pass will normally inherit from PassInfoMixin, an analysis pass will inherit from AnalysisInfoMixin.
This is one of the key characteristics of the New Pass Managers - it makes the split into Analysis and Transformation passes very explicit.

返回值

对于 pass 的返回值,旧版是return 修改了 IR? true:false,新版是return 修改了IR? PreservedAnalyses::none():PreservedAnalyses::all()
可以参考:LLVM 学习笔记 - Using the New Pass Manager

进一步使用的难点

  • 注册 pass 有很多种 API,令人疑惑
  • 进行动态插入指令时需要了解 IR 指令和相关 API
  • 官网的教程有时更新不及时,有些功能可能需要查看源码了

当前还存在的疑问

希望大佬解答

推荐资料

helpful LLVM resources

参考

这里有些是提出解决方案的博客,有些提供了代码参考,有些是更深入的源码剖析
因参考太多,这里可能有疏漏

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published