From e21eed118ce900ea5a255ad33d36912cf8194367 Mon Sep 17 00:00:00 2001 From: Alessandro Di Federico Date: Wed, 14 Nov 2018 13:53:31 +0100 Subject: [PATCH] Tag `function_call` targeting external symbols This commit uses SET, information about canonical values and labels to detect if an indirect function call is targeting an external symbol. The strings used for the name of external symbols are uniqued global variables. This commit also uses this approach for the disassembly of original instructions, which used to be metadata. --- .../BasicAnalyses/GeneratedCodeBasicInfo.h | 6 +- include/revng/Support/IRHelpers.h | 12 + include/revng/Support/revng.h | 1 + .../FunctionCallIdentification.cpp | 17 +- lib/DebugHelper/DebugHelper.cpp | 68 ++--- lib/Support/CMakeLists.txt | 2 +- lib/Support/Debug.cpp | 8 - lib/Support/IRHelpers.cpp | 70 +++++ tools/revamb/BinaryFile.cpp | 15 ++ tools/revamb/CodeGenerator.cpp | 24 +- tools/revamb/InstructionTranslator.cpp | 23 +- tools/revamb/JumpTargetManager.cpp | 29 +- tools/revamb/SET.cpp | 252 ++++++++++++++++-- 13 files changed, 422 insertions(+), 105 deletions(-) create mode 100644 lib/Support/IRHelpers.cpp diff --git a/include/revng/BasicAnalyses/GeneratedCodeBasicInfo.h b/include/revng/BasicAnalyses/GeneratedCodeBasicInfo.h index 1c746d441..ff6ea4fa9 100644 --- a/include/revng/BasicAnalyses/GeneratedCodeBasicInfo.h +++ b/include/revng/BasicAnalyses/GeneratedCodeBasicInfo.h @@ -184,9 +184,9 @@ public: revng_assert(T != nullptr); for (llvm::BasicBlock *Successor : T->successors()) { - if (not(Successor == Dispatcher or Successor == DispatcherFail - or Successor == AnyPC or Successor == UnexpectedPC - or isJumpTarget(Successor))) + if (not(Successor->empty() or Successor == Dispatcher + or Successor == DispatcherFail or Successor == AnyPC + or Successor == UnexpectedPC or isJumpTarget(Successor))) return false; } diff --git a/include/revng/Support/IRHelpers.h b/include/revng/Support/IRHelpers.h index 21a9539a9..bdc590bf1 100644 --- a/include/revng/Support/IRHelpers.h +++ b/include/revng/Support/IRHelpers.h @@ -647,4 +647,16 @@ inline std::string dumpToString(const llvm::Module *M) { void dumpModule(const llvm::Module *M, const char *Path) debug_function; +llvm::GlobalVariable * +buildString(llvm::Module *M, llvm::StringRef String, const llvm::Twine &Name); + +llvm::Constant *buildStringPtr(llvm::Module *M, + llvm::StringRef String, + const llvm::Twine &Name); + +llvm::Constant *getUniqueString(llvm::Module *M, + llvm::StringRef Namespace, + llvm::StringRef String, + const llvm::Twine &Name = llvm::Twine()); + #endif // IRHELPERS_H diff --git a/include/revng/Support/revng.h b/include/revng/Support/revng.h index 3f60b17d1..537943ac7 100644 --- a/include/revng/Support/revng.h +++ b/include/revng/Support/revng.h @@ -283,6 +283,7 @@ public: const char *name() const { return llvm::Triple::getArchTypeName(Type).data(); } + llvm::Triple::ArchType type() const { return Type; } unsigned pcMContextIndex() const { return PCMContextIndex; } llvm::StringRef writeRegisterAsm() const { return WriteRegisterAsm; } diff --git a/lib/BasicAnalyses/FunctionCallIdentification.cpp b/lib/BasicAnalyses/FunctionCallIdentification.cpp index 754e245fc..c0faeee92 100644 --- a/lib/BasicAnalyses/FunctionCallIdentification.cpp +++ b/lib/BasicAnalyses/FunctionCallIdentification.cpp @@ -31,12 +31,12 @@ bool FunctionCallIdentification::runOnFunction(llvm::Function &F) { Module *M = F.getParent(); LLVMContext &C = M->getContext(); PointerType *Int8PtrTy = Type::getInt8PtrTy(C); + auto *Int8NullPtr = ConstantPointerNull::get(Int8PtrTy); auto *PCTy = IntegerType::get(C, GCBI.pcRegSize() * 8); auto *PCPtrTy = cast(GCBI.pcReg()->getType()); - std::initializer_list FunctionArgsTy = { Int8PtrTy, - Int8PtrTy, - PCTy, - PCPtrTy }; + std::initializer_list FunctionArgsTy = { + Int8PtrTy, Int8PtrTy, PCTy, PCPtrTy, Int8PtrTy + }; using FT = FunctionType; auto *Ty = FT::get(Type::getVoidTy(C), FunctionArgsTy, false); Constant *FunctionCallC = M->getOrInsertFunction("function_call", Ty); @@ -58,7 +58,7 @@ bool FunctionCallIdentification::runOnFunction(llvm::Function &F) { // Consider the basic block only if it's terminator is an actual jump and it // hasn't been already marked as a function call TerminatorInst *Terminator = BB.getTerminator(); - if (!GCBI.isJump(Terminator) || isCall(Terminator)) + if (BB.empty() or not GCBI.isJump(Terminator) or isCall(Terminator)) continue; // To be a function call we need to find: @@ -191,7 +191,7 @@ bool FunctionCallIdentification::runOnFunction(llvm::Function &F) { Value *Callee = nullptr; if (SuccessorsCount == 0) { - Callee = ConstantPointerNull::get(Int8PtrTy); + Callee = Int8NullPtr; } else if (SuccessorsCount == 1) { Callee = BlockAddress::get(Terminator->getSuccessor(0)); } else { @@ -209,14 +209,15 @@ bool FunctionCallIdentification::runOnFunction(llvm::Function &F) { revng_assert(Found); // It's an indirect call - Callee = ConstantPointerNull::get(Int8PtrTy); + Callee = Int8NullPtr; } const std::initializer_list Args{ Callee, BlockAddress::get(ReturnBB), ConstantInt::get(PCTy, ReturnPC), - LinkRegister }; + LinkRegister, + Int8NullPtr }; FallthroughAddresses.insert(ReturnPC); diff --git a/lib/DebugHelper/DebugHelper.cpp b/lib/DebugHelper/DebugHelper.cpp index b2ddd02af..7b1507efa 100644 --- a/lib/DebugHelper/DebugHelper.cpp +++ b/lib/DebugHelper/DebugHelper.cpp @@ -26,13 +26,13 @@ using namespace llvm; /// Boring code to get the text of the metadata with the specified kind /// associated to the given instruction -static MDString *getMD(const Instruction *Instruction, unsigned Kind) { +static StringRef getText(const Instruction *Instruction, unsigned Kind) { revng_assert(Instruction != nullptr); Metadata *MD = Instruction->getMetadata(Kind); if (MD == nullptr) - return nullptr; + return StringRef(); auto Node = dyn_cast(MD); @@ -43,26 +43,30 @@ static MDString *getMD(const Instruction *Instruction, unsigned Kind) { Metadata *MDOperand = Operand.get(); if (MDOperand == nullptr) - return nullptr; + return StringRef(); - auto *String = dyn_cast(MDOperand); - revng_assert(String != nullptr); - - return String; + if (auto *String = dyn_cast(MDOperand)) { + return String->getString(); + } else if (auto *CAM = dyn_cast(MDOperand)) { + auto *Cast = cast(CAM->getValue()); + auto *GV = cast(Cast->getOperand(0)); + auto *Initializer = GV->getInitializer(); + return cast(Initializer)->getAsString().drop_back(); + } else { + revng_abort(); + } } -static void replaceAll(std::string &Input, - const std::string &From, - const std::string &To) { - if(From.empty()) +static void +replaceAll(std::string &Input, const std::string &From, const std::string &To) { + if (From.empty()) return; size_t Start = 0; - while((Start = Input.find(From, Start)) != std::string::npos) { + while ((Start = Input.find(From, Start)) != std::string::npos) { Input.replace(Start, From.length(), To); Start += To.length(); } - } /// Writes the text contained in the metadata with the specified kind ID to the @@ -72,23 +76,24 @@ static void writeMetadataIfNew(const Instruction *TheInstruction, unsigned MDKind, formatted_raw_ostream &Output, StringRef Prefix) { - MDString *MD = getMD(TheInstruction, MDKind); - if (MD != nullptr) { - MDString *PrevMD = nullptr; + auto BeginIt = TheInstruction->getParent()->begin(); + StringRef Text = getText(TheInstruction, MDKind); + if (Text.size()) { + StringRef LastText; do { - if (TheInstruction->getIterator() == TheInstruction->getParent()->begin()) + if (TheInstruction->getIterator() == BeginIt) { TheInstruction = nullptr; - else { + } else { TheInstruction = TheInstruction->getPrevNode(); - PrevMD = getMD(TheInstruction, MDKind); + LastText = getText(TheInstruction, MDKind); } - } while (TheInstruction != nullptr && PrevMD == nullptr); + } while (TheInstruction != nullptr && LastText.size() == 0); - if (TheInstruction == nullptr || PrevMD != MD) { - std::string Text = MD->getString().str(); - replaceAll(Text, "\n", " "); - Output << Prefix << Text << "\n"; + if (TheInstruction == nullptr or LastText != Text) { + std::string TextToSerialize = Text.str(); + replaceAll(TextToSerialize, "\n", " "); + Output << Prefix << TextToSerialize << "\n"; } } } @@ -134,7 +139,6 @@ void DAW::emitInstructionAnnot(const Instruction *Instr, // Flushing is required to have correct line and column numbers Output.flush(); - StringRef FunctionName = Instr->getParent()->getParent()->getName(); auto *Location = DILocation::get(Context, Output.getLine() + 1, Output.getColumn(), @@ -223,7 +227,7 @@ void DebugHelper::generateDebugInfo() { PTCInstrMDKind : OriginalInstrMDKind; - MDString *Last = nullptr; + StringRef Last; std::ofstream Source(DebugPath); for (Function &F : TheModule->functions()) { @@ -233,22 +237,18 @@ void DebugHelper::generateDebugInfo() { if (DISubprogram *CurrentSubprogram = F.getSubprogram()) { for (BasicBlock &Block : F) { for (Instruction &Instruction : Block) { - MDString *Body = getMD(&Instruction, MetadataKind); + StringRef Body = getText(&Instruction, MetadataKind); - if (Body != nullptr && Last != Body) { + if (Body.size() != 0 && Last != Body) { Last = Body; - std::string BodyString = Body->getString().str(); - - Source << BodyString; + Source << Body.data(); auto *Location = DILocation::get(TheModule->getContext(), LineIndex, 0, CurrentSubprogram); Instruction.setMetadata(DbgMDKind, Location); - LineIndex += std::count(BodyString.begin(), - BodyString.end(), - '\n'); + LineIndex += std::count(Body.begin(), Body.end(), '\n'); } } } diff --git a/lib/Support/CMakeLists.txt b/lib/Support/CMakeLists.txt index 69e500bf4..13dfe637c 100644 --- a/lib/Support/CMakeLists.txt +++ b/lib/Support/CMakeLists.txt @@ -1 +1 @@ -add_library(Support STATIC Assert.cpp CommandLine.cpp Statistics.cpp Debug.cpp ExampleAnalysis.cpp) +add_library(Support STATIC Assert.cpp CommandLine.cpp Statistics.cpp Debug.cpp ExampleAnalysis.cpp IRHelpers.cpp) diff --git a/lib/Support/Debug.cpp b/lib/Support/Debug.cpp index 338bca82f..aad65c918 100644 --- a/lib/Support/Debug.cpp +++ b/lib/Support/Debug.cpp @@ -7,14 +7,12 @@ // Standard includes #include -#include #include #include #include // LLVM includes #include "llvm/IR/Value.h" -#include "llvm/Support/raw_os_ostream.h" // Local libraries includes #include "revng/Support/CommandLine.h" @@ -100,9 +98,3 @@ unsigned Logger::IndentLevel; // Force instantiation template class Logger; template class Logger; - -void dumpModule(const llvm::Module *M, const char *Path) { - std::ofstream FileStream(Path); - llvm::raw_os_ostream Stream(FileStream); - M->print(Stream, nullptr, true); -} diff --git a/lib/Support/IRHelpers.cpp b/lib/Support/IRHelpers.cpp new file mode 100644 index 000000000..a08ec81bb --- /dev/null +++ b/lib/Support/IRHelpers.cpp @@ -0,0 +1,70 @@ +/// \file IRHelpers.cpp +/// \brief Implementation of IR helper functions + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +// Standard includes +#include + +// LLVM includes +#include "llvm/Support/raw_os_ostream.h" + +// Local libraries includes +#include "revng/Support/IRHelpers.h" + +using namespace llvm; + +void dumpModule(const Module *M, const char *Path) { + std::ofstream FileStream(Path); + raw_os_ostream Stream(FileStream); + M->print(Stream, nullptr, true); +} + +GlobalVariable *buildString(Module *M, StringRef String, const Twine &Name) { + LLVMContext &C = M->getContext(); + auto *Initializer = ConstantDataArray::getString(C, String, true); + return new GlobalVariable(*M, + Initializer->getType(), + true, + GlobalVariable::InternalLinkage, + Initializer, + Name); +} + +Constant *buildStringPtr(Module *M, StringRef String, const Twine &Name) { + LLVMContext &C = M->getContext(); + Type *Int8PtrTy = Type::getInt8Ty(C)->getPointerTo(); + GlobalVariable *NewVariable = buildString(M, String, Name); + return ConstantExpr::getBitCast(NewVariable, Int8PtrTy); +} + +Constant *getUniqueString(Module *M, + StringRef Namespace, + StringRef String, + const Twine &Name) { + LLVMContext &C = M->getContext(); + Type *Int8PtrTy = Type::getInt8Ty(C)->getPointerTo(); + NamedMDNode *StringsList = M->getOrInsertNamedMetadata(Namespace); + + for (MDNode *Operand : StringsList->operands()) { + auto *T = cast(Operand); + revng_assert(T->getNumOperands() == 1); + auto *CAM = cast(T->getOperand(0).get()); + auto *GV = cast(CAM->getValue()); + revng_assert(GV->isConstant() and GV->hasInitializer()); + + const Constant *Initializer = GV->getInitializer(); + StringRef Content = cast(Initializer)->getAsString(); + + // Ignore the terminator + if (Content.drop_back() == String) + return ConstantExpr::getBitCast(GV, Int8PtrTy); + } + + GlobalVariable *NewVariable = buildString(M, String, Name); + auto *CAM = ConstantAsMetadata::get(NewVariable); + StringsList->addOperand(MDTuple::get(C, { CAM })); + return ConstantExpr::getBitCast(NewVariable, Int8PtrTy); +} diff --git a/tools/revamb/BinaryFile.cpp b/tools/revamb/BinaryFile.cpp index 74154a228..726f9a6e4 100644 --- a/tools/revamb/BinaryFile.cpp +++ b/tools/revamb/BinaryFile.cpp @@ -400,6 +400,10 @@ struct RelocationHelper { } }; +static bool shouldIgnoreSymbol(StringRef Name) { + return Name == "$a" or Name == "$d"; +} + template void BinaryFile::parseELF(object::ObjectFile *TheBinary, uint64_t BaseAddress) { // Parse the ELF file @@ -477,6 +481,9 @@ void BinaryFile::parseELF(object::ObjectFile *TheBinary, uint64_t BaseAddress) { revng_abort(); } + if (shouldIgnoreSymbol(*Name)) + continue; + registerLabel(Label::createSymbol(LabelOrigin::StaticSymbol, Symbol.st_value, Symbol.st_size, @@ -702,6 +709,10 @@ void BinaryFile::parseELF(object::ObjectFile *TheBinary, uint64_t BaseAddress) { logAllUnhandledErrors(std::move(Name.takeError()), errs(), ""); revng_abort(); } + + if (shouldIgnoreSymbol(*Name)) + continue; + auto SymbolType = SymbolType::fromELF(Symbol.getType()); registerLabel(Label::createSymbol(LabelOrigin::DynamicSymbol, Symbol.st_value, @@ -875,6 +886,8 @@ Label BinaryFile::parseRelocation(unsigned char RelocationType, return Label::createBaseRelativeValue(Origin, Target, PointerSize, Offset); case RD::LabelOnly: + if (shouldIgnoreSymbol(SymbolName)) + return Label::createInvalid(); return Label::createSymbol(Origin, Target, SymbolSize, @@ -882,6 +895,8 @@ Label BinaryFile::parseRelocation(unsigned char RelocationType, SymbolType); case RD::SymbolRelative: + if (shouldIgnoreSymbol(SymbolName)) + return Label::createInvalid(); return Label::createSymbolRelativeValue(Origin, Target, PointerSize, diff --git a/tools/revamb/CodeGenerator.cpp b/tools/revamb/CodeGenerator.cpp index 22185ed4c..745fde2f1 100644 --- a/tools/revamb/CodeGenerator.cpp +++ b/tools/revamb/CodeGenerator.cpp @@ -119,25 +119,24 @@ static cl::alias A5("f", cl::cat(MainCategory)); // Enable Debug Options to be specified on the command line -auto X = cl::values(clEnumValN(DebugInfoType::None, - "none", - "no debug information"), - clEnumValN(DebugInfoType::OriginalAssembly, +namespace DIT = DebugInfoType; +auto X = cl::values(clEnumValN(DIT::None, "none", "no debug information"), + clEnumValN(DIT::OriginalAssembly, "asm", "debug information referred to the assembly " "of the input file"), - clEnumValN(DebugInfoType::PTC, + clEnumValN(DIT::PTC, "ptc", "debug information referred to the Portable " "Tiny Code"), - clEnumValN(DebugInfoType::LLVMIR, + clEnumValN(DIT::LLVMIR, "ll", "debug information referred to the LLVM IR")); -static cl::opt DebugInfo("debug-info", - cl::desc("emit debug " - "information"), - X, - cl::cat(MainCategory)); +static cl::opt DebugInfo("debug-info", + cl::desc("emit debug information"), + X, + cl::cat(MainCategory), + cl::init(DIT::LLVMIR)); static cl::alias A6("g", cl::desc("Alias for -debug-info"), @@ -147,8 +146,7 @@ static cl::alias A6("g", // TODO: is this still active? static cl::opt DebugPath("debug-path", cl::desc("destination path for the generated " - "debug " - "source"), + "debug source"), cl::value_desc("path"), cl::cat(MainCategory)); diff --git a/tools/revamb/InstructionTranslator.cpp b/tools/revamb/InstructionTranslator.cpp index d857cbfbf..60842a29d 100644 --- a/tools/revamb/InstructionTranslator.cpp +++ b/tools/revamb/InstructionTranslator.cpp @@ -484,11 +484,13 @@ IT::InstructionTranslator(IRBuilder<> &Builder, // * address of the instruction // * instruction size // * isJT (-1: unknown, 0: no, 1: yes) + // * pointer to the disassembled instruction // * all the local variables used by this instruction auto *NewPCMarkerTy = FT::get(Type::getVoidTy(Context), { Type::getInt64Ty(Context), Type::getInt64Ty(Context), - Type::getInt32Ty(Context) }, + Type::getInt32Ty(Context), + Type::getInt8PtrTy(Context) }, true); NewPCMarker = Function::Create(NewPCMarkerTy, GlobalValue::ExternalLinkage, @@ -567,6 +569,9 @@ IT::newInstruction(PTCInstruction *Instr, bool ForceNew) { using R = std::tuple; revng_assert(Instr != nullptr); + + LLVMContext &Context = TheModule.getContext(); + const PTC::Instruction TheInstruction(Instr); // A new original instruction, let's create a new metadata node // referencing it for all the next instructions to come @@ -576,11 +581,17 @@ IT::newInstruction(PTCInstruction *Instr, std::stringstream OriginalStringStream; disassemble(OriginalStringStream, PC, NextPC - PC); std::string OriginalString = OriginalStringStream.str(); - LLVMContext &Context = TheModule.getContext(); - MDString *MDOriginalString = MDString::get(Context, OriginalString); + + // We don't deduplicate this string since performing a lookup each time is + // increasingly expensive and we should have relatively few collisions + std::string AddressName = JumpTargets.nameForAddress(PC); + Constant *String = buildStringPtr(&TheModule, + OriginalString, + Twine("disam_") + AddressName); + + auto *MDOriginalString = ConstantAsMetadata::get(String); auto *MDPC = ConstantAsMetadata::get(Builder.getInt64(PC)); - MDNode *MDOriginalInstr = MDNode::getDistinct(Context, - { MDOriginalString, MDPC }); + MDNode *MDOriginalInstr = MDNode::get(Context, { MDOriginalString, MDPC }); if (ForceNew) JumpTargets.registerJT(PC, JTReason::PostHelper); @@ -611,7 +622,7 @@ IT::newInstruction(PTCInstruction *Instr, std::vector Args = { Builder.getInt64(PC), Builder.getInt64(NextPC - PC), Builder.getInt32(-1), - MetadataAsValue::get(Context, MDOriginalString) }; + String }; for (AllocaInst *Local : Variables.locals()) Args.push_back(Local); diff --git a/tools/revamb/JumpTargetManager.cpp b/tools/revamb/JumpTargetManager.cpp index 225bbe61d..841a6a0fa 100644 --- a/tools/revamb/JumpTargetManager.cpp +++ b/tools/revamb/JumpTargetManager.cpp @@ -423,6 +423,22 @@ JumpTargetManager::JumpTargetManager(Function *TheFunction, // getOption(Options, "max-recurse-depth")->setInitialValue(10); } +static bool isBetterThan(const Label *NewCandidate, const Label *OldCandidate) { + if (OldCandidate == nullptr) + return true; + + if (NewCandidate->address() > OldCandidate->address()) + return true; + + if (NewCandidate->address() == OldCandidate->address()) { + StringRef OldName = OldCandidate->symbolName(); + if (OldName.size() == 0) + return true; + } + + return false; +} + // TODO: move this in BinaryFile? std::string JumpTargetManager::nameForAddress(uint64_t Address, uint64_t Size) const { @@ -446,21 +462,22 @@ JumpTargetManager::nameForAddress(uint64_t Address, uint64_t Size) const { continue; if (L->matches(Address, Size)) { + // It's an exact match ExactMatch = L; break; + } else if (not L->isSizeVirtual() and L->contains(Address, Size)) { + // It's contained in a not 0-sized symbol - if (ContainedNonZeroSized == nullptr - or L->address() > ContainedNonZeroSized->address()) { + if (isBetterThan(L, ContainedNonZeroSized)) ContainedNonZeroSized = L; - } + } else if (L->isSizeVirtual() and L->contains(Address, 0)) { + // It's contained in a 0-sized symbol - if (ContainedZeroSized == nullptr - or L->address() > ContainedZeroSized->address()) { + if (isBetterThan(L, ContainedZeroSized)) ContainedZeroSized = L; - } } } diff --git a/tools/revamb/SET.cpp b/tools/revamb/SET.cpp index 69700fe54..f90de8c08 100644 --- a/tools/revamb/SET.cpp +++ b/tools/revamb/SET.cpp @@ -33,6 +33,39 @@ using std::make_pair; static Logger<> OSRJTSLog("osrjts"); +class MaterializedValue { +private: + bool IsValid; + Optional SymbolName; + uint64_t Value; + +private: + MaterializedValue() : IsValid(false), Value(0) {} + +public: + MaterializedValue(uint64_t Value) : IsValid(true), Value(Value) {} + MaterializedValue(StringRef Name, uint64_t Offset) : + IsValid(true), + SymbolName(Name), + Value(Offset) {} + +public: + static MaterializedValue invalid() { return MaterializedValue(); } + +public: + uint64_t value() const { + revng_assert(isValid()); + return Value; + } + bool isValid() const { return IsValid; } + bool hasSymbol() const { return SymbolName.hasValue(); } + StringRef symbolName() const { + revng_assert(isValid()); + revng_assert(hasSymbol()); + return *SymbolName; + } +}; + /// \brief Stack to keep track of the operations generating a specific value /// /// The OperationsStacks offers the following features: @@ -46,17 +79,29 @@ static Logger<> OSRJTSLog("osrjts"); /// whether this information is precise or not class OperationsStack { public: - OperationsStack(JumpTargetManager *JTM, const DataLayout &DL) : + OperationsStack(JumpTargetManager *JTM, + const DataLayout &DL, + FunctionCallIdentification *FCI) : JTM(JTM), DL(DL), - LoadsCount(0) { + LoadsCount(0), + FCI(FCI) { reset(); } ~OperationsStack() { reset(); } void explore(Constant *NewOperand); - uint64_t materialize(Constant *NewOperand); + uint64_t materializeSimple(Constant *NewOperand) { + MaterializedValue Result = materialize(NewOperand, false); + if (not Result.isValid()) + return 0; + + revng_assert(not Result.hasSymbol()); + + return Result.value(); + } + MaterializedValue materialize(Constant *NewOperand, bool HandleSymbols); /// \brief What values should be tracked enum TrackingType { @@ -201,6 +246,14 @@ public: bool readsMemory() const { return LoadsCount > 0; } + void dump() const debug_function { dump(dbg); } + + template + void dump(O &Output) const { + for (Instruction *I : Operations) + Output << dumpToString(I) << "\n"; + } + private: JumpTargetManager *JTM; const DataLayout &DL; @@ -218,29 +271,82 @@ private: unsigned LoadsCount; Instruction *Target; + FunctionCallIdentification *FCI; }; -uint64_t OperationsStack::materialize(Constant *NewOperand) { +MaterializedValue +OperationsStack::materialize(Constant *NewOperand, bool HandleSymbols) { + Optional SymbolName; + for (Instruction *I : make_range(Operations.rbegin(), Operations.rend())) { if (auto *Load = dyn_cast(I)) { - // OK, we've got a load, let's see if the load address is - // constant + // OK, we've got a load, let's see if the load address is constant revng_assert(NewOperand != nullptr && !isa(NewOperand)); + if (SymbolName) + return MaterializedValue::invalid(); + + uint64_t LoadAddress = getZExtValue(NewOperand, DL); + unsigned LoadSize; + // Read the value using the endianess of the destination architecture, // since, if there's a mismatch, in the stack we will also have a byteswap // instruction - JumpTargetManager::Endianess E = JumpTargetManager::DestinationEndianess; + using Endianess = BinaryFile::Endianess; + Endianess E = (DL.isLittleEndian() ? Endianess::LittleEndian : + Endianess::BigEndian); if (Load->getType()->isIntegerTy()) { - unsigned Size = Load->getType()->getPrimitiveSizeInBits() / 8; - revng_assert(Size != 0); - NewOperand = JTM->readConstantInt(NewOperand, Size, E); + LoadSize = Load->getType()->getPrimitiveSizeInBits() / 8; + revng_assert(LoadSize != 0); + NewOperand = JTM->readConstantInt(NewOperand, LoadSize, E); } else if (Load->getType()->isPointerTy()) { + LoadSize = JTM->binary().architecture().pointerSize() / 8; NewOperand = JTM->readConstantPointer(NewOperand, Load->getType(), E); } else { revng_abort(); } + const auto &Labels = JTM->binary().labels(); + using interval = boost::icl::interval; + auto Interval = interval::right_open(LoadAddress, LoadAddress + LoadSize); + auto It = Labels.find(Interval); + if (It != Labels.end()) { + const Label *Match = nullptr; + for (const Label *Candidate : It->second) { + if (Candidate->size() == LoadSize + and (Candidate->isAbsoluteValue() + or Candidate->isBaseRelativeValue() + or (HandleSymbols and Candidate->isSymbolRelativeValue()))) { + revng_assert(Match == nullptr, + "Multiple value labels at the same location"); + Match = Candidate; + } + } + + if (Match != nullptr) { + uint64_t Value; + switch (Match->type()) { + case LabelType::AbsoluteValue: + Value = Match->value(); + break; + + case LabelType::BaseRelativeValue: + Value = JTM->binary().relocate(Match->value()); + break; + + case LabelType::SymbolRelativeValue: + Value = Match->offset(); + SymbolName = Match->symbolName(); + break; + + default: + revng_abort(); + } + + NewOperand = ConstantInt::get(Load->getType(), Value); + } + } + if (NewOperand == nullptr) break; @@ -263,6 +369,21 @@ uint64_t OperationsStack::materialize(Constant *NewOperand) { NewOperand = ConstantInt::get(T, Value); } else { + + if (SymbolName) { + // In case the result is relative to a symbol, whitelist the allowed + // instructions + switch (I->getOpcode()) { + case Instruction::Add: + case Instruction::Sub: + case Instruction::And: + break; + default: + if (I->getNumOperands() > 1) + return MaterializedValue::invalid(); + } + } + // Replace non-const operand with NewOperand std::vector Operands; bool NonConstFound = false; @@ -292,51 +413,89 @@ uint64_t OperationsStack::materialize(Constant *NewOperand) { // We made it, mark the value to be explored if (NewOperand != nullptr) { revng_assert(!isa(NewOperand)); - return getZExtValue(NewOperand, DL); + uint64_t Value = getZExtValue(NewOperand, DL); + if (SymbolName) + return MaterializedValue(*SymbolName, Value); + else + return MaterializedValue(Value); } - return 0; + return MaterializedValue::invalid(); } void OperationsStack::explore(Constant *NewOperand) { - uint64_t MaterializedValue = materialize(NewOperand); + MaterializedValue SymbolicValue = materialize(NewOperand, true); + + if (not SymbolicValue.isValid()) + return; + + if (SymbolicValue.hasSymbol()) { + if (IsPCStore and SymbolicValue.value() == 0) { + if (CallInst *Call = FCI->getCall(Target->getParent())) { + LLVMContext &Context = getContext(Call); + QuickMetadata QMD(Context); + auto *Callee = cast(Call->getOperand(0)); + revng_assert(Callee->isNullValue(), "Direct call to external symbol"); + + StringRef Name = SymbolicValue.symbolName(); + + Value *Old = Call->getOperand(4); + if (not isa(Old)) { + auto *Casted = cast(Old)->getOperand(0); + auto *Initializer = cast(Casted)->getInitializer(); + auto String = cast(Initializer)->getAsString(); + revng_assert(String.drop_back() == Name); + } + + Module *M = Call->getParent()->getParent()->getParent(); + Constant *String = getUniqueString(M, + "revamb.input.symbol-names", + Name, + Twine("symbol_") + Name); + Call->setOperand(4, String); + } + } + + return; + } + + uint64_t Value = SymbolicValue.value(); bool IsStore = Target != nullptr; revng_assert(!(!IsStore && (Tracking == PCsOnly || SetsSyscallNumber))); if (IsStore) { - if (MaterializedValue != 0 && JTM->isPC(MaterializedValue)) - NewPCs.insert({ MaterializedValue, IsPCStore }); + if (Value != 0 && JTM->isPC(Value)) + NewPCs.insert({ Value, IsPCStore }); - if (MaterializedValue != 0 - && (Tracking == PCsOnly && JTM->isPC(MaterializedValue))) - TrackedValues.insert(MaterializedValue); + if (Value != 0 && (Tracking == PCsOnly && JTM->isPC(Value))) + TrackedValues.insert(Value); if (SetsSyscallNumber) { Instruction *Top = Operations.size() == 0 ? Target : Operations.back(); // TODO: don't ignore temporary instructions if (Top->getParent() != nullptr) - JTM->noReturn().registerKiller(MaterializedValue, Top, Target); + JTM->noReturn().registerKiller(Value, Top, Target); } } else { // It's a load - LoadAddresses.insert(MaterializedValue); + LoadAddresses.insert(Value); } } /// \brief Simple Expression Tracker implementation class SET { - public: SET(Function &F, JumpTargetManager *JTM, OSRAPass *OSRA, + FunctionCallIdentification *FCI, std::set *Visited, std::vector &Jumps) : DL(F.getParent()->getDataLayout()), JTM(JTM), - OS(JTM, DL), + OS(JTM, DL, FCI), F(F), OSRA(OSRA), Visited(Visited), @@ -360,6 +519,8 @@ private: /// \return true if the instruction was handled. bool handleInstructionWithOSRA(Instruction *Target, Value *V); + void collectMetadata(); + private: const unsigned MaxDepth = 3; const DataLayout &DL; @@ -370,6 +531,7 @@ private: std::set *Visited; std::vector> WorkList; std::vector &Jumps; + std::map CanonicalValues; }; bool SET::enqueueStores(LoadInst *Start) { @@ -442,7 +604,27 @@ bool SET::enqueueStores(LoadInst *Start) { return Handled; } +void SET::collectMetadata() { + const Module *M = getModule(&F); + QuickMetadata QMD(getContext(M)); + + // Collect canonical values + const char *MDName = "revamb.input.canonical-values"; + NamedMDNode *CanonicalValuesMD = M->getNamedMetadata(MDName); + for (MDNode *CanonicalValueMD : CanonicalValuesMD->operands()) { + auto *CanonicalValueTuple = cast(CanonicalValueMD); + auto Name = QMD.extract(CanonicalValueTuple, 0); + if (GlobalVariable *CSV = M->getGlobalVariable(Name)) { + uint64_t Value = QMD.extract(CanonicalValueTuple, 1); + CanonicalValues[CSV] = Value; + } + } +} + bool SET::run() { + collectMetadata(); + + // Run the actual analysis for (BasicBlock &BB : make_range(F.begin(), F.end())) { if (Visited->find(&BB) != Visited->end()) @@ -515,6 +697,8 @@ void SETPass::getAnalysisUsage(AnalysisUsage &AU) const { AU.addRequired(); AU.addRequired(); } + + AU.addRequired(); } bool SETPass::runOnFunction(Function &F) { @@ -528,7 +712,9 @@ bool SETPass::runOnFunction(Function &F) { JTM->noReturn().collectDefinitions(CRDP); } - SET SimpleExpressionTracker(F, JTM, OSRA, Visited, Jumps); + FunctionCallIdentification &FCI = getAnalysis(); + + SET SimpleExpressionTracker(F, JTM, OSRA, &FCI, Visited, Jumps); revng_log(PassesLog, "Ending SETPass"); return SimpleExpressionTracker.run(); @@ -567,9 +753,9 @@ bool SET::handleInstructionWithOSRA(Instruction *Target, Value *V) { // here is probably restore it to int64_t::max(), assert if it's // larger than 10000 and only apply it to store to memory, pc and // maybe other registers (lr?) - auto MaterializedMin = OS.materialize(MinConst); - auto MaterializedMax = OS.materialize(MaxConst); - auto MaterializedStep = OS.materialize(CI::get(Int64, O->factor())); + auto MaterializedMin = OS.materializeSimple(MinConst); + auto MaterializedMax = OS.materializeSimple(MaxConst); + auto MaterializedStep = OS.materializeSimple(CI::get(Int64, O->factor())); if (OS.readsMemory()) { // If there's a load in the stack only check the first and last element @@ -616,6 +802,20 @@ Value *SET::handleInstruction(Instruction *Target, Value *V) { return nullptr; } + if (auto *Load = dyn_cast(V)) { + + // + // Handle canonical values + // + if (auto *Target = dyn_cast(Load->getPointerOperand())) { + auto It = CanonicalValues.find(Target); + if (It != CanonicalValues.end()) { + OS.explore(ConstantInt::get(Load->getType(), It->second)); + // Do not return, proceed as usual + } + } + } + if (OSRA != nullptr && !OS.empty()) { if (handleInstructionWithOSRA(Target, V)) return nullptr;