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