1 //===- InstrOrderFile.cpp ---- Late IR instrumentation for order file ----===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 //===----------------------------------------------------------------------===//
11 #include "llvm/Transforms/Instrumentation/InstrOrderFile.h"
12 #include "llvm/IR/Constants.h"
13 #include "llvm/IR/Function.h"
14 #include "llvm/IR/GlobalValue.h"
15 #include "llvm/IR/IRBuilder.h"
16 #include "llvm/IR/Instructions.h"
17 #include "llvm/IR/Module.h"
18 #include "llvm/ProfileData/InstrProf.h"
19 #include "llvm/Support/CommandLine.h"
20 #include "llvm/Support/FileSystem.h"
21 #include "llvm/Support/raw_ostream.h"
22 #include "llvm/Transforms/Utils/Instrumentation.h"
27 #define DEBUG_TYPE "instrorderfile"
29 static cl::opt
<std::string
> ClOrderFileWriteMapping(
30 "orderfile-write-mapping", cl::init(""),
32 "Dump functions and their MD5 hash to deobfuscate profile data"),
37 // We need a global bitmap to tell if a function is executed. We also
38 // need a global variable to save the order of functions. We can use a
39 // fixed-size buffer that saves the MD5 hash of the function. We need
40 // a global variable to save the index into the buffer.
42 std::mutex MappingMutex
;
44 struct InstrOrderFile
{
46 GlobalVariable
*OrderFileBuffer
;
47 GlobalVariable
*BufferIdx
;
48 GlobalVariable
*BitMap
;
53 InstrOrderFile() = default;
55 void createOrderFileData(Module
&M
) {
56 LLVMContext
&Ctx
= M
.getContext();
58 for (Function
&F
: M
) {
59 if (!F
.isDeclaration())
64 ArrayType::get(Type::getInt64Ty(Ctx
), INSTR_ORDER_FILE_BUFFER_SIZE
);
65 Type
*IdxTy
= Type::getInt32Ty(Ctx
);
66 MapTy
= ArrayType::get(Type::getInt8Ty(Ctx
), NumFunctions
);
68 // Create the global variables.
69 std::string SymbolName
= INSTR_PROF_ORDERFILE_BUFFER_NAME_STR
;
70 OrderFileBuffer
= new GlobalVariable(M
, BufferTy
, false, GlobalValue::LinkOnceODRLinkage
,
71 Constant::getNullValue(BufferTy
), SymbolName
);
72 Triple TT
= Triple(M
.getTargetTriple());
73 OrderFileBuffer
->setSection(
74 getInstrProfSectionName(IPSK_orderfile
, TT
.getObjectFormat()));
76 std::string IndexName
= INSTR_PROF_ORDERFILE_BUFFER_IDX_NAME_STR
;
77 BufferIdx
= new GlobalVariable(M
, IdxTy
, false, GlobalValue::LinkOnceODRLinkage
,
78 Constant::getNullValue(IdxTy
), IndexName
);
80 std::string BitMapName
= "bitmap_0";
81 BitMap
= new GlobalVariable(M
, MapTy
, false, GlobalValue::PrivateLinkage
,
82 Constant::getNullValue(MapTy
), BitMapName
);
85 // Generate the code sequence in the entry block of each function to
87 void generateCodeSequence(Module
&M
, Function
&F
, int FuncId
) {
88 if (!ClOrderFileWriteMapping
.empty()) {
89 std::lock_guard
<std::mutex
> LogLock(MappingMutex
);
91 llvm::raw_fd_ostream
OS(ClOrderFileWriteMapping
, EC
,
92 llvm::sys::fs::OF_Append
);
94 report_fatal_error(Twine("Failed to open ") + ClOrderFileWriteMapping
+
95 " to save mapping file for order file instrumentation\n");
97 std::stringstream stream
;
98 stream
<< std::hex
<< MD5Hash(F
.getName());
99 std::string singleLine
= "MD5 " + stream
.str() + " " +
100 std::string(F
.getName()) + '\n';
105 BasicBlock
*OrigEntry
= &F
.getEntryBlock();
107 LLVMContext
&Ctx
= M
.getContext();
108 IntegerType
*Int32Ty
= Type::getInt32Ty(Ctx
);
109 IntegerType
*Int8Ty
= Type::getInt8Ty(Ctx
);
111 // Create a new entry block for instrumentation. We will check the bitmap
112 // in this basic block.
113 BasicBlock
*NewEntry
=
114 BasicBlock::Create(M
.getContext(), "order_file_entry", &F
, OrigEntry
);
115 IRBuilder
<> entryB(NewEntry
);
116 // Create a basic block for updating the circular buffer.
117 BasicBlock
*UpdateOrderFileBB
=
118 BasicBlock::Create(M
.getContext(), "order_file_set", &F
, OrigEntry
);
119 IRBuilder
<> updateB(UpdateOrderFileBB
);
121 // Check the bitmap, if it is already 1, do nothing.
122 // Otherwise, set the bit, grab the index, update the buffer.
123 Value
*IdxFlags
[] = {ConstantInt::get(Int32Ty
, 0),
124 ConstantInt::get(Int32Ty
, FuncId
)};
125 Value
*MapAddr
= entryB
.CreateGEP(MapTy
, BitMap
, IdxFlags
, "");
126 LoadInst
*loadBitMap
= entryB
.CreateLoad(Int8Ty
, MapAddr
, "");
127 entryB
.CreateStore(ConstantInt::get(Int8Ty
, 1), MapAddr
);
128 Value
*IsNotExecuted
=
129 entryB
.CreateICmpEQ(loadBitMap
, ConstantInt::get(Int8Ty
, 0));
130 entryB
.CreateCondBr(IsNotExecuted
, UpdateOrderFileBB
, OrigEntry
);
132 // Fill up UpdateOrderFileBB: grab the index, update the buffer!
133 Value
*IdxVal
= updateB
.CreateAtomicRMW(
134 AtomicRMWInst::Add
, BufferIdx
, ConstantInt::get(Int32Ty
, 1),
135 MaybeAlign(), AtomicOrdering::SequentiallyConsistent
);
136 // We need to wrap around the index to fit it inside the buffer.
137 Value
*WrappedIdx
= updateB
.CreateAnd(
138 IdxVal
, ConstantInt::get(Int32Ty
, INSTR_ORDER_FILE_BUFFER_MASK
));
139 Value
*BufferGEPIdx
[] = {ConstantInt::get(Int32Ty
, 0), WrappedIdx
};
141 updateB
.CreateGEP(BufferTy
, OrderFileBuffer
, BufferGEPIdx
, "");
142 updateB
.CreateStore(ConstantInt::get(Type::getInt64Ty(Ctx
), MD5Hash(F
.getName())),
144 updateB
.CreateBr(OrigEntry
);
147 bool run(Module
&M
) {
148 createOrderFileData(M
);
151 for (Function
&F
: M
) {
152 if (F
.isDeclaration())
154 generateCodeSequence(M
, F
, FuncId
);
161 }; // End of InstrOrderFile struct
162 } // End anonymous namespace
165 InstrOrderFilePass::run(Module
&M
, ModuleAnalysisManager
&AM
) {
166 if (InstrOrderFile().run(M
))
167 return PreservedAnalyses::none();
168 return PreservedAnalyses::all();