/// \file ELFImporter.cpp // // 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 "llvm/Support/Error.h" #include "llvm/Support/Progress.h" #include "revng/ABI/DefaultFunctionPrototype.h" #include "revng/Model/Binary.h" #include "revng/Model/IRHelpers.h" #include "revng/Model/Importer/Binary/BinaryImporterHelper.h" #include "revng/Model/Importer/Binary/Options.h" #include "revng/Model/Importer/DebugInfo/DwarfImporter.h" #include "revng/Model/Pass/AllPasses.h" #include "revng/Model/RawBinaryView.h" #include "revng/Support/CommandLine.h" #include "revng/Support/Debug.h" #include "revng/Support/LDDTree.h" #include "CrossModelFindTypeHelper.h" #include "DwarfReader.h" #include "ELFImporter.h" #include "Importers.h" #include "MIPSELFImporter.h" using namespace llvm; using namespace llvm::object; Logger<> ELFImporterLog("elf-importer"); template static bool hasFlag(A Flag, B Value) { return (Flag & Value) != 0; } FilePortion::FilePortion(const RawBinaryView &File) : File(File), HasAddress(false), HasSize(false), Size(0), Address(MetaAddress::invalid()) { } void FilePortion::setAddress(MetaAddress Address) { HasAddress = true; this->Address = Address; } void FilePortion::setSize(uint64_t Size) { HasSize = true; this->Size = Size; } MetaAddress FilePortion::addressAtOffset(uint64_t Offset) { if (not(HasAddress and HasSize and Offset <= Size)) return MetaAddress::invalid(); return Address + Offset; } template MetaAddress FilePortion::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 FilePortion::isAvailable() const { return HasAddress; } bool FilePortion::isExact() const { revng_assert(HasAddress); return HasSize; } StringRef FilePortion::extractString() const { auto Data = extractData(); const char *AsChar = reinterpret_cast(Data.data()); return StringRef(AsChar, Data.size()); } template ArrayRef FilePortion::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 FilePortion::extractData() const { revng_assert(HasAddress); if (HasSize) { auto MaybeData = File.getByAddress(Address, Size); if (MaybeData) { return *MaybeData; } else { revng_log(ELFImporterLog, "Cannot access address " << Address.toString() << " and size " << Size); return {}; } } else { auto MaybeData = File.getFromAddressOn(Address); if (MaybeData) { return *MaybeData; } else { revng_log(ELFImporterLog, "Cannot access address " << Address.toString()); return {}; } } } 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; } } template uint64_t symbolsCount(const FilePortion &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; } template Error ELFImporter::import(const ImporterOptions &Options) { revng_log(ELFImporterLog, "Starting ELF import"); llvm::Task Task(13, "Import ELF"); Task.advance("Parse ELF", true); // Parse the ELF file auto TheELFOrErr = object::ELFFile::create(TheBinary.getData()); if (not TheELFOrErr) return TheELFOrErr.takeError(); object::ELFFile &TheELF = *TheELFOrErr; // Parse segments Task.advance("Parse segments", true); parseSegments(TheELF); // Set default ABI if (Model->DefaultABI() == model::ABI::Invalid) { revng_assert(Model->Architecture() != model::Architecture::Invalid); if (auto ABI = model::ABI::getDefaultForELF(Model->Architecture())) { Model->DefaultABI() = ABI.value(); } else { auto ArchName = model::Architecture::getName(Model->Architecture()).str(); return revng::createError("Unsupported architecture for ELF: " + ArchName); } } // BaseAddress makes sense only for shared (relocatable, PIC) objects auto Type = TheELF.getHeader().e_type; ImporterOptions AdjustedOptions = ImporterOptions{ .BaseAddress = Options.BaseAddress, .DebugInfo = Options.DebugInfo, .EnableRemoteDebugInfo = Options.EnableRemoteDebugInfo, .AdditionalDebugInfoPaths = Options.AdditionalDebugInfoPaths }; if (not(Type == ELF::ET_DYN or Type == ELF::ET_EXEC)) return revng::createError("Only ELF executables and ELF dynamic libraries " "are supported"); // Look for static or dynamic symbols and relocations ConstElf_Shdr *SymtabShdr = nullptr; std::optional EHFrameAddress; std::optional EHFrameSize; Task.advance("Parse sections", true); SmallVector Sections; auto ELFSections = TheELF.sections(); if (auto Error = ELFSections.takeError()) { revng_log(ELFImporterLog, "Sections unavailable: " << Error); llvm::consumeError(std::move(Error)); } else { for (ConstElf_Shdr &SectionHeader : *ELFSections) { // Obtain the section name StringRef SectionName; auto MaybeSectionName = TheELF.getSectionName(SectionHeader); if (auto Error = MaybeSectionName.takeError()) { consumeError(std::move(Error)); } else { SectionName = *MaybeSectionName; } // Collect section names if (hasFlag(SectionHeader.sh_flags, ELF::SHF_ALLOC)) { bool CanContainCode = hasFlag(SectionHeader.sh_flags, ELF::SHF_EXECINSTR); auto SectionStart = relocate(fromGeneric(SectionHeader.sh_addr)); uint64_t Size = SectionHeader.sh_size; auto SectionEnd = SectionStart + Size; // Note: we will discard overlapping sections later on, in // populateSegmentTypeStruct if (SectionStart.isValid() and SectionEnd.isValid() and SectionStart.addressLowerThan(SectionEnd)) { Section NewSection(SectionStart, SectionHeader.sh_size); NewSection.Name = SectionName.str(); NewSection.CanContainCode = CanContainCode; Sections.push_back(std::move(NewSection)); } } // Handle well-known sections if (SectionName == ".symtab") { // TODO: check dedicated field in section header if (SymtabShdr == nullptr) SymtabShdr = &SectionHeader; else revng_log(ELFImporterLog, "Multiple .symtab. Ignoring."); } else if (SectionName == ".eh_frame") { if (not EHFrameAddress) { EHFrameAddress = relocate(fromGeneric(SectionHeader.sh_addr)); EHFrameSize = static_cast(SectionHeader.sh_size); } else { revng_log(ELFImporterLog, "Duplicate .eh_frame. Ignoring."); } } else if (SectionName == ".dynamic") { if (not DynamicAddress) DynamicAddress = relocate(fromGeneric(SectionHeader.sh_addr)); else revng_log(ELFImporterLog, "Duplicate .dynamic. Ignoring."); } } } Task.advance("Parse static symbols", true); parseSymbols(TheELF, SymtabShdr); const auto &ElfHeader = TheELF.getHeader(); if (ElfHeader.e_entry != 0) setEntryPoint(relocate(fromPC(ElfHeader.e_entry))); // Parse program headers Task.advance("Parse program headers", true); parseProgramHeaders(TheELF); std::optional FDEsCount; if (EHFrameHdrAddress) { MetaAddress Address = MetaAddress::invalid(); std::tie(Address, FDEsCount) = ehFrameFromEhFrameHdr(); if (Address.isValid()) { if (EHFrameAddress and *EHFrameAddress != Address) { revng_log(ELFImporterLog, "Incoherent .eh_frame information: .eh_frame is at " << EHFrameAddress->toString() << " while .eh_frame_hdr reports " << Address.toString()); } EHFrameAddress = Address; } } Task.advance("Parse .eh_frame", true); if (EHFrameAddress and EHFrameAddress->isValid()) parseEHFrame(*EHFrameAddress, FDEsCount, EHFrameSize); // Parse the .dynamic table Task.advance("Parse .dynamic", true); auto DynamicEntries = TheELF.dynamicEntries(); if (auto Error = DynamicEntries.takeError()) { revng_log(ELFImporterLog, "Cannot access dynamic entries: " << Error); consumeError(std::move(Error)); } else { SmallVector NeededLibraryNameOffsets; // TODO: use std::optional DynstrPortion = std::make_unique(File); DynsymPortion = std::make_unique(File); ReldynPortion = std::make_unique(File); RelpltPortion = std::make_unique(File); GotPortion = std::make_unique(File); bool IsX86 = Model->Architecture() == model::Architecture::x86; 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) { parseDynamicTag(DynamicTag.getTag(), DynamicTag.getVal(), DynamicTag.getPtr(), NeededLibraryNameOffsets); } 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.get()), symbolsCount(*RelpltPortion.get())); } // Collect function addresses contained in dynamic symbols if (SymbolsCount and *SymbolsCount > 0 and DynsymPortion->isAvailable()) { Task.advance("Parse dynamic symbols", true); 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.get(), *DynstrPortion.get()); } auto SetCanonicalValue = [this](model::Register::Values Register, uint64_t Value) { for (model::Segment &Segment : Model->Segments()) if (Segment.IsExecutable()) Segment.CanonicalRegisterValues()[Register].Value() = Value; }; if (GotPortion->isAvailable()) { if (IsX86) { SetCanonicalValue(model::Register::ebx_x86, GotPortion->address().address()); } else if (IsMIPS) { SetCanonicalValue(model::Register::gp_mips, GotPortion->address().address() + 0x7ff0); } } if (RelpltPortion->isAvailable()) { registerRelocations(RelpltPortion->extractAs(), *DynsymPortion.get(), *DynstrPortion.get()); } } } // Dynamic symbols harvested too, segment type creation can be finalized. // Do not replace it, if `Type` is present (may have been added by the user). Task.advance("Parse segment struct from data symbols", true); for (auto &Segment : Model->Segments()) { if (Segment.Type().isEmpty()) { Segment.Type() = populateSegmentTypeStruct(*Model, Segment, DataSymbols, Sections, Segment.IsExecutable()); } } // Create a default prototype auto &Ptr = *Model.get(); Model->DefaultPrototype() = abi::registerDefaultFunctionPrototype(Ptr); if (AdjustedOptions.DebugInfo != DebugInfoLevel::No) { Task.advance("Parse debug info", true); // Import Dwarf DwarfImporter Importer(Model); Importer.import(TheBinary.getFileName(), AdjustedOptions); // Now we try to find missing types in the dependencies. Task.advance("Find missing types from debug info", true); findMissingTypes(TheELF, AdjustedOptions); } Task.advance("Flatten primitive typedefs", true); model::flattenPrimitiveTypedefs(Model); Task.advance("Deduplicate colliding names", true); model::deduplicateCollidingNames(Model); return Error::success(); } template void ELFImporter::findMissingTypes(object::ELFFile &TheELF, const ImporterOptions &Opts) { if (Opts.DebugInfo != DebugInfoLevel::Yes) return; ModelMap ModelsOfLibraries; TypeCopierMap TypeCopiers; // TODO: disclose a way to modify this value with // the `ImporterOptions::DebugInfo`, if the need ever arises. unsigned MaximumRecursionDepth = 1; LDDTree Dependencies; const std::string &BinaryPath = not InputPath.empty() ? InputPath : TheBinary.getFileName().str(); lddtree(Dependencies, BinaryPath, MaximumRecursionDepth); for (auto &Library : Dependencies) { revng_log(ELFImporterLog, "Importing Models for dependencies of " << Library.first << ":"); for (auto &DependencyLibrary : Library.second) { if (ModelsOfLibraries.contains(DependencyLibrary)) continue; revng_log(ELFImporterLog, " Importing Model for: " << DependencyLibrary); auto BinaryOrErr = llvm::object::createBinary(DependencyLibrary); if (auto Error = BinaryOrErr.takeError()) { revng_log(ELFImporterLog, "Can't create object for " << DependencyLibrary << " due to " << Error); llvm::consumeError(std::move(Error)); continue; } auto &Object = *cast(BinaryOrErr->getBinary()); auto *TheBinary = dyn_cast(&Object); if (!TheBinary) { revng_log(ELFImporterLog, "Can't parse the binary"); continue; } revng_assert(!ModelsOfLibraries.contains(DependencyLibrary)); TupleTree &DepModel = ModelsOfLibraries[DependencyLibrary]; DepModel->Architecture() = Model->Architecture(); ImporterOptions AdjustedOptions{ .BaseAddress = Opts.BaseAddress, .DebugInfo = DebugInfoLevel::IgnoreLibraries, .EnableRemoteDebugInfo = Opts.EnableRemoteDebugInfo, .AdditionalDebugInfoPaths = Opts.AdditionalDebugInfoPaths }; if (auto E = importELF(DepModel, *TheBinary, AdjustedOptions)) { revng_log(ELFImporterLog, "Can't import model for " << DependencyLibrary << " due to " << E); llvm::consumeError(std::move(E)); ModelsOfLibraries.erase(DependencyLibrary); continue; } } } auto GetOrMakeACopier = [&](llvm::StringRef Name) -> TypeCopier & { if (auto It = TypeCopiers.find(Name.str()); It != TypeCopiers.end()) return *It->second; auto Iterator = ModelsOfLibraries.find(Name.str()); revng_assert(Iterator != ModelsOfLibraries.end()); auto NewCopier = std::make_unique(Iterator->second, Model); auto &&[Result, Success] = TypeCopiers.emplace(Name.str(), std::move(NewCopier)); revng_assert(Success); return *Result->second; }; for (auto &Fn : Model->ImportedDynamicFunctions()) { if (not Fn.Prototype().isEmpty() or Fn.Name().size() == 0) continue; if (auto Found = findPrototype(Fn.Name(), ModelsOfLibraries)) { revng_assert(!Found->ModuleName.empty()); revng_assert(Found->Prototype.verify(true)); model::UpcastableTypeDefinition SerializablePrototype = Found->Prototype; revng_log(ELFImporterLog, "Found type for " << Fn.Name() << " in " << Found->ModuleName << ": " << toString(SerializablePrototype)); TypeCopier &TheTypeCopier = GetOrMakeACopier(Found->ModuleName); Fn.Prototype() = TheTypeCopier.copyTypeInto(Found->Prototype); // Copy all the Attributes except for `Inline`. for (auto &Attribute : Found->Attributes) if (Attribute != model::FunctionAttribute::Inline) Fn.Attributes().insert(Attribute); } else { revng_log(ELFImporterLog, "Prototype for " << Fn.Name() << " not found"); } } // Finalize the copies for (auto &[_, TC] : TypeCopiers) TC->finalize(); // Purge cached references and update the reference to Root. Model.evictCachedReferences(); Model.initializeReferences(); model::flattenPrimitiveTypedefs(Model); deduplicateEquivalentTypes(Model); model::deduplicateCollidingNames(Model); } using Libs = SmallVectorImpl; template void ELFImporter::parseDynamicTag(uint64_t Tag, uint64_t Val, uint64_t Pointer, Libs &LibrariesOffsets) { MetaAddress GenericAddress = relocate(fromGeneric(Pointer)); MetaAddress PCAddress = relocate(fromPC(Pointer)); switch (Tag) { case ELF::DT_NEEDED: LibrariesOffsets.push_back(Val); break; case ELF::DT_STRTAB: DynstrPortion->setAddress(GenericAddress); break; case ELF::DT_STRSZ: DynstrPortion->setSize(Val); break; case ELF::DT_SYMTAB: DynsymPortion->setAddress(GenericAddress); break; case ELF::DT_JMPREL: RelpltPortion->setAddress(GenericAddress); break; case ELF::DT_PLTRELSZ: RelpltPortion->setSize(Val); break; case ELF::DT_REL: case ELF::DT_RELA: if (Tag != (HasAddend ? ELF::DT_RELA : ELF::DT_REL)) { if (Tag == ELF::DT_RELA) revng_log(ELFImporterLog, "Unexpected addend in relocation"); else revng_log(ELFImporterLog, "Addend was expected in relocation"); } ReldynPortion->setAddress(GenericAddress); break; case ELF::DT_RELSZ: case ELF::DT_RELASZ: if (Tag != (HasAddend ? ELF::DT_RELASZ : ELF::DT_RELSZ)) { if (Tag == ELF::DT_RELASZ) revng_log(ELFImporterLog, "Unexpected addend in relocation"); else revng_log(ELFImporterLog, "Addend was expected in relocation"); } ReldynPortion->setSize(Val); break; case ELF::DT_PLTGOT: GotPortion->setAddress(GenericAddress); break; case ELF::DT_INIT: case ELF::DT_FINI: revng_assert(PCAddress.isValid()); registerFunctionEntry(PCAddress); break; default: parseTargetDynamicTags(Tag, GenericAddress, LibrariesOffsets, Val); break; } } 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 (auto Error = Strtab.takeError()) { revng_log(ELFImporterLog, "Cannot find .strtab: " << Error); consumeError(std::move(Error)); return; } auto StrtabArray = TheELF.getSectionContents(**Strtab); if (auto Error = StrtabArray.takeError()) { revng_log(ELFImporterLog, "Cannot access .strtab: " << Error); consumeError(std::move(Error)); return; } StringRef StrtabContent(reinterpret_cast(StrtabArray->data()), StrtabArray->size()); // Collect symbol names auto ELFSymbols = TheELF.symbols(SymtabShdr); if (auto Error = ELFSymbols.takeError()) { revng_log(ELFImporterLog, "Cannot get symbols: " << Error); consumeError(std::move(Error)); return; } for (auto &Symbol : *ELFSymbols) { auto MaybeName = expectedToOptional(Symbol.getName(StrtabContent)); if ((MaybeName and shouldIgnoreSymbol(*MaybeName)) or (Symbol.st_shndx == ELF::SHN_UNDEF)) continue; MetaAddress Address = MetaAddress::invalid(); bool IsCode = Symbol.getType() == ELF::STT_FUNC; bool IsDataObject = Symbol.getType() == ELF::STT_OBJECT; uint64_t Size = Symbol.st_size; if (IsCode) Address = relocate(fromPC(Symbol.st_value)); else Address = relocate(fromGeneric(Symbol.st_value)); if (IsCode) { revng_assert(Address.isValid()); if (Model->Functions().tryGet(Address) == nullptr) { auto *Function = registerFunctionEntry(Address); if (Function != nullptr and MaybeName and MaybeName->size() > 0) { Function->Name() = *MaybeName; // Insert Original name into exported ones, since it is by default // true. Function->ExportedNames().insert((*MaybeName).str()); } } } else if (IsDataObject and Size > 0) { auto IsSameAddress = [Address](const auto &E) { return Address == E.Address; }; if (llvm::count_if(DataSymbols, IsSameAddress) == 0) DataSymbols.emplace_back(Address, Size, *MaybeName); } } } template void ELFImporter::parseSegments(ELFFile &TheELF) { auto ProgHeaders = TheELF.program_headers(); if (auto Error = ProgHeaders.takeError()) { revng_log(ELFImporterLog, "Cannot access program headers: " << Error); consumeError(std::move(Error)); return; } for (auto &ProgramHeader : *ProgHeaders) { if (ProgramHeader.p_type == 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(ELFImporterLog, "Invalid segment found"); continue; } auto VirtualSize = ProgramHeader.p_memsz; if (VirtualSize == 0) { revng_log(ELFImporterLog, "Ignoring zero-sized segment"); continue; } if (VirtualSize >= std::numeric_limits::max()) { revng_log(ELFImporterLog, "Ignoring too large segment: " << VirtualSize << " bytes"); 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(ELFImporterLog, "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); NewSegment.verify(true); Model->Segments().insert(std::move(NewSegment)); } } processSegments(); } template void ELFImporter::parseProgramHeaders(ELFFile &TheELF) { using Elf_Phdr = const typename object::ELFFile::Elf_Phdr; Elf_Phdr *DynamicPhdr = nullptr; auto ProgHeaders = TheELF.program_headers(); if (auto Error = ProgHeaders.takeError()) { revng_log(ELFImporterLog, "Cannot access program headers: " << Error); consumeError(std::move(Error)); return; } for (Elf_Phdr &ProgramHeader : *ProgHeaders) { switch (ProgramHeader.p_type) { case ELF::PT_LOAD: { // Already processed } break; case ELF::PT_GNU_EH_FRAME: if (not EHFrameHdrAddress) EHFrameHdrAddress = relocate(fromGeneric(ProgramHeader.p_vaddr)); else revng_log(ELFImporterLog, "Multiple PT_GNU_EH_FRAME. Ignoring."); break; case ELF::PT_DYNAMIC: if (DynamicPhdr != nullptr) { revng_log(ELFImporterLog, "Duplicate .dynamic program header"); break; } DynamicPhdr = &ProgramHeader; MetaAddress DynamicPhdrMA = relocate(fromGeneric(DynamicPhdr->p_vaddr)); if (DynamicAddress and DynamicPhdrMA != *DynamicAddress) { revng_log(ELFImporterLog, "Different addresses for .dynamic (" << DynamicAddress->toString() << ") and PT_DYNAMIC program header (" << DynamicPhdrMA.toString() << ")"); break; } DynamicAddress = relocate(DynamicPhdrMA); break; } } if ((DynamicPhdr != nullptr) != (DynamicAddress.has_value())) { revng_log(ELFImporterLog, "Invalid .dynamic/PT_DYNAMIC"); DynamicPhdr = nullptr; DynamicAddress = {}; } } template void ELFImporter::parseDynamicSymbol(Elf_Sym_Impl &Symbol, StringRef Dynstr) { Expected MaybeName = Symbol.getName(Dynstr); if (auto TheError = MaybeName.takeError()) { revng_log(ELFImporterLog, "Cannot access symbol name: " << TheError); consumeError(std::move(TheError)); return; } StringRef Name = *MaybeName; if (Name.contains('\0')) { revng_log(ELFImporterLog, "SymbolName contains a NUL character: \"" << Name.str() << "\""); return; } bool IsCode = Symbol.getType() == ELF::STT_FUNC; bool IsDataObject = Symbol.getType() == ELF::STT_OBJECT; 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(); uint64_t Size = Symbol.st_size; if (IsCode) { Address = relocate(fromPC(Symbol.st_value)); // TODO: record model::Function::IsDynamic = true model::Function *Function = nullptr; revng_assert(Address.isValid()); auto It = Model->Functions().find(Address); if (It != Model->Functions().end()) { Function = &*It; } else { Function = registerFunctionEntry(Address); if (Function != nullptr) Function->Name() = Name; } if (Function != nullptr and Name.size() > 0) Function->ExportedNames().insert(Name.str()); } else { Address = relocate(fromGeneric(Symbol.st_value)); if (not llvm::is_contained(DataSymbols, DataSymbol{ Address, Size, Name })) if (IsDataObject and Size > 0) DataSymbols.emplace_back(Address, Size, Name); } } } template std::pair ELFImporter::ehFrameFromEhFrameHdr() { revng_assert(EHFrameHdrAddress); auto MaybeEHFrameHdr = File.getFromAddressOn(*EHFrameHdrAddress); if (not MaybeEHFrameHdr) { revng_log(ELFImporterLog, ".eh_frame_hdr section not available in any segment"); return { MetaAddress::invalid(), 0 }; } ArrayRef EHFrameHdr = *MaybeEHFrameHdr; using namespace model::Architecture; DwarfReader EHFrameHdrReader(toLLVMArchitecture(Binary.Architecture()), EHFrameHdr, *EHFrameHdrAddress); uint64_t VersionNumber = EHFrameHdrReader.readNextU8(); if (VersionNumber != 1) { revng_log(ELFImporterLog, "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); auto MaybeFDEsCount = EHFrameHdrReader.readUnsignedValue(FDEsCountEncoding); if (not MaybeFDEsCount) { revng_log(ELFImporterLog, "FDE count unavailable in .eh_frame_hdr"); return { MetaAddress::invalid(), 0 }; } MetaAddress Address = getGenericPointer(EHFramePointer); if (Address.isInvalid()) { revng_log(ELFImporterLog, "Invalid address of .eh_frame in .eh_frame_hdr"); return { MetaAddress::invalid(), 0 }; } return { Address, *MaybeFDEsCount }; } template void ELFImporter::parseEHFrame(MetaAddress EHFrameAddress, optional FDEsCount, optional EHFrameSize) { if (not FDEsCount and not EHFrameSize) { revng_log(ELFImporterLog, "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 { std::optional FDEPointerEncoding; std::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(ELFImporterLog, ".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(ELFImporterLog, "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).contains("eh")) EHFrameReader.readNextU(); // CodeAlignmentFactor EHFrameReader.readULEB128(); // DataAlignmentFactor EHFrameReader.readULEB128(); // ReturnAddressRegister EHFrameReader.readNextU8(); std::optional AugmentationLength; std::optional LSDAPointerEncoding; std::optional PersonalityEncoding; std::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(ELFImporterLog, "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(ELFImporterLog, "Duplicate LSDA encoding. Ignoroing."); break; case 'P': { if (PersonalityEncoding) { revng_log(ELFImporterLog, "Duplicate personality. Ignoring."); break; } PersonalityEncoding = EHFrameReader.readNextU8(); // Personality Pointer Personality; Personality = EHFrameReader.readPointer(*PersonalityEncoding); auto PersonalityPtr = getCodePointer(Personality); logAddress(ELFImporterLog, "Personality function: ", PersonalityPtr); // Register in the model for exploration registerExtraCodeAddress(PersonalityPtr); break; } case 'R': if (FDEPointerEncoding) { revng_log(ELFImporterLog, "Duplicate FDE encoding. Ignoring."); break; } FDEPointerEncoding = EHFrameReader.readNextU8(); break; case 'z': revng_log(ELFImporterLog, "'z' must be first in the augmentation string"); return; } } } // Cache this entry CachedCIEs[StartOffset] = { FDEPointerEncoding, LSDAPointerEncoding, AugmentationLength.has_value() }; } 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(ELFImporterLog, "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(ELFImporterLog, "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(ELFImporterLog, "LSDAAddress: ", LSDAAddress); auto MaybeLSDA = File.getFromAddressOn(LSDAAddress); if (not MaybeLSDA) { revng_log(ELFImporterLog, "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(ELFImporterLog, "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 = this->getCodePointer(LandingPadPointer); // Action LSDAReader.readULEB128(); if (LandingPad.isValid()) registerExtraCodeAddress(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 FilePortion &Dynsym, const FilePortion &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; 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(ELFImporterLog, "Invalid symbol index " << SymbolIndex << ". " << "Symbol count: " << Symbols.size()); } const Elf_Sym &Symbol = Symbols[SymbolIndex]; auto MaybeName = Symbol.getName(Dynstr.extractString()); if (auto Error = MaybeName.takeError()) { consumeError(std::move(Error)); } else { SymbolName = *MaybeName; } SymbolType = Symbol.getType(); } using namespace model::RelocationType; auto RelocationType = fromELFRelocation(Model->Architecture(), Type); auto RelocationName = getELFRelocationTypeName(TheBinary.getEMachine(), Type); if (RelocationType == Invalid) { revng_log(ELFImporterLog, "Ignoring unknown relocation: " << RelocationName); continue; } model::Relocation NewRelocation(Address, RelocationType, Addend); bool HasName = SymbolName.size() != 0; bool IsBaseRelative = isELFRelocationBaseRelative(Model->Architecture(), Type); if (HasName and IsBaseRelative) { revng_log(ELFImporterLog, "We found a base-relative relocation (" << RelocationName << ") associated to a symbol, ignoring."); } else if (not HasName and not IsBaseRelative) { if (ELFImporterLog.isEnabled()) { ELFImporterLog << "We found a non-base-relative relocation (" << RelocationName << ") not associated to a symbol, ignoring." << DoLog; } } else if (HasName) { // Symbol-relative relocation 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(ELFImporterLog, "Found a base-relative relocation, but no segment is " "available! Ignoring."); } } } } static std::unique_ptr createELFImporter(TupleTree &M, const object::ELFObjectFileBase &TheBinary, bool IsLittleEndian, uint64_t PointerSize, bool HasRelocationAddend, uint64_t BaseAddress) { if (TheBinary.getEType() != ELF::ET_DYN) BaseAddress = 0; // In the case of MIPS architecture, we handle some specific import // as a part of a separate derived (from ELFImporter) class. // TODO: Investigate other architectures as well. bool IsMIPS = (M->Architecture() == model::Architecture::mips or M->Architecture() == model::Architecture::mipsel); if (PointerSize == 4) { if (IsLittleEndian && HasRelocationAddend && !IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } else if (IsLittleEndian && HasRelocationAddend && IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } else if (IsLittleEndian && !HasRelocationAddend && !IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } else if (IsLittleEndian && !HasRelocationAddend && IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } else if (!IsLittleEndian && HasRelocationAddend && !IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } else if (!IsLittleEndian && HasRelocationAddend && IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } else if (!IsLittleEndian && !HasRelocationAddend && !IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } else if (!IsLittleEndian && !HasRelocationAddend && IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } } else if (PointerSize == 8) { if (IsLittleEndian && HasRelocationAddend && !IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } else if (IsLittleEndian && HasRelocationAddend && IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } else if (IsLittleEndian && !HasRelocationAddend && !IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } else if (IsLittleEndian && !HasRelocationAddend && IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } else if (!IsLittleEndian && HasRelocationAddend && !IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } else if (!IsLittleEndian && HasRelocationAddend && IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } else if (!IsLittleEndian && !HasRelocationAddend && !IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } else if (!IsLittleEndian && !HasRelocationAddend && IsMIPS) { return make_unique>(M, TheBinary, BaseAddress); } } revng_abort("Unexpected address size"); } Error importELF(TupleTree &Model, const object::ELFObjectFileBase &TheBinary, const ImporterOptions &Options) { // In the case of MIPS architecture, we handle some specific import // as a part of a separate derived (from ELFImporter) class. // TODO: Investigate other architectures as well. bool IsMIPS = (Model->Architecture() == model::Architecture::mips or Model->Architecture() == model::Architecture::mipsel); using namespace model::Architecture; bool IsLittleEndian = isLittleEndian(Model->Architecture()); uint64_t PointerSize = getPointerSize(Model->Architecture()); bool HasRelocationAddend = hasELFRelocationAddend(Model->Architecture()); auto Importer = createELFImporter(Model, TheBinary, IsLittleEndian, PointerSize, HasRelocationAddend, Options.BaseAddress); return Importer->import(Options); }