LLVM 18.0.0git
DebuggerSupportPlugin.cpp
Go to the documentation of this file.
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
14
15#include "llvm/ADT/SmallSet.h"
17#include "llvm/ADT/StringSet.h"
21
22#include <chrono>
23
24#define DEBUG_TYPE "orc"
25
26using namespace llvm;
27using namespace llvm::jitlink;
28using namespace llvm::orc;
29
30static const char *SynthDebugSectionName = "__jitlink_synth_debug_object";
31
32namespace {
33
34class MachODebugObjectSynthesizerBase
36public:
37 static bool isDebugSection(Section &Sec) {
38 return Sec.getName().startswith("__DWARF,");
39 }
40
41 MachODebugObjectSynthesizerBase(LinkGraph &G, ExecutorAddr RegisterActionAddr)
42 : G(G), RegisterActionAddr(RegisterActionAddr) {}
43 virtual ~MachODebugObjectSynthesizerBase() = default;
44
46 if (G.findSectionByName(SynthDebugSectionName)) {
48 dbgs() << "MachODebugObjectSynthesizer skipping graph " << G.getName()
49 << " which contains an unexpected existing "
50 << SynthDebugSectionName << " section.\n";
51 });
52 return Error::success();
53 }
54
56 dbgs() << "MachODebugObjectSynthesizer visiting graph " << G.getName()
57 << "\n";
58 });
59 for (auto &Sec : G.sections()) {
60 if (!isDebugSection(Sec))
61 continue;
62 // Preserve blocks in this debug section by marking one existing symbol
63 // live for each block, and introducing a new live, anonymous symbol for
64 // each currently unreferenced block.
66 dbgs() << " Preserving debug section " << Sec.getName() << "\n";
67 });
68 SmallSet<Block *, 8> PreservedBlocks;
69 for (auto *Sym : Sec.symbols()) {
70 bool NewPreservedBlock =
71 PreservedBlocks.insert(&Sym->getBlock()).second;
72 if (NewPreservedBlock)
73 Sym->setLive(true);
74 }
75 for (auto *B : Sec.blocks())
76 if (!PreservedBlocks.count(B))
77 G.addAnonymousSymbol(*B, 0, 0, false, true);
78 }
79
80 return Error::success();
81 }
82
83protected:
84 LinkGraph &G;
85 ExecutorAddr RegisterActionAddr;
86};
87
88template <typename MachOTraits>
89class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase {
90public:
91 MachODebugObjectSynthesizer(ExecutionSession &ES, LinkGraph &G,
92 ExecutorAddr RegisterActionAddr)
93 : MachODebugObjectSynthesizerBase(G, RegisterActionAddr),
94 Builder(ES.getPageSize()) {}
95
96 using MachODebugObjectSynthesizerBase::MachODebugObjectSynthesizerBase;
97
98 Error startSynthesis() override {
100 dbgs() << "Creating " << SynthDebugSectionName << " for " << G.getName()
101 << "\n";
102 });
103
104 for (auto &Sec : G.sections()) {
105 if (Sec.blocks().empty())
106 continue;
107
108 // Skip sections whose name's don't fit the MachO standard.
109 if (Sec.getName().empty() || Sec.getName().size() > 33 ||
110 Sec.getName().find(',') > 16)
111 continue;
112
113 if (isDebugSection(Sec))
114 DebugSections.push_back({&Sec, nullptr});
115 else if (Sec.getMemLifetime() != MemLifetime::NoAlloc)
116 NonDebugSections.push_back({&Sec, nullptr});
117 }
118
119 // Bail out early if no debug sections.
120 if (DebugSections.empty())
121 return Error::success();
122
123 // Write MachO header and debug section load commands.
124 Builder.Header.filetype = MachO::MH_OBJECT;
125 switch (G.getTargetTriple().getArch()) {
126 case Triple::x86_64:
127 Builder.Header.cputype = MachO::CPU_TYPE_X86_64;
128 Builder.Header.cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL;
129 break;
130 case Triple::aarch64:
131 Builder.Header.cputype = MachO::CPU_TYPE_ARM64;
132 Builder.Header.cpusubtype = MachO::CPU_SUBTYPE_ARM64_ALL;
133 break;
134 default:
135 llvm_unreachable("Unsupported architecture");
136 }
137
138 Seg = &Builder.addSegment("");
139
141 StringRef DebugLineSectionData;
142 for (auto &DSec : DebugSections) {
143 auto [SegName, SecName] = DSec.GraphSec->getName().split(',');
144 DSec.BuilderSec = &Seg->addSection(SecName, SegName);
145
146 SectionRange SR(*DSec.GraphSec);
147 DSec.BuilderSec->Content.Size = SR.getSize();
148 if (!SR.empty()) {
149 DSec.BuilderSec->align = Log2_64(SR.getFirstBlock()->getAlignment());
150 StringRef SectionData(SR.getFirstBlock()->getContent().data(),
151 SR.getFirstBlock()->getSize());
152 DebugSectionMap[SecName] =
153 MemoryBuffer::getMemBuffer(SectionData, G.getName(), false);
154 if (SecName == "__debug_line")
155 DebugLineSectionData = SectionData;
156 }
157 }
158
159 std::optional<StringRef> FileName;
160 if (!DebugLineSectionData.empty()) {
161 assert((G.getEndianness() == support::endianness::big ||
162 G.getEndianness() == support::endianness::little) &&
163 "G.getEndianness() must be either big or little");
164 auto DWARFCtx = DWARFContext::create(DebugSectionMap, G.getPointerSize(),
165 G.getEndianness() ==
166 support::endianness::little);
167 DWARFDataExtractor DebugLineData(
168 DebugLineSectionData,
169 G.getEndianness() == support::endianness::little, G.getPointerSize());
170 uint64_t Offset = 0;
172
173 // Try to parse line data. Consume error on failure.
174 if (auto Err = LineTable.parse(DebugLineData, &Offset, *DWARFCtx, nullptr,
175 consumeError)) {
176 handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
177 LLVM_DEBUG({
178 dbgs() << "Cannot parse line table for \"" << G.getName() << "\": ";
179 EIB.log(dbgs());
180 dbgs() << "\n";
181 });
182 });
183 } else {
184 if (!LineTable.Prologue.FileNames.empty())
185 FileName = *dwarf::toString(LineTable.Prologue.FileNames[0].Name);
186 }
187 }
188
189 // If no line table (or unable to use) then use graph name.
190 // FIXME: There are probably other debug sections we should look in first.
191 if (!FileName)
192 FileName = StringRef(G.getName());
193
194 Builder.addSymbol("", MachO::N_SO, 0, 0, 0);
195 Builder.addSymbol(*FileName, MachO::N_SO, 0, 0, 0);
196 auto TimeStamp = std::chrono::duration_cast<std::chrono::seconds>(
197 std::chrono::system_clock::now().time_since_epoch())
198 .count();
199 Builder.addSymbol("", MachO::N_OSO, 3, 1, TimeStamp);
200
201 for (auto &NDSP : NonDebugSections) {
202 auto [SegName, SecName] = NDSP.GraphSec->getName().split(',');
203 NDSP.BuilderSec = &Seg->addSection(SecName, SegName);
204 SectionRange SR(*NDSP.GraphSec);
205 if (!SR.empty())
206 NDSP.BuilderSec->align = Log2_64(SR.getFirstBlock()->getAlignment());
207
208 // Add stabs.
209 for (auto *Sym : NDSP.GraphSec->symbols()) {
210 // Skip anonymous symbols.
211 if (!Sym->hasName())
212 continue;
213
214 uint8_t SymType = Sym->isCallable() ? MachO::N_FUN : MachO::N_GSYM;
215
216 Builder.addSymbol("", MachO::N_BNSYM, 1, 0, 0);
217 StabSymbols.push_back(
218 {*Sym, Builder.addSymbol(Sym->getName(), SymType, 1, 0, 0),
219 Builder.addSymbol(Sym->getName(), SymType, 0, 0, 0)});
220 Builder.addSymbol("", MachO::N_ENSYM, 1, 0, 0);
221 }
222 }
223
224 Builder.addSymbol("", MachO::N_SO, 1, 0, 0);
225
226 // Lay out the debug object, create a section and block for it.
227 size_t DebugObjectSize = Builder.layout();
228
229 auto &SDOSec = G.createSection(SynthDebugSectionName, MemProt::Read);
230 MachOContainerBlock = &G.createMutableContentBlock(
231 SDOSec, G.allocateBuffer(DebugObjectSize), orc::ExecutorAddr(), 8, 0);
232
233 return Error::success();
234 }
235
236 Error completeSynthesisAndRegister() override {
237 if (!MachOContainerBlock) {
238 LLVM_DEBUG({
239 dbgs() << "Not writing MachO debug object header for " << G.getName()
240 << " since createDebugSection failed\n";
241 });
242
243 return Error::success();
244 }
245 ExecutorAddr MaxAddr;
246 for (auto &NDSec : NonDebugSections) {
247 SectionRange SR(*NDSec.GraphSec);
248 NDSec.BuilderSec->addr = SR.getStart().getValue();
249 NDSec.BuilderSec->size = SR.getSize();
250 NDSec.BuilderSec->offset = SR.getStart().getValue();
251 if (SR.getEnd() > MaxAddr)
252 MaxAddr = SR.getEnd();
253 }
254
255 for (auto &DSec : DebugSections) {
256 if (DSec.GraphSec->blocks_size() != 1)
257 return make_error<StringError>(
258 "Unexpected number of blocks in debug info section",
260
261 if (ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size > MaxAddr)
262 MaxAddr = ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size;
263
264 auto &B = **DSec.GraphSec->blocks().begin();
265 DSec.BuilderSec->Content.Data = B.getContent().data();
266 DSec.BuilderSec->Content.Size = B.getContent().size();
267 DSec.BuilderSec->flags |= MachO::S_ATTR_DEBUG;
268 }
269
270 LLVM_DEBUG({
271 dbgs() << "Writing MachO debug object header for " << G.getName() << "\n";
272 });
273
274 // Update stab symbol addresses.
275 for (auto &SS : StabSymbols) {
276 SS.StartStab.nlist().n_value = SS.Sym.getAddress().getValue();
277 SS.EndStab.nlist().n_value = SS.Sym.getSize();
278 }
279
280 Builder.write(MachOContainerBlock->getAlreadyMutableContent());
281
282 static constexpr bool AutoRegisterCode = true;
283 SectionRange R(MachOContainerBlock->getSection());
284 G.allocActions().push_back(
287 RegisterActionAddr, R.getRange(), AutoRegisterCode)),
288 {}});
289
290 return Error::success();
291 }
292
293private:
294 struct SectionPair {
295 Section *GraphSec = nullptr;
296 typename MachOBuilder<MachOTraits>::Section *BuilderSec = nullptr;
297 };
298
299 struct StabSymbolsEntry {
300 using RelocTarget = typename MachOBuilder<MachOTraits>::RelocTarget;
301
302 StabSymbolsEntry(Symbol &Sym, RelocTarget StartStab, RelocTarget EndStab)
303 : Sym(Sym), StartStab(StartStab), EndStab(EndStab) {}
304
305 Symbol &Sym;
306 RelocTarget StartStab, EndStab;
307 };
308
309 using BuilderType = MachOBuilder<MachOTraits>;
310
311 Block *MachOContainerBlock = nullptr;
313 typename MachOBuilder<MachOTraits>::Segment *Seg = nullptr;
314 std::vector<StabSymbolsEntry> StabSymbols;
315 SmallVector<SectionPair, 16> DebugSections;
316 SmallVector<SectionPair, 16> NonDebugSections;
317};
318
319} // end anonymous namespace
320
321namespace llvm {
322namespace orc {
323
326 JITDylib &ProcessJD,
327 const Triple &TT) {
328 auto RegisterActionAddr =
329 TT.isOSBinFormatMachO()
330 ? ES.intern("_llvm_orc_registerJITLoaderGDBAllocAction")
331 : ES.intern("llvm_orc_registerJITLoaderGDBAllocAction");
332
333 if (auto RegisterSym = ES.lookup({&ProcessJD}, RegisterActionAddr))
334 return std::make_unique<GDBJITDebugInfoRegistrationPlugin>(
335 RegisterSym->getAddress());
336 else
337 return RegisterSym.takeError();
338}
339
342 return Error::success();
343}
344
346 JITDylib &JD, ResourceKey K) {
347 return Error::success();
348}
349
351 JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) {}
352
355 PassConfiguration &PassConfig) {
356
358 modifyPassConfigForMachO(MR, LG, PassConfig);
359 else {
360 LLVM_DEBUG({
361 dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph "
362 << LG.getName() << "(triple = " << LG.getTargetTriple().str()
363 << "\n";
364 });
365 }
366}
367
368void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO(
370 jitlink::PassConfiguration &PassConfig) {
371
372 switch (LG.getTargetTriple().getArch()) {
373 case Triple::x86_64:
374 case Triple::aarch64:
375 // Supported, continue.
376 assert(LG.getPointerSize() == 8 && "Graph has incorrect pointer size");
378 "Graph has incorrect endianness");
379 break;
380 default:
381 // Unsupported.
382 LLVM_DEBUG({
383 dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported "
384 << "MachO graph " << LG.getName()
385 << "(triple = " << LG.getTargetTriple().str()
386 << ", pointer size = " << LG.getPointerSize() << ", endianness = "
387 << (LG.getEndianness() == support::big ? "big" : "little")
388 << ")\n";
389 });
390 return;
391 }
392
393 // Scan for debug sections. If we find one then install passes.
394 bool HasDebugSections = false;
395 for (auto &Sec : LG.sections())
396 if (MachODebugObjectSynthesizerBase::isDebugSection(Sec)) {
397 HasDebugSections = true;
398 break;
399 }
400
401 if (HasDebugSections) {
402 LLVM_DEBUG({
403 dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
404 << " contains debug info. Installing debugger support passes.\n";
405 });
406
407 auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>(
408 MR.getTargetJITDylib().getExecutionSession(), LG, RegisterActionAddr);
409 PassConfig.PrePrunePasses.push_back(
410 [=](LinkGraph &G) { return MDOS->preserveDebugSections(); });
411 PassConfig.PostPrunePasses.push_back(
412 [=](LinkGraph &G) { return MDOS->startSynthesis(); });
413 PassConfig.PostFixupPasses.push_back(
414 [=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); });
415 } else {
416 LLVM_DEBUG({
417 dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
418 << " contains no debug info. Skipping.\n";
419 });
420 }
421}
422
423} // namespace orc
424} // namespace llvm
assume Assume Builder
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
#define LLVM_DEBUG(X)
Definition: Debug.h:101
static const char * SynthDebugSectionName
static bool isDebugSection(const SectionBase &Sec)
Definition: ELFObjcopy.cpp:54
Symbol * Sym
Definition: ELF_riscv.cpp:468
#define G(x, y, z)
Definition: MD5.cpp:56
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
This file defines the SmallSet class.
This file defines the SmallVector class.
StringSet - A set-like wrapper for the StringMap.
const T * data() const
Definition: ArrayRef.h:162
static std::unique_ptr< DWARFContext > create(const object::ObjectFile &Obj, ProcessDebugRelocations RelocAction=ProcessDebugRelocations::Process, const LoadedObjectInfo *L=nullptr, std::string DWPName="", std::function< void(Error)> RecoverableErrorHandler=WithColor::defaultErrorHandler, std::function< void(Error)> WarningHandler=WithColor::defaultWarningHandler, bool ThreadSafe=false)
A DataExtractor (typically for an in-memory copy of an object-file section) plus a relocation map for...
Base class for error info classes.
Definition: Error.h:45
virtual void log(raw_ostream &OS) const =0
Print an error message to an output stream.
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
static ErrorSuccess success()
Create a success value.
Definition: Error.h:334
Tagged union holding either a T or a Error.
Definition: Error.h:474
static std::unique_ptr< MemoryBuffer > getMemBuffer(StringRef InputData, StringRef BufferName="", bool RequiresNullTerminator=true)
Open the specified memory range as a MemoryBuffer.
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition: SmallSet.h:135
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
Definition: SmallSet.h:179
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1200
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition: StringMap.h:112
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
std::pair< StringRef, StringRef > split(char Separator) const
Split into two substrings around the first occurrence of a separator character.
Definition: StringRef.h:704
constexpr bool empty() const
empty - Check if the string is empty.
Definition: StringRef.h:134
bool startswith(StringRef Prefix) const
Definition: StringRef.h:261
Triple - Helper class for working with autoconf configuration names.
Definition: Triple.h:44
ObjectFormatType getObjectFormat() const
Get the object format for this triple.
Definition: Triple.h:381
ArchType getArch() const
Get the parsed architecture type of this triple.
Definition: Triple.h:355
const std::string & str() const
Definition: Triple.h:414
An ExecutionSession represents a running JIT program.
Definition: Core.h:1389
SymbolStringPtr intern(StringRef SymName)
Add a symbol name to the SymbolStringPool and return a pointer to it.
Definition: Core.h:1446
void lookup(LookupKind K, const JITDylibSearchOrder &SearchOrder, SymbolLookupSet Symbols, SymbolState RequiredState, SymbolsResolvedCallback NotifyComplete, RegisterDependenciesFunction RegisterDependencies)
Search the given JITDylibs for the given symbols.
Definition: Core.cpp:2121
size_t getPageSize() const
Definition: Core.h:1438
Represents an address in the executor process.
void modifyPassConfig(MaterializationResponsibility &MR, jitlink::LinkGraph &LG, jitlink::PassConfiguration &PassConfig) override
static Expected< std::unique_ptr< GDBJITDebugInfoRegistrationPlugin > > Create(ExecutionSession &ES, JITDylib &ProcessJD, const Triple &TT)
void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) override
Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override
Error notifyFailed(MaterializationResponsibility &MR) override
Represents a JIT'd dynamic library.
Definition: Core.h:958
ExecutionSession & getExecutionSession() const
Get a reference to the ExecutionSession for this JITDylib.
Definition: Core.h:977
Tracks responsibility for materialization, and mediates interactions between MaterializationUnits and...
Definition: Core.h:527
JITDylib & getTargetJITDylib() const
Returns the target JITDylib that these symbols are being materialized into.
Definition: Core.h:549
A utility class for serializing to a blob from a variadic list.
static Expected< WrapperFunctionCall > Create(ExecutorAddr FnAddr, const ArgTs &...Args)
Create a WrapperFunctionCall using the given SPS serializer to serialize the arguments.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ N_ENSYM
Definition: MachO.h:372
@ N_GSYM
Definition: MachO.h:361
@ N_BNSYM
Definition: MachO.h:366
@ CPU_SUBTYPE_ARM64_ALL
Definition: MachO.h:1647
@ MH_OBJECT
Definition: MachO.h:43
@ S_ATTR_DEBUG
S_ATTR_DEBUG - A debug section.
Definition: MachO.h:207
@ CPU_SUBTYPE_X86_64_ALL
Definition: MachO.h:1617
@ CPU_TYPE_ARM64
Definition: MachO.h:1576
@ CPU_TYPE_X86_64
Definition: MachO.h:1572
@ SS
Definition: X86.h:208
std::optional< const char * > toString(const std::optional< DWARFFormValue > &V)
Take an optional DWARFFormValue and try to extract a string value from it.
Error preserveDebugSections(jitlink::LinkGraph &G)
uintptr_t ResourceKey
Definition: Core.h:53
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
@ Offset
Definition: DWP.cpp:440
void handleAllErrors(Error E, HandlerTs &&... Handlers)
Behaves the same as handleErrors, except that by contract all errors must be handled by the given han...
Definition: Error.h:970
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition: Error.cpp:90
unsigned Log2_64(uint64_t Value)
Return the floor log base 2 of the specified value, -1 if the value is zero.
Definition: MathExtras.h:319
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition: Error.h:749
void consumeError(Error Err)
Consume a Error without doing anything.
Definition: Error.h:1041
Error parse(DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, const DWARFContext &Ctx, const DWARFUnit *U, function_ref< void(Error)> RecoverableErrorHandler, raw_ostream *OS=nullptr, bool Verbose=false)
Parse prologue and all rows.
std::vector< FileNameEntry > FileNames