Path: blob/main/contrib/llvm-project/llvm/lib/ExecutionEngine/Orc/Debugging/DebuggerSupportPlugin.cpp
35294 views
//===------- DebuggerSupportPlugin.cpp - Utils for debugger support -------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8//9//===----------------------------------------------------------------------===//1011#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupportPlugin.h"12#include "llvm/ExecutionEngine/Orc/MachOBuilder.h"1314#include "llvm/ADT/SmallSet.h"15#include "llvm/ADT/SmallVector.h"16#include "llvm/BinaryFormat/MachO.h"17#include "llvm/DebugInfo/DWARF/DWARFContext.h"18#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"1920#include <chrono>2122#define DEBUG_TYPE "orc"2324using namespace llvm;25using namespace llvm::jitlink;26using namespace llvm::orc;2728static const char *SynthDebugSectionName = "__jitlink_synth_debug_object";2930namespace {3132class MachODebugObjectSynthesizerBase33: public GDBJITDebugInfoRegistrationPlugin::DebugSectionSynthesizer {34public:35static bool isDebugSection(Section &Sec) {36return Sec.getName().starts_with("__DWARF,");37}3839MachODebugObjectSynthesizerBase(LinkGraph &G, ExecutorAddr RegisterActionAddr)40: G(G), RegisterActionAddr(RegisterActionAddr) {}41virtual ~MachODebugObjectSynthesizerBase() = default;4243Error preserveDebugSections() {44if (G.findSectionByName(SynthDebugSectionName)) {45LLVM_DEBUG({46dbgs() << "MachODebugObjectSynthesizer skipping graph " << G.getName()47<< " which contains an unexpected existing "48<< SynthDebugSectionName << " section.\n";49});50return Error::success();51}5253LLVM_DEBUG({54dbgs() << "MachODebugObjectSynthesizer visiting graph " << G.getName()55<< "\n";56});57for (auto &Sec : G.sections()) {58if (!isDebugSection(Sec))59continue;60// Preserve blocks in this debug section by marking one existing symbol61// live for each block, and introducing a new live, anonymous symbol for62// each currently unreferenced block.63LLVM_DEBUG({64dbgs() << " Preserving debug section " << Sec.getName() << "\n";65});66SmallSet<Block *, 8> PreservedBlocks;67for (auto *Sym : Sec.symbols()) {68bool NewPreservedBlock =69PreservedBlocks.insert(&Sym->getBlock()).second;70if (NewPreservedBlock)71Sym->setLive(true);72}73for (auto *B : Sec.blocks())74if (!PreservedBlocks.count(B))75G.addAnonymousSymbol(*B, 0, 0, false, true);76}7778return Error::success();79}8081protected:82LinkGraph &G;83ExecutorAddr RegisterActionAddr;84};8586template <typename MachOTraits>87class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase {88public:89MachODebugObjectSynthesizer(ExecutionSession &ES, LinkGraph &G,90ExecutorAddr RegisterActionAddr)91: MachODebugObjectSynthesizerBase(G, RegisterActionAddr),92Builder(ES.getPageSize()) {}9394using MachODebugObjectSynthesizerBase::MachODebugObjectSynthesizerBase;9596Error startSynthesis() override {97LLVM_DEBUG({98dbgs() << "Creating " << SynthDebugSectionName << " for " << G.getName()99<< "\n";100});101102for (auto &Sec : G.sections()) {103if (Sec.blocks().empty())104continue;105106// Skip sections whose name's don't fit the MachO standard.107if (Sec.getName().empty() || Sec.getName().size() > 33 ||108Sec.getName().find(',') > 16)109continue;110111if (isDebugSection(Sec))112DebugSections.push_back({&Sec, nullptr});113else if (Sec.getMemLifetime() != MemLifetime::NoAlloc)114NonDebugSections.push_back({&Sec, nullptr});115}116117// Bail out early if no debug sections.118if (DebugSections.empty())119return Error::success();120121// Write MachO header and debug section load commands.122Builder.Header.filetype = MachO::MH_OBJECT;123switch (G.getTargetTriple().getArch()) {124case Triple::x86_64:125Builder.Header.cputype = MachO::CPU_TYPE_X86_64;126Builder.Header.cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL;127break;128case Triple::aarch64:129Builder.Header.cputype = MachO::CPU_TYPE_ARM64;130Builder.Header.cpusubtype = MachO::CPU_SUBTYPE_ARM64_ALL;131break;132default:133llvm_unreachable("Unsupported architecture");134}135136Seg = &Builder.addSegment("");137138StringMap<std::unique_ptr<MemoryBuffer>> DebugSectionMap;139StringRef DebugLineSectionData;140for (auto &DSec : DebugSections) {141auto [SegName, SecName] = DSec.GraphSec->getName().split(',');142DSec.BuilderSec = &Seg->addSection(SecName, SegName);143144SectionRange SR(*DSec.GraphSec);145DSec.BuilderSec->Content.Size = SR.getSize();146if (!SR.empty()) {147DSec.BuilderSec->align = Log2_64(SR.getFirstBlock()->getAlignment());148StringRef SectionData(SR.getFirstBlock()->getContent().data(),149SR.getFirstBlock()->getSize());150DebugSectionMap[SecName] =151MemoryBuffer::getMemBuffer(SectionData, G.getName(), false);152if (SecName == "__debug_line")153DebugLineSectionData = SectionData;154}155}156157std::optional<StringRef> FileName;158if (!DebugLineSectionData.empty()) {159assert((G.getEndianness() == llvm::endianness::big ||160G.getEndianness() == llvm::endianness::little) &&161"G.getEndianness() must be either big or little");162auto DWARFCtx =163DWARFContext::create(DebugSectionMap, G.getPointerSize(),164G.getEndianness() == llvm::endianness::little);165DWARFDataExtractor DebugLineData(166DebugLineSectionData, G.getEndianness() == llvm::endianness::little,167G.getPointerSize());168uint64_t Offset = 0;169DWARFDebugLine::LineTable LineTable;170171// Try to parse line data. Consume error on failure.172if (auto Err = LineTable.parse(DebugLineData, &Offset, *DWARFCtx, nullptr,173consumeError)) {174handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {175LLVM_DEBUG({176dbgs() << "Cannot parse line table for \"" << G.getName() << "\": ";177EIB.log(dbgs());178dbgs() << "\n";179});180});181} else {182if (!LineTable.Prologue.FileNames.empty())183FileName = *dwarf::toString(LineTable.Prologue.FileNames[0].Name);184}185}186187// If no line table (or unable to use) then use graph name.188// FIXME: There are probably other debug sections we should look in first.189if (!FileName)190FileName = StringRef(G.getName());191192Builder.addSymbol("", MachO::N_SO, 0, 0, 0);193Builder.addSymbol(*FileName, MachO::N_SO, 0, 0, 0);194auto TimeStamp = std::chrono::duration_cast<std::chrono::seconds>(195std::chrono::system_clock::now().time_since_epoch())196.count();197Builder.addSymbol("", MachO::N_OSO, 3, 1, TimeStamp);198199for (auto &NDSP : NonDebugSections) {200auto [SegName, SecName] = NDSP.GraphSec->getName().split(',');201NDSP.BuilderSec = &Seg->addSection(SecName, SegName);202SectionRange SR(*NDSP.GraphSec);203if (!SR.empty())204NDSP.BuilderSec->align = Log2_64(SR.getFirstBlock()->getAlignment());205206// Add stabs.207for (auto *Sym : NDSP.GraphSec->symbols()) {208// Skip anonymous symbols.209if (!Sym->hasName())210continue;211212uint8_t SymType = Sym->isCallable() ? MachO::N_FUN : MachO::N_GSYM;213214Builder.addSymbol("", MachO::N_BNSYM, 1, 0, 0);215StabSymbols.push_back(216{*Sym, Builder.addSymbol(Sym->getName(), SymType, 1, 0, 0),217Builder.addSymbol(Sym->getName(), SymType, 0, 0, 0)});218Builder.addSymbol("", MachO::N_ENSYM, 1, 0, 0);219}220}221222Builder.addSymbol("", MachO::N_SO, 1, 0, 0);223224// Lay out the debug object, create a section and block for it.225size_t DebugObjectSize = Builder.layout();226227auto &SDOSec = G.createSection(SynthDebugSectionName, MemProt::Read);228MachOContainerBlock = &G.createMutableContentBlock(229SDOSec, G.allocateBuffer(DebugObjectSize), orc::ExecutorAddr(), 8, 0);230231return Error::success();232}233234Error completeSynthesisAndRegister() override {235if (!MachOContainerBlock) {236LLVM_DEBUG({237dbgs() << "Not writing MachO debug object header for " << G.getName()238<< " since createDebugSection failed\n";239});240241return Error::success();242}243ExecutorAddr MaxAddr;244for (auto &NDSec : NonDebugSections) {245SectionRange SR(*NDSec.GraphSec);246NDSec.BuilderSec->addr = SR.getStart().getValue();247NDSec.BuilderSec->size = SR.getSize();248NDSec.BuilderSec->offset = SR.getStart().getValue();249if (SR.getEnd() > MaxAddr)250MaxAddr = SR.getEnd();251}252253for (auto &DSec : DebugSections) {254if (DSec.GraphSec->blocks_size() != 1)255return make_error<StringError>(256"Unexpected number of blocks in debug info section",257inconvertibleErrorCode());258259if (ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size > MaxAddr)260MaxAddr = ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size;261262auto &B = **DSec.GraphSec->blocks().begin();263DSec.BuilderSec->Content.Data = B.getContent().data();264DSec.BuilderSec->Content.Size = B.getContent().size();265DSec.BuilderSec->flags |= MachO::S_ATTR_DEBUG;266}267268LLVM_DEBUG({269dbgs() << "Writing MachO debug object header for " << G.getName() << "\n";270});271272// Update stab symbol addresses.273for (auto &SS : StabSymbols) {274SS.StartStab.nlist().n_value = SS.Sym.getAddress().getValue();275SS.EndStab.nlist().n_value = SS.Sym.getSize();276}277278Builder.write(MachOContainerBlock->getAlreadyMutableContent());279280static constexpr bool AutoRegisterCode = true;281SectionRange R(MachOContainerBlock->getSection());282G.allocActions().push_back(283{cantFail(shared::WrapperFunctionCall::Create<284shared::SPSArgList<shared::SPSExecutorAddrRange, bool>>(285RegisterActionAddr, R.getRange(), AutoRegisterCode)),286{}});287288return Error::success();289}290291private:292struct SectionPair {293Section *GraphSec = nullptr;294typename MachOBuilder<MachOTraits>::Section *BuilderSec = nullptr;295};296297struct StabSymbolsEntry {298using RelocTarget = typename MachOBuilder<MachOTraits>::RelocTarget;299300StabSymbolsEntry(Symbol &Sym, RelocTarget StartStab, RelocTarget EndStab)301: Sym(Sym), StartStab(StartStab), EndStab(EndStab) {}302303Symbol &Sym;304RelocTarget StartStab, EndStab;305};306307using BuilderType = MachOBuilder<MachOTraits>;308309Block *MachOContainerBlock = nullptr;310MachOBuilder<MachOTraits> Builder;311typename MachOBuilder<MachOTraits>::Segment *Seg = nullptr;312std::vector<StabSymbolsEntry> StabSymbols;313SmallVector<SectionPair, 16> DebugSections;314SmallVector<SectionPair, 16> NonDebugSections;315};316317} // end anonymous namespace318319namespace llvm {320namespace orc {321322Expected<std::unique_ptr<GDBJITDebugInfoRegistrationPlugin>>323GDBJITDebugInfoRegistrationPlugin::Create(ExecutionSession &ES,324JITDylib &ProcessJD,325const Triple &TT) {326auto RegisterActionAddr =327TT.isOSBinFormatMachO()328? ES.intern("_llvm_orc_registerJITLoaderGDBAllocAction")329: ES.intern("llvm_orc_registerJITLoaderGDBAllocAction");330331if (auto RegisterSym = ES.lookup({&ProcessJD}, RegisterActionAddr))332return std::make_unique<GDBJITDebugInfoRegistrationPlugin>(333RegisterSym->getAddress());334else335return RegisterSym.takeError();336}337338Error GDBJITDebugInfoRegistrationPlugin::notifyFailed(339MaterializationResponsibility &MR) {340return Error::success();341}342343Error GDBJITDebugInfoRegistrationPlugin::notifyRemovingResources(344JITDylib &JD, ResourceKey K) {345return Error::success();346}347348void GDBJITDebugInfoRegistrationPlugin::notifyTransferringResources(349JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) {}350351void GDBJITDebugInfoRegistrationPlugin::modifyPassConfig(352MaterializationResponsibility &MR, LinkGraph &LG,353PassConfiguration &PassConfig) {354355if (LG.getTargetTriple().getObjectFormat() == Triple::MachO)356modifyPassConfigForMachO(MR, LG, PassConfig);357else {358LLVM_DEBUG({359dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph "360<< LG.getName() << "(triple = " << LG.getTargetTriple().str()361<< "\n";362});363}364}365366void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO(367MaterializationResponsibility &MR, jitlink::LinkGraph &LG,368jitlink::PassConfiguration &PassConfig) {369370switch (LG.getTargetTriple().getArch()) {371case Triple::x86_64:372case Triple::aarch64:373// Supported, continue.374assert(LG.getPointerSize() == 8 && "Graph has incorrect pointer size");375assert(LG.getEndianness() == llvm::endianness::little &&376"Graph has incorrect endianness");377break;378default:379// Unsupported.380LLVM_DEBUG({381dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported "382<< "MachO graph " << LG.getName()383<< "(triple = " << LG.getTargetTriple().str()384<< ", pointer size = " << LG.getPointerSize() << ", endianness = "385<< (LG.getEndianness() == llvm::endianness::big ? "big" : "little")386<< ")\n";387});388return;389}390391// Scan for debug sections. If we find one then install passes.392bool HasDebugSections = false;393for (auto &Sec : LG.sections())394if (MachODebugObjectSynthesizerBase::isDebugSection(Sec)) {395HasDebugSections = true;396break;397}398399if (HasDebugSections) {400LLVM_DEBUG({401dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()402<< " contains debug info. Installing debugger support passes.\n";403});404405auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>(406MR.getTargetJITDylib().getExecutionSession(), LG, RegisterActionAddr);407PassConfig.PrePrunePasses.push_back(408[=](LinkGraph &G) { return MDOS->preserveDebugSections(); });409PassConfig.PostPrunePasses.push_back(410[=](LinkGraph &G) { return MDOS->startSynthesis(); });411PassConfig.PostFixupPasses.push_back(412[=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); });413} else {414LLVM_DEBUG({415dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()416<< " contains no debug info. Skipping.\n";417});418}419}420421} // namespace orc422} // namespace llvm423424425