1 //===------- DebuggerSupportPlugin.cpp - Utils for debugger support -------===//
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 //
10 //===----------------------------------------------------------------------===//
11 
12 #include "llvm/ExecutionEngine/Orc/DebuggerSupportPlugin.h"
13 
14 #include "llvm/ADT/SmallSet.h"
15 #include "llvm/ADT/SmallVector.h"
16 #include "llvm/ADT/StringSet.h"
17 #include "llvm/BinaryFormat/MachO.h"
18 
19 #define DEBUG_TYPE "orc"
20 
21 using namespace llvm;
22 using namespace llvm::jitlink;
23 using namespace llvm::orc;
24 
25 static const char *SynthDebugSectionName = "__jitlink_synth_debug_object";
26 
27 namespace {
28 
29 struct MachO64LE {
30   using UIntPtr = uint64_t;
31 
32   using Header = MachO::mach_header_64;
33   using SegmentLC = MachO::segment_command_64;
34   using Section = MachO::section_64;
35   using NList = MachO::nlist_64;
36 
37   static constexpr support::endianness Endianness = support::little;
38   static constexpr const uint32_t Magic = MachO::MH_MAGIC_64;
39   static constexpr const uint32_t SegmentCmd = MachO::LC_SEGMENT_64;
40 };
41 
42 class MachODebugObjectSynthesizerBase
43     : public GDBJITDebugInfoRegistrationPlugin::DebugSectionSynthesizer {
44 public:
isDebugSection(Section & Sec)45   static bool isDebugSection(Section &Sec) {
46     return Sec.getName().startswith("__DWARF,");
47   }
48 
MachODebugObjectSynthesizerBase(LinkGraph & G,ExecutorAddr RegisterActionAddr)49   MachODebugObjectSynthesizerBase(LinkGraph &G, ExecutorAddr RegisterActionAddr)
50       : G(G), RegisterActionAddr(RegisterActionAddr) {}
51   virtual ~MachODebugObjectSynthesizerBase() = default;
52 
preserveDebugSections()53   Error preserveDebugSections() {
54     if (G.findSectionByName(SynthDebugSectionName)) {
55       LLVM_DEBUG({
56         dbgs() << "MachODebugObjectSynthesizer skipping graph " << G.getName()
57                << " which contains an unexpected existing "
58                << SynthDebugSectionName << " section.\n";
59       });
60       return Error::success();
61     }
62 
63     LLVM_DEBUG({
64       dbgs() << "MachODebugObjectSynthesizer visiting graph " << G.getName()
65              << "\n";
66     });
67     for (auto &Sec : G.sections()) {
68       if (!isDebugSection(Sec))
69         continue;
70       // Preserve blocks in this debug section by marking one existing symbol
71       // live for each block, and introducing a new live, anonymous symbol for
72       // each currently unreferenced block.
73       LLVM_DEBUG({
74         dbgs() << "  Preserving debug section " << Sec.getName() << "\n";
75       });
76       SmallSet<Block *, 8> PreservedBlocks;
77       for (auto *Sym : Sec.symbols()) {
78         bool NewPreservedBlock =
79             PreservedBlocks.insert(&Sym->getBlock()).second;
80         if (NewPreservedBlock)
81           Sym->setLive(true);
82       }
83       for (auto *B : Sec.blocks())
84         if (!PreservedBlocks.count(B))
85           G.addAnonymousSymbol(*B, 0, 0, false, true);
86     }
87     return Error::success();
88   }
89 
90 protected:
91   LinkGraph &G;
92   ExecutorAddr RegisterActionAddr;
93 };
94 
95 template <typename MachOTraits>
96 class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase {
97 private:
98   class MachOStructWriter {
99   public:
MachOStructWriter(MutableArrayRef<char> Buffer)100     MachOStructWriter(MutableArrayRef<char> Buffer) : Buffer(Buffer) {}
101 
getOffset() const102     size_t getOffset() const { return Offset; }
103 
write(MachOStruct S)104     template <typename MachOStruct> void write(MachOStruct S) {
105       assert(Offset + sizeof(S) <= Buffer.size() &&
106              "Container block overflow while constructing debug MachO");
107       if (MachOTraits::Endianness != support::endian::system_endianness())
108         MachO::swapStruct(S);
109       memcpy(Buffer.data() + Offset, &S, sizeof(S));
110       Offset += sizeof(S);
111     }
112 
113   private:
114     MutableArrayRef<char> Buffer;
115     size_t Offset = 0;
116   };
117 
118 public:
119   using MachODebugObjectSynthesizerBase::MachODebugObjectSynthesizerBase;
120 
startSynthesis()121   Error startSynthesis() override {
122     LLVM_DEBUG({
123       dbgs() << "Creating " << SynthDebugSectionName << " for " << G.getName()
124              << "\n";
125     });
126     auto &SDOSec = G.createSection(SynthDebugSectionName, MemProt::Read);
127 
128     struct DebugSectionInfo {
129       Section *Sec = nullptr;
130       StringRef SegName;
131       StringRef SecName;
132       uint64_t Alignment = 0;
133       orc::ExecutorAddr StartAddr;
134       uint64_t Size = 0;
135     };
136 
137     SmallVector<DebugSectionInfo, 12> DebugSecInfos;
138     size_t NumSections = 0;
139     for (auto &Sec : G.sections()) {
140       if (Sec.blocks().empty())
141         continue;
142 
143       ++NumSections;
144       if (isDebugSection(Sec)) {
145         size_t SepPos = Sec.getName().find(',');
146         if (SepPos > 16 || (Sec.getName().size() - (SepPos + 1) > 16)) {
147           LLVM_DEBUG({
148             dbgs() << "Skipping debug object synthesis for graph "
149                    << G.getName()
150                    << ": encountered non-standard DWARF section name \""
151                    << Sec.getName() << "\"\n";
152           });
153           return Error::success();
154         }
155         DebugSecInfos.push_back({&Sec, Sec.getName().substr(0, SepPos),
156                                  Sec.getName().substr(SepPos + 1), 0,
157                                  orc::ExecutorAddr(), 0});
158       } else {
159         NonDebugSections.push_back(&Sec);
160 
161         // If the first block in the section has a non-zero alignment offset
162         // then we need to add a padding block, since the section command in
163         // the header doesn't allow for aligment offsets.
164         SectionRange R(Sec);
165         if (!R.empty()) {
166           auto &FB = *R.getFirstBlock();
167           if (FB.getAlignmentOffset() != 0) {
168             auto Padding = G.allocateBuffer(FB.getAlignmentOffset());
169             memset(Padding.data(), 0, Padding.size());
170             G.createContentBlock(Sec, Padding,
171                                  FB.getAddress() - FB.getAlignmentOffset(),
172                                  FB.getAlignment(), 0);
173           }
174         }
175       }
176     }
177 
178     // Create container block.
179     size_t SectionsCmdSize =
180         sizeof(typename MachOTraits::Section) * NumSections;
181     size_t SegmentLCSize =
182         sizeof(typename MachOTraits::SegmentLC) + SectionsCmdSize;
183     size_t ContainerBlockSize =
184         sizeof(typename MachOTraits::Header) + SegmentLCSize;
185     auto ContainerBlockContent = G.allocateBuffer(ContainerBlockSize);
186     MachOContainerBlock = &G.createMutableContentBlock(
187         SDOSec, ContainerBlockContent, orc::ExecutorAddr(), 8, 0);
188 
189     // Copy debug section blocks and symbols.
190     orc::ExecutorAddr NextBlockAddr(MachOContainerBlock->getSize());
191     for (auto &SI : DebugSecInfos) {
192       assert(!SI.Sec->blocks().empty() && "Empty debug info section?");
193 
194       // Update addresses in debug section.
195       LLVM_DEBUG({
196         dbgs() << "  Appending " << SI.Sec->getName() << " ("
197                << SI.Sec->blocks_size() << " block(s)) at "
198                << formatv("{0:x8}", NextBlockAddr) << "\n";
199       });
200       for (auto *B : SI.Sec->blocks()) {
201         NextBlockAddr = alignToBlock(NextBlockAddr, *B);
202         B->setAddress(NextBlockAddr);
203         NextBlockAddr += B->getSize();
204       }
205 
206       auto &FirstBlock = **SI.Sec->blocks().begin();
207       if (FirstBlock.getAlignmentOffset() != 0)
208         return make_error<StringError>(
209             "First block in " + SI.Sec->getName() +
210                 " section has non-zero alignment offset",
211             inconvertibleErrorCode());
212       if (FirstBlock.getAlignment() > std::numeric_limits<uint32_t>::max())
213         return make_error<StringError>("First block in " + SI.Sec->getName() +
214                                            " has alignment >4Gb",
215                                        inconvertibleErrorCode());
216 
217       SI.Alignment = FirstBlock.getAlignment();
218       SI.StartAddr = FirstBlock.getAddress();
219       SI.Size = NextBlockAddr - SI.StartAddr;
220       G.mergeSections(SDOSec, *SI.Sec);
221       SI.Sec = nullptr;
222     }
223     size_t DebugSectionsSize =
224         NextBlockAddr - orc::ExecutorAddr(MachOContainerBlock->getSize());
225 
226     // Write MachO header and debug section load commands.
227     MachOStructWriter Writer(MachOContainerBlock->getAlreadyMutableContent());
228     typename MachOTraits::Header Hdr;
229     memset(&Hdr, 0, sizeof(Hdr));
230     Hdr.magic = MachOTraits::Magic;
231     switch (G.getTargetTriple().getArch()) {
232     case Triple::x86_64:
233       Hdr.cputype = MachO::CPU_TYPE_X86_64;
234       Hdr.cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL;
235       break;
236     case Triple::aarch64:
237       Hdr.cputype = MachO::CPU_TYPE_ARM64;
238       Hdr.cpusubtype = MachO::CPU_SUBTYPE_ARM64_ALL;
239       break;
240     default:
241       llvm_unreachable("Unsupported architecture");
242     }
243     Hdr.filetype = MachO::MH_OBJECT;
244     Hdr.ncmds = 1;
245     Hdr.sizeofcmds = SegmentLCSize;
246     Hdr.flags = 0;
247     Writer.write(Hdr);
248 
249     typename MachOTraits::SegmentLC SegLC;
250     memset(&SegLC, 0, sizeof(SegLC));
251     SegLC.cmd = MachOTraits::SegmentCmd;
252     SegLC.cmdsize = SegmentLCSize;
253     SegLC.vmaddr = ContainerBlockSize;
254     SegLC.vmsize = DebugSectionsSize;
255     SegLC.fileoff = ContainerBlockSize;
256     SegLC.filesize = DebugSectionsSize;
257     SegLC.maxprot =
258         MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE;
259     SegLC.initprot =
260         MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE;
261     SegLC.nsects = NumSections;
262     SegLC.flags = 0;
263     Writer.write(SegLC);
264 
265     StringSet<> ExistingLongNames;
266     for (auto &SI : DebugSecInfos) {
267       typename MachOTraits::Section Sec;
268       memset(&Sec, 0, sizeof(Sec));
269       memcpy(Sec.sectname, SI.SecName.data(), SI.SecName.size());
270       memcpy(Sec.segname, SI.SegName.data(), SI.SegName.size());
271       Sec.addr = SI.StartAddr.getValue();
272       Sec.size = SI.Size;
273       Sec.offset = SI.StartAddr.getValue();
274       Sec.align = SI.Alignment;
275       Sec.reloff = 0;
276       Sec.nreloc = 0;
277       Sec.flags = MachO::S_ATTR_DEBUG;
278       Writer.write(Sec);
279     }
280 
281     // Set MachOContainerBlock to indicate success to
282     // completeSynthesisAndRegister.
283     NonDebugSectionsStart = Writer.getOffset();
284     return Error::success();
285   }
286 
completeSynthesisAndRegister()287   Error completeSynthesisAndRegister() override {
288     if (!MachOContainerBlock) {
289       LLVM_DEBUG({
290         dbgs() << "Not writing MachO debug object header for " << G.getName()
291                << " since createDebugSection failed\n";
292       });
293       return Error::success();
294     }
295 
296     LLVM_DEBUG({
297       dbgs() << "Writing MachO debug object header for " << G.getName() << "\n";
298     });
299 
300     MachOStructWriter Writer(
301         MachOContainerBlock->getAlreadyMutableContent().drop_front(
302             NonDebugSectionsStart));
303 
304     unsigned LongSectionNameIdx = 0;
305     for (auto *Sec : NonDebugSections) {
306       size_t SepPos = Sec->getName().find(',');
307       StringRef SegName, SecName;
308       std::string CustomSecName;
309 
310       if ((SepPos == StringRef::npos && Sec->getName().size() <= 16)) {
311         // No embedded segment name, short section name.
312         SegName = "__JITLINK_CUSTOM";
313         SecName = Sec->getName();
314       } else if (SepPos < 16 && (Sec->getName().size() - (SepPos + 1) <= 16)) {
315         // Canonical embedded segment and section name.
316         SegName = Sec->getName().substr(0, SepPos);
317         SecName = Sec->getName().substr(SepPos + 1);
318       } else {
319         // Long section name that needs to be truncated.
320         assert(Sec->getName().size() > 16 &&
321                "Short section name should have been handled above");
322         SegName = "__JITLINK_CUSTOM";
323         auto IdxStr = std::to_string(++LongSectionNameIdx);
324         CustomSecName = Sec->getName().substr(0, 15 - IdxStr.size()).str();
325         CustomSecName += ".";
326         CustomSecName += IdxStr;
327         SecName = StringRef(CustomSecName.data(), 16);
328       }
329 
330       SectionRange R(*Sec);
331       if (R.getFirstBlock()->getAlignmentOffset() != 0)
332         return make_error<StringError>(
333             "While building MachO debug object for " + G.getName() +
334                 " first block has non-zero alignment offset",
335             inconvertibleErrorCode());
336 
337       typename MachOTraits::Section SecCmd;
338       memset(&SecCmd, 0, sizeof(SecCmd));
339       memcpy(SecCmd.sectname, SecName.data(), SecName.size());
340       memcpy(SecCmd.segname, SegName.data(), SegName.size());
341       SecCmd.addr = R.getStart().getValue();
342       SecCmd.size = R.getSize();
343       SecCmd.offset = 0;
344       SecCmd.align = R.getFirstBlock()->getAlignment();
345       SecCmd.reloff = 0;
346       SecCmd.nreloc = 0;
347       SecCmd.flags = 0;
348       Writer.write(SecCmd);
349     }
350 
351     SectionRange R(MachOContainerBlock->getSection());
352     G.allocActions().push_back(
353         {cantFail(shared::WrapperFunctionCall::Create<
354                   shared::SPSArgList<shared::SPSExecutorAddrRange>>(
355              RegisterActionAddr, R.getRange())),
356          {}});
357     return Error::success();
358   }
359 
360 private:
361   Block *MachOContainerBlock = nullptr;
362   SmallVector<Section *, 16> NonDebugSections;
363   size_t NonDebugSectionsStart = 0;
364 };
365 
366 } // end anonymous namespace
367 
368 namespace llvm {
369 namespace orc {
370 
371 Expected<std::unique_ptr<GDBJITDebugInfoRegistrationPlugin>>
Create(ExecutionSession & ES,JITDylib & ProcessJD,const Triple & TT)372 GDBJITDebugInfoRegistrationPlugin::Create(ExecutionSession &ES,
373                                           JITDylib &ProcessJD,
374                                           const Triple &TT) {
375   auto RegisterActionAddr =
376       TT.isOSBinFormatMachO()
377           ? ES.intern("_llvm_orc_registerJITLoaderGDBAllocAction")
378           : ES.intern("llvm_orc_registerJITLoaderGDBAllocAction");
379 
380   if (auto Addr = ES.lookup({&ProcessJD}, RegisterActionAddr))
381     return std::make_unique<GDBJITDebugInfoRegistrationPlugin>(
382         ExecutorAddr(Addr->getAddress()));
383   else
384     return Addr.takeError();
385 }
386 
notifyFailed(MaterializationResponsibility & MR)387 Error GDBJITDebugInfoRegistrationPlugin::notifyFailed(
388     MaterializationResponsibility &MR) {
389   return Error::success();
390 }
391 
notifyRemovingResources(JITDylib & JD,ResourceKey K)392 Error GDBJITDebugInfoRegistrationPlugin::notifyRemovingResources(
393     JITDylib &JD, ResourceKey K) {
394   return Error::success();
395 }
396 
notifyTransferringResources(JITDylib & JD,ResourceKey DstKey,ResourceKey SrcKey)397 void GDBJITDebugInfoRegistrationPlugin::notifyTransferringResources(
398     JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) {}
399 
modifyPassConfig(MaterializationResponsibility & MR,LinkGraph & LG,PassConfiguration & PassConfig)400 void GDBJITDebugInfoRegistrationPlugin::modifyPassConfig(
401     MaterializationResponsibility &MR, LinkGraph &LG,
402     PassConfiguration &PassConfig) {
403 
404   if (LG.getTargetTriple().getObjectFormat() == Triple::MachO)
405     modifyPassConfigForMachO(MR, LG, PassConfig);
406   else {
407     LLVM_DEBUG({
408       dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph "
409              << LG.getName() << "(triple = " << LG.getTargetTriple().str()
410              << "\n";
411     });
412   }
413 }
414 
modifyPassConfigForMachO(MaterializationResponsibility & MR,jitlink::LinkGraph & LG,jitlink::PassConfiguration & PassConfig)415 void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO(
416     MaterializationResponsibility &MR, jitlink::LinkGraph &LG,
417     jitlink::PassConfiguration &PassConfig) {
418 
419   switch (LG.getTargetTriple().getArch()) {
420   case Triple::x86_64:
421   case Triple::aarch64:
422     // Supported, continue.
423     assert(LG.getPointerSize() == 8 && "Graph has incorrect pointer size");
424     assert(LG.getEndianness() == support::little &&
425            "Graph has incorrect endianness");
426     break;
427   default:
428     // Unsupported.
429     LLVM_DEBUG({
430       dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported "
431              << "MachO graph " << LG.getName()
432              << "(triple = " << LG.getTargetTriple().str()
433              << ", pointer size = " << LG.getPointerSize() << ", endianness = "
434              << (LG.getEndianness() == support::big ? "big" : "little")
435              << ")\n";
436     });
437     return;
438   }
439 
440   // Scan for debug sections. If we find one then install passes.
441   bool HasDebugSections = false;
442   for (auto &Sec : LG.sections())
443     if (MachODebugObjectSynthesizerBase::isDebugSection(Sec)) {
444       HasDebugSections = true;
445       break;
446     }
447 
448   if (HasDebugSections) {
449     LLVM_DEBUG({
450       dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
451              << " contains debug info. Installing debugger support passes.\n";
452     });
453 
454     auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>(
455         LG, RegisterActionAddr);
456     PassConfig.PrePrunePasses.push_back(
457         [=](LinkGraph &G) { return MDOS->preserveDebugSections(); });
458     PassConfig.PostPrunePasses.push_back(
459         [=](LinkGraph &G) { return MDOS->startSynthesis(); });
460     PassConfig.PreFixupPasses.push_back(
461         [=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); });
462   } else {
463     LLVM_DEBUG({
464       dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
465              << " contains no debug info. Skipping.\n";
466     });
467   }
468 }
469 
470 } // namespace orc
471 } // namespace llvm
472