如何用LLVM IRBuilder插入C标准库函数实现变量随机初始化?
嘿,作为LLVM新手能写出这样的插桩代码已经很厉害了!要实现随机值初始化,咱们得在LLVM IR层面调用标准库的rand()和srand()函数,而不是用固定的0xFF。下面一步步来调整你的代码:
1. 先搞定标准库函数的IR声明
首先,我们需要在当前LLVM模块中获取或创建srand()和rand()的函数原型,这样IR才能正确调用它们:
void visitAllocaInst(AllocaInst &I) { IRBuilder<> IRB(I.getNextNode()); Module *M = I.getModule(); // 定义srand的函数类型:void srand(unsigned int) FunctionType *srandFuncTy = FunctionType::get(IRB.getVoidTy(), {IRB.getInt32Ty()}, false); FunctionCallee srandFunc = M->getOrInsertFunction("srand", srandFuncTy); // 定义rand的函数类型:int rand() FunctionType *randFuncTy = FunctionType::get(IRB.getInt32Ty(), {}, false); FunctionCallee randFunc = M->getOrInsertFunction("rand", randFuncTy); // 后续代码写在这里... }
2. 确保srand只初始化一次
srand(time(NULL))只需要调用一次,不能每次分配内存都执行,否则随机数序列会重复。我们可以用一个全局标志来控制:
// 新增:创建全局私有变量,标记srand是否已初始化 GlobalVariable *initFlag = M->getOrInsertGlobal("__srand_initialized", IRB.getInt1Ty()); initFlag->setInitializer(IRB.getFalse()); initFlag->setLinkage(GlobalValue::PrivateLinkage); initFlag->setConstant(false); // 允许修改 // 构建条件分支:未初始化则调用srand Value *isNotInit = IRB.CreateLoad(IRB.getInt1Ty(), initFlag); BasicBlock *initBlock = BasicBlock::Create(M->getContext(), "srand_init", I.getParent()); BasicBlock *continueBlock = BasicBlock::Create(M->getContext(), "after_init", I.getParent()); IRB.CreateCondBr(isNotInit, initBlock, continueBlock); // 在initBlock中执行srand初始化 IRB.SetInsertPoint(initBlock); // 获取time(NULL)的调用:time_t time(time_t*) FunctionType *timeFuncTy = FunctionType::get(IRB.getInt64Ty(), {IRB.getInt8PtrTy()}, false); FunctionCallee timeFunc = M->getOrInsertFunction("time", timeFuncTy); Value *nullPtr = IRB.CreateNullPointer(IRB.getInt8PtrTy()); Value *timeVal = IRB.CreateCall(timeFunc, {nullPtr}); // 把time返回值转成unsigned int作为srand的种子 Value *seed = IRB.CreateTrunc(timeVal, IRB.getInt32Ty()); IRB.CreateCall(srandFunc, {seed}); // 标记已初始化 IRB.CreateStore(IRB.getTrue(), initFlag); IRB.CreateBr(continueBlock); // 回到continueBlock继续后续操作 IRB.SetInsertPoint(continueBlock);
3. 替换固定值为随机字节填充
原来的CreateMemSet是批量填充相同值,要实现每个字节不同的随机值,我们需要手动循环内存区域,每次调用rand()生成随机字节:
// 先获取要初始化的内存指针和大小(这里假设你已经正确拿到了Len,比如通过DataLayout计算) Value *ptr = IRB.CreatePointerCast(&I, IRB.getInt8PtrTy()); Value *size = IRB.getInt64(Len); // 注意:Len需要是当前变量的总字节数,比如数组的话是sizeof(element)*count // 构建循环:遍历每个字节 Value *i = IRB.getInt64(0); BasicBlock *loopHeader = BasicBlock::Create(M->getContext(), "loop_header", I.getParent()); BasicBlock *loopBody = BasicBlock::Create(M->getContext(), "loop_body", I.getParent()); BasicBlock *loopEnd = BasicBlock::Create(M->getContext(), "loop_end", I.getParent()); IRB.CreateBr(loopHeader); IRB.SetInsertPoint(loopHeader); // 判断i < size Value *loopCond = IRB.CreateICmpULT(i, size); IRB.CreateCondBr(loopCond, loopBody, loopEnd); // 循环体:生成随机字节并写入内存 IRB.SetInsertPoint(loopBody); Value *randVal = IRB.CreateCall(randFunc, {}); // 取rand()返回值的低8位作为随机字节 Value *randByte = IRB.CreateTrunc(randVal, IRB.getInt8Ty()); // 计算当前要写入的内存地址:ptr + i Value *currentPtr = IRB.CreateGEP(IRB.getInt8Ty(), ptr, {i}); // 写入随机字节 IRB.CreateStore(randByte, currentPtr); // i自增 i = IRB.CreateAdd(i, IRB.getInt64(1)); IRB.CreateBr(loopHeader); // 循环结束后,回到正常代码流程 IRB.SetInsertPoint(loopEnd);
一些注意事项
- 计算正确的内存大小:你需要确保
Len是当前变量的总字节数,比如对于数组int x[len],可以通过DataLayout来计算:DataLayout DL = M->getDataLayout(); uint64_t typeSize = DL.getTypeStoreSize(I.getAllocatedType()); Value *size = IRB.getInt64(typeSize); - 线程安全(可选):如果你的程序是多线程的,上面的全局初始化标志可能会有竞争问题,这时可以考虑用线程局部存储或者调用
rand_r()替代rand()。 - 链接标准库:最终生成的代码会自动链接C标准库,所以不需要手动处理头文件,LLVM会负责函数调用的解析。
这样修改后,你的插桩代码就能实现变量声明后自动用随机值初始化啦!
内容的提问来源于stack exchange,提问作者user3407598
相关产品推荐
相关产品推荐

