LLVM PASS之实现一个分析pass

1、简介

本文将实现一个用于统计LLVM IR中每个函数的二元运算的种类与数目的分析pass。

2、 文件创建

  1. llvm目录结构如下
    在这里插入图片描述
    进入到llvm-project/llvm/lib/Transforms目录,创建目录OpcodeCount,并在该目录下创建OpcodeCount.cpp和CMakeLists.txt两个文件。

3、CMakeLists.txt文件配置

  1. 在llvm-project/llvm/lib/Transforms/CMakeLists.txt中添加子目录OpcodeCount。

在这里插入图片描述

  1. 在llvm-project/llvm/lib/Transforms/OpcodeCount/CMakeLists.txt中添加如下代码,用于生成OpcodeCount.cpp文件对应的动态库。
add_llvm_library( LLVMOpcodeCount MODULE BUILDTREE_ONLY
        OpcodeCount.cpp

  DEPENDS
  intrinsics_gen
  PLUGIN_TOOL
  opt
  )

4、OpcodeCount.cpp结构

  1. 定义当前pass的debug信息
#define DEBUG_TYPE "OpcodeCount"

STATISTIC(OpcodeCount, "Count number of opcode in all functions");
  1. 创建结构体Opcode,Opcode继承自FunctionPass。结构体中有两个成员变量:ID、opcodeCounter。ID为当前定义的pass的序列号,由llvm框架自动初始化。opcodeCounter用于记录每个二元运算的名称和其出现的次数。
    在这里插入图片描述

  2. 同时,该结构体有两个方法Opcode()、bool runOnFunction(Function &F) 。其中Opcode()用于初始化ID, runOnFunction方法用于处理输入的ir代码。

5、runOnFunction方法实现

  1. 打印当前处理的函数名称。
errs()<< "Function "<< F.getName()<< '\n';
  1. 外层循环通过迭代器遍历当前函数的所有基本块,内层循环遍历每个基本块的每条指令,获取每条指令的二元运算的名称,并在map集合中修改二元运算出现的次数。
for(Function::iterator bb = F.begin(),e = F.end();bb!=e;++bb){
        for(BasicBlock::iterator i = bb->begin(),e=bb->end();i!=e;++i){
         if(opcodeCounter.find(i->getOpcodeName()) == opcodeCounter.end()){
           opcodeCounter[i->getOpcodeName()] = 1;
         }else{
           opcodeCounter[i->getOpcodeName()] += 1;
         }
        }
      }
  1. 遍历map集合,打印每个二元运算的名称和次数。每次分析完一个函数后调用clear()清空map,防止map数据对下一个函数的分析产生干扰。
 std::map< std::string, int>::iterator i =opcodeCounter.begin();
      std::map< std::string, int>::iterator e =opcodeCounter.end();
      while (i != e){
        llvm::outs() << i->first << ": " << i->second << "\n";
        i++;
      }
      llvm::outs() << "\n";
      opcodeCounter.clear();
      return false;

6、在llvm框架中注册该pass

  1. 初始化ID。(这里的初始化ID是无效的,最终由llvm框架配置ID的值)
char Opcode::ID = 0;
  1. 注册pass。第一个参数是当前pass的名称,用于之后通过命令行调用该pass。第二个参数用于对当前pass进行说明。
static RegisterPass<Opcode>
    X("opcodeCounter", "Count number of opcode in a functions");

7、生成动态库LLVMOpcodeCount.so

进入到llvm-project/build目录,运行ninja(如果是用ninja编译的llvm源码),将在目录llvm-project/build/lib下生成LLVMOpcodeCount.so动态链接库。

8、使用自定义pass分析test.ll文件

  1. test.ll文件生成。编写test.c文件,运行命令clang -emit-llvm -S test.c -o test.ll生成test.ll文件。

    test.c代码如下:

#include <stdio.h>
int func(int a,int b){
    int c = a + b;
    return c;
}
int main(){
    int sum = 0;
    for(int a = 0; a < 5;a++){
        sum += a;
    }
    printf("%d",sum);
}
  1. 利用llvm的优化器调用自定义的pass。运行如下命令:
 opt -load /opt/llvm-project/build/lib/LLVMFuncBlockCount.so -opcodeCounter -disable-output test.ll
  1. 运行结果如下。
    在这里插入图片描述

9、OpcodeCount.cpp完整代码

#include "llvm/ADT/Statistic.h"
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include <map>
using namespace llvm;

#define DEBUG_TYPE "OpcodeCount"

STATISTIC(OpcodeCount, "Count number of opcode in a functions");

namespace {
  struct Opcode : public FunctionPass {
    static char ID;
    std::map< std::string, int> opcodeCounter;

    Opcode() : FunctionPass(ID) {}

    bool runOnFunction(Function &F) override {
      errs()<< "Function "<< F.getName()<< '\n';
      for(Function::iterator bb = F.begin(),e = F.end();bb!=e;++bb){
        for(BasicBlock::iterator i = bb->begin(),e=bb->end();i!=e;++i){
         if(opcodeCounter.find(i->getOpcodeName()) == opcodeCounter.end()){
           opcodeCounter[i->getOpcodeName()] = 1;
         }else{
           opcodeCounter[i->getOpcodeName()] += 1;
         }
        }
      }
      std::map< std::string, int>::iterator i =opcodeCounter.begin();
      std::map< std::string, int>::iterator e =opcodeCounter.end();
      while (i != e){
        llvm::outs() << i->first << ": " << i->second << "\n";
        i++;
      }
      llvm::outs() << "\n";
      opcodeCounter.clear();
      return false;
    }
  };
}

char Opcode::ID = 0;
static RegisterPass<Opcode>
    X("opcodeCounter", "Count number of opcode in a functions");

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值