xref: /freebsd/contrib/llvm-project/llvm/lib/ExecutionEngine/JITLink/CompactUnwindSupport.h (revision 2c2ec6bbc9cc7762a250ffe903bda6c2e44d25ff)
1 //===- CompactUnwindSupportImpl.h - Compact Unwind format impl --*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Compact Unwind format support implementation details.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef LIB_EXECUTIONENGINE_JITLINK_COMPACTUNWINDSUPPORTIMPL_H
14 #define LIB_EXECUTIONENGINE_JITLINK_COMPACTUNWINDSUPPORTIMPL_H
15 
16 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/ExecutionEngine/JITLink/MachO.h"
18 #include "llvm/Support/Debug.h"
19 #include "llvm/Support/Endian.h"
20 
21 #define DEBUG_TYPE "jitlink_cu"
22 
23 namespace llvm {
24 namespace jitlink {
25 
26 /// Split blocks in an __LD,__compact_unwind section on record boundaries.
27 /// When this function returns edges within each record are guaranteed to be
28 /// sorted by offset.
29 Error splitCompactUnwindBlocks(LinkGraph &G, Section &CompactUnwindSection,
30                                size_t RecordSize);
31 
32 /// CRTP base for compact unwind traits classes. Automatically provides derived
33 /// constants.
34 ///
35 /// FIXME: Passing PtrSize as a template parameter is a hack to work around a
36 ///        bug in older MSVC compilers (until at least MSVC 15) where constexpr
37 ///        fields in the CRTP impl class were not visible to the base class.
38 ///        Once we no longer need to support these compilers the PtrSize
39 ///        template argument should be removed and PointerSize should be
40 ///        defined as a member in the CRTP Impl classes.
41 template <typename CRTPImpl, size_t PtrSize> struct CompactUnwindTraits {
42   static constexpr size_t PointerSize = PtrSize;
43   static constexpr size_t Size = 3 * PointerSize + 2 * 4;
44   static constexpr size_t FnFieldOffset = 0;
45   static constexpr size_t SizeFieldOffset = FnFieldOffset + PointerSize;
46   static constexpr size_t EncodingFieldOffset = SizeFieldOffset + 4;
47   static constexpr size_t PersonalityFieldOffset = EncodingFieldOffset + 4;
48   static constexpr size_t LSDAFieldOffset =
49       PersonalityFieldOffset + PointerSize;
50 
51   static uint32_t readPCRangeSize(ArrayRef<char> RecordContent) {
52     assert(SizeFieldOffset + 4 <= RecordContent.size() &&
53            "Truncated CU record?");
54     return support::endian::read32<CRTPImpl::Endianness>(RecordContent.data() +
55                                                          SizeFieldOffset);
56   }
57 
58   static uint32_t readEncoding(ArrayRef<char> RecordContent) {
59     assert(EncodingFieldOffset + 4 <= RecordContent.size() &&
60            "Truncated CU record?");
61     return support::endian::read32<CRTPImpl::Endianness>(RecordContent.data() +
62                                                          EncodingFieldOffset);
63   }
64 
65   static std::optional<uint32_t> encodeDWARFOffset(size_t Delta) {
66     uint32_t Encoded =
67         static_cast<uint32_t>(Delta) & CRTPImpl::DWARFSectionOffsetMask;
68     if (Encoded != Delta)
69       return std::nullopt;
70     return Encoded;
71   }
72 };
73 
74 /// Architecture specific implementation of CompactUnwindManager.
75 template <typename CURecTraits> class CompactUnwindManager {
76 public:
77   CompactUnwindManager(StringRef CompactUnwindSectionName,
78                        StringRef UnwindInfoSectionName,
79                        StringRef EHFrameSectionName)
80       : CompactUnwindSectionName(CompactUnwindSectionName),
81         UnwindInfoSectionName(UnwindInfoSectionName),
82         EHFrameSectionName(EHFrameSectionName) {}
83 
84   // Split compact unwind records, add keep-alive edges from functions to
85   // compact unwind records, and from compact unwind records to FDEs where
86   // needed.
87   //
88   // This method must be called *after* __eh_frame has been processed: it
89   // assumes that eh-frame records have been split up and keep-alive edges have
90   // been inserted.
91   Error prepareForPrune(LinkGraph &G) {
92     Section *CUSec = G.findSectionByName(CompactUnwindSectionName);
93     if (!CUSec || CUSec->empty()) {
94       LLVM_DEBUG({
95         dbgs() << "Compact unwind: No compact unwind info for " << G.getName()
96                << "\n";
97       });
98       return Error::success();
99     }
100 
101     LLVM_DEBUG({
102       dbgs() << "Compact unwind: preparing " << G.getName() << " for prune\n";
103     });
104 
105     Section *EHFrameSec = G.findSectionByName(EHFrameSectionName);
106 
107     if (auto Err = splitCompactUnwindBlocks(G, *CUSec, CURecTraits::Size))
108       return Err;
109 
110     LLVM_DEBUG({
111       dbgs() << "  Preparing " << CUSec->blocks_size() << " blocks in "
112              << CompactUnwindSectionName << "\n";
113     });
114 
115     for (auto *B : CUSec->blocks()) {
116 
117       // Find target function edge.
118       Edge *PCBeginEdge = nullptr;
119       for (auto &E : B->edges_at(CURecTraits::FnFieldOffset)) {
120         PCBeginEdge = &E;
121         break;
122       }
123 
124       if (!PCBeginEdge)
125         return make_error<JITLinkError>(
126             "In " + G.getName() + ", compact unwind record at " +
127             formatv("{0:x}", B->getAddress()) + " has no pc-begin edge");
128 
129       if (!PCBeginEdge->getTarget().isDefined())
130         return make_error<JITLinkError>(
131             "In " + G.getName() + ", compact unwind record at " +
132             formatv("{0:x}", B->getAddress()) + " points at external symbol " +
133             *PCBeginEdge->getTarget().getName());
134 
135       auto &Fn = PCBeginEdge->getTarget();
136 
137       if (!Fn.isDefined()) {
138         LLVM_DEBUG({
139           dbgs() << "In " << CompactUnwindSectionName << " for " << G.getName()
140                  << " encountered unexpected pc-edge to undefined symbol "
141                  << Fn.getName() << "\n";
142         });
143         continue;
144       }
145 
146       uint32_t Encoding = CURecTraits::readEncoding(B->getContent());
147       bool NeedsDWARF = CURecTraits::encodingSpecifiesDWARF(Encoding);
148 
149       LLVM_DEBUG({
150         dbgs() << "    Found record for function ";
151         if (Fn.hasName())
152           dbgs() << Fn.getName();
153         else
154           dbgs() << "<anon @ " << Fn.getAddress() << '>';
155         dbgs() << ": encoding = " << formatv("{0:x}", Encoding);
156         if (NeedsDWARF)
157           dbgs() << " (needs DWARF)";
158         dbgs() << "\n";
159       });
160 
161       auto &CURecSym =
162           G.addAnonymousSymbol(*B, 0, CURecTraits::Size, false, false);
163 
164       bool KeepAliveAlreadyPresent = false;
165       if (EHFrameSec) {
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) {
170             KeepAliveEdge = &E;
171             break;
172           }
173         }
174 
175         if (KeepAliveEdge) {
176           // Found a keep-alive edge to an FDE in the eh-frame. Switch the keep
177           // alive edge to point to the CU and if the CU needs DWARF then add
178           // an extra keep-alive edge from the CU to the FDE.
179           auto &FDE = KeepAliveEdge->getTarget();
180           KeepAliveEdge->setTarget(CURecSym);
181           KeepAliveAlreadyPresent = true;
182           if (NeedsDWARF) {
183             LLVM_DEBUG({
184               dbgs() << "      Adding keep-alive edge to FDE at "
185                      << FDE.getAddress() << "\n";
186             });
187             B->addEdge(Edge::KeepAlive, 0, FDE, 0);
188           }
189         } else {
190           if (NeedsDWARF)
191             return make_error<JITLinkError>(
192                 "In " + G.getName() + ", compact unwind recard ot " +
193                 formatv("{0:x}", B->getAddress()) +
194                 " needs DWARF, but no FDE was found");
195         }
196       } else {
197         if (NeedsDWARF)
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");
202       }
203 
204       if (!KeepAliveAlreadyPresent) {
205         // No FDE edge. We'll need to add a new edge from the function back
206         // to the CU record.
207         Fn.getBlock().addEdge(Edge::KeepAlive, 0, CURecSym, 0);
208       }
209     }
210 
211     return Error::success();
212   }
213 
214   /// Process all __compact_unwind records and reserve space for __unwind_info.
215   Error processAndReserveUnwindInfo(LinkGraph &G) {
216     // Bail out early if no unwind info.
217     Section *CUSec = G.findSectionByName(CompactUnwindSectionName);
218     if (!CUSec)
219       return Error::success();
220 
221     // The __LD/__compact_unwind section is only used as input for the linker.
222     // We'll create a new __TEXT,__unwind_info section for unwind info output.
223     CUSec->setMemLifetime(orc::MemLifetime::NoAlloc);
224 
225     // Find / make a mach-header to act as the base for unwind-info offsets
226     // (and to report the arch / subarch to libunwind).
227     if (auto Err = getOrCreateCompactUnwindBase(G))
228       return Err;
229 
230     // Error out if there's already unwind-info in the graph: We have no idea
231     // how to merge unwind-info sections.
232     if (G.findSectionByName(UnwindInfoSectionName))
233       return make_error<JITLinkError>("In " + G.getName() + ", " +
234                                       UnwindInfoSectionName +
235                                       " already exists");
236 
237     // Process the __compact_unwind section to build the Records vector that
238     // we'll use for writing the __unwind_info section.
239     if (auto Err = processCompactUnwind(G, *CUSec))
240       return Err;
241 
242     // Calculate the size of __unwind_info.
243     size_t UnwindInfoSectionSize =
244         UnwindInfoSectionHeaderSize +
245         Personalities.size() * PersonalityEntrySize +
246         (NumSecondLevelPages + 1) * IndexEntrySize + NumLSDAs * LSDAEntrySize +
247         NumSecondLevelPages * SecondLevelPageHeaderSize +
248         Records.size() * SecondLevelPageEntrySize;
249 
250     LLVM_DEBUG({
251       dbgs() << "In " << G.getName() << ", reserving "
252              << formatv("{0:x}", UnwindInfoSectionSize) << " bytes for "
253              << UnwindInfoSectionName << "\n";
254     });
255 
256     // Create the __unwind_info section and reserve space for it.
257     Section &UnwindInfoSec =
258         G.createSection(UnwindInfoSectionName, orc::MemProt::Read);
259 
260     auto UnwindInfoSectionContent = G.allocateBuffer(UnwindInfoSectionSize);
261     memset(UnwindInfoSectionContent.data(), 0, UnwindInfoSectionContent.size());
262     auto &B = G.createMutableContentBlock(
263         UnwindInfoSec, UnwindInfoSectionContent, orc::ExecutorAddr(), 8, 0);
264 
265     // Add Keep-alive edges from the __unwind_info block to all of the target
266     // functions.
267     for (auto &R : Records)
268       B.addEdge(Edge::KeepAlive, 0, *R.Fn, 0);
269 
270     return Error::success();
271   }
272 
273   Error writeUnwindInfo(LinkGraph &G) {
274     Section *CUSec = G.findSectionByName(CompactUnwindSectionName);
275     if (!CUSec || CUSec->empty())
276       return Error::success();
277 
278     Section *UnwindInfoSec = G.findSectionByName(UnwindInfoSectionName);
279     if (!UnwindInfoSec)
280       return make_error<JITLinkError>("In " + G.getName() + ", " +
281                                       UnwindInfoSectionName +
282                                       " missing after allocation");
283 
284     if (UnwindInfoSec->blocks_size() != 1)
285       return make_error<JITLinkError>(
286           "In " + G.getName() + ", " + UnwindInfoSectionName +
287           " contains more than one block post-allocation");
288 
289     LLVM_DEBUG(
290         { dbgs() << "Writing unwind info for " << G.getName() << "...\n"; });
291 
292     mergeRecords();
293 
294     auto &UnwindInfoBlock = **UnwindInfoSec->blocks().begin();
295     auto Content = UnwindInfoBlock.getMutableContent(G);
296     BinaryStreamWriter Writer(
297         {reinterpret_cast<uint8_t *>(Content.data()), Content.size()},
298         CURecTraits::Endianness);
299 
300     // __unwind_info format, from mach-o/compact_unwind_encoding.h on Darwin:
301     //
302     // #define UNWIND_SECTION_VERSION 1
303     // struct unwind_info_section_header
304     // {
305     //     uint32_t    version;            // UNWIND_SECTION_VERSION
306     //     uint32_t    commonEncodingsArraySectionOffset;
307     //     uint32_t    commonEncodingsArrayCount;
308     //     uint32_t    personalityArraySectionOffset;
309     //     uint32_t    personalityArrayCount;
310     //     uint32_t    indexSectionOffset;
311     //     uint32_t    indexCount;
312     //     // compact_unwind_encoding_t[]
313     //     // uint32_t personalities[]
314     //     // unwind_info_section_header_index_entry[]
315     //     // unwind_info_section_header_lsda_index_entry[]
316     // };
317 
318     if (auto Err = writeHeader(G, Writer))
319       return Err;
320 
321     // Skip common encodings: JITLink doesn't use them.
322 
323     if (auto Err = writePersonalities(G, Writer))
324       return Err;
325 
326     // Calculate the offset to the LSDAs.
327     size_t SectionOffsetToLSDAs =
328         Writer.getOffset() + (NumSecondLevelPages + 1) * IndexEntrySize;
329 
330     // Calculate offset to the 1st second-level page.
331     size_t SectionOffsetToSecondLevelPages =
332         SectionOffsetToLSDAs + NumLSDAs * LSDAEntrySize;
333 
334     if (auto Err = writeIndexes(G, Writer, SectionOffsetToLSDAs,
335                                 SectionOffsetToSecondLevelPages))
336       return Err;
337 
338     if (auto Err = writeLSDAs(G, Writer))
339       return Err;
340 
341     if (auto Err = writeSecondLevelPages(G, Writer))
342       return Err;
343 
344     LLVM_DEBUG({
345       dbgs() << "    Wrote " << formatv("{0:x}", Writer.getOffset())
346              << " bytes of unwind info.\n";
347     });
348 
349     return Error::success();
350   }
351 
352 private:
353   // Calculate the size of unwind-info.
354   static constexpr size_t MaxPersonalities = 4;
355   static constexpr size_t PersonalityShift = 28;
356 
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;
367 
368   struct CompactUnwindRecord {
369     Symbol *Fn = nullptr;
370     uint32_t Size = 0;
371     uint32_t Encoding = 0;
372     Symbol *LSDA = nullptr;
373     Symbol *FDE = nullptr;
374   };
375 
376   Error processCompactUnwind(LinkGraph &G, Section &CUSec) {
377     // TODO: Reset NumLSDAs, Personalities and CompactUnwindRecords if
378     // processing more than once.
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.");
382 
383     SmallVector<CompactUnwindRecord> NonUniquedRecords;
384     NonUniquedRecords.reserve(CUSec.blocks_size());
385 
386     // Process __compact_unwind blocks.
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:
393           // This could be the function-pointer, or the FDE keep-alive. Check
394           // the type to decide.
395           if (E.getKind() == Edge::KeepAlive)
396             R.FDE = &E.getTarget();
397           else
398             R.Fn = &E.getTarget();
399           break;
400         case CURecTraits::PersonalityFieldOffset: {
401           // Add the Personality to the Personalities map and update the
402           // encoding.
403           size_t PersonalityIdx = 0;
404           for (; PersonalityIdx != Personalities.size(); ++PersonalityIdx)
405             if (Personalities[PersonalityIdx] == &E.getTarget())
406               break;
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());
414 
415           R.Encoding |= (PersonalityIdx + 1) << PersonalityShift;
416           break;
417         }
418         case CURecTraits::LSDAFieldOffset:
419           ++NumLSDAs;
420           R.LSDA = &E.getTarget();
421           break;
422         default:
423           return make_error<JITLinkError>("In " + G.getName() +
424                                           ", compact unwind record at " +
425                                           formatv("{0:x}", B->getAddress()) +
426                                           " has unrecognized edge at offset " +
427                                           formatv("{0:x}", E.getOffset()));
428         }
429       }
430       Records.push_back(R);
431     }
432 
433     // Sort the records into ascending order.
434     llvm::sort(Records, [](const CompactUnwindRecord &LHS,
435                            const CompactUnwindRecord &RHS) {
436       return LHS.Fn->getAddress() < RHS.Fn->getAddress();
437     });
438 
439     // Calculate the number of second-level pages required.
440     NumSecondLevelPages = (Records.size() + NumRecordsPerSecondLevelPage - 1) /
441                           NumRecordsPerSecondLevelPage;
442 
443     // Convert personality symbols to GOT entry pointers.
444     typename CURecTraits::GOTManager GOT(G);
445     for (auto &Personality : Personalities)
446       Personality = &GOT.getEntryForTarget(G, *Personality);
447 
448     LLVM_DEBUG({
449       dbgs() << "  In " << G.getName() << ", " << CompactUnwindSectionName
450              << ": raw records = " << Records.size()
451              << ", personalities = " << Personalities.size()
452              << ", lsdas = " << NumLSDAs << "\n";
453     });
454 
455     return Error::success();
456   }
457 
458   void mergeRecords() {
459     SmallVector<CompactUnwindRecord> NonUniqued = std::move(Records);
460     Records.reserve(NonUniqued.size());
461 
462     Records.push_back(NonUniqued.front());
463     for (size_t I = 1; I != NonUniqued.size(); ++I) {
464       auto &Next = NonUniqued[I];
465       auto &Last = Records.back();
466 
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)
471         Records.push_back(Next);
472     }
473 
474     // Recalculate derived values that may have changed.
475     NumSecondLevelPages = (Records.size() + NumRecordsPerSecondLevelPage - 1) /
476                           NumRecordsPerSecondLevelPage;
477   }
478 
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");
484 
485     // Write __unwind_info header.
486     size_t IndexArrayOffset = UnwindInfoSectionHeaderSize +
487                               Personalities.size() * PersonalityEntrySize;
488 
489     cantFail(W.writeInteger<uint32_t>(1));
490     cantFail(W.writeInteger<uint32_t>(UnwindInfoSectionHeaderSize));
491     cantFail(W.writeInteger<uint32_t>(0));
492     cantFail(W.writeInteger<uint32_t>(UnwindInfoSectionHeaderSize));
493     cantFail(W.writeInteger<uint32_t>(Personalities.size()));
494     cantFail(W.writeInteger<uint32_t>(IndexArrayOffset));
495     cantFail(W.writeInteger<uint32_t>(NumSecondLevelPages + 1));
496 
497     return Error::success();
498   }
499 
500   Error writePersonalities(LinkGraph &G, BinaryStreamWriter &W) {
501     // Write personalities.
502     for (auto *PSym : Personalities) {
503       auto Delta = PSym->getAddress() - CompactUnwindBase->getAddress();
504       if (!isUInt<32>(Delta))
505         return makePersonalityRangeError(G, *PSym);
506       cantFail(W.writeInteger<uint32_t>(Delta));
507     }
508     return Error::success();
509   }
510 
511   Error writeIndexes(LinkGraph &G, BinaryStreamWriter &W,
512                      size_t SectionOffsetToLSDAs,
513                      size_t SectionOffsetToSecondLevelPages) {
514     // Assume that function deltas are ok in this method -- we'll error
515     // check all of them when we write the second level pages.
516 
517     // Write the header index entries.
518     size_t RecordIdx = 0;
519     size_t NumPreviousLSDAs = 0;
520     for (auto &R : Records) {
521       // If this record marks the start of a new second level page.
522       if (RecordIdx % NumRecordsPerSecondLevelPage == 0) {
523         auto FnDelta = R.Fn->getAddress() - CompactUnwindBase->getAddress();
524         auto SecondLevelPageOffset =
525             SectionOffsetToSecondLevelPages +
526             SecondLevelPageSize * (RecordIdx / NumRecordsPerSecondLevelPage);
527         auto LSDAOffset =
528             SectionOffsetToLSDAs + NumPreviousLSDAs * LSDAEntrySize;
529 
530         cantFail(W.writeInteger<uint32_t>(FnDelta));
531         cantFail(W.writeInteger<uint32_t>(SecondLevelPageOffset));
532         cantFail(W.writeInteger<uint32_t>(LSDAOffset));
533       }
534       if (R.LSDA)
535         ++NumPreviousLSDAs;
536       ++RecordIdx;
537     }
538 
539     // Write the index array terminator.
540     {
541       auto FnEndDelta =
542           Records.back().Fn->getRange().End - CompactUnwindBase->getAddress();
543 
544       if (LLVM_UNLIKELY(!isUInt<32>(FnEndDelta)))
545         return make_error<JITLinkError>(
546             "In " + G.getName() + " " + UnwindInfoSectionName +
547             ", delta to end of functions  " +
548             formatv("{0:x}", Records.back().Fn->getRange().End) +
549             " exceeds 32 bits");
550 
551       cantFail(W.writeInteger<uint32_t>(FnEndDelta));
552       cantFail(W.writeInteger<uint32_t>(0));
553       cantFail(W.writeInteger<uint32_t>(SectionOffsetToSecondLevelPages));
554     }
555 
556     return Error::success();
557   }
558 
559   Error writeLSDAs(LinkGraph &G, BinaryStreamWriter &W) {
560     // As with writeIndexes, assume that function deltas are ok for now.
561     for (auto &R : Records) {
562       if (R.LSDA) {
563         auto FnDelta = R.Fn->getAddress() - CompactUnwindBase->getAddress();
564         auto LSDADelta = R.LSDA->getAddress() - CompactUnwindBase->getAddress();
565 
566         if (LLVM_UNLIKELY(!isUInt<32>(LSDADelta)))
567           return make_error<JITLinkError>(
568               "In " + G.getName() + " " + UnwindInfoSectionName +
569               ", delta to lsda at " + formatv("{0:x}", R.LSDA->getAddress()) +
570               " exceeds 32 bits");
571 
572         cantFail(W.writeInteger<uint32_t>(FnDelta));
573         cantFail(W.writeInteger<uint32_t>(LSDADelta));
574       }
575     }
576 
577     return Error::success();
578   }
579 
580   Error writeSecondLevelPages(LinkGraph &G, BinaryStreamWriter &W) {
581     size_t RecordIdx = 0;
582 
583     for (auto &R : Records) {
584       // When starting a new second-level page, write the page header:
585       //
586       //   2     : uint32_t    -- UNWIND_SECOND_LEVEL_REGULAR
587       //   8     : uint16_t    -- size of second level page table header
588       //   count : uint16_t    -- num entries in this second-level page
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);
594 
595         cantFail(W.writeInteger<uint32_t>(SecondLevelPageHeaderKind));
596         cantFail(W.writeInteger<uint16_t>(SecondLevelPageHeaderSize));
597         cantFail(W.writeInteger<uint16_t>(SecondLevelPageNumEntries));
598       }
599 
600       // Write entry.
601       auto FnDelta = R.Fn->getAddress() - CompactUnwindBase->getAddress();
602 
603       if (LLVM_UNLIKELY(!isUInt<32>(FnDelta)))
604         return make_error<JITLinkError>(
605             "In " + G.getName() + " " + UnwindInfoSectionName +
606             ", delta to function at " + formatv("{0:x}", R.Fn->getAddress()) +
607             " exceeds 32 bits");
608 
609       auto Encoding = R.Encoding;
610 
611       if (LLVM_UNLIKELY(CURecTraits::encodingSpecifiesDWARF(R.Encoding))) {
612         if (!EHFrameBase)
613           EHFrameBase = SectionRange(R.FDE->getSection()).getStart();
614         auto FDEDelta = R.FDE->getAddress() - EHFrameBase;
615 
616         if (auto EncodedFDEDelta = CURecTraits::encodeDWARFOffset(FDEDelta))
617           Encoding |= *EncodedFDEDelta;
618         else
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()));
623       }
624 
625       cantFail(W.writeInteger<uint32_t>(FnDelta));
626       cantFail(W.writeInteger<uint32_t>(Encoding));
627 
628       ++RecordIdx;
629     }
630 
631     return Error::success();
632   }
633 
634   Error getOrCreateCompactUnwindBase(LinkGraph &G) {
635     auto Name = G.intern("__jitlink$libunwind_dso_base");
636     CompactUnwindBase = G.findAbsoluteSymbolByName(Name);
637     if (!CompactUnwindBase) {
638       if (auto LocalCUBase = getOrCreateLocalMachOHeader(G)) {
639         CompactUnwindBase = &*LocalCUBase;
640         auto &B = LocalCUBase->getBlock();
641         G.addDefinedSymbol(B, 0, *Name, B.getSize(), Linkage::Strong,
642                            Scope::Local, false, true);
643       } else
644         return LocalCUBase.takeError();
645     }
646     CompactUnwindBase->setLive(true);
647     return Error::success();
648   }
649 
650   Error makePersonalityRangeError(LinkGraph &G, Symbol &PSym) {
651     std::string ErrMsg;
652     {
653       raw_string_ostream ErrStream(ErrMsg);
654       ErrStream << "In " << G.getName() << " " << UnwindInfoSectionName
655                 << ", personality ";
656       if (PSym.hasName())
657         ErrStream << PSym.getName() << " ";
658       ErrStream << "at " << PSym.getAddress()
659                 << " is out of 32-bit delta range of compact-unwind base at "
660                 << CompactUnwindBase->getAddress();
661     }
662     return make_error<JITLinkError>(std::move(ErrMsg));
663   }
664 
665   StringRef CompactUnwindSectionName;
666   StringRef UnwindInfoSectionName;
667   StringRef EHFrameSectionName;
668   Symbol *CompactUnwindBase = nullptr;
669   orc::ExecutorAddr EHFrameBase;
670 
671   size_t NumLSDAs = 0;
672   size_t NumSecondLevelPages = 0;
673   SmallVector<Symbol *, MaxPersonalities> Personalities;
674   SmallVector<CompactUnwindRecord> Records;
675 };
676 
677 } // end namespace jitlink
678 } // end namespace llvm
679 
680 #undef DEBUG_TYPE
681 
682 #endif // LIB_EXECUTIONENGINE_JITLINK_COMPACTUNWINDSUPPORTIMPL_H
683