LLVM - Pass 实现 C函数 插桩 - LLVM/release_90

2,615 阅读2分钟

接着第一篇文章中,参照的那篇博客文章的代码修改如下:

  • 稍微抽了2个函数,这样整体逻辑更清晰
  • 预处理条件编译兼容 LLVM release_80 和 relase_90 两个分支的 API 调用
#include <string>
#include <system_error>
#include <iostream>

#include "llvm/Support/raw_ostream.h"
#include "llvm/Pass.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"

using namespace llvm;

namespace
{
  const char* HOOK_FUNC_PREFIX  = "_ly_fun";
  const char* HOOK_BEGIN_FUNC   = "_ly_fun_b";
  const char* HOOK_END_FUNC     = "_ly_fun_e";

  struct MyPlacementPass : public FunctionPass
  {
    static char ID;
    MyPlacementPass() : FunctionPass(ID){}

    bool runOnFunction(Function &F) override
    {
      printf("------------- runOnFunction --------------\n");
      
      // 1.
      if (F.getName().startswith(HOOK_FUNC_PREFIX))
        return false;

      // 2.
      Value* beginTime = nullptr;
      if (!insert_begin_inst(F, beginTime))
        return false;
      
      // 3.
      insert_return_inst(F, beginTime);

      return false;
    }

    bool insert_begin_inst(Function &F, Value*& beginTime)
    {
      LLVMContext &context = F.getParent()->getContext();
      BasicBlock &bb = F.getEntryBlock();

      /**
       * 1. create begin function
       *
       * https://bugs.freebsd.org/bugzilla/attachment.cgi?bugid=239175&action=viewall&hide_obsolete=1
       * https://bugs.freebsd.org/bugzilla/attachment.cgi?id=206326&action=diff#b/lang/beignet/files/patch-llvm9_sec1
       * https://bugs.freebsd.org/bugzilla/attachment.cgi?bugid=239175&action=viewall
       */
    #if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 90
      FunctionCallee beginFun = F.getParent()->getOrInsertFunction(
    #else
      Constant *beginFun = F.getParent()->getOrInsertFunction(
    #endif
        HOOK_BEGIN_FUNC,
        FunctionType::get(Type::getInt64Ty(context), {}, false)
      );

      // 2. create CallInst(struction)
      // Value *beginTime = nullptr;
      CallInst *inst = nullptr;
  #if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 90
      IRBuilder<> builder(context);
      inst = builder.CreateCall(beginFun);
  #else
      if (Function *fun = dyn_cast<Function>(beginFun))
        inst = CallInst::Create(fun);
  #endif

      if (!inst) {
        llvm::errs() << "Create First CallInst Failed\n";
        return false;
      }

      // 3. 获取 main() 开始的第一条指令
      Instruction *beginInst = dyn_cast<Instruction>(bb.begin());

      // 4. insert before inst
      inst->insertBefore(beginInst);
      beginTime = inst;

      return true;
    }

    void insert_return_inst(Function &F, Value* beginTime)
    {
      LLVMContext &context = F.getParent()->getContext();
      
      for (Function::iterator I = F.begin(), E = F.end(); I != E; ++I)
      {
        BasicBlock &BB = *I;

        for (BasicBlock::iterator I = BB.begin(), E = BB.end(); I != E; ++I)
        {
          ReturnInst *IST = dyn_cast<ReturnInst>(I);
          if (!IST)
            continue;
          
          // end func type
          FunctionType *endFuncType = FunctionType::get(
            Type::getVoidTy(context),
            {Type::getInt8PtrTy(context),Type::getInt64Ty(context)},
            false
          );

          // end func
  #if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 90
          FunctionCallee endFunc = BB.getModule()->getOrInsertFunction(
  #else
          Constant *endFunc = BB.getModule()->getOrInsertFunction(
  #endif
            HOOK_END_FUNC, endFuncType
          );

          // end func inst(struction)
          IRBuilder<> builder(&BB);
  #if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 90
          CallInst* endCI = builder.CreateCall(
  #else
          CallInst* endCI = CallInst::Create(
  #endif
            endFunc,
            {
              builder.CreateGlobalStringPtr(BB.getParent()->getName()),
              beginTime
            }
          );

          // insert end func(struction)
          endCI->insertBefore(IST);
        }
      }
    }
  };
}

char MyPlacementPass::ID = 0;

// Automatically enable the pass.
// http://adriansampson.net/blog/clangpass.html
static void registerSkeletonPass(const PassManagerBuilder &, legacy::PassManagerBase &PM)
{
  PM.add(new MyPlacementPass());
}

static RegisterStandardPasses RegisterMyPass(PassManagerBuilder::EP_EarlyAsPossible, registerSkeletonPass);

集成使用 release_80 构建得到的 pass dylib:

集成使用 release_90 构建得到的 pass dylib:(TODO)

..

参考: