xref: /freebsd/contrib/llvm-project/llvm/lib/ExecutionEngine/Orc/Debugging/DebuggerSupportPlugin.cpp (revision 1fd880742ace94e11fa60ee0b074f0b18e54c54f)
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:
36    static bool isDebugSection(Section &Sec) {
37      return Sec.getName().starts_with("__DWARF,");
38    }
39  
40    MachODebugObjectSynthesizerBase(LinkGraph &G, ExecutorAddr RegisterActionAddr)
41        : G(G), RegisterActionAddr(RegisterActionAddr) {}
42    virtual ~MachODebugObjectSynthesizerBase() = default;
43  
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:
90    MachODebugObjectSynthesizer(ExecutionSession &ES, LinkGraph &G,
91                                ExecutorAddr RegisterActionAddr)
92        : MachODebugObjectSynthesizerBase(G, RegisterActionAddr),
93          Builder(ES.getPageSize()) {}
94  
95    using MachODebugObjectSynthesizerBase::MachODebugObjectSynthesizerBase;
96  
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  
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  
301      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>>
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  
339  Error GDBJITDebugInfoRegistrationPlugin::notifyFailed(
340      MaterializationResponsibility &MR) {
341    return Error::success();
342  }
343  
344  Error GDBJITDebugInfoRegistrationPlugin::notifyRemovingResources(
345      JITDylib &JD, ResourceKey K) {
346    return Error::success();
347  }
348  
349  void GDBJITDebugInfoRegistrationPlugin::notifyTransferringResources(
350      JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) {}
351  
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  
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