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