Files
revng-revng/binaryfile.cpp
T
2018-09-21 20:05:18 +02:00

985 lines
32 KiB
C++

/// \file binaryfile.cpp
/// \brief
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
// Standard includes
#include <string>
#include <tuple>
#include <utility>
// LLVM includes
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Object/ELF.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/ELF.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/LEB128.h"
// Local includes
#include "binaryfile.h"
#include "debug.h"
// using directives
using namespace llvm;
using std::make_pair;
BinaryFile::BinaryFile(std::string FilePath,
bool UseSections,
uint64_t BaseAddress) : BaseAddress(0) {
auto BinaryOrErr = object::createBinary(FilePath);
assert(BinaryOrErr && "Couldn't open the input file");
BinaryHandle = std::move(BinaryOrErr.get());
auto *TheBinary = cast<object::ObjectFile>(BinaryHandle.getBinary());
// TODO: QEMU should provide this information
unsigned InstructionAlignment = 0;
StringRef SyscallHelper = "";
StringRef SyscallNumberRegister = "";
StringRef StackPointerRegister = "";
ArrayRef<uint64_t> NoReturnSyscalls = { };
SmallVector<ABIRegister, 20> ABIRegisters;
unsigned DelaySlotSize = 0;
unsigned PCMContextIndex = ABIRegister::NotInMContext;
llvm::StringRef WriteRegisterAsm = "";
llvm::StringRef ReadRegisterAsm = "";
llvm::StringRef JumpAsm = "";
bool HasRelocationAddend = false;
uint32_t BaseRelativeRelocation = ~uint32_t(0);
switch (TheBinary->getArch()) {
case Triple::x86:
InstructionAlignment = 1;
SyscallHelper = "helper_raise_interrupt";
SyscallNumberRegister = "eax";
StackPointerRegister = "esp";
NoReturnSyscalls = {
0xfc, // exit_group
0x01, // exit
0x0b // execve
};
HasRelocationAddend = false;
BaseRelativeRelocation = llvm::ELF::R_386_RELATIVE;
break;
case Triple::x86_64:
InstructionAlignment = 1;
SyscallHelper = "helper_syscall";
SyscallNumberRegister = "rax";
StackPointerRegister = "rsp";
NoReturnSyscalls = {
0xe7, // exit_group
0x3c, // exit
0x3b // execve
};
PCMContextIndex = 0x10;
// The offsets associated to the registers have been obtained running the
// following command:
//
// scripts/compile-time-constants.py gcc ucontext.c
//
// where `ucontext.c` is:
//
// #define _GNU_SOURCE
// #include <sys/ucontext.h>
// #include <stdint.h>
//
// static ucontext_t UContext;
//
// #define REGISTER_OFFSET(reg) const int MContextIndex ## reg = REG_ ## reg
//
// REGISTER_OFFSET(R8);
// REGISTER_OFFSET(R9);
// REGISTER_OFFSET(R10);
// REGISTER_OFFSET(R11);
// REGISTER_OFFSET(R12);
// REGISTER_OFFSET(R13);
// REGISTER_OFFSET(R14);
// REGISTER_OFFSET(R15);
// REGISTER_OFFSET(RDI);
// REGISTER_OFFSET(RSI);
// REGISTER_OFFSET(RBP);
// REGISTER_OFFSET(RBX);
// REGISTER_OFFSET(RDX);
// REGISTER_OFFSET(RAX);
// REGISTER_OFFSET(RCX);
// REGISTER_OFFSET(RSP);
// REGISTER_OFFSET(RIP);
// TODO: here we're hardcoding the offsets in the QEMU struct
ABIRegisters = { { "rax", 0xD }, { "rbx", 0xB }, { "rcx", 0xE },
{ "rdx", 0xC }, { "rbp", 0xA }, { "rsp", 0xF },
{ "rsi", 0x9 }, { "rdi", 0x8 }, { "r8", 0x0 },
{ "r9", 0x1 }, { "r10", 0x2 }, { "r11", 0x3 },
{ "r12", 0x4 }, { "r13", 0x5 }, { "r14", 0x6 },
{ "r15", 0x7 }, { "xmm0", "state_0x8558" },
{ "xmm1", "state_0x8598" }, { "xmm2", "state_0x85d8" },
{ "xmm3", "state_0x8618" }, { "xmm4", "state_0x8658" },
{ "xmm5", "state_0x8698" }, { "xmm6", "state_0x86d8" },
{ "xmm7", "state_0x8718" } };
WriteRegisterAsm = "movq $0, %REGISTER";
ReadRegisterAsm = "movq %REGISTER, $0";
JumpAsm = "movq $0, %r11; jmpq *%r11";
HasRelocationAddend = true;
BaseRelativeRelocation = llvm::ELF::R_X86_64_RELATIVE;
break;
case Triple::arm:
InstructionAlignment = 4;
SyscallHelper = "helper_exception_with_syndrome";
SyscallNumberRegister = "r7";
StackPointerRegister = "r13";
NoReturnSyscalls = {
0xf8, // exit_group
0x1, // exit
0xb // execve
};
ABIRegisters = { { "r0" }, { "r1" }, { "r2" }, { "r3" }, { "r4" },
{ "r5" }, { "r6" }, { "r7" }, { "r8" }, { "r9" },
{ "r10" }, { "r11" }, { "r12" }, { "r13" }, { "r14" } };
HasRelocationAddend = false;
BaseRelativeRelocation = llvm::ELF::R_ARM_RELATIVE;
break;
case Triple::mips:
InstructionAlignment = 4;
SyscallHelper = "helper_raise_exception";
SyscallNumberRegister = "v0";
StackPointerRegister = "sp";
NoReturnSyscalls = {
0x1096, // exit_group
0xfa1, // exit
0xfab // execve
};
DelaySlotSize = 1;
ABIRegisters = { {"v0" }, { "v1" }, { "a0" }, { "a1" }, { "a2" }, { "a3" },
{ "s0" }, { "s1" }, { "s2", }, { "s3" }, { "s4" },
{ "s5" }, { "s6" }, { "s7" }, { "gp" }, { "sp" },
{ "fp" }, { "ra" } };
HasRelocationAddend = false;
// TODO: check if this is correct
// BaseRelativeRelocation = llvm::ELF::R_MIPS_REL32;
break;
case Triple::systemz:
SyscallHelper = "helper_exception";
SyscallNumberRegister = "r1";
StackPointerRegister = "r15";
InstructionAlignment = 2;
NoReturnSyscalls = {
0xf8, // exit_group
0x1, // exit
0xb, // execve
};
HasRelocationAddend = true;
BaseRelativeRelocation = llvm::ELF::R_390_RELATIVE;
break;
default:
assert(false);
}
TheArchitecture = Architecture(TheBinary->getArch(),
InstructionAlignment,
1,
TheBinary->isLittleEndian(),
TheBinary->getBytesInAddress() * 8,
SyscallHelper,
SyscallNumberRegister,
NoReturnSyscalls,
DelaySlotSize,
StackPointerRegister,
ABIRegisters,
PCMContextIndex,
WriteRegisterAsm,
ReadRegisterAsm,
JumpAsm,
HasRelocationAddend,
BaseRelativeRelocation);
assert(TheBinary->getFileFormatName().startswith("ELF")
&& "Only the ELF file format is currently supported");
if (TheArchitecture.pointerSize() == 32) {
if (TheArchitecture.isLittleEndian()) {
if (TheArchitecture.hasRelocationAddend()) {
parseELF<object::ELF32LE, true>(TheBinary, UseSections, BaseAddress);
} else {
parseELF<object::ELF32LE, false>(TheBinary, UseSections, BaseAddress);
}
} else {
if (TheArchitecture.hasRelocationAddend()) {
parseELF<object::ELF32BE, true>(TheBinary, UseSections, BaseAddress);
} else {
parseELF<object::ELF32BE, false>(TheBinary, UseSections, BaseAddress);
}
}
} else if (TheArchitecture.pointerSize() == 64) {
if (TheArchitecture.isLittleEndian()) {
if (TheArchitecture.hasRelocationAddend()) {
parseELF<object::ELF64LE, true>(TheBinary, UseSections, BaseAddress);
} else {
parseELF<object::ELF64LE, false>(TheBinary, UseSections, BaseAddress);
}
} else {
if (TheArchitecture.hasRelocationAddend()) {
parseELF<object::ELF64BE, true>(TheBinary, UseSections, BaseAddress);
} else {
parseELF<object::ELF64BE, false>(TheBinary, UseSections, BaseAddress);
}
}
} else {
assert("Unexpect address size");
}
}
class FilePortion {
private:
bool HasAddress;
bool HasSize;
uint64_t Size;
uint64_t Address;
public:
FilePortion() : HasAddress(false), HasSize(false), Size(0), Address(0) { }
public:
void setAddress(uint64_t Address) {
HasAddress = true;
this->Address = Address;
}
void setSize(uint64_t Size) {
HasSize = true;
this->Size = Size;
}
bool isAvailable() const {
return HasAddress;
}
bool isExact() const {
assert(HasAddress);
return HasSize;
}
StringRef extractString(const std::vector<SegmentInfo> &Segments) const {
ArrayRef<uint8_t> Data = extractData(Segments);
const char *AsChar = reinterpret_cast<const char *>(Data.data());
return StringRef(AsChar, Data.size());
}
template<typename T>
ArrayRef<T> extractAs(const std::vector<SegmentInfo> &Segments) const {
ArrayRef<uint8_t> Data = extractData(Segments);
const size_t TypeSize = sizeof(T);
assert(Data.size() % TypeSize == 0);
return ArrayRef<T>(reinterpret_cast<const T *>(Data.data()),
Data.size() / TypeSize);
}
ArrayRef<uint8_t>
extractData(const std::vector<SegmentInfo> &Segments) const {
assert(HasAddress);
for (const SegmentInfo &Segment : Segments) {
if (Segment.contains(Address)) {
uint64_t Offset = Address - Segment.StartVirtualAddress;
uint64_t AvailableSize = Segment.size() - Offset;
uint64_t TheSize = AvailableSize;
if (HasSize) {
assert(AvailableSize >= Size);
TheSize = Size;
}
return { ArrayRef<uint8_t>(Segment.Data.data() + Offset, TheSize) };
}
}
abort();
}
};
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> Relocation) {
return 0;
}
};
template<typename T, bool HasAddend>
void BinaryFile::parseELF(object::ObjectFile *TheBinary,
bool UseSections,
uint64_t BaseAddress) {
// Parse the ELF file
std::error_code EC;
object::ELFFile<T> TheELF(TheBinary->getData(), EC);
assert(!EC && "Error while loading the ELF file");
// BaseAddress makes sense only for shared (relocatable, PIC) objects
if (TheELF.getHeader()->e_type == ELF::ET_DYN)
this->BaseAddress = BaseAddress;
// Look for static or dynamic symbols and relocations
using Elf_ShdrPtr = decltype(&(*TheELF.sections().begin()));
using Elf_PhdrPtr = decltype(&(*TheELF.program_headers().begin()));
Elf_ShdrPtr SymtabShdr = nullptr;
Elf_PhdrPtr DynamicPhdr = nullptr;
Optional<uint64_t> DynamicAddress;
Optional<uint64_t> EHFrameAddress;
Optional<uint64_t> EHFrameSize;
Optional<uint64_t> EHFrameHdrAddress;
for (auto &Section : TheELF.sections()) {
if (ErrorOr<StringRef> Name = TheELF.getSectionName(&Section)) {
if (*Name == ".symtab") {
assert(SymtabShdr == nullptr && "Duplicate .symtab");
SymtabShdr = &Section;
} else if (*Name == ".eh_frame") {
assert(not EHFrameAddress && "Duplicate .eh_frame");
EHFrameAddress = relocate(static_cast<uint64_t>(Section.sh_addr));
EHFrameSize = static_cast<uint64_t>(Section.sh_size);
} else if (*Name == ".dynamic") {
assert(not DynamicAddress && "Duplicate .dynamic");
DynamicAddress = relocate(static_cast<uint64_t>(Section.sh_addr));
}
}
}
// If we found a symbol table
if (SymtabShdr != nullptr && SymtabShdr->sh_link != 0) {
// Obtain a reference to the string table
const Elf_ShdrPtr Strtab = TheELF.getSection(SymtabShdr->sh_link).get();
ArrayRef<uint8_t> StrtabArray = TheELF.getSectionContents(Strtab).get();
StringRef StrtabContent(reinterpret_cast<const char *>(StrtabArray.data()),
StrtabArray.size());
// Collect symbol names
for (auto &Symbol : TheELF.symbols(SymtabShdr)) {
Symbols.push_back({
Symbol.getName(StrtabContent).get(),
Symbol.st_value,
Symbol.st_size,
Symbol.getType() == ELF::STT_FUNC
});
}
}
const auto *ElfHeader = TheELF.getHeader();
EntryPoint = relocate(static_cast<uint64_t>(ElfHeader->e_entry));
ProgramHeaders.Count = ElfHeader->e_phnum;
ProgramHeaders.Size = ElfHeader->e_phentsize;
// 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<T>::Elf_Phdr;
using Elf_Dyn = const typename object::ELFFile<T>::Elf_Dyn;
for (Elf_Phdr &ProgramHeader : TheELF.program_headers()) {
switch (ProgramHeader.p_type) {
case ELF::PT_LOAD:
{
SegmentInfo Segment;
auto Start = relocate(ProgramHeader.p_vaddr);
Segment.StartVirtualAddress = Start;
Segment.EndVirtualAddress = Start + ProgramHeader.p_memsz;
Segment.IsReadable = ProgramHeader.p_flags & ELF::PF_R;
Segment.IsWriteable = ProgramHeader.p_flags & ELF::PF_W;
Segment.IsExecutable = ProgramHeader.p_flags & ELF::PF_X;
auto ActualAddress = TheELF.base() + ProgramHeader.p_offset;
Segment.Data = ArrayRef<uint8_t>(ActualAddress, ProgramHeader.p_filesz);
// If it's an executable segment, and we've been asked so, register
// which sections actually contain code
if (UseSections && Segment.IsExecutable) {
using Elf_Shdr = const typename object::ELFFile<T>::Elf_Shdr;
auto Inserter = std::back_inserter(Segment.ExecutableSections);
for (Elf_Shdr &SectionHeader : TheELF.sections()) {
if (SectionHeader.sh_flags & ELF::SHF_EXECINSTR) {
auto SectionStart = relocate(SectionHeader.sh_addr);
auto SectionEnd = SectionStart + SectionHeader.sh_size;
Inserter = make_pair(SectionStart, SectionEnd);
}
}
}
Segments.push_back(Segment);
// Check if it's the segment containing the program headers
auto ProgramHeaderStart = ProgramHeader.p_offset;
auto ProgramHeaderEnd = ProgramHeader.p_offset + ProgramHeader.p_filesz;
if (ProgramHeaderStart <= ElfHeader->e_phoff
&& ElfHeader->e_phoff < ProgramHeaderEnd) {
uint64_t PhdrAddress = (relocate(ProgramHeader.p_vaddr)
+ ElfHeader->e_phoff
- ProgramHeader.p_offset);
ProgramHeaders.Address = PhdrAddress;
}
}
break;
case ELF::PT_GNU_EH_FRAME:
assert(!EHFrameHdrAddress);
EHFrameHdrAddress = relocate(ProgramHeader.p_vaddr);
break;
case ELF::PT_DYNAMIC:
assert(DynamicPhdr == nullptr && "Duplicate .dynamic program header");
DynamicPhdr = &ProgramHeader;
assert(((not DynamicAddress)
or (relocate(DynamicPhdr->p_vaddr) == *DynamicAddress))
and ".dynamic and PT_DYNAMIC have different addresses");
break;
}
}
assert((DynamicPhdr != nullptr) == (DynamicAddress.hasValue()));
Optional<uint64_t> FDEsCount;
if (EHFrameHdrAddress) {
uint64_t Address;
std::tie(Address, FDEsCount) = ehFrameFromEhFrameHdr<T>(*EHFrameHdrAddress);
if (EHFrameAddress) {
assert(*EHFrameAddress == Address);
}
EHFrameAddress = Address;
}
if (EHFrameAddress)
parseEHFrame<T>(*EHFrameAddress, FDEsCount, EHFrameSize);
// Parse the .dynamic table
if (DynamicPhdr != nullptr) {
SmallVector<uint64_t, 10> NeededLibraryNameOffsets;
FilePortion DynstrPortion;
FilePortion DynsymPortion;
FilePortion ReldynPortion;
FilePortion RelpltPortion;
for (Elf_Dyn &DynamicTag : *TheELF.dynamic_table(DynamicPhdr)) {
auto TheTag = DynamicTag.getTag();
switch(TheTag) {
case ELF::DT_NEEDED:
NeededLibraryNameOffsets.push_back(DynamicTag.getVal());
break;
case ELF::DT_STRTAB:
DynstrPortion.setAddress(relocate(DynamicTag.getPtr()));
break;
case ELF::DT_STRSZ:
DynstrPortion.setSize(DynamicTag.getVal());
break;
case ELF::DT_SYMTAB:
DynsymPortion.setAddress(relocate(DynamicTag.getPtr()));
break;
case ELF::DT_JMPREL:
RelpltPortion.setAddress(relocate(DynamicTag.getPtr()));
break;
case ELF::DT_PLTRELSZ:
RelpltPortion.setSize(DynamicTag.getVal());
break;
case ELF::DT_REL:
case ELF::DT_RELA:
assert(TheTag == (HasAddend ? ELF::DT_RELA : ELF::DT_REL));
ReldynPortion.setAddress(relocate(DynamicTag.getPtr()));
break;
case ELF::DT_RELSZ:
case ELF::DT_RELASZ:
assert(TheTag == (HasAddend ? ELF::DT_RELASZ : ELF::DT_RELSZ));
ReldynPortion.setSize(DynamicTag.getVal());
break;
}
}
if (NeededLibraryNames.size() > 0)
assert(DynstrPortion.isAvailable());
if (DynstrPortion.isAvailable()) {
StringRef Dynstr = DynstrPortion.extractString(Segments);
for(auto Offset : NeededLibraryNameOffsets) {
StringRef LibraryName = Dynstr.slice(Offset, Dynstr.size());
NeededLibraryNames.push_back(LibraryName.data());
}
}
// Collect symbols count and code pointers in image base-relative
// relocations
uint32_t ReldynSymbolsCount = parseRelocations<T, HasAddend>(ReldynPortion);
uint32_t RelpltSymbolsCount = parseRelocations<T, HasAddend>(RelpltPortion);
uint32_t SymbolsCount = std::max(ReldynSymbolsCount, RelpltSymbolsCount);
// Collect function addresses contained in dynamic symbols
if (SymbolsCount > 0 and DynsymPortion.isAvailable()) {
using Elf_Sym = llvm::object::Elf_Sym_Impl<T>;
DynsymPortion.setSize(SymbolsCount * sizeof(Elf_Sym));
for (Elf_Sym Symbol : DynsymPortion.extractAs<Elf_Sym>(Segments))
if (Symbol.st_value != 0 and Symbol.getType() == ELF::STT_FUNC)
CodePointers.insert(relocate(Symbol.st_value));
}
}
}
template<typename T, bool HasAddend>
uint64_t BinaryFile::parseRelocations(const FilePortion &Relocations) {
using Elf_Rel = llvm::object::Elf_Rel_Impl<T, HasAddend>;
uint32_t SymbolsCount = 0;
if (Relocations.isAvailable()) {
assert(Relocations.isExact());
for (Elf_Rel Relocation : Relocations.extractAs<Elf_Rel>(Segments)) {
SymbolsCount = std::max(SymbolsCount, Relocation.getSymbol(false) + 1);
auto RelocationType = Relocation.getType(false);
if (RelocationType == TheArchitecture.baseRelativeRelocation()) {
uint64_t Value;
// If it's a relocation with an addend, use it, otherwise the
// value is taken from the value stored in the relocated
// address (r_offset).
if (HasAddend) {
Value = RelocationHelper<T, HasAddend>::getAddend(Relocation);
} else {
auto Data = getAddressData(relocate(Relocation.r_offset));
assert(Data && "r_offset is not in any segment.");
Value = ::readPointer<T>(Data->data());
}
CodePointers.insert(relocate(Value));
}
}
}
return SymbolsCount;
}
//
// .eh_frame-related functions
//
template<typename E>
class DwarfReader {
public:
DwarfReader(ArrayRef<uint8_t> Buffer, uint64_t Address) :
Address(Address),
Start(Buffer.data()),
Cursor(Buffer.data()),
End(Buffer.data() + Buffer.size()) { }
template<typename T>
T readNext() {
assert(Cursor + sizeof(T) <= End);
T Result = Endianess<T, E>::read(Cursor);
Cursor += sizeof(T);
return Result;
}
uint8_t readNextU8() { return readNext<uint8_t>(); }
uint16_t readNextU16() { return readNext<uint16_t>(); }
uint32_t readNextU32() { return readNext<uint32_t>(); }
uint64_t readNextU64() { return readNext<uint64_t>(); }
uint64_t readNextU() {
if (is64())
return readNextU64();
else
return readNextU32();
}
uint64_t readULEB128() {
unsigned Length;
uint64_t Result = decodeULEB128(Cursor, &Length);
Cursor += Length;
assert(Cursor <= End);
return Result;
}
int64_t readSLEB128() {
unsigned Length;
int64_t Result = decodeSLEB128(Cursor, &Length);
Cursor += Length;
assert(Cursor <= End);
return Result;
}
Pointer readPointer(unsigned Encoding, uint64_t Base=0) {
assert((Encoding & ~(0x70 | 0x0F | dwarf::DW_EH_PE_indirect)) == 0);
if ((Encoding & 0x70) == dwarf::DW_EH_PE_pcrel)
Base = Address + (Cursor - Start);
unsigned Format = Encoding & 0x0F;
switch (Format) {
case dwarf::DW_EH_PE_uleb128:
return readPointerInternal(readULEB128(), Encoding, Base);
case dwarf::DW_EH_PE_sleb128:
return readPointerInternal(readSLEB128(), Encoding, Base);
case dwarf::DW_EH_PE_absptr:
if (is64())
return readPointerInternal(readNext<uint64_t>(), Encoding, Base);
else
return readPointerInternal(readNext<uint32_t>(), Encoding, Base);
case dwarf::DW_EH_PE_signed:
if (is64())
return readPointerInternal(readNext<int64_t>(), Encoding, Base);
else
return readPointerInternal(readNext<int32_t>(), Encoding, Base);
case dwarf::DW_EH_PE_udata2:
return readPointerInternal(readNext<uint16_t>(), Encoding, Base);
case dwarf::DW_EH_PE_sdata2:
return readPointerInternal(readNext<int16_t>(), Encoding, Base);
case dwarf::DW_EH_PE_udata4:
return readPointerInternal(readNext<uint32_t>(), Encoding, Base);
case dwarf::DW_EH_PE_sdata4:
return readPointerInternal(readNext<int32_t>(), Encoding, Base);
case dwarf::DW_EH_PE_udata8:
return readPointerInternal(readNext<uint64_t>(), Encoding, Base);
case dwarf::DW_EH_PE_sdata8:
return readPointerInternal(readNext<int64_t>(), Encoding, Base);
default:
llvm_unreachable("Unknown Encoding");
}
}
void moveTo(uint64_t Offset) {
const uint8_t *NewCursor = Start + Offset;
assert(NewCursor >= Cursor && NewCursor <= End);
Cursor = NewCursor;
}
bool eof() const { return Cursor >= End; }
uint64_t offset() const { return Cursor - Start; }
private:
template<typename T>
Pointer readPointerInternal(T Value, unsigned Encoding, uint64_t Base) {
uint64_t Result = Value;
if (Value != 0) {
int EncodingRelative = Encoding & 0x70;
assert(EncodingRelative == 0 || EncodingRelative == 0x10);
Result = Base;
if (std::numeric_limits<T>::is_signed)
Result += static_cast<int64_t>(Value);
else
Result += static_cast<uint64_t>(Value);
}
return Pointer(Encoding & dwarf::DW_EH_PE_indirect, Result);
}
bool is64() const;
private:
uint64_t Address;
const uint8_t *Start;
const uint8_t *Cursor;
const uint8_t *End;
};
template<> bool DwarfReader<object::ELF32BE>::is64() const { return false; }
template<> bool DwarfReader<object::ELF32LE>::is64() const { return false; }
template<> bool DwarfReader<object::ELF64BE>::is64() const { return true; }
template<> bool DwarfReader<object::ELF64LE>::is64() const { return true; }
template<typename T>
std::pair<uint64_t, uint64_t>
BinaryFile::ehFrameFromEhFrameHdr(uint64_t EHFrameHdrAddress) {
auto R = getAddressData(EHFrameHdrAddress);
assert(R && ".eh_frame_hdr section not available in any segment");
llvm::ArrayRef<uint8_t> EHFrameHdr = *R;
DwarfReader<T> EHFrameHdrReader(EHFrameHdr, EHFrameHdrAddress);
uint64_t VersionNumber = EHFrameHdrReader.readNextU8();
assert(VersionNumber == 1);
// ExceptionFrameEncoding
uint64_t ExceptionFrameEncoding = EHFrameHdrReader.readNextU8();
// FDEsCountEncoding
unsigned FDEsCountEncoding = EHFrameHdrReader.readNextU8();
// LookupTableEncoding
EHFrameHdrReader.readNextU8();
Pointer EHFramePointer = EHFrameHdrReader.readPointer(ExceptionFrameEncoding);
Pointer FDEsCountPointer = EHFrameHdrReader.readPointer(FDEsCountEncoding);
return { getPointer<T>(EHFramePointer), getPointer<T>(FDEsCountPointer) };
}
template<typename T>
void BinaryFile::parseEHFrame(uint64_t EHFrameAddress,
Optional<uint64_t> FDEsCount,
Optional<uint64_t> EHFrameSize) {
assert(FDEsCount || EHFrameSize);
auto R = getAddressData(EHFrameAddress);
// 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.
if (!R)
return;
llvm::ArrayRef<uint8_t> EHFrame = *R;
DwarfReader<T> EHFrameReader(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<uint32_t> FDEPointerEncoding;
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) {
assert(EHFrameReader.offset() == EndOffset);
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
DBG("ehframe", dbg << "New CIE\n");
// Ensure the version is the one we expect
uint32_t Version = EHFrameReader.readNextU8();
assert(Version == 1);
// 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<uint64_t> AugmentationLength;
Optional<uint32_t> LSDAPointerEncoding;
Optional<uint32_t> PersonalityEncoding;
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':
assert((i + 1) != e && AugmentationString[i + 1] == 'h' &&
"Expected 'eh' in augmentation string");
break;
case 'L':
// This is the only information we really care about, all the rest
// is processed just so we can get here
assert(!LSDAPointerEncoding && "Duplicate LSDA encoding");
LSDAPointerEncoding = EHFrameReader.readNextU8();
break;
case 'P': {
assert(!PersonalityEncoding && "Duplicate personality");
PersonalityEncoding = EHFrameReader.readNextU8();
// Personality
Pointer Personality;
Personality = EHFrameReader.readPointer(*PersonalityEncoding);
uint64_t PersonalityPtr = getPointer<T>(Personality);
DBG("ehframe", {
dbg << "Personality function: " << PersonalityPtr << "\n";
});
// TODO: technically this is not a landing pad
LandingPads.insert(PersonalityPtr);
break;
}
case 'R':
assert(!FDEPointerEncoding && "Duplicate FDE encoding");
FDEPointerEncoding = EHFrameReader.readNextU8();
break;
case 'z':
llvm_unreachable("'z' must be first in the augmentation string");
}
}
}
// 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);
assert(CIEIt != CachedCIEs.end()
&& "Couldn't find CIE at offset in to __eh_frame section");
// Ensure we have at least the pointer encoding
const DecodedCIE &CIE = CIEIt->getSecond();
assert(CIE.FDEPointerEncoding &&
"FDE references CIE which did not set pointer encoding");
// PCBegin
auto PCBeginPointer = EHFrameReader.readPointer(*CIE.FDEPointerEncoding);
uint64_t PCBegin = getPointer<T>(PCBeginPointer);
DBG("ehframe", dbg << "PCBegin: " << std::hex << PCBegin << "\n");
// 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<T>(PCBegin, getPointer<T>(LSDAPointer));
}
}
// Skip all the remaining parts
EHFrameReader.moveTo(EndOffset);
}
}
template<typename T>
void BinaryFile::parseLSDA(uint64_t FDEStart, uint64_t LSDAAddress) {
DBG("ehframe", dbg << "LSDAAddress: " << std::hex << LSDAAddress << "\n");
auto R = getAddressData(LSDAAddress);
assert(R && "LSDA not available in any segment");
llvm::ArrayRef<uint8_t> LSDA = *R;
DwarfReader<T> LSDAReader(LSDA, LSDAAddress);
uint32_t LandingPadBaseEncoding = LSDAReader.readNextU8();
uint64_t LandingPadBase = 0;
if (LandingPadBaseEncoding != dwarf::DW_EH_PE_omit) {
auto LandingPadBasePointer = LSDAReader.readPointer(LandingPadBaseEncoding);
LandingPadBase = getPointer<T>(LandingPadBasePointer);
} else {
LandingPadBase = FDEStart;
}
DBG("ehframe",
dbg << "LandingPadBase: " << std::hex << LandingPadBase << "\n");
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);
uint64_t LandingPad = getPointer<T>(LandingPadPointer);
// Action
LSDAReader.readULEB128();
if (LandingPad != 0) {
DBG("ehframe", {
if (LandingPads.count(LandingPad) == 0)
dbg << "New landing pad found: " << std::hex << LandingPad << "\n";
});
LandingPads.insert(LandingPad);
}
}
}