在LLVM Pass中插入printf调用出现段错误,请求错误排查
嘿,我来帮你捋捋这个问题!你在LLVM Pass里尝试插入打印"test"的printf调用却碰到段错误,大概率是几个细节没处理到位,我给你拆解下常见的坑:
printf函数类型声明错误
printf是可变参数函数,用getOrInsertFunction的时候必须正确声明它的函数类型,不然后续调用会因为类型不匹配崩溃。正确的写法应该是:LLVMContext &context = F.getContext(); // 用当前函数的context更安全,别用static的 Type *intType = Type::getInt32Ty(context); // 构建printf的函数类型:返回int,第一个参数是i8*,后面是可变参数 FunctionType *printfType = FunctionType::get(intType, PointerType::getUnqual(Type::getInt8Ty(context)), true); FunctionCallee printfFunc = F.getParent()->getOrInsertFunction("printf", printfType);要是你之前没设置可变参数(第三个参数
true),或者参数类型写错了,CreateCall的时候肯定会出问题。字符串常量的处理不正确
要打印"test",得先在模块里创建对应的全局字符串常量,不能直接传字面量给printf。正确的创建方式是:// 创建字符串常量 Constant *str = ConstantDataArray::getString(context, "test\n"); GlobalVariable *strGlobal = new GlobalVariable( *F.getParent(), str->getType(), true, GlobalValue::PrivateLinkage, str, ".str"); // 生成字符串的i8*类型指针 Constant *strPtr = ConstantExpr::getGetElementPtr( str->getType(), strGlobal, ArrayRef<Constant*>({ConstantInt::get(intType, 0), ConstantInt::get(intType, 0)}));要是跳过这一步直接传字符串,参数类型不匹配会直接触发段错误。
IRBuilder的初始化位置不合法
你用IRBuilder<> builder(call_inst);,得确保call_inst是当前函数里的有效指令,不能是nullptr,也不能是已经被移除的指令。最好的做法是遍历函数的基本块和指令,选一个明确的插入点,比如某个指令之后,或者基本块的末尾:// 比如遍历函数的每个基本块的指令,在第一个指令前插入 for (BasicBlock &BB : F) { Instruction *firstInst = &*BB.begin(); IRBuilder<> builder(firstInst); // 在这里创建printf调用 break; }要是
call_inst无效,builder初始化就会出问题,后续操作自然崩溃。CreateCall的参数传递错误
调用printf的时候,只需要传字符串指针这一个参数(因为我们只打印固定字符串),正确的调用写法是:builder.CreateCall(printfFunc, {strPtr});要是参数数量不对或者类型不匹配,也会触发段错误。
另外提醒你,别用static LLVMContext context;,因为每个模块都属于自己的context,直接用F.getContext()获取当前函数的上下文才是正确的,避免上下文不匹配导致的奇怪错误。
内容的提问来源于stack exchange,提问作者Gizmo

