Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/ExecutionEngine/Orc/Debugging/DebuggerSupportPlugin.cpp
35294 views
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
12
#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupportPlugin.h"
13
#include "llvm/ExecutionEngine/Orc/MachOBuilder.h"
14
15
#include "llvm/ADT/SmallSet.h"
16
#include "llvm/ADT/SmallVector.h"
17
#include "llvm/BinaryFormat/MachO.h"
18
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
19
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
20
21
#include <chrono>
22
23
#define DEBUG_TYPE "orc"
24
25
using namespace llvm;
26
using namespace llvm::jitlink;
27
using namespace llvm::orc;
28
29
static const char *SynthDebugSectionName = "__jitlink_synth_debug_object";
30
31
namespace {
32
33
class MachODebugObjectSynthesizerBase
34
: public GDBJITDebugInfoRegistrationPlugin::DebugSectionSynthesizer {
35
public:
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
44
Error preserveDebugSections() {
45
if (G.findSectionByName(SynthDebugSectionName)) {
46
LLVM_DEBUG({
47
dbgs() << "MachODebugObjectSynthesizer skipping graph " << G.getName()
48
<< " which contains an unexpected existing "
49
<< SynthDebugSectionName << " section.\n";
50
});
51
return Error::success();
52
}
53
54
LLVM_DEBUG({
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.
64
LLVM_DEBUG({
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
82
protected:
83
LinkGraph &G;
84
ExecutorAddr RegisterActionAddr;
85
};
86
87
template <typename MachOTraits>
88
class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase {
89
public:
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 {
98
LLVM_DEBUG({
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
139
StringMap<std::unique_ptr<MemoryBuffer>> DebugSectionMap;
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] =
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;
170
DWARFDebugLine::LineTable LineTable;
171
172
// Try to parse line data. Consume error on failure.
173
if (auto Err = LineTable.parse(DebugLineData, &Offset, *DWARFCtx, nullptr,
174
consumeError)) {
175
handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
176
LLVM_DEBUG({
177
dbgs() << "Cannot parse line table for \"" << G.getName() << "\": ";
178
EIB.log(dbgs());
179
dbgs() << "\n";
180
});
181
});
182
} else {
183
if (!LineTable.Prologue.FileNames.empty())
184
FileName = *dwarf::toString(LineTable.Prologue.FileNames[0].Name);
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
FileName = StringRef(G.getName());
192
193
Builder.addSymbol("", MachO::N_SO, 0, 0, 0);
194
Builder.addSymbol(*FileName, MachO::N_SO, 0, 0, 0);
195
auto TimeStamp = std::chrono::duration_cast<std::chrono::seconds>(
196
std::chrono::system_clock::now().time_since_epoch())
197
.count();
198
Builder.addSymbol("", MachO::N_OSO, 3, 1, TimeStamp);
199
200
for (auto &NDSP : NonDebugSections) {
201
auto [SegName, SecName] = NDSP.GraphSec->getName().split(',');
202
NDSP.BuilderSec = &Seg->addSection(SecName, SegName);
203
SectionRange SR(*NDSP.GraphSec);
204
if (!SR.empty())
205
NDSP.BuilderSec->align = Log2_64(SR.getFirstBlock()->getAlignment());
206
207
// Add stabs.
208
for (auto *Sym : NDSP.GraphSec->symbols()) {
209
// Skip anonymous symbols.
210
if (!Sym->hasName())
211
continue;
212
213
uint8_t SymType = Sym->isCallable() ? MachO::N_FUN : MachO::N_GSYM;
214
215
Builder.addSymbol("", MachO::N_BNSYM, 1, 0, 0);
216
StabSymbols.push_back(
217
{*Sym, Builder.addSymbol(Sym->getName(), SymType, 1, 0, 0),
218
Builder.addSymbol(Sym->getName(), SymType, 0, 0, 0)});
219
Builder.addSymbol("", MachO::N_ENSYM, 1, 0, 0);
220
}
221
}
222
223
Builder.addSymbol("", MachO::N_SO, 1, 0, 0);
224
225
// Lay out the debug object, create a section and block for it.
226
size_t DebugObjectSize = Builder.layout();
227
228
auto &SDOSec = G.createSection(SynthDebugSectionName, MemProt::Read);
229
MachOContainerBlock = &G.createMutableContentBlock(
230
SDOSec, G.allocateBuffer(DebugObjectSize), orc::ExecutorAddr(), 8, 0);
231
232
return Error::success();
233
}
234
235
Error completeSynthesisAndRegister() override {
236
if (!MachOContainerBlock) {
237
LLVM_DEBUG({
238
dbgs() << "Not writing MachO debug object header for " << G.getName()
239
<< " since createDebugSection failed\n";
240
});
241
242
return Error::success();
243
}
244
ExecutorAddr MaxAddr;
245
for (auto &NDSec : NonDebugSections) {
246
SectionRange SR(*NDSec.GraphSec);
247
NDSec.BuilderSec->addr = SR.getStart().getValue();
248
NDSec.BuilderSec->size = SR.getSize();
249
NDSec.BuilderSec->offset = SR.getStart().getValue();
250
if (SR.getEnd() > MaxAddr)
251
MaxAddr = SR.getEnd();
252
}
253
254
for (auto &DSec : DebugSections) {
255
if (DSec.GraphSec->blocks_size() != 1)
256
return make_error<StringError>(
257
"Unexpected number of blocks in debug info section",
258
inconvertibleErrorCode());
259
260
if (ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size > MaxAddr)
261
MaxAddr = ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size;
262
263
auto &B = **DSec.GraphSec->blocks().begin();
264
DSec.BuilderSec->Content.Data = B.getContent().data();
265
DSec.BuilderSec->Content.Size = B.getContent().size();
266
DSec.BuilderSec->flags |= MachO::S_ATTR_DEBUG;
267
}
268
269
LLVM_DEBUG({
270
dbgs() << "Writing MachO debug object header for " << G.getName() << "\n";
271
});
272
273
// Update stab symbol addresses.
274
for (auto &SS : StabSymbols) {
275
SS.StartStab.nlist().n_value = SS.Sym.getAddress().getValue();
276
SS.EndStab.nlist().n_value = SS.Sym.getSize();
277
}
278
279
Builder.write(MachOContainerBlock->getAlreadyMutableContent());
280
281
static constexpr bool AutoRegisterCode = true;
282
SectionRange R(MachOContainerBlock->getSection());
283
G.allocActions().push_back(
284
{cantFail(shared::WrapperFunctionCall::Create<
285
shared::SPSArgList<shared::SPSExecutorAddrRange, bool>>(
286
RegisterActionAddr, R.getRange(), AutoRegisterCode)),
287
{}});
288
289
return Error::success();
290
}
291
292
private:
293
struct SectionPair {
294
Section *GraphSec = nullptr;
295
typename MachOBuilder<MachOTraits>::Section *BuilderSec = nullptr;
296
};
297
298
struct StabSymbolsEntry {
299
using RelocTarget = typename MachOBuilder<MachOTraits>::RelocTarget;
300
301
StabSymbolsEntry(Symbol &Sym, RelocTarget StartStab, RelocTarget EndStab)
302
: Sym(Sym), StartStab(StartStab), EndStab(EndStab) {}
303
304
Symbol &Sym;
305
RelocTarget StartStab, EndStab;
306
};
307
308
using BuilderType = MachOBuilder<MachOTraits>;
309
310
Block *MachOContainerBlock = nullptr;
311
MachOBuilder<MachOTraits> Builder;
312
typename MachOBuilder<MachOTraits>::Segment *Seg = nullptr;
313
std::vector<StabSymbolsEntry> StabSymbols;
314
SmallVector<SectionPair, 16> DebugSections;
315
SmallVector<SectionPair, 16> NonDebugSections;
316
};
317
318
} // end anonymous namespace
319
320
namespace llvm {
321
namespace orc {
322
323
Expected<std::unique_ptr<GDBJITDebugInfoRegistrationPlugin>>
324
GDBJITDebugInfoRegistrationPlugin::Create(ExecutionSession &ES,
325
JITDylib &ProcessJD,
326
const Triple &TT) {
327
auto RegisterActionAddr =
328
TT.isOSBinFormatMachO()
329
? ES.intern("_llvm_orc_registerJITLoaderGDBAllocAction")
330
: ES.intern("llvm_orc_registerJITLoaderGDBAllocAction");
331
332
if (auto RegisterSym = ES.lookup({&ProcessJD}, RegisterActionAddr))
333
return std::make_unique<GDBJITDebugInfoRegistrationPlugin>(
334
RegisterSym->getAddress());
335
else
336
return RegisterSym.takeError();
337
}
338
339
Error GDBJITDebugInfoRegistrationPlugin::notifyFailed(
340
MaterializationResponsibility &MR) {
341
return Error::success();
342
}
343
344
Error GDBJITDebugInfoRegistrationPlugin::notifyRemovingResources(
345
JITDylib &JD, ResourceKey K) {
346
return Error::success();
347
}
348
349
void GDBJITDebugInfoRegistrationPlugin::notifyTransferringResources(
350
JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) {}
351
352
void GDBJITDebugInfoRegistrationPlugin::modifyPassConfig(
353
MaterializationResponsibility &MR, LinkGraph &LG,
354
PassConfiguration &PassConfig) {
355
356
if (LG.getTargetTriple().getObjectFormat() == Triple::MachO)
357
modifyPassConfigForMachO(MR, LG, PassConfig);
358
else {
359
LLVM_DEBUG({
360
dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph "
361
<< LG.getName() << "(triple = " << LG.getTargetTriple().str()
362
<< "\n";
363
});
364
}
365
}
366
367
void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO(
368
MaterializationResponsibility &MR, jitlink::LinkGraph &LG,
369
jitlink::PassConfiguration &PassConfig) {
370
371
switch (LG.getTargetTriple().getArch()) {
372
case Triple::x86_64:
373
case Triple::aarch64:
374
// Supported, continue.
375
assert(LG.getPointerSize() == 8 && "Graph has incorrect pointer size");
376
assert(LG.getEndianness() == llvm::endianness::little &&
377
"Graph has incorrect endianness");
378
break;
379
default:
380
// Unsupported.
381
LLVM_DEBUG({
382
dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported "
383
<< "MachO graph " << LG.getName()
384
<< "(triple = " << LG.getTargetTriple().str()
385
<< ", pointer size = " << LG.getPointerSize() << ", endianness = "
386
<< (LG.getEndianness() == llvm::endianness::big ? "big" : "little")
387
<< ")\n";
388
});
389
return;
390
}
391
392
// Scan for debug sections. If we find one then install passes.
393
bool HasDebugSections = false;
394
for (auto &Sec : LG.sections())
395
if (MachODebugObjectSynthesizerBase::isDebugSection(Sec)) {
396
HasDebugSections = true;
397
break;
398
}
399
400
if (HasDebugSections) {
401
LLVM_DEBUG({
402
dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
403
<< " contains debug info. Installing debugger support passes.\n";
404
});
405
406
auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>(
407
MR.getTargetJITDylib().getExecutionSession(), LG, RegisterActionAddr);
408
PassConfig.PrePrunePasses.push_back(
409
[=](LinkGraph &G) { return MDOS->preserveDebugSections(); });
410
PassConfig.PostPrunePasses.push_back(
411
[=](LinkGraph &G) { return MDOS->startSynthesis(); });
412
PassConfig.PostFixupPasses.push_back(
413
[=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); });
414
} else {
415
LLVM_DEBUG({
416
dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
417
<< " contains no debug info. Skipping.\n";
418
});
419
}
420
}
421
422
} // namespace orc
423
} // namespace llvm
424
425