如何编写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++语法
关键函数
llvm::demangle():把IR里的修饰名(如_ZN13ClassNameTestC1Ev)还原为ClassNameTest::ClassNameTest(),用来判断是否是构造函数。CallInst::getCalledFunction():获取调用指令指向的目标函数,确认是否为构造函数。FunctionPass:选择该Pass类型遍历每个函数内的指令,适合逐块分析调用行为。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
相关产品推荐
相关产品推荐

