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/Debugging/DebuggerSupportPlugin.h"
13 #include "llvm/ExecutionEngine/Orc/MachOBuilder.h"
14 
15 #include "llvm/ADT/SmallSet.h"
16 #include "llvm/ADT/SmallVector.h"
17 #include "llvm/BinaryFormat/MachO.h"
18 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
19 #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
20 
21 #include <chrono>
22 
23 #define DEBUG_TYPE "orc"
24 
25 using namespace llvm;
26 using namespace llvm::jitlink;
27 using namespace llvm::orc;
28 
29 static const char *SynthDebugSectionName = "__jitlink_synth_debug_object";
30 
31 namespace {
32 
33 class MachODebugObjectSynthesizerBase
34     : public GDBJITDebugInfoRegistrationPlugin::DebugSectionSynthesizer {
35 public:
isDebugSection(Section & Sec)36   static bool isDebugSection(Section &Sec) {
37     return Sec.getName().starts_with("__DWARF,");
38   }
39 
MachODebugObjectSynthesizerBase(LinkGraph & G,ExecutorAddr RegisterActionAddr)40   MachODebugObjectSynthesizerBase(LinkGraph &G, ExecutorAddr RegisterActionAddr)
41       : G(G), RegisterActionAddr(RegisterActionAddr) {}
42   virtual ~MachODebugObjectSynthesizerBase() = default;
43 
preserveDebugSections()44   Error preserveDebugSections() {
45     if (G.findSectionByName(SynthDebugSectionName)) {
46       LLVM_DEBUG({
47         dbgs() << "MachODebugObjectSynthesizer skipping graph " << G.getName()
48                << " which contains an unexpected existing "
49                << SynthDebugSectionName << " section.\n";
50       });
51       return Error::success();
52     }
53 
54     LLVM_DEBUG({
55       dbgs() << "MachODebugObjectSynthesizer visiting graph " << G.getName()
56              << "\n";
57     });
58     for (auto &Sec : G.sections()) {
59       if (!isDebugSection(Sec))
60         continue;
61       // Preserve blocks in this debug section by marking one existing symbol
62       // live for each block, and introducing a new live, anonymous symbol for
63       // each currently unreferenced block.
64       LLVM_DEBUG({
65         dbgs() << "  Preserving debug section " << Sec.getName() << "\n";
66       });
67       SmallSet<Block *, 8> PreservedBlocks;
68       for (auto *Sym : Sec.symbols()) {
69         bool NewPreservedBlock =
70             PreservedBlocks.insert(&Sym->getBlock()).second;
71         if (NewPreservedBlock)
72           Sym->setLive(true);
73       }
74       for (auto *B : Sec.blocks())
75         if (!PreservedBlocks.count(B))
76           G.addAnonymousSymbol(*B, 0, 0, false, true);
77     }
78 
79     return Error::success();
80   }
81 
82 protected:
83   LinkGraph &G;
84   ExecutorAddr RegisterActionAddr;
85 };
86 
87 template <typename MachOTraits>
88 class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase {
89 public:
MachODebugObjectSynthesizer(ExecutionSession & ES,LinkGraph & G,ExecutorAddr RegisterActionAddr)90   MachODebugObjectSynthesizer(ExecutionSession &ES, LinkGraph &G,
91                               ExecutorAddr RegisterActionAddr)
92       : MachODebugObjectSynthesizerBase(G, RegisterActionAddr),
93         Builder(ES.getPageSize()) {}
94 
95   using MachODebugObjectSynthesizerBase::MachODebugObjectSynthesizerBase;
96 
startSynthesis()97   Error startSynthesis() override {
98     LLVM_DEBUG({
99       dbgs() << "Creating " << SynthDebugSectionName << " for " << G.getName()
100              << "\n";
101     });
102 
103     for (auto &Sec : G.sections()) {
104       if (Sec.blocks().empty())
105         continue;
106 
107       // Skip sections whose name's don't fit the MachO standard.
108       if (Sec.getName().empty() || Sec.getName().size() > 33 ||
109           Sec.getName().find(',') > 16)
110         continue;
111 
112       if (isDebugSection(Sec))
113         DebugSections.push_back({&Sec, nullptr});
114       else if (Sec.getMemLifetime() != MemLifetime::NoAlloc)
115         NonDebugSections.push_back({&Sec, nullptr});
116     }
117 
118     // Bail out early if no debug sections.
119     if (DebugSections.empty())
120       return Error::success();
121 
122     // Write MachO header and debug section load commands.
123     Builder.Header.filetype = MachO::MH_OBJECT;
124     switch (G.getTargetTriple().getArch()) {
125     case Triple::x86_64:
126       Builder.Header.cputype = MachO::CPU_TYPE_X86_64;
127       Builder.Header.cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL;
128       break;
129     case Triple::aarch64:
130       Builder.Header.cputype = MachO::CPU_TYPE_ARM64;
131       Builder.Header.cpusubtype = MachO::CPU_SUBTYPE_ARM64_ALL;
132       break;
133     default:
134       llvm_unreachable("Unsupported architecture");
135     }
136 
137     Seg = &Builder.addSegment("");
138 
139     StringMap<std::unique_ptr<MemoryBuffer>> DebugSectionMap;
140     StringRef DebugLineSectionData;
141     for (auto &DSec : DebugSections) {
142       auto [SegName, SecName] = DSec.GraphSec->getName().split(',');
143       DSec.BuilderSec = &Seg->addSection(SecName, SegName);
144 
145       SectionRange SR(*DSec.GraphSec);
146       DSec.BuilderSec->Content.Size = SR.getSize();
147       if (!SR.empty()) {
148         DSec.BuilderSec->align = Log2_64(SR.getFirstBlock()->getAlignment());
149         StringRef SectionData(SR.getFirstBlock()->getContent().data(),
150                               SR.getFirstBlock()->getSize());
151         DebugSectionMap[SecName] =
152             MemoryBuffer::getMemBuffer(SectionData, G.getName(), false);
153         if (SecName == "__debug_line")
154           DebugLineSectionData = SectionData;
155       }
156     }
157 
158     std::optional<StringRef> FileName;
159     if (!DebugLineSectionData.empty()) {
160       assert((G.getEndianness() == llvm::endianness::big ||
161               G.getEndianness() == llvm::endianness::little) &&
162              "G.getEndianness() must be either big or little");
163       auto DWARFCtx =
164           DWARFContext::create(DebugSectionMap, G.getPointerSize(),
165                                G.getEndianness() == llvm::endianness::little);
166       DWARFDataExtractor DebugLineData(
167           DebugLineSectionData, G.getEndianness() == llvm::endianness::little,
168           G.getPointerSize());
169       uint64_t Offset = 0;
170       DWARFDebugLine::LineTable LineTable;
171 
172       // Try to parse line data. Consume error on failure.
173       if (auto Err = LineTable.parse(DebugLineData, &Offset, *DWARFCtx, nullptr,
174                                      consumeError)) {
175         handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
176           LLVM_DEBUG({
177             dbgs() << "Cannot parse line table for \"" << G.getName() << "\": ";
178             EIB.log(dbgs());
179             dbgs() << "\n";
180           });
181         });
182       } else {
183         if (!LineTable.Prologue.FileNames.empty())
184           FileName = *dwarf::toString(LineTable.Prologue.FileNames[0].Name);
185       }
186     }
187 
188     // If no line table (or unable to use) then use graph name.
189     // FIXME: There are probably other debug sections we should look in first.
190     if (!FileName)
191       FileName = StringRef(G.getName());
192 
193     Builder.addSymbol("", MachO::N_SO, 0, 0, 0);
194     Builder.addSymbol(*FileName, MachO::N_SO, 0, 0, 0);
195     auto TimeStamp = std::chrono::duration_cast<std::chrono::seconds>(
196                          std::chrono::system_clock::now().time_since_epoch())
197                          .count();
198     Builder.addSymbol("", MachO::N_OSO, 3, 1, TimeStamp);
199 
200     for (auto &NDSP : NonDebugSections) {
201       auto [SegName, SecName] = NDSP.GraphSec->getName().split(',');
202       NDSP.BuilderSec = &Seg->addSection(SecName, SegName);
203       SectionRange SR(*NDSP.GraphSec);
204       if (!SR.empty())
205         NDSP.BuilderSec->align = Log2_64(SR.getFirstBlock()->getAlignment());
206 
207       // Add stabs.
208       for (auto *Sym : NDSP.GraphSec->symbols()) {
209         // Skip anonymous symbols.
210         if (!Sym->hasName())
211           continue;
212 
213         uint8_t SymType = Sym->isCallable() ? MachO::N_FUN : MachO::N_GSYM;
214 
215         Builder.addSymbol("", MachO::N_BNSYM, 1, 0, 0);
216         StabSymbols.push_back(
217             {*Sym, Builder.addSymbol(Sym->getName(), SymType, 1, 0, 0),
218              Builder.addSymbol(Sym->getName(), SymType, 0, 0, 0)});
219         Builder.addSymbol("", MachO::N_ENSYM, 1, 0, 0);
220       }
221     }
222 
223     Builder.addSymbol("", MachO::N_SO, 1, 0, 0);
224 
225     // Lay out the debug object, create a section and block for it.
226     size_t DebugObjectSize = Builder.layout();
227 
228     auto &SDOSec = G.createSection(SynthDebugSectionName, MemProt::Read);
229     MachOContainerBlock = &G.createMutableContentBlock(
230         SDOSec, G.allocateBuffer(DebugObjectSize), orc::ExecutorAddr(), 8, 0);
231 
232     return Error::success();
233   }
234 
completeSynthesisAndRegister()235   Error completeSynthesisAndRegister() override {
236     if (!MachOContainerBlock) {
237       LLVM_DEBUG({
238         dbgs() << "Not writing MachO debug object header for " << G.getName()
239                << " since createDebugSection failed\n";
240       });
241 
242       return Error::success();
243     }
244     ExecutorAddr MaxAddr;
245     for (auto &NDSec : NonDebugSections) {
246       SectionRange SR(*NDSec.GraphSec);
247       NDSec.BuilderSec->addr = SR.getStart().getValue();
248       NDSec.BuilderSec->size = SR.getSize();
249       NDSec.BuilderSec->offset = SR.getStart().getValue();
250       if (SR.getEnd() > MaxAddr)
251         MaxAddr = SR.getEnd();
252     }
253 
254     for (auto &DSec : DebugSections) {
255       if (DSec.GraphSec->blocks_size() != 1)
256         return make_error<StringError>(
257             "Unexpected number of blocks in debug info section",
258             inconvertibleErrorCode());
259 
260       if (ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size > MaxAddr)
261         MaxAddr = ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size;
262 
263       auto &B = **DSec.GraphSec->blocks().begin();
264       DSec.BuilderSec->Content.Data = B.getContent().data();
265       DSec.BuilderSec->Content.Size = B.getContent().size();
266       DSec.BuilderSec->flags |= MachO::S_ATTR_DEBUG;
267     }
268 
269     LLVM_DEBUG({
270       dbgs() << "Writing MachO debug object header for " << G.getName() << "\n";
271     });
272 
273     // Update stab symbol addresses.
274     for (auto &SS : StabSymbols) {
275       SS.StartStab.nlist().n_value = SS.Sym.getAddress().getValue();
276       SS.EndStab.nlist().n_value = SS.Sym.getSize();
277     }
278 
279     Builder.write(MachOContainerBlock->getAlreadyMutableContent());
280 
281     static constexpr bool AutoRegisterCode = true;
282     SectionRange R(MachOContainerBlock->getSection());
283     G.allocActions().push_back(
284         {cantFail(shared::WrapperFunctionCall::Create<
285                   shared::SPSArgList<shared::SPSExecutorAddrRange, bool>>(
286              RegisterActionAddr, R.getRange(), AutoRegisterCode)),
287          {}});
288 
289     return Error::success();
290   }
291 
292 private:
293   struct SectionPair {
294     Section *GraphSec = nullptr;
295     typename MachOBuilder<MachOTraits>::Section *BuilderSec = nullptr;
296   };
297 
298   struct StabSymbolsEntry {
299     using RelocTarget = typename MachOBuilder<MachOTraits>::RelocTarget;
300 
StabSymbolsEntry__anona2d7caf00111::MachODebugObjectSynthesizer::StabSymbolsEntry301     StabSymbolsEntry(Symbol &Sym, RelocTarget StartStab, RelocTarget EndStab)
302         : Sym(Sym), StartStab(StartStab), EndStab(EndStab) {}
303 
304     Symbol &Sym;
305     RelocTarget StartStab, EndStab;
306   };
307 
308   using BuilderType = MachOBuilder<MachOTraits>;
309 
310   Block *MachOContainerBlock = nullptr;
311   MachOBuilder<MachOTraits> Builder;
312   typename MachOBuilder<MachOTraits>::Segment *Seg = nullptr;
313   std::vector<StabSymbolsEntry> StabSymbols;
314   SmallVector<SectionPair, 16> DebugSections;
315   SmallVector<SectionPair, 16> NonDebugSections;
316 };
317 
318 } // end anonymous namespace
319 
320 namespace llvm {
321 namespace orc {
322 
323 Expected<std::unique_ptr<GDBJITDebugInfoRegistrationPlugin>>
Create(ExecutionSession & ES,JITDylib & ProcessJD,const Triple & TT)324 GDBJITDebugInfoRegistrationPlugin::Create(ExecutionSession &ES,
325                                           JITDylib &ProcessJD,
326                                           const Triple &TT) {
327   auto RegisterActionAddr =
328       TT.isOSBinFormatMachO()
329           ? ES.intern("_llvm_orc_registerJITLoaderGDBAllocAction")
330           : ES.intern("llvm_orc_registerJITLoaderGDBAllocAction");
331 
332   if (auto RegisterSym = ES.lookup({&ProcessJD}, RegisterActionAddr))
333     return std::make_unique<GDBJITDebugInfoRegistrationPlugin>(
334         RegisterSym->getAddress());
335   else
336     return RegisterSym.takeError();
337 }
338 
notifyFailed(MaterializationResponsibility & MR)339 Error GDBJITDebugInfoRegistrationPlugin::notifyFailed(
340     MaterializationResponsibility &MR) {
341   return Error::success();
342 }
343 
notifyRemovingResources(JITDylib & JD,ResourceKey K)344 Error GDBJITDebugInfoRegistrationPlugin::notifyRemovingResources(
345     JITDylib &JD, ResourceKey K) {
346   return Error::success();
347 }
348 
notifyTransferringResources(JITDylib & JD,ResourceKey DstKey,ResourceKey SrcKey)349 void GDBJITDebugInfoRegistrationPlugin::notifyTransferringResources(
350     JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) {}
351 
modifyPassConfig(MaterializationResponsibility & MR,LinkGraph & LG,PassConfiguration & PassConfig)352 void GDBJITDebugInfoRegistrationPlugin::modifyPassConfig(
353     MaterializationResponsibility &MR, LinkGraph &LG,
354     PassConfiguration &PassConfig) {
355 
356   if (LG.getTargetTriple().getObjectFormat() == Triple::MachO)
357     modifyPassConfigForMachO(MR, LG, PassConfig);
358   else {
359     LLVM_DEBUG({
360       dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph "
361              << LG.getName() << "(triple = " << LG.getTargetTriple().str()
362              << "\n";
363     });
364   }
365 }
366 
modifyPassConfigForMachO(MaterializationResponsibility & MR,jitlink::LinkGraph & LG,jitlink::PassConfiguration & PassConfig)367 void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO(
368     MaterializationResponsibility &MR, jitlink::LinkGraph &LG,
369     jitlink::PassConfiguration &PassConfig) {
370 
371   switch (LG.getTargetTriple().getArch()) {
372   case Triple::x86_64:
373   case Triple::aarch64:
374     // Supported, continue.
375     assert(LG.getPointerSize() == 8 && "Graph has incorrect pointer size");
376     assert(LG.getEndianness() == llvm::endianness::little &&
377            "Graph has incorrect endianness");
378     break;
379   default:
380     // Unsupported.
381     LLVM_DEBUG({
382       dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported "
383              << "MachO graph " << LG.getName()
384              << "(triple = " << LG.getTargetTriple().str()
385              << ", pointer size = " << LG.getPointerSize() << ", endianness = "
386              << (LG.getEndianness() == llvm::endianness::big ? "big" : "little")
387              << ")\n";
388     });
389     return;
390   }
391 
392   // Scan for debug sections. If we find one then install passes.
393   bool HasDebugSections = false;
394   for (auto &Sec : LG.sections())
395     if (MachODebugObjectSynthesizerBase::isDebugSection(Sec)) {
396       HasDebugSections = true;
397       break;
398     }
399 
400   if (HasDebugSections) {
401     LLVM_DEBUG({
402       dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
403              << " contains debug info. Installing debugger support passes.\n";
404     });
405 
406     auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>(
407         MR.getTargetJITDylib().getExecutionSession(), LG, RegisterActionAddr);
408     PassConfig.PrePrunePasses.push_back(
409         [=](LinkGraph &G) { return MDOS->preserveDebugSections(); });
410     PassConfig.PostPrunePasses.push_back(
411         [=](LinkGraph &G) { return MDOS->startSynthesis(); });
412     PassConfig.PostFixupPasses.push_back(
413         [=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); });
414   } else {
415     LLVM_DEBUG({
416       dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
417              << " contains no debug info. Skipping.\n";
418     });
419   }
420 }
421 
422 } // namespace orc
423 } // namespace llvm
424