539 lines
18 KiB
C++
539 lines
18 KiB
C++
//===-------- JITLink_EHFrameSupport.cpp - JITLink eh-frame utils ---------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "EHFrameSupportImpl.h"
|
|
|
|
#include "llvm/BinaryFormat/Dwarf.h"
|
|
#include "llvm/Support/DynamicLibrary.h"
|
|
|
|
#define DEBUG_TYPE "jitlink"
|
|
|
|
namespace llvm {
|
|
namespace jitlink {
|
|
|
|
EHFrameBinaryParser::EHFrameBinaryParser(JITTargetAddress EHFrameAddress,
|
|
StringRef EHFrameContent,
|
|
unsigned PointerSize,
|
|
support::endianness Endianness)
|
|
: EHFrameAddress(EHFrameAddress), EHFrameContent(EHFrameContent),
|
|
PointerSize(PointerSize), EHFrameReader(EHFrameContent, Endianness) {}
|
|
|
|
Error EHFrameBinaryParser::addToGraph() {
|
|
while (!EHFrameReader.empty()) {
|
|
size_t RecordOffset = EHFrameReader.getOffset();
|
|
|
|
LLVM_DEBUG({
|
|
dbgs() << "Processing eh-frame record at "
|
|
<< format("0x%016" PRIx64, EHFrameAddress + RecordOffset)
|
|
<< " (offset " << RecordOffset << ")\n";
|
|
});
|
|
|
|
size_t RecordLength = 0;
|
|
uint32_t RecordLengthField;
|
|
if (auto Err = EHFrameReader.readInteger(RecordLengthField))
|
|
return Err;
|
|
|
|
// Process CIE/FDE length/extended-length fields to build the blocks.
|
|
//
|
|
// The value of these fields describe the length of the *rest* of the CIE
|
|
// (not including data up to the end of the field itself) so we have to
|
|
// bump RecordLength to include the data up to the end of the field: 4 bytes
|
|
// for Length, or 12 bytes (4 bytes + 8 bytes) for ExtendedLength.
|
|
if (RecordLengthField == 0) // Length 0 means end of __eh_frame section.
|
|
break;
|
|
|
|
// If the regular length field's value is 0xffffffff, use extended length.
|
|
if (RecordLengthField == 0xffffffff) {
|
|
uint64_t ExtendedLengthField;
|
|
if (auto Err = EHFrameReader.readInteger(ExtendedLengthField))
|
|
return Err;
|
|
if (ExtendedLengthField > EHFrameReader.bytesRemaining())
|
|
return make_error<JITLinkError>("CIE record extends past the end of "
|
|
"the __eh_frame section");
|
|
if (ExtendedLengthField + 12 > std::numeric_limits<size_t>::max())
|
|
return make_error<JITLinkError>("CIE record too large to process");
|
|
RecordLength = ExtendedLengthField + 12;
|
|
} else {
|
|
if (RecordLengthField > EHFrameReader.bytesRemaining())
|
|
return make_error<JITLinkError>("CIE record extends past the end of "
|
|
"the __eh_frame section");
|
|
RecordLength = RecordLengthField + 4;
|
|
}
|
|
|
|
LLVM_DEBUG(dbgs() << " length: " << RecordLength << "\n");
|
|
|
|
// Read the CIE Pointer.
|
|
size_t CIEPointerAddress = EHFrameAddress + EHFrameReader.getOffset();
|
|
uint32_t CIEPointer;
|
|
if (auto Err = EHFrameReader.readInteger(CIEPointer))
|
|
return Err;
|
|
|
|
// Based on the CIE pointer value, parse this as a CIE or FDE record.
|
|
if (CIEPointer == 0) {
|
|
if (auto Err = processCIE(RecordOffset, RecordLength))
|
|
return Err;
|
|
} else {
|
|
if (auto Err = processFDE(RecordOffset, RecordLength, CIEPointerAddress,
|
|
CIEPointer))
|
|
return Err;
|
|
}
|
|
|
|
EHFrameReader.setOffset(RecordOffset + RecordLength);
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
void EHFrameBinaryParser::anchor() {}
|
|
|
|
Expected<EHFrameBinaryParser::AugmentationInfo>
|
|
EHFrameBinaryParser::parseAugmentationString() {
|
|
AugmentationInfo AugInfo;
|
|
uint8_t NextChar;
|
|
uint8_t *NextField = &AugInfo.Fields[0];
|
|
|
|
if (auto Err = EHFrameReader.readInteger(NextChar))
|
|
return std::move(Err);
|
|
|
|
while (NextChar != 0) {
|
|
switch (NextChar) {
|
|
case 'z':
|
|
AugInfo.AugmentationDataPresent = true;
|
|
break;
|
|
case 'e':
|
|
if (auto Err = EHFrameReader.readInteger(NextChar))
|
|
return std::move(Err);
|
|
if (NextChar != 'h')
|
|
return make_error<JITLinkError>("Unrecognized substring e" +
|
|
Twine(NextChar) +
|
|
" in augmentation string");
|
|
AugInfo.EHDataFieldPresent = true;
|
|
break;
|
|
case 'L':
|
|
case 'P':
|
|
case 'R':
|
|
*NextField++ = NextChar;
|
|
break;
|
|
default:
|
|
return make_error<JITLinkError>("Unrecognized character " +
|
|
Twine(NextChar) +
|
|
" in augmentation string");
|
|
}
|
|
|
|
if (auto Err = EHFrameReader.readInteger(NextChar))
|
|
return std::move(Err);
|
|
}
|
|
|
|
return std::move(AugInfo);
|
|
}
|
|
|
|
Expected<JITTargetAddress> EHFrameBinaryParser::readAbsolutePointer() {
|
|
static_assert(sizeof(JITTargetAddress) == sizeof(uint64_t),
|
|
"Result must be able to hold a uint64_t");
|
|
JITTargetAddress Addr;
|
|
if (PointerSize == 8) {
|
|
if (auto Err = EHFrameReader.readInteger(Addr))
|
|
return std::move(Err);
|
|
} else if (PointerSize == 4) {
|
|
uint32_t Addr32;
|
|
if (auto Err = EHFrameReader.readInteger(Addr32))
|
|
return std::move(Err);
|
|
Addr = Addr32;
|
|
} else
|
|
llvm_unreachable("Pointer size is not 32-bit or 64-bit");
|
|
return Addr;
|
|
}
|
|
|
|
Error EHFrameBinaryParser::processCIE(size_t RecordOffset,
|
|
size_t RecordLength) {
|
|
// Use the dwarf namespace for convenient access to pointer encoding
|
|
// constants.
|
|
using namespace dwarf;
|
|
|
|
LLVM_DEBUG(dbgs() << " Record is CIE\n");
|
|
|
|
auto &CIESymbol =
|
|
createCIERecord(EHFrameAddress + RecordOffset,
|
|
EHFrameContent.substr(RecordOffset, RecordLength));
|
|
|
|
CIEInformation CIEInfo(CIESymbol);
|
|
|
|
uint8_t Version = 0;
|
|
if (auto Err = EHFrameReader.readInteger(Version))
|
|
return Err;
|
|
|
|
if (Version != 0x01)
|
|
return make_error<JITLinkError>("Bad CIE version " + Twine(Version) +
|
|
" (should be 0x01) in eh-frame");
|
|
|
|
auto AugInfo = parseAugmentationString();
|
|
if (!AugInfo)
|
|
return AugInfo.takeError();
|
|
|
|
// Skip the EH Data field if present.
|
|
if (AugInfo->EHDataFieldPresent)
|
|
if (auto Err = EHFrameReader.skip(PointerSize))
|
|
return Err;
|
|
|
|
// Read and sanity check the code alignment factor.
|
|
{
|
|
uint64_t CodeAlignmentFactor = 0;
|
|
if (auto Err = EHFrameReader.readULEB128(CodeAlignmentFactor))
|
|
return Err;
|
|
if (CodeAlignmentFactor != 1)
|
|
return make_error<JITLinkError>("Unsupported CIE code alignment factor " +
|
|
Twine(CodeAlignmentFactor) +
|
|
" (expected 1)");
|
|
}
|
|
|
|
// Read and sanity check the data alignment factor.
|
|
{
|
|
int64_t DataAlignmentFactor = 0;
|
|
if (auto Err = EHFrameReader.readSLEB128(DataAlignmentFactor))
|
|
return Err;
|
|
if (DataAlignmentFactor != -8)
|
|
return make_error<JITLinkError>("Unsupported CIE data alignment factor " +
|
|
Twine(DataAlignmentFactor) +
|
|
" (expected -8)");
|
|
}
|
|
|
|
// Skip the return address register field.
|
|
if (auto Err = EHFrameReader.skip(1))
|
|
return Err;
|
|
|
|
uint64_t AugmentationDataLength = 0;
|
|
if (auto Err = EHFrameReader.readULEB128(AugmentationDataLength))
|
|
return Err;
|
|
|
|
uint32_t AugmentationDataStartOffset = EHFrameReader.getOffset();
|
|
|
|
uint8_t *NextField = &AugInfo->Fields[0];
|
|
while (uint8_t Field = *NextField++) {
|
|
switch (Field) {
|
|
case 'L': {
|
|
CIEInfo.FDEsHaveLSDAField = true;
|
|
uint8_t LSDAPointerEncoding;
|
|
if (auto Err = EHFrameReader.readInteger(LSDAPointerEncoding))
|
|
return Err;
|
|
if (LSDAPointerEncoding != (DW_EH_PE_pcrel | DW_EH_PE_absptr))
|
|
return make_error<JITLinkError>(
|
|
"Unsupported LSDA pointer encoding " +
|
|
formatv("{0:x2}", LSDAPointerEncoding) + " in CIE at " +
|
|
formatv("{0:x16}", CIESymbol.getAddress()));
|
|
break;
|
|
}
|
|
case 'P': {
|
|
uint8_t PersonalityPointerEncoding = 0;
|
|
if (auto Err = EHFrameReader.readInteger(PersonalityPointerEncoding))
|
|
return Err;
|
|
if (PersonalityPointerEncoding !=
|
|
(DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata4))
|
|
return make_error<JITLinkError>(
|
|
"Unspported personality pointer "
|
|
"encoding " +
|
|
formatv("{0:x2}", PersonalityPointerEncoding) + " in CIE at " +
|
|
formatv("{0:x16}", CIESymbol.getAddress()));
|
|
uint32_t PersonalityPointerAddress;
|
|
if (auto Err = EHFrameReader.readInteger(PersonalityPointerAddress))
|
|
return Err;
|
|
break;
|
|
}
|
|
case 'R': {
|
|
uint8_t FDEPointerEncoding;
|
|
if (auto Err = EHFrameReader.readInteger(FDEPointerEncoding))
|
|
return Err;
|
|
if (FDEPointerEncoding != (DW_EH_PE_pcrel | DW_EH_PE_absptr))
|
|
return make_error<JITLinkError>(
|
|
"Unsupported FDE address pointer "
|
|
"encoding " +
|
|
formatv("{0:x2}", FDEPointerEncoding) + " in CIE at " +
|
|
formatv("{0:x16}", CIESymbol.getAddress()));
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("Invalid augmentation string field");
|
|
}
|
|
}
|
|
|
|
if (EHFrameReader.getOffset() - AugmentationDataStartOffset >
|
|
AugmentationDataLength)
|
|
return make_error<JITLinkError>("Read past the end of the augmentation "
|
|
"data while parsing fields");
|
|
|
|
assert(!CIEInfos.count(CIESymbol.getAddress()) &&
|
|
"Multiple CIEs recorded at the same address?");
|
|
CIEInfos[CIESymbol.getAddress()] = std::move(CIEInfo);
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
Error EHFrameBinaryParser::processFDE(size_t RecordOffset, size_t RecordLength,
|
|
JITTargetAddress CIEPointerAddress,
|
|
uint32_t CIEPointer) {
|
|
LLVM_DEBUG(dbgs() << " Record is FDE\n");
|
|
|
|
LLVM_DEBUG({
|
|
dbgs() << " CIE pointer: "
|
|
<< format("0x%016" PRIx64, CIEPointerAddress - CIEPointer) << "\n";
|
|
});
|
|
|
|
auto CIEInfoItr = CIEInfos.find(CIEPointerAddress - CIEPointer);
|
|
if (CIEInfoItr == CIEInfos.end())
|
|
return make_error<JITLinkError>(
|
|
"FDE at " + formatv("{0:x16}", EHFrameAddress + RecordOffset) +
|
|
" points to non-existant CIE at " +
|
|
formatv("{0:x16}", CIEPointerAddress - CIEPointer));
|
|
auto &CIEInfo = CIEInfoItr->second;
|
|
|
|
// Read and sanity check the PC-start pointer and size.
|
|
JITTargetAddress PCBeginAddress = EHFrameAddress + EHFrameReader.getOffset();
|
|
|
|
auto PCBeginDelta = readAbsolutePointer();
|
|
if (!PCBeginDelta)
|
|
return PCBeginDelta.takeError();
|
|
|
|
JITTargetAddress PCBegin = PCBeginAddress + *PCBeginDelta;
|
|
LLVM_DEBUG({
|
|
dbgs() << " PC begin: " << format("0x%016" PRIx64, PCBegin) << "\n";
|
|
});
|
|
|
|
auto *TargetSymbol = getSymbolAtAddress(PCBegin);
|
|
|
|
if (!TargetSymbol)
|
|
return make_error<JITLinkError>("FDE PC-begin " +
|
|
formatv("{0:x16}", PCBegin) +
|
|
" does not point at symbol");
|
|
|
|
if (TargetSymbol->getAddress() != PCBegin)
|
|
return make_error<JITLinkError>(
|
|
"FDE PC-begin " + formatv("{0:x16}", PCBegin) +
|
|
" does not point to start of symbol at " +
|
|
formatv("{0:x16}", TargetSymbol->getAddress()));
|
|
|
|
LLVM_DEBUG(dbgs() << " FDE target: " << *TargetSymbol << "\n");
|
|
|
|
// Skip over the PC range size field.
|
|
if (auto Err = EHFrameReader.skip(PointerSize))
|
|
return Err;
|
|
|
|
Symbol *LSDASymbol = nullptr;
|
|
JITTargetAddress LSDAAddress = 0;
|
|
if (CIEInfo.FDEsHaveLSDAField) {
|
|
uint64_t AugmentationDataSize;
|
|
if (auto Err = EHFrameReader.readULEB128(AugmentationDataSize))
|
|
return Err;
|
|
if (AugmentationDataSize != PointerSize)
|
|
return make_error<JITLinkError>(
|
|
"Unexpected FDE augmentation data size (expected " +
|
|
Twine(PointerSize) + ", got " + Twine(AugmentationDataSize) +
|
|
") for FDE at " + formatv("{0:x16}", EHFrameAddress + RecordOffset));
|
|
LSDAAddress = EHFrameAddress + EHFrameReader.getOffset();
|
|
auto LSDADelta = readAbsolutePointer();
|
|
if (!LSDADelta)
|
|
return LSDADelta.takeError();
|
|
|
|
JITTargetAddress LSDA = LSDAAddress + *LSDADelta;
|
|
|
|
LSDASymbol = getSymbolAtAddress(LSDA);
|
|
|
|
if (!LSDASymbol)
|
|
return make_error<JITLinkError>("FDE LSDA " + formatv("{0:x16}", LSDA) +
|
|
" does not point at symbol");
|
|
|
|
if (LSDASymbol->getAddress() != LSDA)
|
|
return make_error<JITLinkError>(
|
|
"FDE LSDA " + formatv("{0:x16}", LSDA) +
|
|
" does not point to start of symbol at " +
|
|
formatv("{0:x16}", LSDASymbol->getAddress()));
|
|
|
|
LLVM_DEBUG(dbgs() << " FDE LSDA: " << *LSDASymbol << "\n");
|
|
}
|
|
|
|
JITTargetAddress RecordAddress = EHFrameAddress + RecordOffset;
|
|
auto FDESymbol = createFDERecord(
|
|
RecordAddress, EHFrameContent.substr(RecordOffset, RecordLength),
|
|
*CIEInfo.CIESymbol, CIEPointerAddress - RecordAddress, *TargetSymbol,
|
|
PCBeginAddress - RecordAddress, LSDASymbol, LSDAAddress - RecordAddress);
|
|
|
|
return FDESymbol.takeError();
|
|
}
|
|
|
|
// Determine whether we can register EH tables.
|
|
#if (defined(__GNUC__) && !defined(__ARM_EABI__) && !defined(__ia64__) && \
|
|
!(defined(_AIX) && defined(__ibmxl__)) && !defined(__SEH__) && \
|
|
!defined(__USING_SJLJ_EXCEPTIONS__))
|
|
#define HAVE_EHTABLE_SUPPORT 1
|
|
#else
|
|
#define HAVE_EHTABLE_SUPPORT 0
|
|
#endif
|
|
|
|
#if HAVE_EHTABLE_SUPPORT
|
|
extern "C" void __register_frame(const void *);
|
|
extern "C" void __deregister_frame(const void *);
|
|
|
|
Error registerFrameWrapper(const void *P) {
|
|
__register_frame(P);
|
|
return Error::success();
|
|
}
|
|
|
|
Error deregisterFrameWrapper(const void *P) {
|
|
__deregister_frame(P);
|
|
return Error::success();
|
|
}
|
|
|
|
#else
|
|
|
|
// The building compiler does not have __(de)register_frame but
|
|
// it may be found at runtime in a dynamically-loaded library.
|
|
// For example, this happens when building LLVM with Visual C++
|
|
// but using the MingW runtime.
|
|
static Error registerFrameWrapper(const void *P) {
|
|
static void((*RegisterFrame)(const void *)) = 0;
|
|
|
|
if (!RegisterFrame)
|
|
*(void **)&RegisterFrame =
|
|
llvm::sys::DynamicLibrary::SearchForAddressOfSymbol("__register_frame");
|
|
|
|
if (RegisterFrame) {
|
|
RegisterFrame(P);
|
|
return Error::success();
|
|
}
|
|
|
|
return make_error<JITLinkError>("could not register eh-frame: "
|
|
"__register_frame function not found");
|
|
}
|
|
|
|
static Error deregisterFrameWrapper(const void *P) {
|
|
static void((*DeregisterFrame)(const void *)) = 0;
|
|
|
|
if (!DeregisterFrame)
|
|
*(void **)&DeregisterFrame =
|
|
llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(
|
|
"__deregister_frame");
|
|
|
|
if (DeregisterFrame) {
|
|
DeregisterFrame(P);
|
|
return Error::success();
|
|
}
|
|
|
|
return make_error<JITLinkError>("could not deregister eh-frame: "
|
|
"__deregister_frame function not found");
|
|
}
|
|
#endif
|
|
|
|
#ifdef __APPLE__
|
|
|
|
template <typename HandleFDEFn>
|
|
Error walkAppleEHFrameSection(const char *const SectionStart,
|
|
size_t SectionSize,
|
|
HandleFDEFn HandleFDE) {
|
|
const char *CurCFIRecord = SectionStart;
|
|
const char *End = SectionStart + SectionSize;
|
|
uint64_t Size = *reinterpret_cast<const uint32_t *>(CurCFIRecord);
|
|
|
|
while (CurCFIRecord != End && Size != 0) {
|
|
const char *OffsetField = CurCFIRecord + (Size == 0xffffffff ? 12 : 4);
|
|
if (Size == 0xffffffff)
|
|
Size = *reinterpret_cast<const uint64_t *>(CurCFIRecord + 4) + 12;
|
|
else
|
|
Size += 4;
|
|
uint32_t Offset = *reinterpret_cast<const uint32_t *>(OffsetField);
|
|
if (Offset != 0)
|
|
if (auto Err = HandleFDE(CurCFIRecord))
|
|
return Err;
|
|
|
|
LLVM_DEBUG({
|
|
dbgs() << "Registering eh-frame section:\n";
|
|
dbgs() << "Processing " << (Offset ? "FDE" : "CIE") << " @"
|
|
<< (void *)CurCFIRecord << ": [";
|
|
for (unsigned I = 0; I < Size; ++I)
|
|
dbgs() << format(" 0x%02" PRIx8, *(CurCFIRecord + I));
|
|
dbgs() << " ]\n";
|
|
});
|
|
CurCFIRecord += Size;
|
|
|
|
Size = *reinterpret_cast<const uint32_t *>(CurCFIRecord);
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
#endif // __APPLE__
|
|
|
|
Error registerEHFrameSection(const void *EHFrameSectionAddr,
|
|
size_t EHFrameSectionSize) {
|
|
#ifdef __APPLE__
|
|
// On Darwin __register_frame has to be called for each FDE entry.
|
|
return walkAppleEHFrameSection(static_cast<const char *>(EHFrameSectionAddr),
|
|
EHFrameSectionSize,
|
|
registerFrameWrapper);
|
|
#else
|
|
// On Linux __register_frame takes a single argument:
|
|
// a pointer to the start of the .eh_frame section.
|
|
|
|
// How can it find the end? Because crtendS.o is linked
|
|
// in and it has an .eh_frame section with four zero chars.
|
|
return registerFrameWrapper(EHFrameSectionAddr);
|
|
#endif
|
|
}
|
|
|
|
Error deregisterEHFrameSection(const void *EHFrameSectionAddr,
|
|
size_t EHFrameSectionSize) {
|
|
#ifdef __APPLE__
|
|
return walkAppleEHFrameSection(static_cast<const char *>(EHFrameSectionAddr),
|
|
EHFrameSectionSize,
|
|
deregisterFrameWrapper);
|
|
#else
|
|
return deregisterFrameWrapper(EHFrameSectionAddr);
|
|
#endif
|
|
}
|
|
|
|
EHFrameRegistrar::~EHFrameRegistrar() {}
|
|
|
|
InProcessEHFrameRegistrar &InProcessEHFrameRegistrar::getInstance() {
|
|
static InProcessEHFrameRegistrar Instance;
|
|
return Instance;
|
|
}
|
|
|
|
InProcessEHFrameRegistrar::InProcessEHFrameRegistrar() {}
|
|
|
|
LinkGraphPassFunction
|
|
createEHFrameRecorderPass(const Triple &TT,
|
|
StoreFrameRangeFunction StoreRangeAddress) {
|
|
const char *EHFrameSectionName = nullptr;
|
|
if (TT.getObjectFormat() == Triple::MachO)
|
|
EHFrameSectionName = "__eh_frame";
|
|
else
|
|
EHFrameSectionName = ".eh_frame";
|
|
|
|
auto RecordEHFrame =
|
|
[EHFrameSectionName,
|
|
StoreFrameRange = std::move(StoreRangeAddress)](LinkGraph &G) -> Error {
|
|
// Search for a non-empty eh-frame and record the address of the first
|
|
// symbol in it.
|
|
JITTargetAddress Addr = 0;
|
|
size_t Size = 0;
|
|
if (auto *S = G.findSectionByName(EHFrameSectionName)) {
|
|
auto R = SectionRange(*S);
|
|
Addr = R.getStart();
|
|
Size = R.getSize();
|
|
}
|
|
if (Addr == 0 && Size != 0)
|
|
return make_error<JITLinkError>("__eh_frame section can not have zero "
|
|
"address with non-zero size");
|
|
StoreFrameRange(Addr, Size);
|
|
return Error::success();
|
|
};
|
|
|
|
return RecordEHFrame;
|
|
}
|
|
|
|
} // end namespace jitlink
|
|
} // end namespace llvm
|