// // This file is distributed under the MIT License. See LICENSE.md for details. // #include "llvm/IR/Verifier.h" #include "revng/ADT/Queue.h" #include "revng/Model/FunctionTags.h" #include "revng/Model/IRHelpers.h" #include "revng/PromoteStackPointer/InstrumentStackAccessesPass.h" #include "revng/Support/IRBuilder.h" #include "revng/Support/IRHelpers.h" #include "revng/Support/OpaqueFunctionsPool.h" using namespace llvm; static Logger Log("instrument-stack-accesses"); class InstrumentStackAccesses { private: OpaqueFunctionsPool StackOffsetPool; CallInst *SP0 = nullptr; Type *SPType = nullptr; public: InstrumentStackAccesses(Module &M) : StackOffsetPool(&M, false) { StackOffsetPool.addFnAttribute(Attribute::NoUnwind); StackOffsetPool.addFnAttribute(Attribute::WillReturn); StackOffsetPool.setMemoryEffects(MemoryEffects::inaccessibleMemOnly()); StackOffsetPool.setTags({ &FunctionTags::StackOffsetMarker }); } public: void run(Function &F) { revng_log(Log, "Instrumenting " << F.getName()); LoggerIndent Indent(Log); reset(); std::set StackMemoryAccesses = findStackAccesses(F); if (SP0 == nullptr) return; for (Instruction *I : StackMemoryAccesses) instrumentStackAccess(I); } private: void reset() { SP0 = nullptr; SPType = nullptr; } std::set findStackAccesses(Function &F); void instrumentStackAccess(Instruction *I); }; std::set InstrumentStackAccesses::findStackAccesses(Function &F) { // Identify memory accesses whose pointer operand depends on SP std::set StackMemoryAccesses; for (BasicBlock &BB : F) { for (Instruction &I : BB) { // Is this the call to revng_undefined_local_sp? if (auto *Call = getCallTo(&I, "revng_undefined_local_sp")) { revng_log(Log, "Found call to revng_undefined_local_sp: " << getName(Call)); SP0 = Call; SPType = SP0->getType(); // We found a load from SP: look for memory accesses using its value // to compute the pointer operand OnceQueue Queue; auto EnqueueUsers = [&Queue](Instruction *I) { for (User *U : I->users()) { if (auto *Store = dyn_cast(U)) { // Enqueue store only if the user is the pointer operand if (Store->getPointerOperand() == I) { Queue.insert(Store); } } else if (auto *UI = dyn_cast(U)) { Queue.insert(UI); } } }; EnqueueUsers(Call); while (not Queue.empty()) { Instruction *I = Queue.pop(); if (isa(I) or isa(I)) { revng_log(Log, "Record stack access: " << getName(I)); StackMemoryAccesses.insert(I); } // Enqueue users (unless it's load) if (not isa(I)) EnqueueUsers(I); } } } } revng_log(Log, StackMemoryAccesses.size() << " memory accesses recorded"); return StackMemoryAccesses; } void InstrumentStackAccesses::instrumentStackAccess(Instruction *I) { auto *Pointer = cast(getPointer(I)); auto *IntegerPointer = cast(skipCasts(Pointer)); revng::IRBuilder B(I); if (not IntegerPointer->getType()->isIntegerTy()) { Value *PtrToInt = B.CreatePtrToInt(IntegerPointer, SPType); IntegerPointer = cast(PtrToInt); } // Compute offset from SP0 // Note: here we're using `(x + - SP0)` instead of `x - SP0` since otherwise // certain optimization passes cannot correctly reassociate the sum and // elide SP0. Value *StackOffset = B.CreateAdd(B.CreateZExtOrTrunc(IntegerPointer, SPType), B.CreateNeg(SP0)); // Get/create an identity function taking as second argument the stack // offset Type *PointerTy = Pointer->getType(); auto *StackOffsetFunction = StackOffsetPool.get(PointerTy, PointerTy, { PointerTy, SPType, SPType }, "stack_offset"); // Emit the call auto *AccessSize = ConstantInt::get(SPType, getMemoryAccessSize(I)); auto *ID = B.CreateCall(StackOffsetFunction, { Pointer, StackOffset, B.CreateAdd(StackOffset, AccessSize) }); // Set the pointer operand if (auto *Load = dyn_cast(I)) { Load->setOperand(LoadInst::getPointerOperandIndex(), ID); } else if (auto *Store = dyn_cast(I)) { Store->setOperand(StoreInst::getPointerOperandIndex(), ID); } else { revng_abort(); } } bool InstrumentStackAccessesPass::runOnModule(Module &M) { InstrumentStackAccesses Instrumenter(M); for (Function &F : FunctionTags::Isolated.functions(&M)) Instrumenter.run(F); return true; } void InstrumentStackAccessesPass::getAnalysisUsage(AnalysisUsage &AU) const { AU.setPreservesCFG(); } char InstrumentStackAccessesPass::ID = 0; using RegisterISA = RegisterPass; static RegisterISA R("instrument-stack-accesses", "Instrument Stack Accesses Pass");