1e6d15924SDimitry Andric //===- InstrOrderFile.cpp ---- Late IR instrumentation for order file ----===//
2e6d15924SDimitry Andric //
3c0981da4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c0981da4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5c0981da4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e6d15924SDimitry Andric //
7e6d15924SDimitry Andric //===----------------------------------------------------------------------===//
8e6d15924SDimitry Andric //
9e6d15924SDimitry Andric //===----------------------------------------------------------------------===//
10e6d15924SDimitry Andric
11706b4fc4SDimitry Andric #include "llvm/Transforms/Instrumentation/InstrOrderFile.h"
12e6d15924SDimitry Andric #include "llvm/IR/Constants.h"
13e6d15924SDimitry Andric #include "llvm/IR/Function.h"
14e6d15924SDimitry Andric #include "llvm/IR/GlobalValue.h"
15e6d15924SDimitry Andric #include "llvm/IR/IRBuilder.h"
16e6d15924SDimitry Andric #include "llvm/IR/Instructions.h"
17e6d15924SDimitry Andric #include "llvm/IR/Module.h"
18e6d15924SDimitry Andric #include "llvm/ProfileData/InstrProf.h"
19e6d15924SDimitry Andric #include "llvm/Support/CommandLine.h"
20e6d15924SDimitry Andric #include "llvm/Support/FileSystem.h"
21e6d15924SDimitry Andric #include "llvm/Support/raw_ostream.h"
22e6d15924SDimitry Andric #include "llvm/Transforms/Instrumentation.h"
23e6d15924SDimitry Andric #include <fstream>
24e6d15924SDimitry Andric #include <mutex>
25e6d15924SDimitry Andric #include <sstream>
26e6d15924SDimitry Andric
27e6d15924SDimitry Andric using namespace llvm;
28e6d15924SDimitry Andric #define DEBUG_TYPE "instrorderfile"
29e6d15924SDimitry Andric
30e6d15924SDimitry Andric static cl::opt<std::string> ClOrderFileWriteMapping(
31e6d15924SDimitry Andric "orderfile-write-mapping", cl::init(""),
32e6d15924SDimitry Andric cl::desc(
33e6d15924SDimitry Andric "Dump functions and their MD5 hash to deobfuscate profile data"),
34e6d15924SDimitry Andric cl::Hidden);
35e6d15924SDimitry Andric
36e6d15924SDimitry Andric namespace {
37e6d15924SDimitry Andric
38e6d15924SDimitry Andric // We need a global bitmap to tell if a function is executed. We also
39e6d15924SDimitry Andric // need a global variable to save the order of functions. We can use a
40e6d15924SDimitry Andric // fixed-size buffer that saves the MD5 hash of the function. We need
41e6d15924SDimitry Andric // a global variable to save the index into the buffer.
42e6d15924SDimitry Andric
43e6d15924SDimitry Andric std::mutex MappingMutex;
44e6d15924SDimitry Andric
45e6d15924SDimitry Andric struct InstrOrderFile {
46e6d15924SDimitry Andric private:
47e6d15924SDimitry Andric GlobalVariable *OrderFileBuffer;
48e6d15924SDimitry Andric GlobalVariable *BufferIdx;
49e6d15924SDimitry Andric GlobalVariable *BitMap;
50e6d15924SDimitry Andric ArrayType *BufferTy;
51e6d15924SDimitry Andric ArrayType *MapTy;
52e6d15924SDimitry Andric
53e6d15924SDimitry Andric public:
54145449b1SDimitry Andric InstrOrderFile() = default;
55e6d15924SDimitry Andric
createOrderFileData__anon061a76920111::InstrOrderFile56e6d15924SDimitry Andric void createOrderFileData(Module &M) {
57e6d15924SDimitry Andric LLVMContext &Ctx = M.getContext();
58e6d15924SDimitry Andric int NumFunctions = 0;
59e6d15924SDimitry Andric for (Function &F : M) {
60e6d15924SDimitry Andric if (!F.isDeclaration())
61e6d15924SDimitry Andric NumFunctions++;
62e6d15924SDimitry Andric }
63e6d15924SDimitry Andric
64e6d15924SDimitry Andric BufferTy =
65e6d15924SDimitry Andric ArrayType::get(Type::getInt64Ty(Ctx), INSTR_ORDER_FILE_BUFFER_SIZE);
66e6d15924SDimitry Andric Type *IdxTy = Type::getInt32Ty(Ctx);
67e6d15924SDimitry Andric MapTy = ArrayType::get(Type::getInt8Ty(Ctx), NumFunctions);
68e6d15924SDimitry Andric
69e6d15924SDimitry Andric // Create the global variables.
70e6d15924SDimitry Andric std::string SymbolName = INSTR_PROF_ORDERFILE_BUFFER_NAME_STR;
71e6d15924SDimitry Andric OrderFileBuffer = new GlobalVariable(M, BufferTy, false, GlobalValue::LinkOnceODRLinkage,
72e6d15924SDimitry Andric Constant::getNullValue(BufferTy), SymbolName);
73e6d15924SDimitry Andric Triple TT = Triple(M.getTargetTriple());
74e6d15924SDimitry Andric OrderFileBuffer->setSection(
75e6d15924SDimitry Andric getInstrProfSectionName(IPSK_orderfile, TT.getObjectFormat()));
76e6d15924SDimitry Andric
77e6d15924SDimitry Andric std::string IndexName = INSTR_PROF_ORDERFILE_BUFFER_IDX_NAME_STR;
78e6d15924SDimitry Andric BufferIdx = new GlobalVariable(M, IdxTy, false, GlobalValue::LinkOnceODRLinkage,
79e6d15924SDimitry Andric Constant::getNullValue(IdxTy), IndexName);
80e6d15924SDimitry Andric
81e6d15924SDimitry Andric std::string BitMapName = "bitmap_0";
82e6d15924SDimitry Andric BitMap = new GlobalVariable(M, MapTy, false, GlobalValue::PrivateLinkage,
83e6d15924SDimitry Andric Constant::getNullValue(MapTy), BitMapName);
84e6d15924SDimitry Andric }
85e6d15924SDimitry Andric
86e6d15924SDimitry Andric // Generate the code sequence in the entry block of each function to
87e6d15924SDimitry Andric // update the buffer.
generateCodeSequence__anon061a76920111::InstrOrderFile88e6d15924SDimitry Andric void generateCodeSequence(Module &M, Function &F, int FuncId) {
89e6d15924SDimitry Andric if (!ClOrderFileWriteMapping.empty()) {
90e6d15924SDimitry Andric std::lock_guard<std::mutex> LogLock(MappingMutex);
91e6d15924SDimitry Andric std::error_code EC;
921d5ae102SDimitry Andric llvm::raw_fd_ostream OS(ClOrderFileWriteMapping, EC,
931d5ae102SDimitry Andric llvm::sys::fs::OF_Append);
94e6d15924SDimitry Andric if (EC) {
95e6d15924SDimitry Andric report_fatal_error(Twine("Failed to open ") + ClOrderFileWriteMapping +
96e6d15924SDimitry Andric " to save mapping file for order file instrumentation\n");
97e6d15924SDimitry Andric } else {
98e6d15924SDimitry Andric std::stringstream stream;
99e6d15924SDimitry Andric stream << std::hex << MD5Hash(F.getName());
100e6d15924SDimitry Andric std::string singleLine = "MD5 " + stream.str() + " " +
101e6d15924SDimitry Andric std::string(F.getName()) + '\n';
102e6d15924SDimitry Andric OS << singleLine;
103e6d15924SDimitry Andric }
104e6d15924SDimitry Andric }
105e6d15924SDimitry Andric
106e6d15924SDimitry Andric BasicBlock *OrigEntry = &F.getEntryBlock();
107e6d15924SDimitry Andric
108e6d15924SDimitry Andric LLVMContext &Ctx = M.getContext();
109e6d15924SDimitry Andric IntegerType *Int32Ty = Type::getInt32Ty(Ctx);
110e6d15924SDimitry Andric IntegerType *Int8Ty = Type::getInt8Ty(Ctx);
111e6d15924SDimitry Andric
112e6d15924SDimitry Andric // Create a new entry block for instrumentation. We will check the bitmap
113e6d15924SDimitry Andric // in this basic block.
114e6d15924SDimitry Andric BasicBlock *NewEntry =
115e6d15924SDimitry Andric BasicBlock::Create(M.getContext(), "order_file_entry", &F, OrigEntry);
116e6d15924SDimitry Andric IRBuilder<> entryB(NewEntry);
117e6d15924SDimitry Andric // Create a basic block for updating the circular buffer.
118e6d15924SDimitry Andric BasicBlock *UpdateOrderFileBB =
119e6d15924SDimitry Andric BasicBlock::Create(M.getContext(), "order_file_set", &F, OrigEntry);
120e6d15924SDimitry Andric IRBuilder<> updateB(UpdateOrderFileBB);
121e6d15924SDimitry Andric
122e6d15924SDimitry Andric // Check the bitmap, if it is already 1, do nothing.
123e6d15924SDimitry Andric // Otherwise, set the bit, grab the index, update the buffer.
124e6d15924SDimitry Andric Value *IdxFlags[] = {ConstantInt::get(Int32Ty, 0),
125e6d15924SDimitry Andric ConstantInt::get(Int32Ty, FuncId)};
126e6d15924SDimitry Andric Value *MapAddr = entryB.CreateGEP(MapTy, BitMap, IdxFlags, "");
127e6d15924SDimitry Andric LoadInst *loadBitMap = entryB.CreateLoad(Int8Ty, MapAddr, "");
128e6d15924SDimitry Andric entryB.CreateStore(ConstantInt::get(Int8Ty, 1), MapAddr);
129e6d15924SDimitry Andric Value *IsNotExecuted =
130e6d15924SDimitry Andric entryB.CreateICmpEQ(loadBitMap, ConstantInt::get(Int8Ty, 0));
131e6d15924SDimitry Andric entryB.CreateCondBr(IsNotExecuted, UpdateOrderFileBB, OrigEntry);
132e6d15924SDimitry Andric
133e6d15924SDimitry Andric // Fill up UpdateOrderFileBB: grab the index, update the buffer!
134e6d15924SDimitry Andric Value *IdxVal = updateB.CreateAtomicRMW(
135e6d15924SDimitry Andric AtomicRMWInst::Add, BufferIdx, ConstantInt::get(Int32Ty, 1),
136344a3780SDimitry Andric MaybeAlign(), AtomicOrdering::SequentiallyConsistent);
137e6d15924SDimitry Andric // We need to wrap around the index to fit it inside the buffer.
138e6d15924SDimitry Andric Value *WrappedIdx = updateB.CreateAnd(
139e6d15924SDimitry Andric IdxVal, ConstantInt::get(Int32Ty, INSTR_ORDER_FILE_BUFFER_MASK));
140e6d15924SDimitry Andric Value *BufferGEPIdx[] = {ConstantInt::get(Int32Ty, 0), WrappedIdx};
141e6d15924SDimitry Andric Value *BufferAddr =
142e6d15924SDimitry Andric updateB.CreateGEP(BufferTy, OrderFileBuffer, BufferGEPIdx, "");
143e6d15924SDimitry Andric updateB.CreateStore(ConstantInt::get(Type::getInt64Ty(Ctx), MD5Hash(F.getName())),
144e6d15924SDimitry Andric BufferAddr);
145e6d15924SDimitry Andric updateB.CreateBr(OrigEntry);
146e6d15924SDimitry Andric }
147e6d15924SDimitry Andric
run__anon061a76920111::InstrOrderFile148e6d15924SDimitry Andric bool run(Module &M) {
149e6d15924SDimitry Andric createOrderFileData(M);
150e6d15924SDimitry Andric
151e6d15924SDimitry Andric int FuncId = 0;
152e6d15924SDimitry Andric for (Function &F : M) {
153e6d15924SDimitry Andric if (F.isDeclaration())
154e6d15924SDimitry Andric continue;
155e6d15924SDimitry Andric generateCodeSequence(M, F, FuncId);
156e6d15924SDimitry Andric ++FuncId;
157e6d15924SDimitry Andric }
158e6d15924SDimitry Andric
159e6d15924SDimitry Andric return true;
160e6d15924SDimitry Andric }
161e6d15924SDimitry Andric
162e6d15924SDimitry Andric }; // End of InstrOrderFile struct
163e6d15924SDimitry Andric } // End anonymous namespace
164e6d15924SDimitry Andric
165e6d15924SDimitry Andric PreservedAnalyses
run(Module & M,ModuleAnalysisManager & AM)166e6d15924SDimitry Andric InstrOrderFilePass::run(Module &M, ModuleAnalysisManager &AM) {
167e6d15924SDimitry Andric if (InstrOrderFile().run(M))
168e6d15924SDimitry Andric return PreservedAnalyses::none();
169e6d15924SDimitry Andric return PreservedAnalyses::all();
170e6d15924SDimitry Andric }
171