diff options
Diffstat (limited to 'contrib/llvm-project/llvm/lib/ExecutionEngine/Orc/Debugging/DebuggerSupportPlugin.cpp')
| -rw-r--r-- | contrib/llvm-project/llvm/lib/ExecutionEngine/Orc/Debugging/DebuggerSupportPlugin.cpp | 423 | 
1 files changed, 423 insertions, 0 deletions
diff --git a/contrib/llvm-project/llvm/lib/ExecutionEngine/Orc/Debugging/DebuggerSupportPlugin.cpp b/contrib/llvm-project/llvm/lib/ExecutionEngine/Orc/Debugging/DebuggerSupportPlugin.cpp new file mode 100644 index 000000000000..e387b06ee934 --- /dev/null +++ b/contrib/llvm-project/llvm/lib/ExecutionEngine/Orc/Debugging/DebuggerSupportPlugin.cpp @@ -0,0 +1,423 @@ +//===------- DebuggerSupportPlugin.cpp - Utils for debugger support -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupportPlugin.h" +#include "llvm/ExecutionEngine/Orc/MachOBuilder.h" + +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" + +#include <chrono> + +#define DEBUG_TYPE "orc" + +using namespace llvm; +using namespace llvm::jitlink; +using namespace llvm::orc; + +static const char *SynthDebugSectionName = "__jitlink_synth_debug_object"; + +namespace { + +class MachODebugObjectSynthesizerBase +    : public GDBJITDebugInfoRegistrationPlugin::DebugSectionSynthesizer { +public: +  static bool isDebugSection(Section &Sec) { +    return Sec.getName().starts_with("__DWARF,"); +  } + +  MachODebugObjectSynthesizerBase(LinkGraph &G, ExecutorAddr RegisterActionAddr) +      : G(G), RegisterActionAddr(RegisterActionAddr) {} +  virtual ~MachODebugObjectSynthesizerBase() = default; + +  Error preserveDebugSections() { +    if (G.findSectionByName(SynthDebugSectionName)) { +      LLVM_DEBUG({ +        dbgs() << "MachODebugObjectSynthesizer skipping graph " << G.getName() +               << " which contains an unexpected existing " +               << SynthDebugSectionName << " section.\n"; +      }); +      return Error::success(); +    } + +    LLVM_DEBUG({ +      dbgs() << "MachODebugObjectSynthesizer visiting graph " << G.getName() +             << "\n"; +    }); +    for (auto &Sec : G.sections()) { +      if (!isDebugSection(Sec)) +        continue; +      // Preserve blocks in this debug section by marking one existing symbol +      // live for each block, and introducing a new live, anonymous symbol for +      // each currently unreferenced block. +      LLVM_DEBUG({ +        dbgs() << "  Preserving debug section " << Sec.getName() << "\n"; +      }); +      SmallSet<Block *, 8> PreservedBlocks; +      for (auto *Sym : Sec.symbols()) { +        bool NewPreservedBlock = +            PreservedBlocks.insert(&Sym->getBlock()).second; +        if (NewPreservedBlock) +          Sym->setLive(true); +      } +      for (auto *B : Sec.blocks()) +        if (!PreservedBlocks.count(B)) +          G.addAnonymousSymbol(*B, 0, 0, false, true); +    } + +    return Error::success(); +  } + +protected: +  LinkGraph &G; +  ExecutorAddr RegisterActionAddr; +}; + +template <typename MachOTraits> +class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase { +public: +  MachODebugObjectSynthesizer(ExecutionSession &ES, LinkGraph &G, +                              ExecutorAddr RegisterActionAddr) +      : MachODebugObjectSynthesizerBase(G, RegisterActionAddr), +        Builder(ES.getPageSize()) {} + +  using MachODebugObjectSynthesizerBase::MachODebugObjectSynthesizerBase; + +  Error startSynthesis() override { +    LLVM_DEBUG({ +      dbgs() << "Creating " << SynthDebugSectionName << " for " << G.getName() +             << "\n"; +    }); + +    for (auto &Sec : G.sections()) { +      if (Sec.blocks().empty()) +        continue; + +      // Skip sections whose name's don't fit the MachO standard. +      if (Sec.getName().empty() || Sec.getName().size() > 33 || +          Sec.getName().find(',') > 16) +        continue; + +      if (isDebugSection(Sec)) +        DebugSections.push_back({&Sec, nullptr}); +      else if (Sec.getMemLifetime() != MemLifetime::NoAlloc) +        NonDebugSections.push_back({&Sec, nullptr}); +    } + +    // Bail out early if no debug sections. +    if (DebugSections.empty()) +      return Error::success(); + +    // Write MachO header and debug section load commands. +    Builder.Header.filetype = MachO::MH_OBJECT; +    switch (G.getTargetTriple().getArch()) { +    case Triple::x86_64: +      Builder.Header.cputype = MachO::CPU_TYPE_X86_64; +      Builder.Header.cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL; +      break; +    case Triple::aarch64: +      Builder.Header.cputype = MachO::CPU_TYPE_ARM64; +      Builder.Header.cpusubtype = MachO::CPU_SUBTYPE_ARM64_ALL; +      break; +    default: +      llvm_unreachable("Unsupported architecture"); +    } + +    Seg = &Builder.addSegment(""); + +    StringMap<std::unique_ptr<MemoryBuffer>> DebugSectionMap; +    StringRef DebugLineSectionData; +    for (auto &DSec : DebugSections) { +      auto [SegName, SecName] = DSec.GraphSec->getName().split(','); +      DSec.BuilderSec = &Seg->addSection(SecName, SegName); + +      SectionRange SR(*DSec.GraphSec); +      DSec.BuilderSec->Content.Size = SR.getSize(); +      if (!SR.empty()) { +        DSec.BuilderSec->align = Log2_64(SR.getFirstBlock()->getAlignment()); +        StringRef SectionData(SR.getFirstBlock()->getContent().data(), +                              SR.getFirstBlock()->getSize()); +        DebugSectionMap[SecName] = +            MemoryBuffer::getMemBuffer(SectionData, G.getName(), false); +        if (SecName == "__debug_line") +          DebugLineSectionData = SectionData; +      } +    } + +    std::optional<StringRef> FileName; +    if (!DebugLineSectionData.empty()) { +      assert((G.getEndianness() == llvm::endianness::big || +              G.getEndianness() == llvm::endianness::little) && +             "G.getEndianness() must be either big or little"); +      auto DWARFCtx = +          DWARFContext::create(DebugSectionMap, G.getPointerSize(), +                               G.getEndianness() == llvm::endianness::little); +      DWARFDataExtractor DebugLineData( +          DebugLineSectionData, G.getEndianness() == llvm::endianness::little, +          G.getPointerSize()); +      uint64_t Offset = 0; +      DWARFDebugLine::LineTable LineTable; + +      // Try to parse line data. Consume error on failure. +      if (auto Err = LineTable.parse(DebugLineData, &Offset, *DWARFCtx, nullptr, +                                     consumeError)) { +        handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) { +          LLVM_DEBUG({ +            dbgs() << "Cannot parse line table for \"" << G.getName() << "\": "; +            EIB.log(dbgs()); +            dbgs() << "\n"; +          }); +        }); +      } else { +        if (!LineTable.Prologue.FileNames.empty()) +          FileName = *dwarf::toString(LineTable.Prologue.FileNames[0].Name); +      } +    } + +    // If no line table (or unable to use) then use graph name. +    // FIXME: There are probably other debug sections we should look in first. +    if (!FileName) +      FileName = StringRef(G.getName()); + +    Builder.addSymbol("", MachO::N_SO, 0, 0, 0); +    Builder.addSymbol(*FileName, MachO::N_SO, 0, 0, 0); +    auto TimeStamp = std::chrono::duration_cast<std::chrono::seconds>( +                         std::chrono::system_clock::now().time_since_epoch()) +                         .count(); +    Builder.addSymbol("", MachO::N_OSO, 3, 1, TimeStamp); + +    for (auto &NDSP : NonDebugSections) { +      auto [SegName, SecName] = NDSP.GraphSec->getName().split(','); +      NDSP.BuilderSec = &Seg->addSection(SecName, SegName); +      SectionRange SR(*NDSP.GraphSec); +      if (!SR.empty()) +        NDSP.BuilderSec->align = Log2_64(SR.getFirstBlock()->getAlignment()); + +      // Add stabs. +      for (auto *Sym : NDSP.GraphSec->symbols()) { +        // Skip anonymous symbols. +        if (!Sym->hasName()) +          continue; + +        uint8_t SymType = Sym->isCallable() ? MachO::N_FUN : MachO::N_GSYM; + +        Builder.addSymbol("", MachO::N_BNSYM, 1, 0, 0); +        StabSymbols.push_back( +            {*Sym, Builder.addSymbol(Sym->getName(), SymType, 1, 0, 0), +             Builder.addSymbol(Sym->getName(), SymType, 0, 0, 0)}); +        Builder.addSymbol("", MachO::N_ENSYM, 1, 0, 0); +      } +    } + +    Builder.addSymbol("", MachO::N_SO, 1, 0, 0); + +    // Lay out the debug object, create a section and block for it. +    size_t DebugObjectSize = Builder.layout(); + +    auto &SDOSec = G.createSection(SynthDebugSectionName, MemProt::Read); +    MachOContainerBlock = &G.createMutableContentBlock( +        SDOSec, G.allocateBuffer(DebugObjectSize), orc::ExecutorAddr(), 8, 0); + +    return Error::success(); +  } + +  Error completeSynthesisAndRegister() override { +    if (!MachOContainerBlock) { +      LLVM_DEBUG({ +        dbgs() << "Not writing MachO debug object header for " << G.getName() +               << " since createDebugSection failed\n"; +      }); + +      return Error::success(); +    } +    ExecutorAddr MaxAddr; +    for (auto &NDSec : NonDebugSections) { +      SectionRange SR(*NDSec.GraphSec); +      NDSec.BuilderSec->addr = SR.getStart().getValue(); +      NDSec.BuilderSec->size = SR.getSize(); +      NDSec.BuilderSec->offset = SR.getStart().getValue(); +      if (SR.getEnd() > MaxAddr) +        MaxAddr = SR.getEnd(); +    } + +    for (auto &DSec : DebugSections) { +      if (DSec.GraphSec->blocks_size() != 1) +        return make_error<StringError>( +            "Unexpected number of blocks in debug info section", +            inconvertibleErrorCode()); + +      if (ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size > MaxAddr) +        MaxAddr = ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size; + +      auto &B = **DSec.GraphSec->blocks().begin(); +      DSec.BuilderSec->Content.Data = B.getContent().data(); +      DSec.BuilderSec->Content.Size = B.getContent().size(); +      DSec.BuilderSec->flags |= MachO::S_ATTR_DEBUG; +    } + +    LLVM_DEBUG({ +      dbgs() << "Writing MachO debug object header for " << G.getName() << "\n"; +    }); + +    // Update stab symbol addresses. +    for (auto &SS : StabSymbols) { +      SS.StartStab.nlist().n_value = SS.Sym.getAddress().getValue(); +      SS.EndStab.nlist().n_value = SS.Sym.getSize(); +    } + +    Builder.write(MachOContainerBlock->getAlreadyMutableContent()); + +    static constexpr bool AutoRegisterCode = true; +    SectionRange R(MachOContainerBlock->getSection()); +    G.allocActions().push_back( +        {cantFail(shared::WrapperFunctionCall::Create< +                  shared::SPSArgList<shared::SPSExecutorAddrRange, bool>>( +             RegisterActionAddr, R.getRange(), AutoRegisterCode)), +         {}}); + +    return Error::success(); +  } + +private: +  struct SectionPair { +    Section *GraphSec = nullptr; +    typename MachOBuilder<MachOTraits>::Section *BuilderSec = nullptr; +  }; + +  struct StabSymbolsEntry { +    using RelocTarget = typename MachOBuilder<MachOTraits>::RelocTarget; + +    StabSymbolsEntry(Symbol &Sym, RelocTarget StartStab, RelocTarget EndStab) +        : Sym(Sym), StartStab(StartStab), EndStab(EndStab) {} + +    Symbol &Sym; +    RelocTarget StartStab, EndStab; +  }; + +  using BuilderType = MachOBuilder<MachOTraits>; + +  Block *MachOContainerBlock = nullptr; +  MachOBuilder<MachOTraits> Builder; +  typename MachOBuilder<MachOTraits>::Segment *Seg = nullptr; +  std::vector<StabSymbolsEntry> StabSymbols; +  SmallVector<SectionPair, 16> DebugSections; +  SmallVector<SectionPair, 16> NonDebugSections; +}; + +} // end anonymous namespace + +namespace llvm { +namespace orc { + +Expected<std::unique_ptr<GDBJITDebugInfoRegistrationPlugin>> +GDBJITDebugInfoRegistrationPlugin::Create(ExecutionSession &ES, +                                          JITDylib &ProcessJD, +                                          const Triple &TT) { +  auto RegisterActionAddr = +      TT.isOSBinFormatMachO() +          ? ES.intern("_llvm_orc_registerJITLoaderGDBAllocAction") +          : ES.intern("llvm_orc_registerJITLoaderGDBAllocAction"); + +  if (auto RegisterSym = ES.lookup({&ProcessJD}, RegisterActionAddr)) +    return std::make_unique<GDBJITDebugInfoRegistrationPlugin>( +        RegisterSym->getAddress()); +  else +    return RegisterSym.takeError(); +} + +Error GDBJITDebugInfoRegistrationPlugin::notifyFailed( +    MaterializationResponsibility &MR) { +  return Error::success(); +} + +Error GDBJITDebugInfoRegistrationPlugin::notifyRemovingResources( +    JITDylib &JD, ResourceKey K) { +  return Error::success(); +} + +void GDBJITDebugInfoRegistrationPlugin::notifyTransferringResources( +    JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) {} + +void GDBJITDebugInfoRegistrationPlugin::modifyPassConfig( +    MaterializationResponsibility &MR, LinkGraph &LG, +    PassConfiguration &PassConfig) { + +  if (LG.getTargetTriple().getObjectFormat() == Triple::MachO) +    modifyPassConfigForMachO(MR, LG, PassConfig); +  else { +    LLVM_DEBUG({ +      dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph " +             << LG.getName() << "(triple = " << LG.getTargetTriple().str() +             << "\n"; +    }); +  } +} + +void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO( +    MaterializationResponsibility &MR, jitlink::LinkGraph &LG, +    jitlink::PassConfiguration &PassConfig) { + +  switch (LG.getTargetTriple().getArch()) { +  case Triple::x86_64: +  case Triple::aarch64: +    // Supported, continue. +    assert(LG.getPointerSize() == 8 && "Graph has incorrect pointer size"); +    assert(LG.getEndianness() == llvm::endianness::little && +           "Graph has incorrect endianness"); +    break; +  default: +    // Unsupported. +    LLVM_DEBUG({ +      dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported " +             << "MachO graph " << LG.getName() +             << "(triple = " << LG.getTargetTriple().str() +             << ", pointer size = " << LG.getPointerSize() << ", endianness = " +             << (LG.getEndianness() == llvm::endianness::big ? "big" : "little") +             << ")\n"; +    }); +    return; +  } + +  // Scan for debug sections. If we find one then install passes. +  bool HasDebugSections = false; +  for (auto &Sec : LG.sections()) +    if (MachODebugObjectSynthesizerBase::isDebugSection(Sec)) { +      HasDebugSections = true; +      break; +    } + +  if (HasDebugSections) { +    LLVM_DEBUG({ +      dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName() +             << " contains debug info. Installing debugger support passes.\n"; +    }); + +    auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>( +        MR.getTargetJITDylib().getExecutionSession(), LG, RegisterActionAddr); +    PassConfig.PrePrunePasses.push_back( +        [=](LinkGraph &G) { return MDOS->preserveDebugSections(); }); +    PassConfig.PostPrunePasses.push_back( +        [=](LinkGraph &G) { return MDOS->startSynthesis(); }); +    PassConfig.PostFixupPasses.push_back( +        [=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); }); +  } else { +    LLVM_DEBUG({ +      dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName() +             << " contains no debug info. Skipping.\n"; +    }); +  } +} + +} // namespace orc +} // namespace llvm  | 
