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;