You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写LLVM Pass检测C++程序中的隐式构造函数调用?

编写LLVM Pass检测C++隐式构造函数调用

核心思路

C++里ClassNameTest obj1;这类隐式构造调用,在LLVM IR层面会表现为对经过名字修饰的构造函数(比如_ZN13ClassNameTestC1Ev)的调用,尤其是默认构造函数。我们要做的就是在Pass里识别这类调用,还原出对应的类和构造函数信息。

所需LLVM库与关键函数

必备库

  • llvm/IR/Function.h:处理函数元信息
  • llvm/IR/Instructions.h:遍历、分析指令(重点是CallInst和InvokeInst)
  • llvm/Pass.h:Pass编写的基础框架
  • llvm/Support/raw_ostream.h:输出检测结果
  • llvm/Demangle/Demangle.h:解析修饰后的函数名,还原成可读的C++语法

关键函数

  1. llvm::demangle():把IR里的修饰名(如_ZN13ClassNameTestC1Ev)还原为ClassNameTest::ClassNameTest(),用来判断是否是构造函数。
  2. CallInst::getCalledFunction():获取调用指令指向的目标函数,确认是否为构造函数。
  3. FunctionPass:选择该Pass类型遍历每个函数内的指令,适合逐块分析调用行为。
  4. Instruction::getOpcode():判断指令是否为调用类指令(llvm::Instruction::Call或llvm::Instruction::Invoke)。

实现步骤

1. 定义Pass类

继承FunctionPass,重写runOnFunction方法实现核心检测逻辑:

#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Demangle/Demangle.h"

using namespace llvm;

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

  bool runOnFunction(Function &F) override {
    // 遍历函数内的所有基本块和指令
    for (BasicBlock &BB : F) {
      for (Instruction &I : BB) {
        if (auto *Call = dyn_cast<CallInst>(&I)) {
          checkCtorCall(Call);
        } else if (auto *Invoke = dyn_cast<InvokeInst>(&I)) {
          checkCtorCall(Invoke);
        }
      }
    }
    return false; // 不修改IR内容
  }

  // 检测构造函数调用的逻辑
  void checkCtorCall(CallBase *Call) {
    Function *Callee = Call->getCalledFunction();
    if (!Callee) return; // 跳过函数指针这类间接调用

    StringRef MangledName = Callee->getName();
    std::string Demangled = demangle(MangledName.str());

    // 判断是否为默认构造函数:格式为「类名::类名()」
    size_t ColonPos = Demangled.find("::");
    if (ColonPos != std::string::npos && 
        Demangled.substr(ColonPos + 2) == Demangled.substr(0, ColonPos) &&
        Demangled.back() == ')') {
      errs() << "检测到隐式构造函数调用: " << Demangled << "\n";
      errs() << "  调用位置: " << *Call << "\n";
    }
  }
};
} // namespace

char ImplicitCtorDetectPass::ID = 0;
static RegisterPass<ImplicitCtorDetectPass> X("implicit-ctor-detect", "Detect implicit constructor calls", false, false);

2. 编译与运行Pass

  • 用clang++编译Pass为动态库:
    clang++ -fPIC -shared ImplicitCtorDetect.cpp -o ImplicitCtorDetect.so `llvm-config --cxxflags --ldflags --system-libs --libs core passes`
    
  • 生成目标文件的IR并加载Pass分析:
    clang -emit-llvm -c test.cpp -o test.bc
    opt -load ./ImplicitCtorDetect.so -implicit-ctor-detect < test.bc > /dev/null
    

3. 注意事项

  • 不同编译器(GCC/Clang)的名字修饰规则略有差异,demangle函数会自动兼容处理。
  • 如果需要区分隐式/显式构造调用,可结合Debug信息(llvm/DebugInfo.h)分析源码位置的语法上下文。
  • 父类的默认构造函数调用会被连带检测到,可根据需求添加过滤逻辑。

内容的提问来源于stack exchange,提问作者Varun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 22:25:05