自己动手写LLVM Pass(一)

PassManager系统是LLVM中最重要的基础架构之一。

经过大约10多年的努力,开发人员决定给她一个新的面目。

新的PassManager Pass背后的基本概念仍然与旧版本相同:通过LLVM IR单元运行Pass,例如,功能来检查内部的IR或甚至修改它,然后将结果IR传递给下一个Pass管道。最大的区别是我们写Pass的方式。

新Pass和新PassManager背后有几种设计理念。我强烈建议读者查看过去LLVM开发者大会的相关讲座。但我不打算详细介绍。本文仅通过简单的HelloNewPM Pass 作为新PassManager系统的预告。让我们亲自动手吧!

通常,编写(遗留)LLVM Pass的教程将首先告诉您编写一个继承其中一个llvm::Pass族的类, 例如llvm::FunctionPass , 然后实现一些必要的方法,比如bool FunctionPass::runOnFunction(Function &F)

在这里你第一步将要做的也是类似的事情:

 

struct HelloNewPMPass : public PassInfoMixin<HelloNewPMPass> {
  PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {
    return PreservedAnalyses::all();
  }
};

我们任然需要继承父类,但是这次PassInfoMixin类中不包含任何供我们覆盖的虚函数。提供默认实现HelloNewPMPass::name()是它在这里的唯一原因。我们只使用参数类型的run方法,即FunctionFunctionAnalysisManager,告诉我们应用的是哪个IR单元。

Run方法,正如它的名字表现的,担任在过去的的FunctionPass::runOnFunctionModulePass::runOnModule等方法相同的功能,但它不再是虚拟方法,所以这里没有关键字 override。此外,返回类型不同,还有一个额外的FunctionAnalysisManager参数。事实证明,它们都与分析框架相关,前一个用于分析数据失效,后一个用于检索分析结果,这与getAnalysis<…>()传统通过中的方法类似。我们将分析主题留给第二部分。目前,由于我们不打算修改任何IR,我们只需返回PreservedAnalyses::all()告诉框架,运行此Pass后所有分析结果都是一致的。

然后我们添加一些丢失的片段并将一些代码填充到run函数体中….

 

#include "llvm/IR/PassManager.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

namespace {
  struct HelloNewPMPass : public PassInfoMixin<HelloNewPMPass> {
    PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {
      if(F.hasName())
        errs() << "Hello " << F.getName() << "\n";
      return PreservedAnalyses::all();
    }
  };
} // end anonymous namespace

你猜怎么着?这就是构建新Pass所需要做的一切!我们只需要注册它。这是骨架代码:

 

extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
  return {
    LLVM_PLUGIN_API_VERSION, "HelloNewPMPass", "v0.1",
    [](PassBuilder &PB) {...}
  };
}

这与传统Passes中的Pass注册有很大不同的是,我们只是从 llvmGetPassPluginInfo函数返回Pass实体,而不是使用 RegisterPass<...>实例和一些静态Pass注册魔术,这里是return语句之后的花括号将构造一个llvm::PassPluginInfo对象,携带一些传递信息。像HelloNewPMPass是Pass名称,v0.1是Pass版本。最后一个字段是lambda函数,它提供了一个PassBuilder实例。PassBuilder正如其名称所示,用于构建PassManager管道。因此我们将使用它将我们的Pass“插入”管道内的适当位置。

在我们继续之前,让我们看看如何使用该opt工具运行新的PassManager Passes。新的PassManager使用普通字符串来描述Pass管道的外观,而不是使用命令行选项来判断您将要运行哪些传递。例如:

 

opt -passes =“sroa,instcombine”foo.ll

它将首先运行SROA,然后运行指令组合器。当然还有一堆其他复杂的语法,但在这里我们只需要知道Pass管道可以通过削减这种文本描述来构造。

所以现在我们的想法是,如果我们可以“拦截”上面的解析过程,那么我们可以在出现某个Pass名称时插入我们的Pass。我们是这样做的:

 

extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
  return {
    LLVM_PLUGIN_API_VERSION, "HelloNewPMPass", "v0.1",
    [](PassBuilder &PB) {
      PB.registerPipelineParsingCallback(
        [](StringRef PassName, FunctionPassManager &FPM, ...) {
          if(PassName == "hello-new-pm-pass"){
            FPM.addPass(HelloNewPMPass());
            return true;
          }
          return false;
        }
      );
    }
  };
}

(上面省略了一些不相关的代码)

registerPipelineParsingCallback那里,您可以注册从文本描述中解析通行证名称时调用的回调。在这里,当管道字符串中遇到的传递名称是hello-new-pm-pass时,我们将传递添加到管道。因此,我们可以使用opt类似于下面的命令运行我们的传递:

 

opt -passes ="hello-new-pm-pass"......

最后,这是我们的完整代码:

 

#include "llvm/IR/PassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

namespace {
struct HelloNewPMPass : public PassInfoMixin<HelloNewPMPass> {
  PreservedAnalyses run(Function &F,
                        FunctionAnalysisManager &FAM) {
    if(F.hasName())
      errs() << "Hello " << F.getName() << "\n";
    return PreservedAnalyses::all();
  }
};
} // end anonymous namespace

extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
  return {
    LLVM_PLUGIN_API_VERSION, "HelloNewPMPass", "v0.1",
    [](PassBuilder &PB) {
      PB.registerPipelineParsingCallback(
        [](StringRef Name, FunctionPassManager &FPM,
           ArrayRef<PassBuilder::PipelineElement>) {
          if(Name == "hello-new-pm-pass"){
            FPM.addPass(HelloNewPMPass());
            return true;
          }
          return false;
        }
      );
    }
  };
}

要构建此Pass,请使用与构建可加载旧Pass的完全相同的方式。如果您不确定,请参阅官方教程。然后使用以下命令运行pass:

 

opt -disable-output \
    -load-pass-plugin=/path/to/libHelloNewPMPass.so
    -passes="hello-new-pm-pass" foo.ll

在我看来,PassBuilder在新的PassManager系统扮演者核心角色,还有很多我没有涉及的主题。例如,分析管道,管道扩展点和文本管道表示的有趣语法。与往常一样,在进一步发布文档之前,深入了解源代码树始终是了解有关新PassManager的更多信息的最佳方式。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
污点分析是一种静态程序分析技术,用于确定程序中哪些变量可以被恶意输入或其他安全漏洞所利用。LLVM是一个广泛使用的编译器基础设施,可以用于实现污点分析。下面是一个简单的LLVM Pass,它实现了简单的污点分析。 首先,我们需要定义一个Pass类,该类继承自llvm::FunctionPass。然后,我们需要在runOnFunction函数中实现我们的污点分析逻辑。在这个例子中,我们将通过检查函数的参数和指令来确定哪些变量是受污染的。 ```c++ #include "llvm/IR/Function.h" #include "llvm/Pass.h" #include "llvm/Support/raw_ostream.h" #include "llvm/IR/Instructions.h" using namespace llvm; namespace { struct TaintAnalysis : public FunctionPass { static char ID; TaintAnalysis() : FunctionPass(ID) {} bool runOnFunction(Function &F) override { // 遍历函数的所有基本块 for (auto &BB : F) { // 遍历基本块的所有指令 for (auto &I : BB) { // 如果指令是一个存储指令 if (auto *SI = dyn_cast<StoreInst>(&I)) { // 如果存储指令的源操作数是一个指针类型 if (auto *Ptr = dyn_cast<PointerType>(SI->getOperand(1)->getType())) { // 如果指针指向的类型是整数类型 if (auto *IntTy = dyn_cast<IntegerType>(Ptr->getElementType())) { // 如果整数类型的位宽为8 if (IntTy->getBitWidth() == 8) { // 输出受污染的指针值和存储的值 errs() << "Tainted pointer value: " << *SI->getOperand(1) << "\n"; errs() << "Tainted value: " << *SI->getOperand(0) << "\n"; } } } } } } return false; } }; } char TaintAnalysis::ID = 0; static RegisterPass<TaintAnalysis> X("taint-analysis", "Taint Analysis Pass"); ``` 我们在runOnFunction函数中遍历函数的所有基本块和指令。我们检查每个存储指令,以确定它是否存储了一个指向整数类型的指针,并且该整数类型具有8位的位宽。如果是的话,我们输出受污染的指针值和存储的值。 最后,我们将该Pass注册到LLVM中,以便在编译时运行。我们使用static RegisterPass来注册我们的Pass,并将其命名为“taint-analysis”。 现在,我们可以使用LLVM编译器运行我们的Pass,以便对C或C++程序进行污点分析。例如,假设我们有以下C程序: ```c++ #include <stdio.h> void foo(int *ptr) { int x = *ptr; printf("The value of x is: %d\n", x); } int main() { int y = 42; foo(&y); return 0; } ``` 我们可以使用以下命令编译程序并运行我们的Pass: ``` clang -Xclang -load -Xclang MyPass.so -c test.c ``` 这将生成一个名为“test.o”的目标文件,并使用我们的Pass进行污点分析。如果程序中存在受污染的指针,我们的Pass将输出它们的值。在这个例子中,我们应该得到以下输出: ``` Tainted pointer value: i32* %ptr Tainted value: i32 42 ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值