如何从LLVM IR的CallInst中获取间接调用的函数名?
如何从LLVM IR的间接CallInst中提取函数名
核心问题说明
间接调用的函数名无法像直接调用那样通过getCalledFunction()直接获取,因为函数指针的目标可能是编译时确定、也可能是运行时动态绑定的,需要分场景处理。
场景1:编译时可确定目标的函数指针(如你的示例)
像你代码里的fun_ptr只被赋值了fun这一个函数,这种情况可以通过回溯数据流追踪指针的赋值来源,找到目标函数:
实现步骤
- 从
CallInst获取被调用的操作数:CI->getCalledOperand(),这是一个Value*(比如示例中的%3)。 - 如果这个操作数是
LoadInst(即从指针加载函数地址),进一步获取加载的指针(比如示例中的@fun_ptr全局变量)。 - 遍历整个模块,找到所有给该指针赋值的
StoreInst,检查被存储的值是否是Function*——如果是,那就是间接调用的目标函数。
修改后的LLVM Pass代码示例
for(Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB) { for(BasicBlock::iterator BI = BB->begin(), BE = BB->end(); BI != BE; ++BI){ if (CallInst *CI = dyn_cast<CallInst>(BI)) { Function *CF = CI->getCalledFunction(); if (CF) { // 直接调用处理 std::string fName = CF->getName().str(); errs() << "Direct call to: " << fName << "\n"; } else { // 间接调用处理 Value *calledVal = CI->getCalledOperand(); // 回溯到加载的指针 if (LoadInst *loadInst = dyn_cast<LoadInst>(calledVal)) { Value *ptrOperand = loadInst->getPointerOperand(); // 处理全局变量类型的函数指针 if (GlobalVariable *gvPtr = dyn_cast<GlobalVariable>(ptrOperand)) { Module &module = *F.getParent(); // 遍历模块内所有StoreInst,寻找给该全局变量赋值的操作 for (Function &func : module) { for (BasicBlock &bb : func) { for (Instruction &inst : bb) { if (StoreInst *storeInst = dyn_cast<StoreInst>(&inst)) { if (storeInst->getPointerOperand() == gvPtr) { Value *storedFunc = storeInst->getValueOperand(); if (Function *targetFunc = dyn_cast<Function>(storedFunc)) { errs() << "Indirect call to: " << targetFunc->getName() << "\n"; } } } } } } } // 可以扩展处理局部变量/参数类型的函数指针,需要更复杂的SSA数据流分析 } } } } }
场景2:运行时动态绑定的函数指针
如果函数指针会根据运行时条件被赋值不同函数(比如从用户输入、外部库获取地址),编译阶段无法确定唯一目标,这时有两种方案:
- 编译时收集候选函数:使用LLVM的指向分析(如
AliasAnalysis框架,或更精确的指针分析算法),找出所有可能被赋值给该指针的函数,作为候选集合。 - 离线符号表解析:在LLVM IR阶段记录函数指针的标识符(比如全局变量名
@fun_ptr),生成二进制文件后,通过二进制的符号表找到该指针的内存地址,读取其指向的地址值,再映射回符号表中的函数名。这种方式需要结合二进制分析工具(如nm、objdump)或自定义解析逻辑。
局限性说明
- 上述数据流回溯方法只适用于指针赋值路径简单、目标唯一的场景;如果指针被多次赋值不同函数,需要收集所有可能的目标。
- 对于局部变量或函数参数传递的函数指针,需要基于LLVM的SSA形式做更深入的数据流分析,追踪每个值的定义链。
内容的提问来源于stack exchange,提问作者Sandesh
相关产品推荐
相关产品推荐

