llvm-project/llvm/lib/ExecutionEngine/JITLink/CompactUnwindSupport.h
Lang Hames 9d88ffe7f7 [JITLink] Handle compact-unwind records that depend on DWARF FDEs.
Compact-unwind encodings are more limited than DWARF frame descriptions. For
functions whose frame layout cannot be described by a compact unwind encoding,
the encoding for the function will specify "use DWARF", and the corresponding
unwind-info record will use the low bits of the encoding to point to the FDE
for the function.

We test this with a frame-pointer=none function, since these frame layouts
always triger a fall-back to DWARF on arm64.
2025-02-06 16:10:30 +11:00

683 lines
25 KiB
C++

//===- CompactUnwindSupportImpl.h - Compact Unwind format impl --*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Compact Unwind format support implementation details.
//
//===----------------------------------------------------------------------===//
#ifndef LIB_EXECUTIONENGINE_JITLINK_COMPACTUNWINDSUPPORTIMPL_H
#define LIB_EXECUTIONENGINE_JITLINK_COMPACTUNWINDSUPPORTIMPL_H
#include "llvm/ADT/STLExtras.h"
#include "llvm/ExecutionEngine/JITLink/MachO.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#define DEBUG_TYPE "jitlink_cu"
namespace llvm {
namespace jitlink {
/// Split blocks in an __LD,__compact_unwind section on record boundaries.
/// When this function returns edges within each record are guaranteed to be
/// sorted by offset.
Error splitCompactUnwindBlocks(LinkGraph &G, Section &CompactUnwindSection,
size_t RecordSize);
/// CRTP base for compact unwind traits classes. Automatically provides derived
/// constants.
///
/// FIXME: Passing PtrSize as a template parameter is a hack to work around a
/// bug in older MSVC compilers (until at least MSVC 15) where constexpr
/// fields in the CRTP impl class were not visible to the base class.
/// Once we no longer need to support these compilers the PtrSize
/// template argument should be removed and PointerSize should be
/// defined as a member in the CRTP Impl classes.
template <typename CRTPImpl, size_t PtrSize> struct CompactUnwindTraits {
static constexpr size_t PointerSize = PtrSize;
static constexpr size_t Size = 3 * PointerSize + 2 * 4;
static constexpr size_t FnFieldOffset = 0;
static constexpr size_t SizeFieldOffset = FnFieldOffset + PointerSize;
static constexpr size_t EncodingFieldOffset = SizeFieldOffset + 4;
static constexpr size_t PersonalityFieldOffset = EncodingFieldOffset + 4;
static constexpr size_t LSDAFieldOffset =
PersonalityFieldOffset + PointerSize;
static uint32_t readPCRangeSize(ArrayRef<char> RecordContent) {
assert(SizeFieldOffset + 4 <= RecordContent.size() &&
"Truncated CU record?");
return support::endian::read32<CRTPImpl::Endianness>(RecordContent.data() +
SizeFieldOffset);
}
static uint32_t readEncoding(ArrayRef<char> RecordContent) {
assert(EncodingFieldOffset + 4 <= RecordContent.size() &&
"Truncated CU record?");
return support::endian::read32<CRTPImpl::Endianness>(RecordContent.data() +
EncodingFieldOffset);
}
static std::optional<uint32_t> encodeDWARFOffset(size_t Delta) {
uint32_t Encoded =
static_cast<uint32_t>(Delta) & CRTPImpl::DWARFSectionOffsetMask;
if (Encoded != Delta)
return std::nullopt;
return Encoded;
}
};
/// Architecture specific implementation of CompactUnwindManager.
template <typename CURecTraits> class CompactUnwindManager {
public:
CompactUnwindManager(StringRef CompactUnwindSectionName,
StringRef UnwindInfoSectionName,
StringRef EHFrameSectionName)
: CompactUnwindSectionName(CompactUnwindSectionName),
UnwindInfoSectionName(UnwindInfoSectionName),
EHFrameSectionName(EHFrameSectionName) {}
// Split compact unwind records, add keep-alive edges from functions to
// compact unwind records, and from compact unwind records to FDEs where
// needed.
//
// This method must be called *after* __eh_frame has been processed: it
// assumes that eh-frame records have been split up and keep-alive edges have
// been inserted.
Error prepareForPrune(LinkGraph &G) {
Section *CUSec = G.findSectionByName(CompactUnwindSectionName);
if (!CUSec || CUSec->empty()) {
LLVM_DEBUG({
dbgs() << "Compact unwind: No compact unwind info for " << G.getName()
<< "\n";
});
return Error::success();
}
LLVM_DEBUG({
dbgs() << "Compact unwind: preparing " << G.getName() << " for prune\n";
});
Section *EHFrameSec = G.findSectionByName(EHFrameSectionName);
if (auto Err = splitCompactUnwindBlocks(G, *CUSec, CURecTraits::Size))
return Err;
LLVM_DEBUG({
dbgs() << " Preparing " << CUSec->blocks_size() << " blocks in "
<< CompactUnwindSectionName << "\n";
});
for (auto *B : CUSec->blocks()) {
// Find target function edge.
Edge *PCBeginEdge = nullptr;
for (auto &E : B->edges_at(CURecTraits::FnFieldOffset)) {
PCBeginEdge = &E;
break;
}
if (!PCBeginEdge)
return make_error<JITLinkError>(
"In " + G.getName() + ", compact unwind record at " +
formatv("{0:x}", B->getAddress()) + " has no pc-begin edge");
if (!PCBeginEdge->getTarget().isDefined())
return make_error<JITLinkError>(
"In " + G.getName() + ", compact unwind record at " +
formatv("{0:x}", B->getAddress()) + " points at external symbol " +
*PCBeginEdge->getTarget().getName());
auto &Fn = PCBeginEdge->getTarget();
if (!Fn.isDefined()) {
LLVM_DEBUG({
dbgs() << "In " << CompactUnwindSectionName << " for " << G.getName()
<< " encountered unexpected pc-edge to undefined symbol "
<< Fn.getName() << "\n";
});
continue;
}
uint32_t Encoding = CURecTraits::readEncoding(B->getContent());
bool NeedsDWARF = CURecTraits::encodingSpecifiesDWARF(Encoding);
LLVM_DEBUG({
dbgs() << " Found record for function ";
if (Fn.hasName())
dbgs() << Fn.getName();
else
dbgs() << "<anon @ " << Fn.getAddress() << '>';
dbgs() << ": encoding = " << formatv("{0:x}", Encoding);
if (NeedsDWARF)
dbgs() << " (needs DWARF)";
dbgs() << "\n";
});
auto &CURecSym =
G.addAnonymousSymbol(*B, 0, CURecTraits::Size, false, false);
bool KeepAliveAlreadyPresent = false;
if (EHFrameSec) {
Edge *KeepAliveEdge = nullptr;
for (auto &E : Fn.getBlock().edges_at(0)) {
if (E.getKind() == Edge::KeepAlive && E.getTarget().isDefined() &&
&E.getTarget().getSection() == EHFrameSec) {
KeepAliveEdge = &E;
break;
}
}
if (KeepAliveEdge) {
// Found a keep-alive edge to an FDE in the eh-frame. Switch the keep
// alive edge to point to the CU and if the CU needs DWARF then add
// an extra keep-alive edge from the CU to the FDE.
auto &FDE = KeepAliveEdge->getTarget();
KeepAliveEdge->setTarget(CURecSym);
KeepAliveAlreadyPresent = true;
if (NeedsDWARF) {
LLVM_DEBUG({
dbgs() << " Adding keep-alive edge to FDE at "
<< FDE.getAddress() << "\n";
});
B->addEdge(Edge::KeepAlive, 0, FDE, 0);
}
} else {
if (NeedsDWARF)
return make_error<JITLinkError>(
"In " + G.getName() + ", compact unwind recard ot " +
formatv("{0:x}", B->getAddress()) +
" needs DWARF, but no FDE was found");
}
} else {
if (NeedsDWARF)
return make_error<JITLinkError>(
"In " + G.getName() + ", compact unwind recard ot " +
formatv("{0:x}", B->getAddress()) + " needs DWARF, but no " +
EHFrameSectionName + " section exists");
}
if (!KeepAliveAlreadyPresent) {
// No FDE edge. We'll need to add a new edge from the function back
// to the CU record.
Fn.getBlock().addEdge(Edge::KeepAlive, 0, CURecSym, 0);
}
}
return Error::success();
}
/// Process all __compact_unwind records and reserve space for __unwind_info.
Error processAndReserveUnwindInfo(LinkGraph &G) {
// Bail out early if no unwind info.
Section *CUSec = G.findSectionByName(CompactUnwindSectionName);
if (!CUSec)
return Error::success();
// The __LD/__compact_unwind section is only used as input for the linker.
// We'll create a new __TEXT,__unwind_info section for unwind info output.
CUSec->setMemLifetime(orc::MemLifetime::NoAlloc);
// Find / make a mach-header to act as the base for unwind-info offsets
// (and to report the arch / subarch to libunwind).
if (auto Err = getOrCreateCompactUnwindBase(G))
return Err;
// Error out if there's already unwind-info in the graph: We have no idea
// how to merge unwind-info sections.
if (G.findSectionByName(UnwindInfoSectionName))
return make_error<JITLinkError>("In " + G.getName() + ", " +
UnwindInfoSectionName +
" already exists");
// Process the __compact_unwind section to build the Records vector that
// we'll use for writing the __unwind_info section.
if (auto Err = processCompactUnwind(G, *CUSec))
return Err;
// Calculate the size of __unwind_info.
size_t UnwindInfoSectionSize =
UnwindInfoSectionHeaderSize +
Personalities.size() * PersonalityEntrySize +
(NumSecondLevelPages + 1) * IndexEntrySize + NumLSDAs * LSDAEntrySize +
NumSecondLevelPages * SecondLevelPageHeaderSize +
Records.size() * SecondLevelPageEntrySize;
LLVM_DEBUG({
dbgs() << "In " << G.getName() << ", reserving "
<< formatv("{0:x}", UnwindInfoSectionSize) << " bytes for "
<< UnwindInfoSectionName << "\n";
});
// Create the __unwind_info section and reserve space for it.
Section &UnwindInfoSec =
G.createSection(UnwindInfoSectionName, orc::MemProt::Read);
auto UnwindInfoSectionContent = G.allocateBuffer(UnwindInfoSectionSize);
memset(UnwindInfoSectionContent.data(), 0, UnwindInfoSectionContent.size());
auto &B = G.createMutableContentBlock(
UnwindInfoSec, UnwindInfoSectionContent, orc::ExecutorAddr(), 8, 0);
// Add Keep-alive edges from the __unwind_info block to all of the target
// functions.
for (auto &R : Records)
B.addEdge(Edge::KeepAlive, 0, *R.Fn, 0);
return Error::success();
}
Error writeUnwindInfo(LinkGraph &G) {
Section *CUSec = G.findSectionByName(CompactUnwindSectionName);
if (!CUSec || CUSec->empty())
return Error::success();
Section *UnwindInfoSec = G.findSectionByName(UnwindInfoSectionName);
if (!UnwindInfoSec)
return make_error<JITLinkError>("In " + G.getName() + ", " +
UnwindInfoSectionName +
" missing after allocation");
if (UnwindInfoSec->blocks_size() != 1)
return make_error<JITLinkError>(
"In " + G.getName() + ", " + UnwindInfoSectionName +
" contains more than one block post-allocation");
LLVM_DEBUG(
{ dbgs() << "Writing unwind info for " << G.getName() << "...\n"; });
mergeRecords();
auto &UnwindInfoBlock = **UnwindInfoSec->blocks().begin();
auto Content = UnwindInfoBlock.getMutableContent(G);
BinaryStreamWriter Writer(
{reinterpret_cast<uint8_t *>(Content.data()), Content.size()},
CURecTraits::Endianness);
// __unwind_info format, from mach-o/compact_unwind_encoding.h on Darwin:
//
// #define UNWIND_SECTION_VERSION 1
// struct unwind_info_section_header
// {
// uint32_t version; // UNWIND_SECTION_VERSION
// uint32_t commonEncodingsArraySectionOffset;
// uint32_t commonEncodingsArrayCount;
// uint32_t personalityArraySectionOffset;
// uint32_t personalityArrayCount;
// uint32_t indexSectionOffset;
// uint32_t indexCount;
// // compact_unwind_encoding_t[]
// // uint32_t personalities[]
// // unwind_info_section_header_index_entry[]
// // unwind_info_section_header_lsda_index_entry[]
// };
if (auto Err = writeHeader(G, Writer))
return Err;
// Skip common encodings: JITLink doesn't use them.
if (auto Err = writePersonalities(G, Writer))
return Err;
// Calculate the offset to the LSDAs.
size_t SectionOffsetToLSDAs =
Writer.getOffset() + (NumSecondLevelPages + 1) * IndexEntrySize;
// Calculate offset to the 1st second-level page.
size_t SectionOffsetToSecondLevelPages =
SectionOffsetToLSDAs + NumLSDAs * LSDAEntrySize;
if (auto Err = writeIndexes(G, Writer, SectionOffsetToLSDAs,
SectionOffsetToSecondLevelPages))
return Err;
if (auto Err = writeLSDAs(G, Writer))
return Err;
if (auto Err = writeSecondLevelPages(G, Writer))
return Err;
LLVM_DEBUG({
dbgs() << " Wrote " << formatv("{0:x}", Writer.getOffset())
<< " bytes of unwind info.\n";
});
return Error::success();
}
private:
// Calculate the size of unwind-info.
static constexpr size_t MaxPersonalities = 4;
static constexpr size_t PersonalityShift = 28;
static constexpr size_t UnwindInfoSectionHeaderSize = 4 * 7;
static constexpr size_t PersonalityEntrySize = 4;
static constexpr size_t IndexEntrySize = 3 * 4;
static constexpr size_t LSDAEntrySize = 2 * 4;
static constexpr size_t SecondLevelPageSize = 4096;
static constexpr size_t SecondLevelPageHeaderSize = 8;
static constexpr size_t SecondLevelPageEntrySize = 8;
static constexpr size_t NumRecordsPerSecondLevelPage =
(SecondLevelPageSize - SecondLevelPageHeaderSize) /
SecondLevelPageEntrySize;
struct CompactUnwindRecord {
Symbol *Fn = nullptr;
uint32_t Size = 0;
uint32_t Encoding = 0;
Symbol *LSDA = nullptr;
Symbol *FDE = nullptr;
};
Error processCompactUnwind(LinkGraph &G, Section &CUSec) {
// TODO: Reset NumLSDAs, Personalities and CompactUnwindRecords if
// processing more than once.
assert(NumLSDAs == 0 && "NumLSDAs should be zero");
assert(Records.empty() && "CompactUnwindRecords vector should be empty.");
assert(Personalities.empty() && "Personalities vector should be empty.");
SmallVector<CompactUnwindRecord> NonUniquedRecords;
NonUniquedRecords.reserve(CUSec.blocks_size());
// Process __compact_unwind blocks.
for (auto *B : CUSec.blocks()) {
CompactUnwindRecord R;
R.Encoding = CURecTraits::readEncoding(B->getContent());
for (auto &E : B->edges()) {
switch (E.getOffset()) {
case CURecTraits::FnFieldOffset:
// This could be the function-pointer, or the FDE keep-alive. Check
// the type to decide.
if (E.getKind() == Edge::KeepAlive)
R.FDE = &E.getTarget();
else
R.Fn = &E.getTarget();
break;
case CURecTraits::PersonalityFieldOffset: {
// Add the Personality to the Personalities map and update the
// encoding.
size_t PersonalityIdx = 0;
for (; PersonalityIdx != Personalities.size(); ++PersonalityIdx)
if (Personalities[PersonalityIdx] == &E.getTarget())
break;
if (PersonalityIdx == MaxPersonalities)
return make_error<JITLinkError>(
"In " + G.getName() +
", __compact_unwind contains too many personalities (max " +
formatv("{}", MaxPersonalities) + ")");
if (PersonalityIdx == Personalities.size())
Personalities.push_back(&E.getTarget());
R.Encoding |= (PersonalityIdx + 1) << PersonalityShift;
break;
}
case CURecTraits::LSDAFieldOffset:
++NumLSDAs;
R.LSDA = &E.getTarget();
break;
default:
return make_error<JITLinkError>("In " + G.getName() +
", compact unwind record at " +
formatv("{0:x}", B->getAddress()) +
" has unrecognized edge at offset " +
formatv("{0:x}", E.getOffset()));
}
}
Records.push_back(R);
}
// Sort the records into ascending order.
llvm::sort(Records, [](const CompactUnwindRecord &LHS,
const CompactUnwindRecord &RHS) {
return LHS.Fn->getAddress() < RHS.Fn->getAddress();
});
// Calculate the number of second-level pages required.
NumSecondLevelPages = (Records.size() + NumRecordsPerSecondLevelPage - 1) /
NumRecordsPerSecondLevelPage;
// Convert personality symbols to GOT entry pointers.
typename CURecTraits::GOTManager GOT(G);
for (auto &Personality : Personalities)
Personality = &GOT.getEntryForTarget(G, *Personality);
LLVM_DEBUG({
dbgs() << " In " << G.getName() << ", " << CompactUnwindSectionName
<< ": raw records = " << Records.size()
<< ", personalities = " << Personalities.size()
<< ", lsdas = " << NumLSDAs << "\n";
});
return Error::success();
}
void mergeRecords() {
SmallVector<CompactUnwindRecord> NonUniqued = std::move(Records);
Records.reserve(NonUniqued.size());
Records.push_back(NonUniqued.front());
for (size_t I = 1; I != NonUniqued.size(); ++I) {
auto &Next = NonUniqued[I];
auto &Last = Records.back();
bool NextNeedsDWARF = CURecTraits::encodingSpecifiesDWARF(Next.Encoding);
bool CannotBeMerged = CURecTraits::encodingCannotBeMerged(Next.Encoding);
if (NextNeedsDWARF || (Next.Encoding != Last.Encoding) ||
CannotBeMerged || Next.LSDA || Last.LSDA)
Records.push_back(Next);
}
// Recalculate derived values that may have changed.
NumSecondLevelPages = (Records.size() + NumRecordsPerSecondLevelPage - 1) /
NumRecordsPerSecondLevelPage;
}
Error writeHeader(LinkGraph &G, BinaryStreamWriter &W) {
if (!isUInt<32>(NumSecondLevelPages + 1))
return make_error<JITLinkError>("In " + G.getName() + ", too many " +
UnwindInfoSectionName +
"second-level pages required");
// Write __unwind_info header.
size_t IndexArrayOffset = UnwindInfoSectionHeaderSize +
Personalities.size() * PersonalityEntrySize;
cantFail(W.writeInteger<uint32_t>(1));
cantFail(W.writeInteger<uint32_t>(UnwindInfoSectionHeaderSize));
cantFail(W.writeInteger<uint32_t>(0));
cantFail(W.writeInteger<uint32_t>(UnwindInfoSectionHeaderSize));
cantFail(W.writeInteger<uint32_t>(Personalities.size()));
cantFail(W.writeInteger<uint32_t>(IndexArrayOffset));
cantFail(W.writeInteger<uint32_t>(NumSecondLevelPages + 1));
return Error::success();
}
Error writePersonalities(LinkGraph &G, BinaryStreamWriter &W) {
// Write personalities.
for (auto *PSym : Personalities) {
auto Delta = PSym->getAddress() - CompactUnwindBase->getAddress();
if (!isUInt<32>(Delta))
return makePersonalityRangeError(G, *PSym);
cantFail(W.writeInteger<uint32_t>(Delta));
}
return Error::success();
}
Error writeIndexes(LinkGraph &G, BinaryStreamWriter &W,
size_t SectionOffsetToLSDAs,
size_t SectionOffsetToSecondLevelPages) {
// Assume that function deltas are ok in this method -- we'll error
// check all of them when we write the second level pages.
// Write the header index entries.
size_t RecordIdx = 0;
size_t NumPreviousLSDAs = 0;
for (auto &R : Records) {
// If this record marks the start of a new second level page.
if (RecordIdx % NumRecordsPerSecondLevelPage == 0) {
auto FnDelta = R.Fn->getAddress() - CompactUnwindBase->getAddress();
auto SecondLevelPageOffset =
SectionOffsetToSecondLevelPages +
SecondLevelPageSize * (RecordIdx / NumRecordsPerSecondLevelPage);
auto LSDAOffset =
SectionOffsetToLSDAs + NumPreviousLSDAs * LSDAEntrySize;
cantFail(W.writeInteger<uint32_t>(FnDelta));
cantFail(W.writeInteger<uint32_t>(SecondLevelPageOffset));
cantFail(W.writeInteger<uint32_t>(LSDAOffset));
}
if (R.LSDA)
++NumPreviousLSDAs;
++RecordIdx;
}
// Write the index array terminator.
{
auto FnEndDelta =
Records.back().Fn->getRange().End - CompactUnwindBase->getAddress();
if (LLVM_UNLIKELY(!isUInt<32>(FnEndDelta)))
return make_error<JITLinkError>(
"In " + G.getName() + " " + UnwindInfoSectionName +
", delta to end of functions " +
formatv("{0:x}", Records.back().Fn->getRange().End) +
" exceeds 32 bits");
cantFail(W.writeInteger<uint32_t>(FnEndDelta));
cantFail(W.writeInteger<uint32_t>(0));
cantFail(W.writeInteger<uint32_t>(SectionOffsetToSecondLevelPages));
}
return Error::success();
}
Error writeLSDAs(LinkGraph &G, BinaryStreamWriter &W) {
// As with writeIndexes, assume that function deltas are ok for now.
for (auto &R : Records) {
if (R.LSDA) {
auto FnDelta = R.Fn->getAddress() - CompactUnwindBase->getAddress();
auto LSDADelta = R.LSDA->getAddress() - CompactUnwindBase->getAddress();
if (LLVM_UNLIKELY(!isUInt<32>(LSDADelta)))
return make_error<JITLinkError>(
"In " + G.getName() + " " + UnwindInfoSectionName +
", delta to lsda at " + formatv("{0:x}", R.LSDA->getAddress()) +
" exceeds 32 bits");
cantFail(W.writeInteger<uint32_t>(FnDelta));
cantFail(W.writeInteger<uint32_t>(LSDADelta));
}
}
return Error::success();
}
Error writeSecondLevelPages(LinkGraph &G, BinaryStreamWriter &W) {
size_t RecordIdx = 0;
for (auto &R : Records) {
// When starting a new second-level page, write the page header:
//
// 2 : uint32_t -- UNWIND_SECOND_LEVEL_REGULAR
// 8 : uint16_t -- size of second level page table header
// count : uint16_t -- num entries in this second-level page
if (RecordIdx % NumRecordsPerSecondLevelPage == 0) {
constexpr uint32_t SecondLevelPageHeaderKind = 2;
constexpr uint16_t SecondLevelPageHeaderSize = 8;
uint16_t SecondLevelPageNumEntries =
std::min(Records.size() - RecordIdx, NumRecordsPerSecondLevelPage);
cantFail(W.writeInteger<uint32_t>(SecondLevelPageHeaderKind));
cantFail(W.writeInteger<uint16_t>(SecondLevelPageHeaderSize));
cantFail(W.writeInteger<uint16_t>(SecondLevelPageNumEntries));
}
// Write entry.
auto FnDelta = R.Fn->getAddress() - CompactUnwindBase->getAddress();
if (LLVM_UNLIKELY(!isUInt<32>(FnDelta)))
return make_error<JITLinkError>(
"In " + G.getName() + " " + UnwindInfoSectionName +
", delta to function at " + formatv("{0:x}", R.Fn->getAddress()) +
" exceeds 32 bits");
auto Encoding = R.Encoding;
if (LLVM_UNLIKELY(CURecTraits::encodingSpecifiesDWARF(R.Encoding))) {
if (!EHFrameBase)
EHFrameBase = SectionRange(R.FDE->getSection()).getStart();
auto FDEDelta = R.FDE->getAddress() - EHFrameBase;
if (auto EncodedFDEDelta = CURecTraits::encodeDWARFOffset(FDEDelta))
Encoding |= *EncodedFDEDelta;
else
return make_error<JITLinkError>(
"In " + G.getName() + " " + UnwindInfoSectionName +
", cannot encode delta " + formatv("{0:x}", FDEDelta) +
" to FDE at " + formatv("{0:x}", R.FDE->getAddress()));
}
cantFail(W.writeInteger<uint32_t>(FnDelta));
cantFail(W.writeInteger<uint32_t>(Encoding));
++RecordIdx;
}
return Error::success();
}
Error getOrCreateCompactUnwindBase(LinkGraph &G) {
auto Name = G.intern("__jitlink$libunwind_dso_base");
CompactUnwindBase = G.findAbsoluteSymbolByName(Name);
if (!CompactUnwindBase) {
if (auto LocalCUBase = getOrCreateLocalMachOHeader(G)) {
CompactUnwindBase = &*LocalCUBase;
auto &B = LocalCUBase->getBlock();
G.addDefinedSymbol(B, 0, *Name, B.getSize(), Linkage::Strong,
Scope::Local, false, true);
} else
return LocalCUBase.takeError();
}
CompactUnwindBase->setLive(true);
return Error::success();
}
Error makePersonalityRangeError(LinkGraph &G, Symbol &PSym) {
std::string ErrMsg;
{
raw_string_ostream ErrStream(ErrMsg);
ErrStream << "In " << G.getName() << " " << UnwindInfoSectionName
<< ", personality ";
if (PSym.hasName())
ErrStream << PSym.getName() << " ";
ErrStream << "at " << PSym.getAddress()
<< " is out of 32-bit delta range of compact-unwind base at "
<< CompactUnwindBase->getAddress();
}
return make_error<JITLinkError>(std::move(ErrMsg));
}
StringRef CompactUnwindSectionName;
StringRef UnwindInfoSectionName;
StringRef EHFrameSectionName;
Symbol *CompactUnwindBase = nullptr;
orc::ExecutorAddr EHFrameBase;
size_t NumLSDAs = 0;
size_t NumSecondLevelPages = 0;
SmallVector<Symbol *, MaxPersonalities> Personalities;
SmallVector<CompactUnwindRecord> Records;
};
} // end namespace jitlink
} // end namespace llvm
#undef DEBUG_TYPE
#endif // LIB_EXECUTIONENGINE_JITLINK_COMPACTUNWINDSUPPORTIMPL_H