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