Files
revng-revng/binaryfile.cpp
T
Alessandro Di Federico 61cfbdfc56 Introduce support for dynamic binaries
This commit introduces support for dynamic programs. The current
implementation translate the main binary and uses native libraries. This
works only if the target architecture is the same as the source
one. Currently we only handle x86-64.

* The `ExternalJumpsHandler` class has been introduced. It basically
  takes care of extending the dispatcher handling the case in which the
  program counter is an address outside the range of executable
  addresses of the input program. In this case, a `setjmp` is perfomed,
  the CPU state is serialized to physical registers and jump to the
  value of the program counter is performed.

  Once the target code will try to return to the translated program, a
  segmentation fault will be triggered, a `longjmp` is performed and the
  CPU state is deserialized so that the execution can resume (from the
  dispatcher).

* `early-linked.c` has been introduced. Its purposes is to provide
  declarations of variables and functions defined in `support.c`. In the
  past, we had to manually create these definitions, a cumbersome and
  error prone we now avoid by letting `clang` compile `early-linked.c`
  and then linking it in.

* The old `support.h` is now known as `commonconstants.h`. `support.h`
  now contains declarations that have to be consumed by
  `early-linked.c`.

* Each architecture now provides additional information:

  1. Which registers are part of the ABI and have to be preserved. If
     necessary the QEMU name can be provided. For each register it's
     also possible to provide their position within the `mcontext_t`
     structure, provided by the signal handler.
  2. Three assembly snippets, one to write a register, one to read it
     and one perform an indirect jump.

  Some of this information is also exposed in the output module as
  metadata.

* `support.c` now installs a SIGSEGV signal handler. Since pages that
  were originally executable are no longer executable, jumping there
  (typically, from a library) will trigger a SIGSEGV that we will
  handle. This allows us to properly deserialize the CPU state and
  resume execution of the translate code.

* Now also a dynamic version of each test program is translated and
  tested.

* The `merge-dynamic.py` script has been introduced: it takes case of
  rewriting the translated binary so to tell the linker to performe both
  the relocations of the translate program and the relocations of the
  original program. It does so by rewriting a large portion of the
  sections employed by the dynamic linker such as `.dynamic`, `.dynsym`
  and so on.

* The `compile-time-constants.py` script has been introduced: it a
  user-specified compiler on a source file producing an object
  file. This object file is inspected and the value of global read-only
  variables is produced in a CSV.
2018-05-29 15:10:51 +02:00

759 lines
25 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) {
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 = "";
switch (TheBinary->getArch()) {
case Triple::x86:
InstructionAlignment = 1;
SyscallHelper = "helper_syscall";
SyscallNumberRegister = "eax";
StackPointerRegister = "esp";
NoReturnSyscalls = {
0xfc, // exit_group
0x01, // exit
0x0b // execve
};
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);
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";
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" } };
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" } };
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);
assert(TheBinary->getFileFormatName().startswith("ELF")
&& "Only the ELF file format is currently supported");
if (TheArchitecture.pointerSize() == 32) {
if (TheArchitecture.isLittleEndian()) {
parseELF<object::ELF32LE>(TheBinary, UseSections);
} else {
parseELF<object::ELF32BE>(TheBinary, UseSections);
}
} else if (TheArchitecture.pointerSize() == 64) {
if (TheArchitecture.isLittleEndian()) {
parseELF<object::ELF64LE>(TheBinary, UseSections);
} else {
parseELF<object::ELF64BE>(TheBinary, UseSections);
}
} else {
assert("Unexpect address size");
}
}
template<typename T>
void BinaryFile::parseELF(object::ObjectFile *TheBinary, bool UseSections) {
// Parse the ELF file
std::error_code EC;
object::ELFFile<T> TheELF(TheBinary->getData(), EC);
assert(!EC && "Error while loading the ELF file");
// 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 = 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 = 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 = 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 = 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 = 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) {
auto PhdrAddress = static_cast<uint64_t>(ProgramHeader.p_vaddr
+ ElfHeader->e_phoff
- ProgramHeader.p_offset);
ProgramHeaders.Address = PhdrAddress;
}
}
break;
case ELF::PT_GNU_EH_FRAME:
assert(!EHFrameHdrAddress);
EHFrameHdrAddress = ProgramHeader.p_vaddr;
break;
case ELF::PT_DYNAMIC:
assert(DynamicPhdr == nullptr && "Duplicate .dynamic program header");
DynamicPhdr = &ProgramHeader;
assert(((not DynamicAddress)
or (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);
// Search for needed shared libraries in the .dynamic table
if (DynamicPhdr != nullptr) {
SmallVector<uint64_t, 10> NeededLibraryNameOffsets;
StringRef Dynstr;
Optional<uint64_t> DynstrSize;
for (Elf_Dyn &DynamicTag : *TheELF.dynamic_table(DynamicPhdr)) {
switch(DynamicTag.getTag()) {
case ELF::DT_NEEDED:
NeededLibraryNameOffsets.push_back(DynamicTag.getVal());
break;
case ELF::DT_STRTAB: {
Optional<ArrayRef<uint8_t>> DynstrData;
DynstrData = getAddressData(DynamicTag.getPtr());
assert(DynstrData.hasValue() &&
".dynamic string table not available in any segment");
Dynstr = StringRef(reinterpret_cast<const char *>(DynstrData->data()),
DynstrData->size());
} break;
case ELF::DT_STRSZ:
DynstrSize = DynamicTag.getVal();
break;
}
}
assert(DynstrSize.hasValue() && *DynstrSize < Dynstr.size());
Dynstr = StringRef(Dynstr.data(), *DynstrSize);
for(auto Offset : NeededLibraryNameOffsets)
NeededLibraryNames.push_back(Dynstr.slice(Offset, *DynstrSize).data());
}
}
//
// .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);
}
}
}