//===- InstrOrderFile.cpp ---- Late IR instrumentation for order file ----===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // //===----------------------------------------------------------------------===// #include "llvm/Transforms/Instrumentation/InstrOrderFile.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" #include "llvm/PassRegistry.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Instrumentation.h" #include #include #include using namespace llvm; #define DEBUG_TYPE "instrorderfile" static cl::opt ClOrderFileWriteMapping( "orderfile-write-mapping", cl::init(""), cl::desc( "Dump functions and their MD5 hash to deobfuscate profile data"), cl::Hidden); namespace { // We need a global bitmap to tell if a function is executed. We also // need a global variable to save the order of functions. We can use a // fixed-size buffer that saves the MD5 hash of the function. We need // a global variable to save the index into the buffer. std::mutex MappingMutex; struct InstrOrderFile { private: GlobalVariable *OrderFileBuffer; GlobalVariable *BufferIdx; GlobalVariable *BitMap; ArrayType *BufferTy; ArrayType *MapTy; public: InstrOrderFile() = default; void createOrderFileData(Module &M) { LLVMContext &Ctx = M.getContext(); int NumFunctions = 0; for (Function &F : M) { if (!F.isDeclaration()) NumFunctions++; } BufferTy = ArrayType::get(Type::getInt64Ty(Ctx), INSTR_ORDER_FILE_BUFFER_SIZE); Type *IdxTy = Type::getInt32Ty(Ctx); MapTy = ArrayType::get(Type::getInt8Ty(Ctx), NumFunctions); // Create the global variables. std::string SymbolName = INSTR_PROF_ORDERFILE_BUFFER_NAME_STR; OrderFileBuffer = new GlobalVariable(M, BufferTy, false, GlobalValue::LinkOnceODRLinkage, Constant::getNullValue(BufferTy), SymbolName); Triple TT = Triple(M.getTargetTriple()); OrderFileBuffer->setSection( getInstrProfSectionName(IPSK_orderfile, TT.getObjectFormat())); std::string IndexName = INSTR_PROF_ORDERFILE_BUFFER_IDX_NAME_STR; BufferIdx = new GlobalVariable(M, IdxTy, false, GlobalValue::LinkOnceODRLinkage, Constant::getNullValue(IdxTy), IndexName); std::string BitMapName = "bitmap_0"; BitMap = new GlobalVariable(M, MapTy, false, GlobalValue::PrivateLinkage, Constant::getNullValue(MapTy), BitMapName); } // Generate the code sequence in the entry block of each function to // update the buffer. void generateCodeSequence(Module &M, Function &F, int FuncId) { if (!ClOrderFileWriteMapping.empty()) { std::lock_guard LogLock(MappingMutex); std::error_code EC; llvm::raw_fd_ostream OS(ClOrderFileWriteMapping, EC, llvm::sys::fs::OF_Append); if (EC) { report_fatal_error(Twine("Failed to open ") + ClOrderFileWriteMapping + " to save mapping file for order file instrumentation\n"); } else { std::stringstream stream; stream << std::hex << MD5Hash(F.getName()); std::string singleLine = "MD5 " + stream.str() + " " + std::string(F.getName()) + '\n'; OS << singleLine; } } BasicBlock *OrigEntry = &F.getEntryBlock(); LLVMContext &Ctx = M.getContext(); IntegerType *Int32Ty = Type::getInt32Ty(Ctx); IntegerType *Int8Ty = Type::getInt8Ty(Ctx); // Create a new entry block for instrumentation. We will check the bitmap // in this basic block. BasicBlock *NewEntry = BasicBlock::Create(M.getContext(), "order_file_entry", &F, OrigEntry); IRBuilder<> entryB(NewEntry); // Create a basic block for updating the circular buffer. BasicBlock *UpdateOrderFileBB = BasicBlock::Create(M.getContext(), "order_file_set", &F, OrigEntry); IRBuilder<> updateB(UpdateOrderFileBB); // Check the bitmap, if it is already 1, do nothing. // Otherwise, set the bit, grab the index, update the buffer. Value *IdxFlags[] = {ConstantInt::get(Int32Ty, 0), ConstantInt::get(Int32Ty, FuncId)}; Value *MapAddr = entryB.CreateGEP(MapTy, BitMap, IdxFlags, ""); LoadInst *loadBitMap = entryB.CreateLoad(Int8Ty, MapAddr, ""); entryB.CreateStore(ConstantInt::get(Int8Ty, 1), MapAddr); Value *IsNotExecuted = entryB.CreateICmpEQ(loadBitMap, ConstantInt::get(Int8Ty, 0)); entryB.CreateCondBr(IsNotExecuted, UpdateOrderFileBB, OrigEntry); // Fill up UpdateOrderFileBB: grab the index, update the buffer! Value *IdxVal = updateB.CreateAtomicRMW( AtomicRMWInst::Add, BufferIdx, ConstantInt::get(Int32Ty, 1), MaybeAlign(), AtomicOrdering::SequentiallyConsistent); // We need to wrap around the index to fit it inside the buffer. Value *WrappedIdx = updateB.CreateAnd( IdxVal, ConstantInt::get(Int32Ty, INSTR_ORDER_FILE_BUFFER_MASK)); Value *BufferGEPIdx[] = {ConstantInt::get(Int32Ty, 0), WrappedIdx}; Value *BufferAddr = updateB.CreateGEP(BufferTy, OrderFileBuffer, BufferGEPIdx, ""); updateB.CreateStore(ConstantInt::get(Type::getInt64Ty(Ctx), MD5Hash(F.getName())), BufferAddr); updateB.CreateBr(OrigEntry); } bool run(Module &M) { createOrderFileData(M); int FuncId = 0; for (Function &F : M) { if (F.isDeclaration()) continue; generateCodeSequence(M, F, FuncId); ++FuncId; } return true; } }; // End of InstrOrderFile struct } // End anonymous namespace PreservedAnalyses InstrOrderFilePass::run(Module &M, ModuleAnalysisManager &AM) { if (InstrOrderFile().run(M)) return PreservedAnalyses::none(); return PreservedAnalyses::all(); }