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(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 | |
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 = 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 = 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 (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 = 2; |
591 | constexpr uint16_t = 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 | |