Files
revng-revng/binaryfile.cpp
T
Alessandro Di Federico 6a6cfff17d Introduce support for dynamic objects (PIE)
This commit introduces support for dynamic objects. We do not support
translating dynamic libraries yet, therefore this commit introduces
support for PIE programs.

At the current stage, QEMU does not provide us explicit information
about an instruction using the program counter, but introduces its value
as an immediate. As a consequence, we cannot support arbitrary
relocation. For this reason, we statically relocate the program to a
fixed address (`0x50000000` by default, but it can be customized through
the `--base` argument). Therefore, all the addresses read from ELF data
structure need to be relocated.

Code compiled with `-fPIC` cannot store in global data the address of a
function, since it will be relocated at run-time. This means that the
global data harvesting won't bring any benefit. On the other hand, going
through dynamic symbols can be hugely beneficial. Same argument for
`*_RELATIVE` relocations.

The `merge-dynamic.py` script has been improved to find the appropriate
spot to put the rewritten program/section and headers and the dynamic
sections (the kernel is peeky on them).

Finally the `setRegister` function has been introduced in the module
produced by `revamb`. This function allows to keep CSVs static and, at
the same time, it allow `support.c` to set them. This is particularly
useful when we want to call the `root` function with specific values in
the registers (e.g., during for fuzzing purposes) or, as it's the case
for PIE, to synchronize the value of the FS register, which is
initialized by the dynamic loader, before execution gets to the `main`
function in `support.c`.
2018-08-17 21:55:48 +02:00

980 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 {
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();
}
private:
bool HasAddress;
bool HasSize;
uint64_t Size;
uint64_t Address;
};
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
});
}
}
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)
NeededLibraryNames.push_back(Dynstr.slice(Offset,
Dynstr.size()).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);
}
}
}