Files
revng-revng/lib/Model/Importer/Binary/ELFImporter.cpp
2025-04-17 11:19:17 +03:00

1384 lines
48 KiB
C++

/// \file ELFImporter.cpp
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <cstdint>
#include <optional>
#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<typename A, typename B>
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<typename T>
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<const char *>(Data.data());
return StringRef(AsChar, Data.size());
}
template<typename T>
ArrayRef<T> FilePortion::extractAs() const {
auto Data = extractData();
const size_t TypeSize = sizeof(T);
if (Data.size() % TypeSize != 0)
return {};
return ArrayRef<T>(reinterpret_cast<const T *>(Data.data()),
Data.size() / TypeSize);
}
ArrayRef<uint8_t> 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<typename T>
static void logAddress(T &Logger, const char *Name, MetaAddress Address) {
if (Logger.isEnabled()) {
Logger << Name;
Address.dump(Logger);
Logger << DoLog;
}
}
template<typename T, bool HasAddend>
uint64_t symbolsCount(const FilePortion &Relocations) {
using Elf_Rel = llvm::object::Elf_Rel_Impl<T, HasAddend>;
if (not Relocations.isAvailable() or not Relocations.isExact())
return 0;
uint32_t SymbolsCount = 0;
for (Elf_Rel Relocation : Relocations.extractAs<Elf_Rel>())
SymbolsCount = std::max(SymbolsCount, Relocation.getSymbol(false) + 1);
return SymbolsCount;
}
template<typename T, bool HasAddend>
Error ELFImporter<T, HasAddend>::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<T>::create(TheBinary.getData());
if (not TheELFOrErr)
return TheELFOrErr.takeError();
object::ELFFile<T> &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<MetaAddress> EHFrameAddress;
std::optional<uint64_t> EHFrameSize;
Task.advance("Parse sections", true);
SmallVector<Section, 16> 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<uint64_t>(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<uint64_t> 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<uint64_t, 10> NeededLibraryNameOffsets;
// TODO: use std::optional
DynstrPortion = std::make_unique<FilePortion>(File);
DynsymPortion = std::make_unique<FilePortion>(File);
ReldynPortion = std::make_unique<FilePortion>(File);
RelpltPortion = std::make_unique<FilePortion>(File);
GotPortion = std::make_unique<FilePortion>(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<T>::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<T, HasAddend>(*ReldynPortion.get()),
symbolsCount<T, HasAddend>(*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<T>;
DynsymPortion->setSize(*SymbolsCount * sizeof(Elf_Sym));
ArrayRef<Elf_Sym> Symbols = DynsymPortion->extractAs<Elf_Sym>();
for (Elf_Sym Symbol : Symbols)
parseDynamicSymbol(Symbol, Dynstr);
using Elf_Rel = llvm::object::Elf_Rel_Impl<T, HasAddend>;
if (ReldynPortion->isAvailable()) {
registerRelocations(ReldynPortion->extractAs<Elf_Rel>(),
*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<Elf_Rel>(),
*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<typename T, bool HasAddend>
void ELFImporter<T, HasAddend>::findMissingTypes(object::ELFFile<T> &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<llvm::object::ObjectFile>(BinaryOrErr->getBinary());
auto *TheBinary = dyn_cast<ELFObjectFileBase>(&Object);
if (!TheBinary) {
revng_log(ELFImporterLog, "Can't parse the binary");
continue;
}
revng_assert(!ModelsOfLibraries.contains(DependencyLibrary));
TupleTree<model::Binary> &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<TypeCopier>(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<uint64_t>;
template<typename T, bool HasAddend>
void ELFImporter<T, HasAddend>::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<typename T, bool HasAddend>
void ELFImporter<T, HasAddend>::parseSymbols(object::ELFFile<T> &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<const char *>(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<typename T, bool HasAddend>
void ELFImporter<T, HasAddend>::parseSegments(ELFFile<T> &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<int64_t>::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<typename T, bool HasAddend>
void ELFImporter<T, HasAddend>::parseProgramHeaders(ELFFile<T> &TheELF) {
using Elf_Phdr = const typename object::ELFFile<T>::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<typename T, bool HasAddend>
void ELFImporter<T, HasAddend>::parseDynamicSymbol(Elf_Sym_Impl<T> &Symbol,
StringRef Dynstr) {
Expected<llvm::StringRef> 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<typename T, bool HasAddend>
std::pair<MetaAddress, uint64_t>
ELFImporter<T, HasAddend>::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<uint8_t> EHFrameHdr = *MaybeEHFrameHdr;
using namespace model::Architecture;
DwarfReader<T> 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<typename T, bool HasAddend>
void ELFImporter<T, HasAddend>::parseEHFrame(MetaAddress EHFrameAddress,
optional<uint64_t> FDEsCount,
optional<uint64_t> 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<uint8_t> EHFrame = *MaybeEHFrame;
using namespace model::Architecture;
auto Architecture = toLLVMArchitecture(Model->Architecture());
DwarfReader<T> 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<uint32_t> FDEPointerEncoding;
std::optional<uint32_t> LSDAPointerEncoding;
bool HasAugmentationLength;
};
// Map from the start offset of the CIE to the cached data for that CIE.
DenseMap<uint64_t, DecodedCIE> 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<uint64_t> AugmentationLength;
std::optional<uint32_t> LSDAPointerEncoding;
std::optional<uint32_t> PersonalityEncoding;
std::optional<uint32_t> 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<typename T, bool HasAddend>
void ELFImporter<T, HasAddend>::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<uint8_t> LSDA = *MaybeLSDA;
using namespace model::Architecture;
auto Architecture = toLLVMArchitecture(Model->Architecture());
DwarfReader<T> 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<typename T, bool HasAddend>
struct RelocationHelper {
static uint64_t getAddend(llvm::object::Elf_Rel_Impl<T, HasAddend>);
};
template<typename T>
struct RelocationHelper<T, true> {
static uint64_t getAddend(llvm::object::Elf_Rel_Impl<T, true> Relocation) {
return Relocation.r_addend;
}
};
template<typename T>
struct RelocationHelper<T, false> {
static uint64_t getAddend(llvm::object::Elf_Rel_Impl<T, false>) { return 0; }
};
template<typename T, bool HasAddend>
void ELFImporter<T, HasAddend>::registerRelocations(Elf_Rel_Array Relocations,
const FilePortion &Dynsym,
const FilePortion &Dynstr) {
using namespace llvm::object;
using Elf_Rel = Elf_Rel_Impl<T, HasAddend>;
using Elf_Sym = Elf_Sym_Impl<T>;
model::Segment *LowestSegment = nullptr;
if (auto It = Model->Segments().begin(); It != Model->Segments().end())
LowestSegment = &*It;
ArrayRef<Elf_Sym> Symbols;
if (Dynsym.isAvailable())
Symbols = Dynsym.extractAs<Elf_Sym>();
for (Elf_Rel Relocation : Relocations) {
auto Type = static_cast<unsigned char>(Relocation.getType(false));
uint64_t Addend = RelocationHelper<T, HasAddend>::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<ELFImporterBase>
createELFImporter(TupleTree<model::Binary> &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<ELFImporter<ELF32LE, true>>(M, TheBinary, BaseAddress);
} else if (IsLittleEndian && HasRelocationAddend && IsMIPS) {
return make_unique<MIPSELFImporter<ELF32LE, true>>(M,
TheBinary,
BaseAddress);
} else if (IsLittleEndian && !HasRelocationAddend && !IsMIPS) {
return make_unique<ELFImporter<ELF32LE, false>>(M,
TheBinary,
BaseAddress);
} else if (IsLittleEndian && !HasRelocationAddend && IsMIPS) {
return make_unique<MIPSELFImporter<ELF32LE, false>>(M,
TheBinary,
BaseAddress);
} else if (!IsLittleEndian && HasRelocationAddend && !IsMIPS) {
return make_unique<ELFImporter<ELF32BE, true>>(M, TheBinary, BaseAddress);
} else if (!IsLittleEndian && HasRelocationAddend && IsMIPS) {
return make_unique<MIPSELFImporter<ELF32BE, true>>(M,
TheBinary,
BaseAddress);
} else if (!IsLittleEndian && !HasRelocationAddend && !IsMIPS) {
return make_unique<ELFImporter<ELF32BE, false>>(M,
TheBinary,
BaseAddress);
} else if (!IsLittleEndian && !HasRelocationAddend && IsMIPS) {
return make_unique<MIPSELFImporter<ELF32BE, false>>(M,
TheBinary,
BaseAddress);
}
} else if (PointerSize == 8) {
if (IsLittleEndian && HasRelocationAddend && !IsMIPS) {
return make_unique<ELFImporter<ELF64LE, true>>(M, TheBinary, BaseAddress);
} else if (IsLittleEndian && HasRelocationAddend && IsMIPS) {
return make_unique<MIPSELFImporter<ELF64LE, true>>(M,
TheBinary,
BaseAddress);
} else if (IsLittleEndian && !HasRelocationAddend && !IsMIPS) {
return make_unique<ELFImporter<ELF64LE, false>>(M,
TheBinary,
BaseAddress);
} else if (IsLittleEndian && !HasRelocationAddend && IsMIPS) {
return make_unique<MIPSELFImporter<ELF64LE, false>>(M,
TheBinary,
BaseAddress);
} else if (!IsLittleEndian && HasRelocationAddend && !IsMIPS) {
return make_unique<ELFImporter<ELF64BE, true>>(M, TheBinary, BaseAddress);
} else if (!IsLittleEndian && HasRelocationAddend && IsMIPS) {
return make_unique<MIPSELFImporter<ELF64BE, true>>(M,
TheBinary,
BaseAddress);
} else if (!IsLittleEndian && !HasRelocationAddend && !IsMIPS) {
return make_unique<ELFImporter<ELF64BE, false>>(M,
TheBinary,
BaseAddress);
} else if (!IsLittleEndian && !HasRelocationAddend && IsMIPS) {
return make_unique<MIPSELFImporter<ELF64BE, false>>(M,
TheBinary,
BaseAddress);
}
}
revng_abort("Unexpected address size");
}
Error importELF(TupleTree<model::Binary> &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);
}