LLVM 20.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"
20
21#include <chrono>
22
23#define DEBUG_TYPE "orc"
24
25using namespace llvm;
26using namespace llvm::jitlink;
27using namespace llvm::orc;
28
29static const char *SynthDebugSectionName = "__jitlink_synth_debug_object";
30
31namespace {
32
33class MachODebugObjectSynthesizerBase
35public:
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
45 if (G.findSectionByName(SynthDebugSectionName)) {
47 dbgs() << "MachODebugObjectSynthesizer skipping graph " << G.getName()
48 << " which contains an unexpected existing "
49 << SynthDebugSectionName << " section.\n";
50 });
51 return Error::success();
52 }
53
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.
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
82protected:
83 LinkGraph &G;
84 ExecutorAddr RegisterActionAddr;
85};
86
87template <typename MachOTraits>
88class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase {
89public:
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 {
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
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.drop_front(2)] = // drop "__" prefix.
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;
171
172 // Try to parse line data. Consume error on failure.
173 if (auto Err = P.parse(DebugLineData, &Offset, consumeError, *DWARFCtx)) {
174 handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
175 LLVM_DEBUG({
176 dbgs() << "Cannot parse line table for \"" << G.getName() << "\": ";
177 EIB.log(dbgs());
178 dbgs() << "\n";
179 });
180 });
181 } else {
182 for (auto &FN : P.FileNames)
183 if ((FileName = dwarf::toString(FN.Name))) {
184 LLVM_DEBUG({
185 dbgs() << "Using FileName = \"" << *FileName
186 << "\" from DWARF line table\n";
187 });
188 break;
189 }
190 }
191 }
192
193 // If no line table (or unable to use) then use graph name.
194 // FIXME: There are probably other debug sections we should look in first.
195 if (!FileName) {
196 LLVM_DEBUG({
197 dbgs() << "Could not find source name from DWARF line table. "
198 "Using FileName = \"\"\n";
199 });
200 FileName = "";
201 }
202
203 Builder.addSymbol("", MachO::N_SO, 0, 0, 0);
204 Builder.addSymbol(*FileName, MachO::N_SO, 0, 0, 0);
205 auto TimeStamp = std::chrono::duration_cast<std::chrono::seconds>(
206 std::chrono::system_clock::now().time_since_epoch())
207 .count();
208 Builder.addSymbol("", MachO::N_OSO, 3, 1, TimeStamp);
209
210 for (auto &NDSP : NonDebugSections) {
211 auto [SegName, SecName] = NDSP.GraphSec->getName().split(',');
212 NDSP.BuilderSec = &Seg->addSection(SecName, SegName);
213 SectionRange SR(*NDSP.GraphSec);
214 if (!SR.empty())
215 NDSP.BuilderSec->align = Log2_64(SR.getFirstBlock()->getAlignment());
216
217 // Add stabs.
218 for (auto *Sym : NDSP.GraphSec->symbols()) {
219 // Skip anonymous symbols.
220 if (!Sym->hasName())
221 continue;
222
224
225 Builder.addSymbol("", MachO::N_BNSYM, 1, 0, 0);
226 StabSymbols.push_back(
227 {*Sym, Builder.addSymbol(*Sym->getName(), SymType, 1, 0, 0),
228 Builder.addSymbol(*Sym->getName(), SymType, 0, 0, 0)});
229 Builder.addSymbol("", MachO::N_ENSYM, 1, 0, 0);
230 }
231 }
232
233 Builder.addSymbol("", MachO::N_SO, 1, 0, 0);
234
235 // Lay out the debug object, create a section and block for it.
236 size_t DebugObjectSize = Builder.layout();
237
238 auto &SDOSec = G.createSection(SynthDebugSectionName, MemProt::Read);
239 MachOContainerBlock = &G.createMutableContentBlock(
240 SDOSec, G.allocateBuffer(DebugObjectSize), orc::ExecutorAddr(), 8, 0);
241
242 return Error::success();
243 }
244
245 Error completeSynthesisAndRegister() override {
246 if (!MachOContainerBlock) {
247 LLVM_DEBUG({
248 dbgs() << "Not writing MachO debug object header for " << G.getName()
249 << " since createDebugSection failed\n";
250 });
251
252 return Error::success();
253 }
254 ExecutorAddr MaxAddr;
255 for (auto &NDSec : NonDebugSections) {
256 SectionRange SR(*NDSec.GraphSec);
257 NDSec.BuilderSec->addr = SR.getStart().getValue();
258 NDSec.BuilderSec->size = SR.getSize();
259 NDSec.BuilderSec->offset = SR.getStart().getValue();
260 if (SR.getEnd() > MaxAddr)
261 MaxAddr = SR.getEnd();
262 }
263
264 for (auto &DSec : DebugSections) {
265 if (DSec.GraphSec->blocks_size() != 1)
266 return make_error<StringError>(
267 "Unexpected number of blocks in debug info section",
269
270 if (ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size > MaxAddr)
271 MaxAddr = ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size;
272
273 auto &B = **DSec.GraphSec->blocks().begin();
274 DSec.BuilderSec->Content.Data = B.getContent().data();
275 DSec.BuilderSec->Content.Size = B.getContent().size();
276 DSec.BuilderSec->flags |= MachO::S_ATTR_DEBUG;
277 }
278
279 LLVM_DEBUG({
280 dbgs() << "Writing MachO debug object header for " << G.getName() << "\n";
281 });
282
283 // Update stab symbol addresses.
284 for (auto &SS : StabSymbols) {
285 SS.StartStab.nlist().n_value = SS.Sym.getAddress().getValue();
286 SS.EndStab.nlist().n_value = SS.Sym.getSize();
287 }
288
289 Builder.write(MachOContainerBlock->getAlreadyMutableContent());
290
291 static constexpr bool AutoRegisterCode = true;
292 SectionRange R(MachOContainerBlock->getSection());
293 G.allocActions().push_back(
296 RegisterActionAddr, R.getRange(), AutoRegisterCode)),
297 {}});
298
299 return Error::success();
300 }
301
302private:
303 struct SectionPair {
304 Section *GraphSec = nullptr;
305 typename MachOBuilder<MachOTraits>::Section *BuilderSec = nullptr;
306 };
307
308 struct StabSymbolsEntry {
309 using RelocTarget = typename MachOBuilder<MachOTraits>::RelocTarget;
310
311 StabSymbolsEntry(Symbol &Sym, RelocTarget StartStab, RelocTarget EndStab)
312 : Sym(Sym), StartStab(StartStab), EndStab(EndStab) {}
313
314 Symbol &Sym;
315 RelocTarget StartStab, EndStab;
316 };
317
318 using BuilderType = MachOBuilder<MachOTraits>;
319
320 Block *MachOContainerBlock = nullptr;
322 typename MachOBuilder<MachOTraits>::Segment *Seg = nullptr;
323 std::vector<StabSymbolsEntry> StabSymbols;
324 SmallVector<SectionPair, 16> DebugSections;
325 SmallVector<SectionPair, 16> NonDebugSections;
326};
327
328} // end anonymous namespace
329
330namespace llvm {
331namespace orc {
332
335 JITDylib &ProcessJD,
336 const Triple &TT) {
337 auto RegisterActionAddr =
338 TT.isOSBinFormatMachO()
339 ? ES.intern("_llvm_orc_registerJITLoaderGDBAllocAction")
340 : ES.intern("llvm_orc_registerJITLoaderGDBAllocAction");
341
342 if (auto RegisterSym = ES.lookup({&ProcessJD}, RegisterActionAddr))
343 return std::make_unique<GDBJITDebugInfoRegistrationPlugin>(
344 RegisterSym->getAddress());
345 else
346 return RegisterSym.takeError();
347}
348
351 return Error::success();
352}
353
355 JITDylib &JD, ResourceKey K) {
356 return Error::success();
357}
358
360 JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) {}
361
364 PassConfiguration &PassConfig) {
365
367 modifyPassConfigForMachO(MR, LG, PassConfig);
368 else {
369 LLVM_DEBUG({
370 dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph "
371 << LG.getName() << "(triple = " << LG.getTargetTriple().str()
372 << "\n";
373 });
374 }
375}
376
377void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO(
379 jitlink::PassConfiguration &PassConfig) {
380
381 switch (LG.getTargetTriple().getArch()) {
382 case Triple::x86_64:
383 case Triple::aarch64:
384 // Supported, continue.
385 assert(LG.getPointerSize() == 8 && "Graph has incorrect pointer size");
387 "Graph has incorrect endianness");
388 break;
389 default:
390 // Unsupported.
391 LLVM_DEBUG({
392 dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported "
393 << "MachO graph " << LG.getName()
394 << "(triple = " << LG.getTargetTriple().str()
395 << ", pointer size = " << LG.getPointerSize() << ", endianness = "
396 << (LG.getEndianness() == llvm::endianness::big ? "big" : "little")
397 << ")\n";
398 });
399 return;
400 }
401
402 // Scan for debug sections. If we find one then install passes.
403 bool HasDebugSections = false;
404 for (auto &Sec : LG.sections())
405 if (MachODebugObjectSynthesizerBase::isDebugSection(Sec)) {
406 HasDebugSections = true;
407 break;
408 }
409
410 if (HasDebugSections) {
411 LLVM_DEBUG({
412 dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
413 << " contains debug info. Installing debugger support passes.\n";
414 });
415
416 auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>(
417 MR.getTargetJITDylib().getExecutionSession(), LG, RegisterActionAddr);
418 PassConfig.PrePrunePasses.push_back(
419 [=](LinkGraph &G) { return MDOS->preserveDebugSections(); });
420 PassConfig.PostPrunePasses.push_back(
421 [=](LinkGraph &G) { return MDOS->startSynthesis(); });
422 PassConfig.PostFixupPasses.push_back(
423 [=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); });
424 } else {
425 LLVM_DEBUG({
426 dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
427 << " contains no debug info. Skipping.\n";
428 });
429 }
430}
431
432} // namespace orc
433} // namespace llvm
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
#define LLVM_DEBUG(...)
Definition: Debug.h:106
static const char * SynthDebugSectionName
static bool isDebugSection(const SectionBase &Sec)
Definition: ELFObjcopy.cpp:49
Symbol * Sym
Definition: ELF_riscv.cpp:479
#define G(x, y, z)
Definition: MD5.cpp:56
#define P(N)
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
This file defines the SmallSet class.
This file defines the SmallVector class.
const T * data() const
Definition: ArrayRef.h:165
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:337
Tagged union holding either a T or a Error.
Definition: Error.h:481
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:132
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:181
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1196
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition: StringMap.h:128
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:51
std::pair< StringRef, StringRef > split(char Separator) const
Split into two substrings around the first occurrence of a separator character.
Definition: StringRef.h:700
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition: StringRef.h:265
constexpr bool empty() const
empty - Check if the string is empty.
Definition: StringRef.h:147
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:409
ArchType getArch() const
Get the parsed architecture type of this triple.
Definition: Triple.h:383
const std::string & str() const
Definition: Triple.h:450
An ExecutionSession represents a running JIT program.
Definition: Core.h:1339
SymbolStringPtr intern(StringRef SymName)
Add a symbol name to the SymbolStringPool and return a pointer to it.
Definition: Core.h:1393
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:1788
size_t getPageSize() const
Definition: Core.h:1385
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:897
ExecutionSession & getExecutionSession() const
Get a reference to the ExecutionSession for this JITDylib.
Definition: Core.h:916
Tracks responsibility for materialization, and mediates interactions between MaterializationUnits and...
Definition: Core.h:571
JITDylib & getTargetJITDylib() const
Returns the target JITDylib that these symbols are being materialized into.
Definition: Core.h:596
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
@ S_ATTR_DEBUG
S_ATTR_DEBUG - A debug section.
Definition: MachO.h:207
@ MH_OBJECT
Definition: MachO.h:43
@ CPU_SUBTYPE_ARM64_ALL
Definition: MachO.h:1641
@ CPU_SUBTYPE_X86_64_ALL
Definition: MachO.h:1611
@ CPU_TYPE_ARM64
Definition: MachO.h:1570
@ CPU_TYPE_X86_64
Definition: MachO.h:1566
@ SS
Definition: X86.h:212
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:74
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
@ Offset
Definition: DWP.cpp:480
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:977
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition: Error.cpp:98
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:346
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:756
void consumeError(Error Err)
Consume a Error without doing anything.
Definition: Error.h:1069