13#ifndef LIB_EXECUTIONENGINE_JITLINK_COMPACTUNWINDSUPPORTIMPL_H
14#define LIB_EXECUTIONENGINE_JITLINK_COMPACTUNWINDSUPPORTIMPL_H
21#define DEBUG_TYPE "jitlink_cu"
53 "Truncated CU record?");
54 return support::endian::read32<CRTPImpl::Endianness>(RecordContent.
data() +
60 "Truncated CU record?");
61 return support::endian::read32<CRTPImpl::Endianness>(RecordContent.
data() +
67 static_cast<uint32_t>(Delta) & CRTPImpl::DWARFSectionOffsetMask;
80 : CompactUnwindSectionName(CompactUnwindSectionName),
81 UnwindInfoSectionName(UnwindInfoSectionName),
82 EHFrameSectionName(EHFrameSectionName) {}
92 Section *CUSec =
G.findSectionByName(CompactUnwindSectionName);
93 if (!CUSec || CUSec->
empty()) {
95 dbgs() <<
"Compact unwind: No compact unwind info for " <<
G.getName()
102 dbgs() <<
"Compact unwind: preparing " <<
G.getName() <<
" for prune\n";
105 Section *EHFrameSec =
G.findSectionByName(EHFrameSectionName);
112 << CompactUnwindSectionName <<
"\n";
115 for (
auto *
B : CUSec->
blocks()) {
118 Edge *PCBeginEdge =
nullptr;
119 for (
auto &
E :
B->edges_at(CURecTraits::FnFieldOffset)) {
125 return make_error<JITLinkError>(
126 "In " +
G.getName() +
", compact unwind record at " +
127 formatv(
"{0:x}",
B->getAddress()) +
" has no pc-begin edge");
130 return make_error<JITLinkError>(
131 "In " +
G.getName() +
", compact unwind record at " +
132 formatv(
"{0:x}",
B->getAddress()) +
" points at external symbol " +
137 if (!Fn.isDefined()) {
139 dbgs() <<
"In " << CompactUnwindSectionName <<
" for " <<
G.getName()
140 <<
" encountered unexpected pc-edge to undefined symbol "
141 << Fn.getName() <<
"\n";
146 uint32_t Encoding = CURecTraits::readEncoding(
B->getContent());
147 bool NeedsDWARF = CURecTraits::encodingSpecifiesDWARF(Encoding);
150 dbgs() <<
" Found record for function ";
152 dbgs() << Fn.getName();
154 dbgs() <<
"<anon @ " << Fn.getAddress() <<
'>';
155 dbgs() <<
": encoding = " <<
formatv(
"{0:x}", Encoding);
157 dbgs() <<
" (needs DWARF)";
162 G.addAnonymousSymbol(*
B, 0, CURecTraits::Size,
false,
false);
164 bool KeepAliveAlreadyPresent =
false;
166 Edge *KeepAliveEdge =
nullptr;
167 for (
auto &
E : Fn.getBlock().edges_at(0)) {
168 if (
E.getKind() == Edge::KeepAlive &&
E.getTarget().isDefined() &&
169 &
E.getTarget().getSection() == EHFrameSec) {
181 KeepAliveAlreadyPresent =
true;
184 dbgs() <<
" Adding keep-alive edge to FDE at "
185 << FDE.getAddress() <<
"\n";
187 B->addEdge(Edge::KeepAlive, 0, FDE, 0);
191 return make_error<JITLinkError>(
192 "In " +
G.getName() +
", compact unwind recard ot " +
194 " needs DWARF, but no FDE was found");
198 return make_error<JITLinkError>(
199 "In " +
G.getName() +
", compact unwind recard ot " +
200 formatv(
"{0:x}",
B->getAddress()) +
" needs DWARF, but no " +
201 EHFrameSectionName +
" section exists");
204 if (!KeepAliveAlreadyPresent) {
207 Fn.getBlock().addEdge(Edge::KeepAlive, 0, CURecSym, 0);
217 Section *CUSec =
G.findSectionByName(CompactUnwindSectionName);
227 if (
auto Err = getOrCreateCompactUnwindBase(
G))
232 if (
G.findSectionByName(UnwindInfoSectionName))
233 return make_error<JITLinkError>(
"In " +
G.getName() +
", " +
234 UnwindInfoSectionName +
239 if (
auto Err = processCompactUnwind(
G, *CUSec))
243 size_t UnwindInfoSectionSize =
244 UnwindInfoSectionHeaderSize +
245 Personalities.size() * PersonalityEntrySize +
246 (NumSecondLevelPages + 1) * IndexEntrySize + NumLSDAs * LSDAEntrySize +
247 NumSecondLevelPages * SecondLevelPageHeaderSize +
248 Records.
size() * SecondLevelPageEntrySize;
251 dbgs() <<
"In " <<
G.getName() <<
", reserving "
252 <<
formatv(
"{0:x}", UnwindInfoSectionSize) <<
" bytes for "
253 << UnwindInfoSectionName <<
"\n";
260 auto UnwindInfoSectionContent =
G.allocateBuffer(UnwindInfoSectionSize);
261 memset(UnwindInfoSectionContent.data(), 0, UnwindInfoSectionContent.size());
262 auto &
B =
G.createMutableContentBlock(
267 for (
auto &R : Records)
268 B.addEdge(Edge::KeepAlive, 0, *R.Fn, 0);
274 Section *CUSec =
G.findSectionByName(CompactUnwindSectionName);
275 if (!CUSec || CUSec->
empty())
278 Section *UnwindInfoSec =
G.findSectionByName(UnwindInfoSectionName);
280 return make_error<JITLinkError>(
"In " +
G.getName() +
", " +
281 UnwindInfoSectionName +
282 " missing after allocation");
285 return make_error<JITLinkError>(
286 "In " +
G.getName() +
", " + UnwindInfoSectionName +
287 " contains more than one block post-allocation");
290 {
dbgs() <<
"Writing unwind info for " <<
G.getName() <<
"...\n"; });
294 auto &UnwindInfoBlock = **UnwindInfoSec->
blocks().begin();
295 auto Content = UnwindInfoBlock.getMutableContent(
G);
298 CURecTraits::Endianness);
318 if (
auto Err = writeHeader(
G, Writer))
323 if (
auto Err = writePersonalities(
G, Writer))
327 size_t SectionOffsetToLSDAs =
328 Writer.getOffset() + (NumSecondLevelPages + 1) * IndexEntrySize;
331 size_t SectionOffsetToSecondLevelPages =
332 SectionOffsetToLSDAs + NumLSDAs * LSDAEntrySize;
334 if (
auto Err = writeIndexes(
G, Writer, SectionOffsetToLSDAs,
335 SectionOffsetToSecondLevelPages))
338 if (
auto Err = writeLSDAs(
G, Writer))
341 if (
auto Err = writeSecondLevelPages(
G, Writer))
345 dbgs() <<
" Wrote " <<
formatv(
"{0:x}", Writer.getOffset())
346 <<
" bytes of unwind info.\n";
354 static constexpr size_t MaxPersonalities = 4;
355 static constexpr size_t PersonalityShift = 28;
357 static constexpr size_t UnwindInfoSectionHeaderSize = 4 * 7;
358 static constexpr size_t PersonalityEntrySize = 4;
359 static constexpr size_t IndexEntrySize = 3 * 4;
360 static constexpr size_t LSDAEntrySize = 2 * 4;
361 static constexpr size_t SecondLevelPageSize = 4096;
362 static constexpr size_t SecondLevelPageHeaderSize = 8;
363 static constexpr size_t SecondLevelPageEntrySize = 8;
364 static constexpr size_t NumRecordsPerSecondLevelPage =
365 (SecondLevelPageSize - SecondLevelPageHeaderSize) /
366 SecondLevelPageEntrySize;
368 struct CompactUnwindRecord {
376 Error processCompactUnwind(LinkGraph &
G, Section &CUSec) {
379 assert(NumLSDAs == 0 &&
"NumLSDAs should be zero");
380 assert(Records.
empty() &&
"CompactUnwindRecords vector should be empty.");
381 assert(Personalities.empty() &&
"Personalities vector should be empty.");
384 NonUniquedRecords.
reserve(CUSec.blocks_size());
387 for (
auto *
B : CUSec.blocks()) {
388 CompactUnwindRecord R;
389 R.Encoding = CURecTraits::readEncoding(
B->getContent());
390 for (
auto &
E :
B->edges()) {
391 switch (
E.getOffset()) {
392 case CURecTraits::FnFieldOffset:
395 if (
E.getKind() == Edge::KeepAlive)
396 R.FDE = &
E.getTarget();
398 R.Fn = &
E.getTarget();
400 case CURecTraits::PersonalityFieldOffset: {
403 size_t PersonalityIdx = 0;
404 for (; PersonalityIdx != Personalities.size(); ++PersonalityIdx)
405 if (Personalities[PersonalityIdx] == &
E.getTarget())
407 if (PersonalityIdx == MaxPersonalities)
408 return make_error<JITLinkError>(
409 "In " +
G.getName() +
410 ", __compact_unwind contains too many personalities (max " +
411 formatv(
"{}", MaxPersonalities) +
")");
412 if (PersonalityIdx == Personalities.size())
413 Personalities.push_back(&
E.getTarget());
415 R.Encoding |= (PersonalityIdx + 1) << PersonalityShift;
418 case CURecTraits::LSDAFieldOffset:
420 R.LSDA = &
E.getTarget();
423 return make_error<JITLinkError>(
"In " +
G.getName() +
424 ", compact unwind record at " +
426 " has unrecognized edge at offset " +
435 const CompactUnwindRecord &
RHS) {
436 return LHS.Fn->getAddress() <
RHS.Fn->getAddress();
440 NumSecondLevelPages = (Records.
size() + NumRecordsPerSecondLevelPage - 1) /
441 NumRecordsPerSecondLevelPage;
444 typename CURecTraits::GOTManager
GOT(
G);
445 for (
auto &Personality : Personalities)
446 Personality = &
GOT.getEntryForTarget(
G, *Personality);
449 dbgs() <<
" In " <<
G.getName() <<
", " << CompactUnwindSectionName
450 <<
": raw records = " << Records.
size()
451 <<
", personalities = " << Personalities.size()
452 <<
", lsdas = " << NumLSDAs <<
"\n";
458 void mergeRecords() {
459 SmallVector<CompactUnwindRecord> NonUniqued = std::move(Records);
460 Records.
reserve(NonUniqued.size());
463 for (
size_t I = 1;
I != NonUniqued.size(); ++
I) {
464 auto &Next = NonUniqued[
I];
467 bool NextNeedsDWARF = CURecTraits::encodingSpecifiesDWARF(Next.Encoding);
468 bool CannotBeMerged = CURecTraits::encodingCannotBeMerged(Next.Encoding);
469 if (NextNeedsDWARF || (Next.Encoding !=
Last.Encoding) ||
470 CannotBeMerged || Next.LSDA ||
Last.LSDA)
475 NumSecondLevelPages = (Records.
size() + NumRecordsPerSecondLevelPage - 1) /
476 NumRecordsPerSecondLevelPage;
479 Error writeHeader(LinkGraph &
G, BinaryStreamWriter &W) {
480 if (!isUInt<32>(NumSecondLevelPages + 1))
481 return make_error<JITLinkError>(
"In " +
G.getName() +
", too many " +
482 UnwindInfoSectionName +
483 "second-level pages required");
486 size_t IndexArrayOffset = UnwindInfoSectionHeaderSize +
487 Personalities.size() * PersonalityEntrySize;
500 Error writePersonalities(LinkGraph &
G, BinaryStreamWriter &W) {
502 for (
auto *PSym : Personalities) {
503 auto Delta = PSym->getAddress() - CompactUnwindBase->
getAddress();
504 if (!isUInt<32>(Delta))
505 return makePersonalityRangeError(
G, *PSym);
511 Error writeIndexes(LinkGraph &
G, BinaryStreamWriter &W,
512 size_t SectionOffsetToLSDAs,
513 size_t SectionOffsetToSecondLevelPages) {
518 size_t RecordIdx = 0;
519 size_t NumPreviousLSDAs = 0;
520 for (
auto &R : Records) {
522 if (RecordIdx % NumRecordsPerSecondLevelPage == 0) {
523 auto FnDelta =
R.Fn->getAddress() - CompactUnwindBase->
getAddress();
524 auto SecondLevelPageOffset =
525 SectionOffsetToSecondLevelPages +
526 SecondLevelPageSize * (RecordIdx / NumRecordsPerSecondLevelPage);
528 SectionOffsetToLSDAs + NumPreviousLSDAs * LSDAEntrySize;
545 return make_error<JITLinkError>(
546 "In " +
G.getName() +
" " + UnwindInfoSectionName +
547 ", delta to end of functions " +
559 Error writeLSDAs(LinkGraph &
G, BinaryStreamWriter &W) {
561 for (
auto &R : Records) {
563 auto FnDelta =
R.Fn->getAddress() - CompactUnwindBase->
getAddress();
564 auto LSDADelta =
R.LSDA->getAddress() - CompactUnwindBase->
getAddress();
567 return make_error<JITLinkError>(
568 "In " +
G.getName() +
" " + UnwindInfoSectionName +
569 ", delta to lsda at " +
formatv(
"{0:x}",
R.LSDA->getAddress()) +
580 Error writeSecondLevelPages(LinkGraph &
G, BinaryStreamWriter &W) {
581 size_t RecordIdx = 0;
583 for (
auto &R : Records) {
589 if (RecordIdx % NumRecordsPerSecondLevelPage == 0) {
590 constexpr uint32_t SecondLevelPageHeaderKind = 2;
591 constexpr uint16_t SecondLevelPageHeaderSize = 8;
592 uint16_t SecondLevelPageNumEntries =
593 std::min(
Records.size() - RecordIdx, NumRecordsPerSecondLevelPage);
601 auto FnDelta =
R.Fn->getAddress() - CompactUnwindBase->
getAddress();
604 return make_error<JITLinkError>(
605 "In " +
G.getName() +
" " + UnwindInfoSectionName +
606 ", delta to function at " +
formatv(
"{0:x}",
R.Fn->getAddress()) +
609 auto Encoding =
R.Encoding;
611 if (
LLVM_UNLIKELY(CURecTraits::encodingSpecifiesDWARF(
R.Encoding))) {
613 EHFrameBase = SectionRange(
R.FDE->getSection()).getStart();
614 auto FDEDelta =
R.FDE->getAddress() - EHFrameBase;
616 if (
auto EncodedFDEDelta = CURecTraits::encodeDWARFOffset(FDEDelta))
617 Encoding |= *EncodedFDEDelta;
619 return make_error<JITLinkError>(
620 "In " +
G.getName() +
" " + UnwindInfoSectionName +
621 ", cannot encode delta " +
formatv(
"{0:x}", FDEDelta) +
622 " to FDE at " +
formatv(
"{0:x}",
R.FDE->getAddress()));
634 Error getOrCreateCompactUnwindBase(LinkGraph &
G) {
635 auto Name =
G.intern(
"__jitlink$libunwind_dso_base");
636 CompactUnwindBase =
G.findAbsoluteSymbolByName(
Name);
637 if (!CompactUnwindBase) {
639 CompactUnwindBase = &*LocalCUBase;
640 auto &
B = LocalCUBase->getBlock();
644 return LocalCUBase.takeError();
646 CompactUnwindBase->
setLive(
true);
650 Error makePersonalityRangeError(LinkGraph &
G, Symbol &PSym) {
653 raw_string_ostream ErrStream(ErrMsg);
654 ErrStream <<
"In " <<
G.getName() <<
" " << UnwindInfoSectionName
657 ErrStream << PSym.getName() <<
" ";
658 ErrStream <<
"at " << PSym.getAddress()
659 <<
" is out of 32-bit delta range of compact-unwind base at "
662 return make_error<JITLinkError>(std::move(ErrMsg));
665 StringRef CompactUnwindSectionName;
666 StringRef UnwindInfoSectionName;
667 StringRef EHFrameSectionName;
668 Symbol *CompactUnwindBase =
nullptr;
669 orc::ExecutorAddr EHFrameBase;
672 size_t NumSecondLevelPages = 0;
673 SmallVector<Symbol *, MaxPersonalities> Personalities;
674 SmallVector<CompactUnwindRecord>
Records;
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define LLVM_UNLIKELY(EXPR)
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
size_t size() const
size - Get the array size.
Provides write only access to a subclass of WritableBinaryStream.
Lightweight error class with error context and mandatory checking.
static ErrorSuccess success()
Create a success value.
void reserve(size_type N)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
StringRef - Represent a constant reference to a string, i.e.
Architecture specific implementation of CompactUnwindManager.
Error prepareForPrune(LinkGraph &G)
Error processAndReserveUnwindInfo(LinkGraph &G)
Process all __compact_unwind records and reserve space for __unwind_info.
CompactUnwindManager(StringRef CompactUnwindSectionName, StringRef UnwindInfoSectionName, StringRef EHFrameSectionName)
Error writeUnwindInfo(LinkGraph &G)
Represents fixups and constraints in the LinkGraph.
Symbol & getTarget() const
void setTarget(Symbol &Target)
Represents an object file section.
iterator_range< block_iterator > blocks()
Returns an iterator over the blocks defined in this section.
bool empty() const
Returns true if this section is empty (contains no blocks or symbols).
BlockSet::size_type blocks_size() const
Returns the number of blocks in this section.
void setMemLifetime(orc::MemLifetime ML)
Set the memory lifetime policy for this section.
const orc::SymbolStringPtr & getName() const
Returns the name of this symbol (empty if the symbol is anonymous).
bool isDefined() const
Returns true if this Symbol has content (potentially) defined within this object file (i....
orc::ExecutorAddr getAddress() const
Returns the address of this symbol.
void setLive(bool IsLive)
Set this symbol's live bit.
Represents an address in the executor process.
llvm::SmallVector< std::shared_ptr< RecordsSlice >, 4 > Records
LLVM_ABI Expected< Symbol & > getOrCreateLocalMachOHeader(LinkGraph &G)
Gets or creates a MachO header for the current LinkGraph.
Error splitCompactUnwindBlocks(LinkGraph &G, Section &CompactUnwindSection, size_t RecordSize)
Split blocks in an __LD,__compact_unwind section on record boundaries.
@ NoAlloc
NoAlloc memory should not be allocated by the JITLinkMemoryManager at all.
This is an optimization pass for GlobalISel generic memory operations.
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
void sort(IteratorTy Start, IteratorTy End)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
CRTP base for compact unwind traits classes.
static uint32_t readPCRangeSize(ArrayRef< char > RecordContent)
static constexpr size_t PointerSize
static constexpr size_t SizeFieldOffset
static constexpr size_t Size
static constexpr size_t FnFieldOffset
static constexpr size_t LSDAFieldOffset
static std::optional< uint32_t > encodeDWARFOffset(size_t Delta)
static constexpr size_t PersonalityFieldOffset
static uint32_t readEncoding(ArrayRef< char > RecordContent)
static constexpr size_t EncodingFieldOffset