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
23namespace llvm {
24namespace 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.
29Error 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.
41template <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.
75template <typename CURecTraits> class CompactUnwindManager {
76public:
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(Name: 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(Name: 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 Args: "In " + G.getName() + ", compact unwind record at " +
127 formatv(Fmt: "{0:x}", Vals: B->getAddress()) + " has no pc-begin edge");
128
129 if (!PCBeginEdge->getTarget().isDefined())
130 return make_error<JITLinkError>(
131 Args: "In " + G.getName() + ", compact unwind record at " +
132 formatv(Fmt: "{0:x}", Vals: 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(Content&: *B, Offset: 0, Size: CURecTraits::Size, IsCallable: false, IsLive: false);
163
164 bool KeepAliveAlreadyPresent = false;
165 if (EHFrameSec) {
166 Edge *KeepAliveEdge = nullptr;
167 for (auto &E : Fn.getBlock().edges_at(O: 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(K: Edge::KeepAlive, Offset: 0, Target&: FDE, Addend: 0);
188 }
189 } else {
190 if (NeedsDWARF)
191 return make_error<JITLinkError>(
192 Args: "In " + G.getName() + ", compact unwind recard ot " +
193 formatv(Fmt: "{0:x}", Vals: B->getAddress()) +
194 " needs DWARF, but no FDE was found");
195 }
196 } else {
197 if (NeedsDWARF)
198 return make_error<JITLinkError>(
199 Args: "In " + G.getName() + ", compact unwind recard ot " +
200 formatv(Fmt: "{0:x}", Vals: 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(Name: 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(Name: UnwindInfoSectionName))
233 return make_error<JITLinkError>(Args: "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&: *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(Name: UnwindInfoSectionName, Prot: orc::MemProt::Read);
259
260 auto UnwindInfoSectionContent = G.allocateBuffer(Size: UnwindInfoSectionSize);
261 memset(s: UnwindInfoSectionContent.data(), c: 0, n: UnwindInfoSectionContent.size());
262 auto &B = G.createMutableContentBlock(
263 Parent&: UnwindInfoSec, MutableContent: UnwindInfoSectionContent, Address: orc::ExecutorAddr(), Alignment: 8, AlignmentOffset: 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(Name: CompactUnwindSectionName);
275 if (!CUSec || CUSec->empty())
276 return Error::success();
277
278 Section *UnwindInfoSec = G.findSectionByName(Name: UnwindInfoSectionName);
279 if (!UnwindInfoSec)
280 return make_error<JITLinkError>(Args: "In " + G.getName() + ", " +
281 UnwindInfoSectionName +
282 " missing after allocation");
283
284 if (UnwindInfoSec->blocks_size() != 1)
285 return make_error<JITLinkError>(
286 Args: "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, W&: Writer))
319 return Err;
320
321 // Skip common encodings: JITLink doesn't use them.
322
323 if (auto Err = writePersonalities(G, W&: 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, W&: Writer, SectionOffsetToLSDAs,
335 SectionOffsetToSecondLevelPages))
336 return Err;
337
338 if (auto Err = writeLSDAs(G, W&: Writer))
339 return Err;
340
341 if (auto Err = writeSecondLevelPages(G, W&: 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
352private:
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 Args: "In " + G.getName() +
410 ", __compact_unwind contains too many personalities (max " +
411 formatv(Fmt: "{}", Vals: MaxPersonalities) + ")");
412 if (PersonalityIdx == Personalities.size())
413 Personalities.push_back(Elt: &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>(Args: "In " + G.getName() +
424 ", compact unwind record at " +
425 formatv(Fmt: "{0:x}", Vals: B->getAddress()) +
426 " has unrecognized edge at offset " +
427 formatv(Fmt: "{0:x}", Vals: 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>(x: NumSecondLevelPages + 1))
481 return make_error<JITLinkError>(Args: "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(Err: W.writeInteger<uint32_t>(Value: 1));
490 cantFail(Err: W.writeInteger<uint32_t>(Value: UnwindInfoSectionHeaderSize));
491 cantFail(Err: W.writeInteger<uint32_t>(Value: 0));
492 cantFail(Err: W.writeInteger<uint32_t>(Value: UnwindInfoSectionHeaderSize));
493 cantFail(Err: W.writeInteger<uint32_t>(Value: Personalities.size()));
494 cantFail(Err: W.writeInteger<uint32_t>(Value: IndexArrayOffset));
495 cantFail(Err: W.writeInteger<uint32_t>(Value: 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>(x: Delta))
505 return makePersonalityRangeError(G, PSym&: *PSym);
506 cantFail(Err: W.writeInteger<uint32_t>(Value: 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(Err: W.writeInteger<uint32_t>(Value: SecondLevelPageOffset));
532 cantFail(Err: W.writeInteger<uint32_t>(Value: 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(Err: W.writeInteger<uint32_t>(Value: 0));
553 cantFail(Err: W.writeInteger<uint32_t>(Value: 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(Err: W.writeInteger<uint32_t>(Value: SecondLevelPageHeaderKind));
596 cantFail(Err: W.writeInteger<uint16_t>(Value: SecondLevelPageHeaderSize));
597 cantFail(Err: W.writeInteger<uint16_t>(Value: 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(SymbolName: "__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(Content&: B, Offset: 0, Name: *Name, Size: B.getSize(), L: Linkage::Strong,
642 S: Scope::Local, IsCallable: false, IsLive: 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>(Args: 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