/// \file ELF.cpp /// \brief // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include "llvm/ADT/DenseMap.h" #include "llvm/Object/ELF.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/ObjectFile.h" #include "revng/ABI/DefaultFunctionPrototype.h" #include "revng/Model/Binary.h" #include "revng/Model/Importer/Dwarf/DwarfImporter.h" #include "revng/Model/RawBinaryView.h" #include "revng/Support/Debug.h" #include "BinaryImporterHelper.h" #include "DwarfReader.h" #include "Importers.h" using namespace llvm; static Logger<> Log("elf-importer"); static bool shouldIgnoreSymbol(StringRef Name) { return Name == "$a" or Name == "$d"; } static bool endsWith(StringRef String, char Last) { return not String.empty() and String.back() == Last; } static llvm::StringRef extractNullTerminatedStringAt(llvm::StringRef Source, uint64_t Offset) { auto Size = Source.slice(Offset, Source.size()).find('\0'); return Source.slice(Offset, Offset + Size); } template static void logAddress(T &Logger, const char *Name, MetaAddress Address) { if (Logger.isEnabled()) { Logger << Name; Address.dump(Logger); Logger << DoLog; } } class FilePortion2 { private: const RawBinaryView &File; bool HasAddress; bool HasSize; uint64_t Size; MetaAddress Address; public: FilePortion2(const RawBinaryView &File) : File(File), HasAddress(false), HasSize(false), Size(0), Address(MetaAddress::invalid()) {} public: void setAddress(MetaAddress Address) { HasAddress = true; this->Address = Address; } void setSize(uint64_t Size) { HasSize = true; this->Size = Size; } MetaAddress addressAtOffset(uint64_t Offset) { if (not(HasAddress and HasSize and Offset <= Size)) return MetaAddress::invalid(); return Address + Offset; } template MetaAddress addressAtIndex(uint64_t Index) { uint64_t Offset = Index * sizeof(T); if (not(HasAddress and HasSize and Offset <= Size)) return MetaAddress::invalid(); return Address + Offset; } bool isAvailable() const { return HasAddress; } bool isExact() const { revng_assert(HasAddress); return HasSize; } StringRef extractString() const { auto Data = extractData(); const char *AsChar = reinterpret_cast(Data.data()); return StringRef(AsChar, Data.size()); } template ArrayRef extractAs() const { auto Data = extractData(); const size_t TypeSize = sizeof(T); if (Data.size() % TypeSize != 0) return {}; return ArrayRef(reinterpret_cast(Data.data()), Data.size() / TypeSize); } ArrayRef extractData() const { revng_assert(HasAddress); if (HasSize) { auto MaybeData = File.getByAddress(Address, Size); if (MaybeData) { return *MaybeData; } else { revng_log(Log, "Cannot access address " << Address.toString() << " and size " << Size); return {}; } } else { auto MaybeData = File.getFromAddressOn(Address); if (MaybeData) { return *MaybeData; } else { revng_log(Log, "Cannot access address " << Address.toString()); return {}; } } } }; template uint64_t symbolsCount(const FilePortion2 &Relocations) { using Elf_Rel = llvm::object::Elf_Rel_Impl; if (not Relocations.isAvailable() or not Relocations.isExact()) return 0; uint32_t SymbolsCount = 0; for (Elf_Rel Relocation : Relocations.extractAs()) SymbolsCount = std::max(SymbolsCount, Relocation.getSymbol(false) + 1); return SymbolsCount; } class ELFImporter : public BinaryImporterHelper { private: RawBinaryView File; TupleTree &Model; const object::ELFObjectFileBase &TheBinary; uint64_t PreferredBaseAddress; Optional EHFrameHdrAddress; Optional DynamicAddress; public: ELFImporter(TupleTree &Model, const object::ELFObjectFileBase &TheBinary, uint64_t PreferredBaseAddress) : File(*Model, toArrayRef(TheBinary.getData())), Model(Model), TheBinary(TheBinary), PreferredBaseAddress(PreferredBaseAddress) {} private: template using Elf_Rel_Array = llvm::ArrayRef>; template using ConstElf_Shdr = const typename object::ELFFile::Elf_Shdr; public: template llvm::Error import(); private: template MetaAddress getGenericPointer(Pointer Ptr) const { if (not Ptr.isIndirect()) return Ptr.value(); auto MaybePointer = File.getFromAddressOn(Ptr.value()); if (not MaybePointer) return MetaAddress::invalid(); return fromGeneric(::readPointer(MaybePointer->data())); } template MetaAddress getCodePointer(Pointer Ptr) const { using namespace model::Architecture; auto Architecture = Model->Architecture; return getGenericPointer(Ptr).toPC(toLLVMArchitecture(Architecture)); } /// \brief Parse the .eh_frame_hdr section to obtain the address and the /// number of FDEs in .eh_frame /// /// \return a pair containing a (possibly invalid) pointer to the .eh_frame /// section and the count of FDEs in the .eh_frame_hdr section (which /// should match the number of FDEs in .eh_frame) template std::pair ehFrameFromEhFrameHdr(); /// \brief Parse the .eh_frame section to collect all the landing pads /// /// \param EHFrameAddress the address of the .eh_frame section /// \param FDEsCount the count of FDEs in the .eh_frame section /// \param EHFrameSize the size of the .eh_frame section /// /// \note Either \p FDEsCount or \p EHFrameSize have to be specified template void parseEHFrame(MetaAddress EHFrameAddress, Optional FDEsCount, Optional EHFrameSize); /// \brief Parse an LSDA to collect its landing pads /// /// \param FDEStart the start address of the FDE to which this LSDA is /// associated /// \param LSDAAddress the address of the target LSDA template void parseLSDA(MetaAddress FDEStart, MetaAddress LSDAAddress); /// \brief Register a label for each input relocation template void registerRelocations(Elf_Rel_Array Relocations, const FilePortion2 &Dynsym, const FilePortion2 &Dynstr); template void parseSymbols(object::ELFFile &TheELF, ConstElf_Shdr *SectionHeader); template void parseProgramHeaders(object::ELFFile &TheELF); template void parseDynamicSymbol(llvm::object::Elf_Sym_Impl &Symbol, StringRef Dynstr); }; template Error ELFImporter::import() { // Parse the ELF file auto TheELFOrErr = object::ELFFile::create(TheBinary.getData()); if (not TheELFOrErr) return TheELFOrErr.takeError(); object::ELFFile &TheELF = *TheELFOrErr; revng_assert(Model->Architecture != model::Architecture::Invalid); Architecture = Model->Architecture; // Set default ABI Model->DefaultABI = model::ABI::getDefault(Model->Architecture); // BaseAddress makes sense only for shared (relocatable, PIC) objects auto Type = TheELF.getHeader().e_type; if (Type == ELF::ET_DYN) BaseAddress = PreferredBaseAddress; if (not(Type == ELF::ET_DYN or Type == ELF::ET_EXEC)) return createError("Only ELF executables and ELF dynamic libraries are " "supported"); // Look for static or dynamic symbols and relocations ConstElf_Shdr *SymtabShdr = nullptr; Optional EHFrameAddress; Optional EHFrameSize; auto Sections = TheELF.sections(); if (not Sections) { logAllUnhandledErrors(std::move(Sections.takeError()), errs(), ""); } else { for (auto &Section : *Sections) { auto NameOrErr = TheELF.getSectionName(Section); if (NameOrErr) { auto &Name = *NameOrErr; if (Name == ".symtab") { // TODO: check dedicated field in section header if (SymtabShdr == nullptr) SymtabShdr = &Section; else revng_log(Log, "Multiple .symtab. Ignoring."); } else if (Name == ".eh_frame") { if (not EHFrameAddress) { EHFrameAddress = relocate(fromGeneric(Section.sh_addr)); EHFrameSize = static_cast(Section.sh_size); } else { revng_log(Log, "Duplicate .eh_frame. Ignoring."); } } else if (Name == ".dynamic") { if (not DynamicAddress) DynamicAddress = relocate(fromGeneric(Section.sh_addr)); else revng_log(Log, "Duplicate .dynamic. Ignoring."); } } } } parseSymbols(TheELF, SymtabShdr); const auto &ElfHeader = TheELF.getHeader(); Model->EntryPoint = relocate(fromPC(ElfHeader.e_entry)); parseProgramHeaders(TheELF); using Elf_Addr = const typename object::ELFFile::Elf_Addr; Optional FDEsCount; if (EHFrameHdrAddress) { MetaAddress Address = MetaAddress::invalid(); std::tie(Address, FDEsCount) = this->ehFrameFromEhFrameHdr(); if (Address.isValid()) { if (EHFrameAddress and *EHFrameAddress != Address) { revng_log(Log, "Incoherent .eh_frame information: .eh_frame is at " << EHFrameAddress->toString() << " while .eh_frame_hdr reports " << Address.toString()); } EHFrameAddress = Address; } } if (EHFrameAddress and EHFrameAddress->isValid()) parseEHFrame(*EHFrameAddress, FDEsCount, EHFrameSize); // Parse the .dynamic table auto DynamicEntries = TheELF.dynamicEntries(); if (DynamicEntries) { SmallVector NeededLibraryNameOffsets; FilePortion2 DynstrPortion(File); FilePortion2 DynsymPortion(File); FilePortion2 ReldynPortion(File); FilePortion2 RelpltPortion(File); FilePortion2 GotPortion(File); Optional SymbolsCount; Optional MIPSFirstGotSymbol; Optional MIPSLocalGotEntries; bool IsMIPS = (Model->Architecture == model::Architecture::mips or Model->Architecture == model::Architecture::mipsel); using Elf_Dyn = const typename object::ELFFile::Elf_Dyn; for (Elf_Dyn &DynamicTag : *DynamicEntries) { auto TheTag = DynamicTag.getTag(); MetaAddress Relocated = relocate(fromGeneric(DynamicTag.getPtr())); switch (TheTag) { case ELF::DT_NEEDED: NeededLibraryNameOffsets.push_back(DynamicTag.getVal()); break; case ELF::DT_STRTAB: DynstrPortion.setAddress(Relocated); break; case ELF::DT_STRSZ: DynstrPortion.setSize(DynamicTag.getVal()); break; case ELF::DT_SYMTAB: DynsymPortion.setAddress(Relocated); break; case ELF::DT_JMPREL: RelpltPortion.setAddress(Relocated); break; case ELF::DT_PLTRELSZ: RelpltPortion.setSize(DynamicTag.getVal()); break; case ELF::DT_REL: case ELF::DT_RELA: if (TheTag != (HasAddend ? ELF::DT_RELA : ELF::DT_REL)) { if (TheTag == ELF::DT_RELA) revng_log(Log, "Unexpected addend in relocation"); else revng_log(Log, "Addend was expected in relocation"); } ReldynPortion.setAddress(Relocated); break; case ELF::DT_RELSZ: case ELF::DT_RELASZ: if (TheTag != (HasAddend ? ELF::DT_RELASZ : ELF::DT_RELSZ)) { if (TheTag == ELF::DT_RELASZ) revng_log(Log, "Unexpected addend in relocation"); else revng_log(Log, "Addend was expected in relocation"); } ReldynPortion.setSize(DynamicTag.getVal()); break; case ELF::DT_PLTGOT: GotPortion.setAddress(Relocated); if (IsMIPS) { // TODO: record canonical value of the global pointer to Relocated + // 0x7ff0 } break; case ELF::DT_MIPS_SYMTABNO: if (IsMIPS) SymbolsCount = DynamicTag.getVal(); break; case ELF::DT_MIPS_GOTSYM: if (IsMIPS) MIPSFirstGotSymbol = DynamicTag.getVal(); break; case ELF::DT_MIPS_LOCAL_GOTNO: if (IsMIPS) MIPSLocalGotEntries = DynamicTag.getVal(); break; } } // In MIPS the GOT has one entry per symbol if (IsMIPS and SymbolsCount and MIPSFirstGotSymbol and MIPSLocalGotEntries) { uint32_t GotEntries = (*MIPSLocalGotEntries + (*SymbolsCount - *MIPSFirstGotSymbol)); GotPortion.setSize(GotEntries * sizeof(Elf_Addr)); } StringRef Dynstr; if (DynstrPortion.isAvailable()) { Dynstr = DynstrPortion.extractString(); auto Inserter = Model->ImportedLibraries.batch_insert(); for (auto Offset : NeededLibraryNameOffsets) { StringRef LibraryName = extractNullTerminatedStringAt(Dynstr, Offset); revng_assert(not endsWith(LibraryName, '\0')); Inserter.insert(LibraryName.data()); } } // Collect symbols count and code pointers in image base-relative // relocations if (not SymbolsCount) { SymbolsCount = std::max(symbolsCount(ReldynPortion), symbolsCount(RelpltPortion)); } // Collect function addresses contained in dynamic symbols if (SymbolsCount and *SymbolsCount > 0 and DynsymPortion.isAvailable()) { using Elf_Sym = llvm::object::Elf_Sym_Impl; DynsymPortion.setSize(*SymbolsCount * sizeof(Elf_Sym)); ArrayRef Symbols = DynsymPortion.extractAs(); for (Elf_Sym Symbol : Symbols) parseDynamicSymbol(Symbol, Dynstr); using Elf_Rel = llvm::object::Elf_Rel_Impl; if (ReldynPortion.isAvailable()) { registerRelocations(ReldynPortion.extractAs(), DynsymPortion, DynstrPortion); } if (RelpltPortion.isAvailable()) { registerRelocations(RelpltPortion.extractAs(), DynsymPortion, DynstrPortion); } if (IsMIPS and GotPortion.isAvailable()) { std::vector MIPSImplicitRelocations; uint32_t GotIndex = 0; // Perform local relocations on GOT if (MIPSLocalGotEntries) { for (; GotIndex < *MIPSLocalGotEntries; GotIndex++) { auto Address = GotPortion.addressAtIndex(GotIndex); Elf_Rel NewRelocation; NewRelocation.r_offset = Address.address(); NewRelocation.setSymbolAndType(0, R_MIPS_IMPLICIT_RELATIVE, false); MIPSImplicitRelocations.push_back(NewRelocation); } } // Relocate the remaining entries of the GOT with global symbols if (MIPSFirstGotSymbol and SymbolsCount and DynstrPortion.isAvailable() and DynsymPortion.isAvailable()) { for (uint32_t SymbolIndex = *MIPSFirstGotSymbol; SymbolIndex < *SymbolsCount; SymbolIndex++, GotIndex++) { auto Address = GotPortion.addressAtIndex(GotIndex); Elf_Rel NewRelocation; NewRelocation.r_offset = Address.address(); NewRelocation.setSymbolAndType(SymbolIndex, llvm::ELF::R_MIPS_JUMP_SLOT, false); MIPSImplicitRelocations.push_back(NewRelocation); } } auto Relocations = ArrayRef(MIPSImplicitRelocations); registerRelocations(Relocations, DynsymPortion, DynstrPortion); } } } // Create a default prototype Model->DefaultPrototype = abi::registerDefaultFunctionPrototype(*Model.get()); // Import Dwarf DwarfImporter Importer(Model); Importer.import(TheBinary, ""); return Error::success(); } template void ELFImporter::parseSymbols(object::ELFFile &TheELF, ConstElf_Shdr *SymtabShdr) { // Check if we found a symbol table if (SymtabShdr == nullptr or SymtabShdr->sh_link == 0) return; // Obtain a reference to the string table auto Strtab = TheELF.getSection(SymtabShdr->sh_link); if (not Strtab) { revng_log(Log, "Cannot find .strtab: " << Strtab.takeError()); return; } auto StrtabArray = TheELF.getSectionContents(**Strtab); if (not StrtabArray) { revng_log(Log, "Cannot access .strtab: " << StrtabArray.takeError()); return; } StringRef StrtabContent(reinterpret_cast(StrtabArray->data()), StrtabArray->size()); // Collect symbol names auto ELFSymbols = TheELF.symbols(SymtabShdr); if (not ELFSymbols) { revng_log(Log, "Cannot get symbols: " << ELFSymbols.takeError()); return; } for (auto &Symbol : *ELFSymbols) { auto MaybeName = expectedToOptional(Symbol.getName(StrtabContent)); bool IsCode = Symbol.getType() == ELF::STT_FUNC; if ((MaybeName and shouldIgnoreSymbol(*MaybeName)) or (Symbol.st_shndx == ELF::SHN_UNDEF)) continue; MetaAddress Address = MetaAddress::invalid(); if (IsCode) Address = relocate(fromPC(Symbol.st_value)); else Address = relocate(fromGeneric(Symbol.st_value)); if (IsCode) { auto It = Model->Functions.find(Address); if (It == Model->Functions.end()) { model::Function &Function = Model->Functions[Address]; Function.Type = model::FunctionType::Invalid; if (MaybeName) Function.OriginalName = *MaybeName; } } } } template static bool hasFlag(A Flag, B Value) { return (Flag & Value) != 0; } template void ELFImporter::parseProgramHeaders(object::ELFFile &TheELF) { // Loop over the program headers looking for PT_LOAD segments, read them out // and create a global variable for each one of them (writable or read-only), // assign them a section and output information about them in the linking info // CSV using Elf_Phdr = const typename object::ELFFile::Elf_Phdr; Elf_Phdr *DynamicPhdr = nullptr; auto ProgHeaders = TheELF.program_headers(); if (not ProgHeaders) { revng_log(Log, "Cannot access program headers: " << ProgHeaders.takeError()); return; } for (Elf_Phdr &ProgramHeader : *ProgHeaders) { switch (ProgramHeader.p_type) { case ELF::PT_LOAD: { auto Start = relocate(fromGeneric(ProgramHeader.p_vaddr)); auto EndVirtualAddress = Start + u64(ProgramHeader.p_memsz); if (Start.isInvalid() or EndVirtualAddress.isInvalid()) { revng_log(Log, "Invalid segment found"); continue; } model::Segment NewSegment({ Start, ProgramHeader.p_memsz }); NewSegment.StartOffset = ProgramHeader.p_offset; auto MaybeEndOffset = (OverflowSafeInt(u64(ProgramHeader.p_offset)) + u64(ProgramHeader.p_filesz)); if (not MaybeEndOffset) { revng_log(Log, "Invalid segment found: overflow in computing end offset"); continue; } NewSegment.FileSize = ProgramHeader.p_filesz; NewSegment.IsReadable = hasFlag(ProgramHeader.p_flags, ELF::PF_R); NewSegment.IsWriteable = hasFlag(ProgramHeader.p_flags, ELF::PF_W); NewSegment.IsExecutable = hasFlag(ProgramHeader.p_flags, ELF::PF_X); // If it's an executable segment, and we've been asked so, register // which sections actually contain code auto Sections = TheELF.sections(); if (not Sections) { logAllUnhandledErrors(std::move(Sections.takeError()), errs(), ""); } else { using Elf_Shdr = const typename object::ELFFile::Elf_Shdr; auto Inserter = NewSegment.Sections.batch_insert(); for (Elf_Shdr &SectionHeader : *Sections) { if (not hasFlag(SectionHeader.sh_flags, ELF::SHF_ALLOC)) continue; bool ContainsCode = (NewSegment.IsExecutable and hasFlag(SectionHeader.sh_flags, ELF::SHF_EXECINSTR)); auto SectionStart = relocate(fromGeneric(SectionHeader.sh_addr)); uint64_t Size = SectionHeader.sh_size; auto SectionEnd = SectionStart + Size; if (SectionStart.isValid() and SectionEnd.isValid() and SectionStart.addressLowerThan(SectionEnd) and NewSegment.contains(SectionStart, Size)) { model::Section NewSection(SectionStart, SectionHeader.sh_size); if (auto SectionName = TheELF.getSectionName(SectionHeader); SectionName) NewSection.Name = SectionName->str(); NewSection.ContainsCode = ContainsCode; NewSection.verify(true); Inserter.insert(std::move(NewSection)); } } } NewSegment.verify(true); Model->Segments.insert(std::move(NewSegment)); } break; case ELF::PT_GNU_EH_FRAME: if (not EHFrameHdrAddress) EHFrameHdrAddress = relocate(fromGeneric(ProgramHeader.p_vaddr)); else revng_log(Log, "Multiple PT_GNU_EH_FRAME. Ignoring."); break; case ELF::PT_DYNAMIC: if (DynamicPhdr != nullptr) { revng_log(Log, "Duplicate .dynamic program header"); break; } DynamicPhdr = &ProgramHeader; MetaAddress DynamicPhdrMA = relocate(fromGeneric(DynamicPhdr->p_vaddr)); if (DynamicAddress and DynamicPhdrMA != *DynamicAddress) { revng_log(Log, "Different addresses for .dynamic (" << DynamicAddress->toString() << ") and PT_DYNAMIC program header (" << DynamicPhdrMA.toString() << ")"); break; } DynamicAddress = relocate(DynamicPhdrMA); break; } } if ((DynamicPhdr != nullptr) != (DynamicAddress.hasValue())) { revng_log(Log, "Invalid .dynamic/PT_DYNAMIC"); DynamicPhdr = nullptr; DynamicAddress = {}; } } template void ELFImporter::parseDynamicSymbol(llvm::object::Elf_Sym_Impl &Symbol, StringRef Dynstr) { Expected MaybeName = Symbol.getName(Dynstr); if (not MaybeName) { auto TheError = MaybeName.takeError(); revng_log(Log, "Cannot access symbol name: " << TheError); consumeError(std::move(TheError)); return; } StringRef Name = *MaybeName; if (Name.contains('\0')) { revng_log(Log, "SymbolName contains a NUL character: \"" << Name.str() << "\""); return; } bool IsCode = Symbol.getType() == ELF::STT_FUNC; if (shouldIgnoreSymbol(Name)) return; if (Symbol.st_shndx == ELF::SHN_UNDEF) { if (IsCode) { // Create dynamic function symbol Model->ImportedDynamicFunctions[Name.str()]; } else { // TODO: create dynamic global variable } } else { MetaAddress Address = MetaAddress::invalid(); if (IsCode) { Address = relocate(fromPC(Symbol.st_value)); // TODO: record model::Function::IsDynamic = true auto It = Model->Functions.find(Address); if (It == Model->Functions.end()) { model::Function &Function = Model->Functions[Address]; Function.Type = model::FunctionType::Invalid; Function.OriginalName = Name; } } else { Address = relocate(fromGeneric(Symbol.st_value)); // TODO: create field in segment struct } } } template std::pair ELFImporter::ehFrameFromEhFrameHdr() { revng_assert(EHFrameHdrAddress); auto MaybeEHFrameHdr = File.getFromAddressOn(*EHFrameHdrAddress); if (not MaybeEHFrameHdr) { revng_log(Log, ".eh_frame_hdr section not available in any segment"); return { MetaAddress::invalid(), 0 }; } ArrayRef EHFrameHdr = *MaybeEHFrameHdr; using namespace model::Architecture; DwarfReader EHFrameHdrReader(toLLVMArchitecture(Architecture), EHFrameHdr, *EHFrameHdrAddress); uint64_t VersionNumber = EHFrameHdrReader.readNextU8(); if (VersionNumber != 1) { revng_log(Log, "Unexpected version number in .eh_frame: " << VersionNumber); return { MetaAddress::invalid(), 0 }; } // ExceptionFrameEncoding uint64_t ExceptionFrameEncoding = EHFrameHdrReader.readNextU8(); // FDEsCountEncoding unsigned FDEsCountEncoding = EHFrameHdrReader.readNextU8(); // LookupTableEncoding EHFrameHdrReader.readNextU8(); Pointer EHFramePointer = EHFrameHdrReader.readPointer(ExceptionFrameEncoding); uint64_t FDEsCount = EHFrameHdrReader.readUnsignedValue(FDEsCountEncoding); MetaAddress Address = getGenericPointer(EHFramePointer); if (Address.isInvalid()) { revng_log(Log, "Invalid address of .eh_frame in .eh_frame_hdr"); return { MetaAddress::invalid(), 0 }; } return { Address, FDEsCount }; } template void ELFImporter::parseEHFrame(MetaAddress EHFrameAddress, Optional FDEsCount, Optional EHFrameSize) { if (not FDEsCount and not EHFrameSize) { revng_log(Log, "Neither FDE count and .eh_frame size available"); return; } // Sometimes the .eh_frame section is present but not mapped in memory. This // means it cannot be used at runtime, therefore we can ignore it. auto MaybeEHFrame = File.getFromAddressOn(EHFrameAddress); if (not MaybeEHFrame) return; llvm::ArrayRef EHFrame = *MaybeEHFrame; using namespace model::Architecture; auto Architecture = toLLVMArchitecture(Model->Architecture); DwarfReader EHFrameReader(Architecture, EHFrame, EHFrameAddress); // A few fields of the CIE are used when decoding the FDE's. This struct // will cache those fields we need so that we don't have to decode it // repeatedly for each FDE that references it. struct DecodedCIE { Optional FDEPointerEncoding; Optional LSDAPointerEncoding; bool HasAugmentationLength; }; // Map from the start offset of the CIE to the cached data for that CIE. DenseMap CachedCIEs; unsigned FDEIndex = 0; while (!EHFrameReader.eof() && ((FDEsCount && FDEIndex < *FDEsCount) || (EHFrameSize && EHFrameReader.offset() < *EHFrameSize))) { uint64_t StartOffset = EHFrameReader.offset(); // Read the length of the entry uint64_t Length = EHFrameReader.readNextU32(); if (Length == 0xffffffff) Length = EHFrameReader.readNextU64(); // Compute the end offset of the entry uint64_t OffsetAfterLength = EHFrameReader.offset(); uint64_t EndOffset = OffsetAfterLength + Length; // Zero-sized entry, skip it if (Length == 0) { if (EHFrameReader.offset() != EndOffset) { revng_log(Log, ".eh_frame end was expected"); return; } continue; } // Get the entry ID, 0 means it's a CIE, otherwise it's a FDE uint32_t ID = EHFrameReader.readNextU32(); if (ID == 0) { // This is a CIE // Ensure the version is the one we expect uint32_t Version = EHFrameReader.readNextU8(); if (Version != 1) { revng_log(Log, "Unexpected version: " << Version); return; } // Parse a null terminated augmentation string SmallString<8> AugmentationString; for (uint8_t Char = EHFrameReader.readNextU8(); Char != 0; Char = EHFrameReader.readNextU8()) AugmentationString.push_back(Char); // Optionally parse the EH data if the augmentation string says it's // there if (StringRef(AugmentationString).count("eh") != 0) EHFrameReader.readNextU(); // CodeAlignmentFactor EHFrameReader.readULEB128(); // DataAlignmentFactor EHFrameReader.readULEB128(); // ReturnAddressRegister EHFrameReader.readNextU8(); Optional AugmentationLength; Optional LSDAPointerEncoding; Optional PersonalityEncoding; Optional FDEPointerEncoding; if (!AugmentationString.empty() && AugmentationString.front() == 'z') { AugmentationLength = EHFrameReader.readULEB128(); // Walk the augmentation string to get all the augmentation data. for (unsigned I = 1, E = AugmentationString.size(); I != E; ++I) { char Char = AugmentationString[I]; switch (Char) { case 'e': if (not((I + 1) != E and AugmentationString[I + 1] == 'h')) { revng_log(Log, "Expected 'eh' in augmentation string"); return; } break; case 'L': // This is the only information we really care about, all the rest // is processed just so we can get here if (not LSDAPointerEncoding) LSDAPointerEncoding = EHFrameReader.readNextU8(); else revng_log(Log, "Duplicate LSDA encoding. Ignoroing."); break; case 'P': { if (PersonalityEncoding) { revng_log(Log, "Duplicate personality. Ignoring."); break; } PersonalityEncoding = EHFrameReader.readNextU8(); // Personality Pointer Personality; Personality = EHFrameReader.readPointer(*PersonalityEncoding); auto PersonalityPtr = getCodePointer(Personality); logAddress(Log, "Personality function: ", PersonalityPtr); // Register in the model for exploration Model->ExtraCodeAddresses.insert(PersonalityPtr); break; } case 'R': if (FDEPointerEncoding) { revng_log(Log, "Duplicate FDE encoding. Ignoring."); break; } FDEPointerEncoding = EHFrameReader.readNextU8(); break; case 'z': revng_log(Log, "'z' must be first in the augmentation string"); return; } } } // Cache this entry CachedCIEs[StartOffset] = { FDEPointerEncoding, LSDAPointerEncoding, AugmentationLength.hasValue() }; } else { // This is an FDE FDEIndex++; // The CIE pointer for an FDE is the same location as the ID which we // already read uint64_t CIEOffset = OffsetAfterLength - ID; // Ensure we already met this CIE auto CIEIt = CachedCIEs.find(CIEOffset); if (CIEIt == CachedCIEs.end()) { revng_log(Log, "Couldn't find CIE at offset in to __eh_frame section"); return; } // Ensure we have at least the pointer encoding const DecodedCIE &CIE = CIEIt->getSecond(); if (not CIE.FDEPointerEncoding) { revng_log(Log, "FDE references CIE which did not set pointer encoding"); return; } // PCBegin auto PCBeginPointer = EHFrameReader.readPointer(*CIE.FDEPointerEncoding); MetaAddress PCBegin = getGenericPointer(PCBeginPointer); // PCRange EHFrameReader.readPointer(*CIE.FDEPointerEncoding); if (CIE.HasAugmentationLength) EHFrameReader.readULEB128(); // Decode the LSDA if the CIE augmentation string said we should. if (CIE.LSDAPointerEncoding) { auto LSDAPointer = EHFrameReader.readPointer(*CIE.LSDAPointerEncoding); parseLSDA(PCBegin, getGenericPointer(LSDAPointer)); } } // Skip all the remaining parts EHFrameReader.moveTo(EndOffset); } } template void ELFImporter::parseLSDA(MetaAddress FDEStart, MetaAddress LSDAAddress) { logAddress(Log, "LSDAAddress: ", LSDAAddress); auto MaybeLSDA = File.getFromAddressOn(LSDAAddress); if (not MaybeLSDA) { revng_log(Log, "LSDA not available in any segment"); return; } llvm::ArrayRef LSDA = *MaybeLSDA; using namespace model::Architecture; auto Architecture = toLLVMArchitecture(Model->Architecture); DwarfReader LSDAReader(Architecture, LSDA, LSDAAddress); uint32_t LandingPadBaseEncoding = LSDAReader.readNextU8(); MetaAddress LandingPadBase = MetaAddress::invalid(); if (LandingPadBaseEncoding != dwarf::DW_EH_PE_omit) { auto LandingPadBasePointer = LSDAReader.readPointer(LandingPadBaseEncoding); LandingPadBase = getGenericPointer(LandingPadBasePointer); } else { LandingPadBase = FDEStart; } logAddress(Log, "LandingPadBase: ", LandingPadBase); uint32_t TypeTableEncoding = LSDAReader.readNextU8(); if (TypeTableEncoding != dwarf::DW_EH_PE_omit) LSDAReader.readULEB128(); uint32_t CallSiteTableEncoding = LSDAReader.readNextU8(); uint64_t CallSiteTableLength = LSDAReader.readULEB128(); uint64_t CallSiteTableEnd = LSDAReader.offset() + CallSiteTableLength; while (LSDAReader.offset() < CallSiteTableEnd) { // InstructionStart LSDAReader.readPointer(CallSiteTableEncoding); // InstructionEnd LSDAReader.readPointer(CallSiteTableEncoding); // LandingPad Pointer LandingPadPointer = LSDAReader.readPointer(CallSiteTableEncoding, LandingPadBase); MetaAddress LandingPad = getCodePointer(LandingPadPointer); // Action LSDAReader.readULEB128(); if (LandingPad.isValid()) { auto &ExtraCodeAddresses = Model->ExtraCodeAddresses; if (ExtraCodeAddresses.count(LandingPad) == 0) logAddress(Log, "New landing pad found: ", LandingPad); ExtraCodeAddresses.insert(LandingPad); } } } template struct RelocationHelper { static uint64_t getAddend(llvm::object::Elf_Rel_Impl); }; template struct RelocationHelper { static uint64_t getAddend(llvm::object::Elf_Rel_Impl Relocation) { return Relocation.r_addend; } }; template struct RelocationHelper { static uint64_t getAddend(llvm::object::Elf_Rel_Impl) { return 0; } }; template void ELFImporter::registerRelocations(Elf_Rel_Array Relocations, const FilePortion2 &Dynsym, const FilePortion2 &Dynstr) { using namespace llvm::object; using Elf_Rel = Elf_Rel_Impl; using Elf_Sym = Elf_Sym_Impl; model::Segment *LowestSegment = nullptr; if (auto It = Model->Segments.begin(); It != Model->Segments.end()) LowestSegment = &*It; ArrayRef Symbols; if (Dynsym.isAvailable()) Symbols = Dynsym.extractAs(); for (Elf_Rel Relocation : Relocations) { auto Type = static_cast(Relocation.getType(false)); uint64_t Addend = RelocationHelper::getAddend(Relocation); MetaAddress Address = relocate(fromGeneric(Relocation.r_offset)); StringRef SymbolName; uint64_t SymbolSize = 0; unsigned char SymbolType = llvm::ELF::STT_NOTYPE; if (Dynsym.isAvailable() and Dynstr.isAvailable()) { uint32_t SymbolIndex = Relocation.getSymbol(false); if (not(SymbolIndex < Symbols.size())) { revng_log(Log, "Invalid symbol index " << SymbolIndex << ". " << "Symbol count: " << Symbols.size()); } const Elf_Sym &Symbol = Symbols[SymbolIndex]; auto MaybeName = Symbol.getName(Dynstr.extractString()); if (MaybeName) SymbolName = *MaybeName; SymbolSize = Symbol.st_size; SymbolType = Symbol.getType(); } using namespace model::RelocationType; auto RelocationType = fromELFRelocation(Model->Architecture, Type); auto RelocationName = getELFRelocationTypeName(TheBinary.getEMachine(), Type); if (RelocationType == Invalid) { revng_log(Log, "Ignoring unknown relocation: " << RelocationName); continue; } model::Relocation NewRelocation(Address, RelocationType, Addend); bool HasName = SymbolName.size() != 0; bool IsBaseRelative = isELFRelocationBaseRelative(Architecture, Type); if (HasName and IsBaseRelative) { revng_log(Log, "We found a base-relative relocation (" << RelocationName << ") associated to a symbol, ignoring."); } else if (not HasName and not IsBaseRelative) { if (Log.isEnabled()) { Log << "We found a non-base-relative relocation (" << RelocationName << ") not associated to a symbol, ignoring." << DoLog; } } else if (HasName) { // Symbol-relative relcation if (SymbolType == ELF::STT_FUNC) { auto It = Model->ImportedDynamicFunctions.find(SymbolName.str()); if (It != Model->ImportedDynamicFunctions.end()) { auto &Relocations = It->Relocations; NewRelocation.verify(true); Relocations.insert(NewRelocation); } } else { // TODO: register relocation for dynamic global variable } } else { // Base-relative relocation if (LowestSegment != nullptr) { NewRelocation.verify(true); LowestSegment->Relocations.insert(NewRelocation); } else { revng_log(Log, "Found a base-relative relocation, but no segment is " "available! Ignoring."); } } } } Error importELF(TupleTree &Model, const object::ELFObjectFileBase &TheBinary, uint64_t PreferredBaseAddress) { ELFImporter Importer(Model, TheBinary, PreferredBaseAddress); using namespace model::Architecture; bool IsLittleEndian = isLittleEndian(Model->Architecture); size_t PointerSize = getPointerSize(Model->Architecture); bool HasRelocationAddend = hasELFRelocationAddend(Model->Architecture); if (PointerSize == 4) { if (IsLittleEndian) { if (HasRelocationAddend) { return Importer.import(); } else { return Importer.import(); } } else { if (HasRelocationAddend) { return Importer.import(); } else { return Importer.import(); } } } else if (PointerSize == 8) { if (IsLittleEndian) { if (HasRelocationAddend) { return Importer.import(); } else { return Importer.import(); } } else { if (HasRelocationAddend) { return Importer.import(); } else { return Importer.import(); } } } else { revng_abort("Unexpect address size"); } }