#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Support/Endian.h" #include "llvm/Support/LEB128.h" // // What follows is a set of functions we use to read an integer of a specified // (or pointer) size using the appropriate endianess associated to an ELF type. // template struct Endianess { /// \brief Reads an integer of type T, using the endianess of the ELF type EE static uint64_t read(const uint8_t *Buf); }; template struct Endianess { static uint64_t read(const uint8_t *Buf) { using namespace llvm::support; return llvm::support::endian::read(Buf); } }; template struct Endianess { static uint64_t read(const uint8_t *Buf) { using namespace llvm::support; return llvm::support::endian::read(Buf); } }; template struct Endianess { static uint64_t read(const uint8_t *Buf) { using namespace llvm::support; return llvm::support::endian::read(Buf); } }; template struct Endianess { static uint64_t read(const uint8_t *Buf) { using namespace llvm::support; return llvm::support::endian::read(Buf); } }; /// \brief Read a pointer-sized integer according to the given ELF type EE template inline uint64_t readPointer(const uint8_t *Buf); template<> inline uint64_t readPointer(const uint8_t *Buf) { return Endianess::read(Buf); } template<> inline uint64_t readPointer(const uint8_t *Buf) { return Endianess::read(Buf); } template<> inline uint64_t readPointer(const uint8_t *Buf) { return Endianess::read(Buf); } template<> inline uint64_t readPointer(const uint8_t *Buf) { return Endianess::read(Buf); } /// \brief A pair on steroids to wrap a value or a pointer to a value class Pointer { public: Pointer() : IsIndirect(false), Value(MetaAddress::invalid()) {} Pointer(bool IsIndirect, MetaAddress Value) : IsIndirect(IsIndirect), Value(Value) {} bool isIndirect() const { return IsIndirect; } MetaAddress value() const { return Value; } private: bool IsIndirect; MetaAddress Value; }; template class DwarfReader { public: DwarfReader(llvm::Triple::ArchType Architecture, llvm::ArrayRef Buffer, MetaAddress Address) : Architecture(Architecture), Address(Address), Start(Buffer.data()), Cursor(Buffer.data()), End(Buffer.data() + Buffer.size()) {} uint8_t readNextU8() { return readNext(); } uint16_t readNextU16() { return readNext(); } uint32_t readNextU32() { return readNext(); } uint64_t readNextU64() { return readNext(); } uint64_t readNextU() { if (is64()) return readNextU64(); else return readNextU32(); } uint64_t readULEB128() { unsigned Length; uint64_t Result = llvm::decodeULEB128(Cursor, &Length); Cursor += Length; revng_assert(Cursor <= End); return Result; } int64_t readSLEB128() { unsigned Length; int64_t Result = llvm::decodeSLEB128(Cursor, &Length); Cursor += Length; revng_assert(Cursor <= End); return Result; } int64_t readSignedValue(unsigned Encoding) { return static_cast(readValue(Encoding)); } uint64_t readUnsignedValue(unsigned Encoding) { return static_cast(readValue(Encoding)); } Pointer readPointer(unsigned Encoding, MetaAddress Base = MetaAddress::invalid()) { using namespace llvm; revng_assert((Encoding & ~(0x70 | 0x0F | dwarf::DW_EH_PE_indirect)) == 0); // Handle PC-relative values revng_assert(Cursor >= Start); if ((Encoding & 0x70) == dwarf::DW_EH_PE_pcrel) { revng_assert(Base.isInvalid()); Base = Address + (Cursor - Start); } if (isSigned(Encoding & 0x0F)) { return readPointerInternal(readSignedValue(Encoding), Encoding, Base); } else { return readPointerInternal(readUnsignedValue(Encoding), Encoding, Base); } } void moveTo(uint64_t Offset) { const uint8_t *NewCursor = Start + Offset; revng_assert(NewCursor >= Cursor && NewCursor <= End); Cursor = NewCursor; } bool eof() const { return Cursor >= End; } uint64_t offset() const { return Cursor - Start; } private: template std::conditional_t::is_signed, int64_t, uint64_t> readNext() { using namespace llvm; constexpr bool IsSigned = std::numeric_limits::is_signed; using ReturnType = std::conditional_t; revng_assert(Cursor + sizeof(T) <= End); auto Result = static_cast(Endianess::read(Cursor)); Cursor += sizeof(T); return static_cast(Result); } static bool isSigned(unsigned Format) { using namespace llvm; switch (Format) { case dwarf::DW_EH_PE_sleb128: case dwarf::DW_EH_PE_signed: case dwarf::DW_EH_PE_sdata2: case dwarf::DW_EH_PE_sdata4: case dwarf::DW_EH_PE_sdata8: return true; case dwarf::DW_EH_PE_absptr: case dwarf::DW_EH_PE_uleb128: case dwarf::DW_EH_PE_udata2: case dwarf::DW_EH_PE_udata4: case dwarf::DW_EH_PE_udata8: return false; default: revng_abort("Unknown Encoding"); } } uint64_t readValue(unsigned Encoding) { using namespace llvm; revng_assert((Encoding & ~(0x70 | 0x0F | dwarf::DW_EH_PE_indirect)) == 0); // Extract the format unsigned Format = Encoding & 0x0F; switch (Format) { case dwarf::DW_EH_PE_uleb128: return readULEB128(); case dwarf::DW_EH_PE_sleb128: return readSLEB128(); case dwarf::DW_EH_PE_absptr: if (is64()) return readNext(); else return readNext(); case dwarf::DW_EH_PE_signed: if (is64()) return readNext(); else return readNext(); case dwarf::DW_EH_PE_udata2: return readNext(); case dwarf::DW_EH_PE_sdata2: return readNext(); case dwarf::DW_EH_PE_udata4: return readNext(); case dwarf::DW_EH_PE_sdata4: return readNext(); case dwarf::DW_EH_PE_udata8: return readNext(); case dwarf::DW_EH_PE_sdata8: return readNext(); default: revng_unreachable("Unknown Encoding"); } } template Pointer readPointerInternal(T Value, unsigned Encoding, MetaAddress Base) { using namespace llvm; bool IsIndirect = Encoding & dwarf::DW_EH_PE_indirect; if (Base.isInvalid()) { return Pointer(IsIndirect, MetaAddress::fromGeneric(Architecture, Value)); } else { unsigned EncodingRelative = Encoding & 0x70; revng_assert(EncodingRelative == 0 || EncodingRelative == 0x10); return Pointer(IsIndirect, Base + Value); } } bool is64() const; private: llvm::Triple::ArchType Architecture; MetaAddress Address; const uint8_t *Start; const uint8_t *Cursor; const uint8_t *End; }; template<> inline bool DwarfReader::is64() const { return false; } template<> inline bool DwarfReader::is64() const { return false; } template<> inline bool DwarfReader::is64() const { return true; } template<> inline bool DwarfReader::is64() const { return true; }