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:
45   static bool isDebugSection(Section &Sec) {
46     return Sec.getName().startswith("__DWARF,");
47   }
48 
49   MachODebugObjectSynthesizerBase(LinkGraph &G, ExecutorAddr RegisterActionAddr)
50       : G(G), RegisterActionAddr(RegisterActionAddr) {}
51   virtual ~MachODebugObjectSynthesizerBase() {}
52 
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:
100     MachOStructWriter(MutableArrayRef<char> Buffer) : Buffer(Buffer) {}
101 
102     size_t getOffset() const { return Offset; }
103 
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 
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       JITTargetAddress Alignment = 0;
133       JITTargetAddress StartAddr = 0;
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 (llvm::empty(Sec.blocks()))
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, 0});
157       } else
158         NonDebugSections.push_back(&Sec);
159     }
160 
161     // Create container block.
162     size_t SectionsCmdSize =
163         sizeof(typename MachOTraits::Section) * NumSections;
164     size_t SegmentLCSize =
165         sizeof(typename MachOTraits::SegmentLC) + SectionsCmdSize;
166     size_t ContainerBlockSize =
167         sizeof(typename MachOTraits::Header) + SegmentLCSize;
168     auto ContainerBlockContent = G.allocateBuffer(ContainerBlockSize);
169     MachOContainerBlock =
170         &G.createMutableContentBlock(SDOSec, ContainerBlockContent, 0, 8, 0);
171 
172     // Copy debug section blocks and symbols.
173     JITTargetAddress NextBlockAddr = MachOContainerBlock->getSize();
174     for (auto &SI : DebugSecInfos) {
175       assert(!llvm::empty(SI.Sec->blocks()) && "Empty debug info section?");
176 
177       // Update addresses in debug section.
178       LLVM_DEBUG({
179         dbgs() << "  Appending " << SI.Sec->getName() << " ("
180                << SI.Sec->blocks_size() << " block(s)) at "
181                << formatv("{0:x8}", NextBlockAddr) << "\n";
182       });
183       for (auto *B : SI.Sec->blocks()) {
184         NextBlockAddr = alignToBlock(NextBlockAddr, *B);
185         B->setAddress(NextBlockAddr);
186         NextBlockAddr += B->getSize();
187       }
188 
189       auto &FirstBlock = **SI.Sec->blocks().begin();
190       if (FirstBlock.getAlignmentOffset() != 0)
191         return make_error<StringError>(
192             "First block in " + SI.Sec->getName() +
193                 " section has non-zero alignment offset",
194             inconvertibleErrorCode());
195       if (FirstBlock.getAlignment() > std::numeric_limits<uint32_t>::max())
196         return make_error<StringError>("First block in " + SI.Sec->getName() +
197                                            " has alignment >4Gb",
198                                        inconvertibleErrorCode());
199 
200       SI.Alignment = FirstBlock.getAlignment();
201       SI.StartAddr = FirstBlock.getAddress();
202       SI.Size = NextBlockAddr - SI.StartAddr;
203       G.mergeSections(SDOSec, *SI.Sec);
204       SI.Sec = nullptr;
205     }
206     size_t DebugSectionsSize = NextBlockAddr - MachOContainerBlock->getSize();
207 
208     // Write MachO header and debug section load commands.
209     MachOStructWriter Writer(MachOContainerBlock->getAlreadyMutableContent());
210     typename MachOTraits::Header Hdr;
211     memset(&Hdr, 0, sizeof(Hdr));
212     Hdr.magic = MachOTraits::Magic;
213     switch (G.getTargetTriple().getArch()) {
214     case Triple::x86_64:
215       Hdr.cputype = MachO::CPU_TYPE_X86_64;
216       Hdr.cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL;
217       break;
218     case Triple::aarch64:
219       Hdr.cputype = MachO::CPU_TYPE_ARM64;
220       Hdr.cpusubtype = MachO::CPU_SUBTYPE_ARM64_ALL;
221       break;
222     default:
223       llvm_unreachable("Unsupported architecture");
224     }
225     Hdr.filetype = MachO::MH_OBJECT;
226     Hdr.ncmds = 1;
227     Hdr.sizeofcmds = SegmentLCSize;
228     Hdr.flags = 0;
229     Writer.write(Hdr);
230 
231     typename MachOTraits::SegmentLC SegLC;
232     memset(&SegLC, 0, sizeof(SegLC));
233     SegLC.cmd = MachOTraits::SegmentCmd;
234     SegLC.cmdsize = SegmentLCSize;
235     SegLC.vmaddr = ContainerBlockSize;
236     SegLC.vmsize = DebugSectionsSize;
237     SegLC.fileoff = ContainerBlockSize;
238     SegLC.filesize = DebugSectionsSize;
239     SegLC.maxprot =
240         MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE;
241     SegLC.initprot =
242         MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE;
243     SegLC.nsects = NumSections;
244     SegLC.flags = 0;
245     Writer.write(SegLC);
246 
247     StringSet<> ExistingLongNames;
248     for (auto &SI : DebugSecInfos) {
249       typename MachOTraits::Section Sec;
250       memset(&Sec, 0, sizeof(Sec));
251       memcpy(Sec.sectname, SI.SecName.data(), SI.SecName.size());
252       memcpy(Sec.segname, SI.SegName.data(), SI.SegName.size());
253       Sec.addr = SI.StartAddr;
254       Sec.size = SI.Size;
255       Sec.offset = SI.StartAddr;
256       Sec.align = SI.Alignment;
257       Sec.reloff = 0;
258       Sec.nreloc = 0;
259       Sec.flags = MachO::S_ATTR_DEBUG;
260       Writer.write(Sec);
261     }
262 
263     // Set MachOContainerBlock to indicate success to
264     // completeSynthesisAndRegister.
265     NonDebugSectionsStart = Writer.getOffset();
266     return Error::success();
267   }
268 
269   Error completeSynthesisAndRegister() override {
270     if (!MachOContainerBlock) {
271       LLVM_DEBUG({
272         dbgs() << "Not writing MachO debug object header for " << G.getName()
273                << " since createDebugSection failed\n";
274       });
275       return Error::success();
276     }
277 
278     LLVM_DEBUG({
279       dbgs() << "Writing MachO debug object header for " << G.getName() << "\n";
280     });
281 
282     MachOStructWriter Writer(
283         MachOContainerBlock->getAlreadyMutableContent().drop_front(
284             NonDebugSectionsStart));
285 
286     unsigned LongSectionNameIdx = 0;
287     for (auto *Sec : NonDebugSections) {
288       size_t SepPos = Sec->getName().find(',');
289       StringRef SegName, SecName;
290       std::string CustomSecName;
291 
292       if ((SepPos == StringRef::npos && Sec->getName().size() <= 16)) {
293         // No embedded segment name, short section name.
294         SegName = "__JITLINK_CUSTOM";
295         SecName = Sec->getName();
296       } else if (SepPos < 16 && (Sec->getName().size() - (SepPos + 1) <= 16)) {
297         // Canonical embedded segment and section name.
298         SegName = Sec->getName().substr(0, SepPos);
299         SecName = Sec->getName().substr(SepPos + 1);
300       } else {
301         // Long section name that needs to be truncated.
302         assert(Sec->getName().size() > 16 &&
303                "Short section name should have been handled above");
304         SegName = "__JITLINK_CUSTOM";
305         auto IdxStr = std::to_string(++LongSectionNameIdx);
306         CustomSecName = Sec->getName().substr(0, 15 - IdxStr.size()).str();
307         CustomSecName += ".";
308         CustomSecName += IdxStr;
309         SecName = StringRef(CustomSecName.data(), 16);
310       }
311 
312       SectionRange R(*Sec);
313       if (R.getFirstBlock()->getAlignmentOffset() != 0)
314         return make_error<StringError>(
315             "While building MachO debug object for " + G.getName() +
316                 " first block has non-zero alignment offset",
317             inconvertibleErrorCode());
318 
319       typename MachOTraits::Section SecCmd;
320       memset(&SecCmd, 0, sizeof(SecCmd));
321       memcpy(SecCmd.sectname, SecName.data(), SecName.size());
322       memcpy(SecCmd.segname, SegName.data(), SegName.size());
323       SecCmd.addr = R.getStart();
324       SecCmd.size = R.getSize();
325       SecCmd.offset = 0;
326       SecCmd.align = R.getFirstBlock()->getAlignment();
327       SecCmd.reloff = 0;
328       SecCmd.nreloc = 0;
329       SecCmd.flags = 0;
330       Writer.write(SecCmd);
331     }
332 
333     SectionRange R(MachOContainerBlock->getSection());
334     G.allocActions().push_back(
335         {{RegisterActionAddr.getValue(), R.getStart(), R.getSize()}, {}});
336     return Error::success();
337   }
338 
339 private:
340   Block *MachOContainerBlock = nullptr;
341   SmallVector<Section *, 16> NonDebugSections;
342   size_t NonDebugSectionsStart = 0;
343 };
344 
345 } // end anonymous namespace
346 
347 namespace llvm {
348 namespace orc {
349 
350 Expected<std::unique_ptr<GDBJITDebugInfoRegistrationPlugin>>
351 GDBJITDebugInfoRegistrationPlugin::Create(ExecutionSession &ES,
352                                           JITDylib &ProcessJD,
353                                           const Triple &TT) {
354   auto RegisterActionAddr =
355       TT.isOSBinFormatMachO()
356           ? ES.intern("_llvm_orc_registerJITLoaderGDBAllocAction")
357           : ES.intern("llvm_orc_registerJITLoaderGDBAllocAction");
358 
359   if (auto Addr = ES.lookup({&ProcessJD}, RegisterActionAddr))
360     return std::make_unique<GDBJITDebugInfoRegistrationPlugin>(
361         ExecutorAddr(Addr->getAddress()));
362   else
363     return Addr.takeError();
364 }
365 
366 Error GDBJITDebugInfoRegistrationPlugin::notifyFailed(
367     MaterializationResponsibility &MR) {
368   return Error::success();
369 }
370 
371 Error GDBJITDebugInfoRegistrationPlugin::notifyRemovingResources(
372     ResourceKey K) {
373   return Error::success();
374 }
375 
376 void GDBJITDebugInfoRegistrationPlugin::notifyTransferringResources(
377     ResourceKey DstKey, ResourceKey SrcKey) {}
378 
379 void GDBJITDebugInfoRegistrationPlugin::modifyPassConfig(
380     MaterializationResponsibility &MR, LinkGraph &LG,
381     PassConfiguration &PassConfig) {
382 
383   if (LG.getTargetTriple().getObjectFormat() == Triple::MachO)
384     modifyPassConfigForMachO(MR, LG, PassConfig);
385   else {
386     LLVM_DEBUG({
387       dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph "
388              << LG.getName() << "(triple = " << LG.getTargetTriple().str()
389              << "\n";
390     });
391   }
392 }
393 
394 void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO(
395     MaterializationResponsibility &MR, jitlink::LinkGraph &LG,
396     jitlink::PassConfiguration &PassConfig) {
397 
398   switch (LG.getTargetTriple().getArch()) {
399   case Triple::x86_64:
400   case Triple::aarch64:
401     // Supported, continue.
402     assert(LG.getPointerSize() == 8 && "Graph has incorrect pointer size");
403     assert(LG.getEndianness() == support::little &&
404            "Graph has incorrect endianness");
405     break;
406   default:
407     // Unsupported.
408     LLVM_DEBUG({
409       dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported "
410              << "MachO graph " << LG.getName()
411              << "(triple = " << LG.getTargetTriple().str()
412              << ", pointer size = " << LG.getPointerSize() << ", endianness = "
413              << (LG.getEndianness() == support::big ? "big" : "little")
414              << ")\n";
415     });
416     return;
417   }
418 
419   // Scan for debug sections. If we find one then install passes.
420   bool HasDebugSections = false;
421   for (auto &Sec : LG.sections())
422     if (MachODebugObjectSynthesizerBase::isDebugSection(Sec)) {
423       HasDebugSections = true;
424       break;
425     }
426 
427   if (HasDebugSections) {
428     LLVM_DEBUG({
429       dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
430              << " contains debug info. Installing debugger support passes.\n";
431     });
432 
433     auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>(
434         LG, RegisterActionAddr);
435     PassConfig.PrePrunePasses.push_back(
436         [=](LinkGraph &G) { return MDOS->preserveDebugSections(); });
437     PassConfig.PostPrunePasses.push_back(
438         [=](LinkGraph &G) { return MDOS->startSynthesis(); });
439     PassConfig.PreFixupPasses.push_back(
440         [=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); });
441   } else {
442     LLVM_DEBUG({
443       dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
444              << " contains no debug info. Skipping.\n";
445     });
446   }
447 }
448 
449 } // namespace orc
450 } // namespace llvm
451