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/DebugObjectManagerPlugin.cpp
35266 views
1
//===------- DebugObjectManagerPlugin.cpp - JITLink debug objects ---------===//
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
// FIXME: Update Plugin to poke the debug object into a new JITLink section,
10
// rather than creating a new allocation.
11
//
12
//===----------------------------------------------------------------------===//
13
14
#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
15
16
#include "llvm/ADT/ArrayRef.h"
17
#include "llvm/ADT/StringMap.h"
18
#include "llvm/ADT/StringRef.h"
19
#include "llvm/BinaryFormat/ELF.h"
20
#include "llvm/ExecutionEngine/JITLink/JITLinkDylib.h"
21
#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
22
#include "llvm/ExecutionEngine/JITSymbol.h"
23
#include "llvm/Object/ELFObjectFile.h"
24
#include "llvm/Object/ObjectFile.h"
25
#include "llvm/Support/Errc.h"
26
#include "llvm/Support/MSVCErrorWorkarounds.h"
27
#include "llvm/Support/MemoryBuffer.h"
28
#include "llvm/Support/Process.h"
29
#include "llvm/Support/raw_ostream.h"
30
31
#include <set>
32
33
#define DEBUG_TYPE "orc"
34
35
using namespace llvm::jitlink;
36
using namespace llvm::object;
37
38
namespace llvm {
39
namespace orc {
40
41
class DebugObjectSection {
42
public:
43
virtual void setTargetMemoryRange(SectionRange Range) = 0;
44
virtual void dump(raw_ostream &OS, StringRef Name) {}
45
virtual ~DebugObjectSection() = default;
46
};
47
48
template <typename ELFT>
49
class ELFDebugObjectSection : public DebugObjectSection {
50
public:
51
// BinaryFormat ELF is not meant as a mutable format. We can only make changes
52
// that don't invalidate the file structure.
53
ELFDebugObjectSection(const typename ELFT::Shdr *Header)
54
: Header(const_cast<typename ELFT::Shdr *>(Header)) {}
55
56
void setTargetMemoryRange(SectionRange Range) override;
57
void dump(raw_ostream &OS, StringRef Name) override;
58
59
Error validateInBounds(StringRef Buffer, const char *Name) const;
60
61
private:
62
typename ELFT::Shdr *Header;
63
};
64
65
template <typename ELFT>
66
void ELFDebugObjectSection<ELFT>::setTargetMemoryRange(SectionRange Range) {
67
// All recorded sections are candidates for load-address patching.
68
Header->sh_addr =
69
static_cast<typename ELFT::uint>(Range.getStart().getValue());
70
}
71
72
template <typename ELFT>
73
Error ELFDebugObjectSection<ELFT>::validateInBounds(StringRef Buffer,
74
const char *Name) const {
75
const uint8_t *Start = Buffer.bytes_begin();
76
const uint8_t *End = Buffer.bytes_end();
77
const uint8_t *HeaderPtr = reinterpret_cast<uint8_t *>(Header);
78
if (HeaderPtr < Start || HeaderPtr + sizeof(typename ELFT::Shdr) > End)
79
return make_error<StringError>(
80
formatv("{0} section header at {1:x16} not within bounds of the "
81
"given debug object buffer [{2:x16} - {3:x16}]",
82
Name, &Header->sh_addr, Start, End),
83
inconvertibleErrorCode());
84
if (Header->sh_offset + Header->sh_size > Buffer.size())
85
return make_error<StringError>(
86
formatv("{0} section data [{1:x16} - {2:x16}] not within bounds of "
87
"the given debug object buffer [{3:x16} - {4:x16}]",
88
Name, Start + Header->sh_offset,
89
Start + Header->sh_offset + Header->sh_size, Start, End),
90
inconvertibleErrorCode());
91
return Error::success();
92
}
93
94
template <typename ELFT>
95
void ELFDebugObjectSection<ELFT>::dump(raw_ostream &OS, StringRef Name) {
96
if (uint64_t Addr = Header->sh_addr) {
97
OS << formatv(" {0:x16} {1}\n", Addr, Name);
98
} else {
99
OS << formatv(" {0}\n", Name);
100
}
101
}
102
103
enum DebugObjectFlags : int {
104
// Request final target memory load-addresses for all sections.
105
ReportFinalSectionLoadAddresses = 1 << 0,
106
107
// We found sections with debug information when processing the input object.
108
HasDebugSections = 1 << 1,
109
};
110
111
/// The plugin creates a debug object from when JITLink starts processing the
112
/// corresponding LinkGraph. It provides access to the pass configuration of
113
/// the LinkGraph and calls the finalization function, once the resulting link
114
/// artifact was emitted.
115
///
116
class DebugObject {
117
public:
118
DebugObject(JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD,
119
ExecutionSession &ES)
120
: MemMgr(MemMgr), JD(JD), ES(ES), Flags(DebugObjectFlags{}) {}
121
122
bool hasFlags(DebugObjectFlags F) const { return Flags & F; }
123
void setFlags(DebugObjectFlags F) {
124
Flags = static_cast<DebugObjectFlags>(Flags | F);
125
}
126
void clearFlags(DebugObjectFlags F) {
127
Flags = static_cast<DebugObjectFlags>(Flags & ~F);
128
}
129
130
using FinalizeContinuation = std::function<void(Expected<ExecutorAddrRange>)>;
131
132
void finalizeAsync(FinalizeContinuation OnFinalize);
133
134
virtual ~DebugObject() {
135
if (Alloc) {
136
std::vector<FinalizedAlloc> Allocs;
137
Allocs.push_back(std::move(Alloc));
138
if (Error Err = MemMgr.deallocate(std::move(Allocs)))
139
ES.reportError(std::move(Err));
140
}
141
}
142
143
virtual void reportSectionTargetMemoryRange(StringRef Name,
144
SectionRange TargetMem) {}
145
146
protected:
147
using InFlightAlloc = JITLinkMemoryManager::InFlightAlloc;
148
using FinalizedAlloc = JITLinkMemoryManager::FinalizedAlloc;
149
150
virtual Expected<SimpleSegmentAlloc> finalizeWorkingMemory() = 0;
151
152
JITLinkMemoryManager &MemMgr;
153
const JITLinkDylib *JD = nullptr;
154
155
private:
156
ExecutionSession &ES;
157
DebugObjectFlags Flags;
158
FinalizedAlloc Alloc;
159
};
160
161
// Finalize working memory and take ownership of the resulting allocation. Start
162
// copying memory over to the target and pass on the result once we're done.
163
// Ownership of the allocation remains with us for the rest of our lifetime.
164
void DebugObject::finalizeAsync(FinalizeContinuation OnFinalize) {
165
assert(!Alloc && "Cannot finalize more than once");
166
167
if (auto SimpleSegAlloc = finalizeWorkingMemory()) {
168
auto ROSeg = SimpleSegAlloc->getSegInfo(MemProt::Read);
169
ExecutorAddrRange DebugObjRange(ROSeg.Addr, ROSeg.WorkingMem.size());
170
SimpleSegAlloc->finalize(
171
[this, DebugObjRange,
172
OnFinalize = std::move(OnFinalize)](Expected<FinalizedAlloc> FA) {
173
if (FA) {
174
Alloc = std::move(*FA);
175
OnFinalize(DebugObjRange);
176
} else
177
OnFinalize(FA.takeError());
178
});
179
} else
180
OnFinalize(SimpleSegAlloc.takeError());
181
}
182
183
/// The current implementation of ELFDebugObject replicates the approach used in
184
/// RuntimeDyld: It patches executable and data section headers in the given
185
/// object buffer with load-addresses of their corresponding sections in target
186
/// memory.
187
///
188
class ELFDebugObject : public DebugObject {
189
public:
190
static Expected<std::unique_ptr<DebugObject>>
191
Create(MemoryBufferRef Buffer, JITLinkContext &Ctx, ExecutionSession &ES);
192
193
void reportSectionTargetMemoryRange(StringRef Name,
194
SectionRange TargetMem) override;
195
196
StringRef getBuffer() const { return Buffer->getMemBufferRef().getBuffer(); }
197
198
protected:
199
Expected<SimpleSegmentAlloc> finalizeWorkingMemory() override;
200
201
template <typename ELFT>
202
Error recordSection(StringRef Name,
203
std::unique_ptr<ELFDebugObjectSection<ELFT>> Section);
204
DebugObjectSection *getSection(StringRef Name);
205
206
private:
207
template <typename ELFT>
208
static Expected<std::unique_ptr<ELFDebugObject>>
209
CreateArchType(MemoryBufferRef Buffer, JITLinkMemoryManager &MemMgr,
210
const JITLinkDylib *JD, ExecutionSession &ES);
211
212
static std::unique_ptr<WritableMemoryBuffer>
213
CopyBuffer(MemoryBufferRef Buffer, Error &Err);
214
215
ELFDebugObject(std::unique_ptr<WritableMemoryBuffer> Buffer,
216
JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD,
217
ExecutionSession &ES)
218
: DebugObject(MemMgr, JD, ES), Buffer(std::move(Buffer)) {
219
setFlags(ReportFinalSectionLoadAddresses);
220
}
221
222
std::unique_ptr<WritableMemoryBuffer> Buffer;
223
StringMap<std::unique_ptr<DebugObjectSection>> Sections;
224
};
225
226
static const std::set<StringRef> DwarfSectionNames = {
227
#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \
228
ELF_NAME,
229
#include "llvm/BinaryFormat/Dwarf.def"
230
#undef HANDLE_DWARF_SECTION
231
};
232
233
static bool isDwarfSection(StringRef SectionName) {
234
return DwarfSectionNames.count(SectionName) == 1;
235
}
236
237
std::unique_ptr<WritableMemoryBuffer>
238
ELFDebugObject::CopyBuffer(MemoryBufferRef Buffer, Error &Err) {
239
ErrorAsOutParameter _(&Err);
240
size_t Size = Buffer.getBufferSize();
241
StringRef Name = Buffer.getBufferIdentifier();
242
if (auto Copy = WritableMemoryBuffer::getNewUninitMemBuffer(Size, Name)) {
243
memcpy(Copy->getBufferStart(), Buffer.getBufferStart(), Size);
244
return Copy;
245
}
246
247
Err = errorCodeToError(make_error_code(errc::not_enough_memory));
248
return nullptr;
249
}
250
251
template <typename ELFT>
252
Expected<std::unique_ptr<ELFDebugObject>>
253
ELFDebugObject::CreateArchType(MemoryBufferRef Buffer,
254
JITLinkMemoryManager &MemMgr,
255
const JITLinkDylib *JD, ExecutionSession &ES) {
256
using SectionHeader = typename ELFT::Shdr;
257
258
Error Err = Error::success();
259
std::unique_ptr<ELFDebugObject> DebugObj(
260
new ELFDebugObject(CopyBuffer(Buffer, Err), MemMgr, JD, ES));
261
if (Err)
262
return std::move(Err);
263
264
Expected<ELFFile<ELFT>> ObjRef = ELFFile<ELFT>::create(DebugObj->getBuffer());
265
if (!ObjRef)
266
return ObjRef.takeError();
267
268
Expected<ArrayRef<SectionHeader>> Sections = ObjRef->sections();
269
if (!Sections)
270
return Sections.takeError();
271
272
for (const SectionHeader &Header : *Sections) {
273
Expected<StringRef> Name = ObjRef->getSectionName(Header);
274
if (!Name)
275
return Name.takeError();
276
if (Name->empty())
277
continue;
278
if (isDwarfSection(*Name))
279
DebugObj->setFlags(HasDebugSections);
280
281
// Only record text and data sections (i.e. no bss, comments, rel, etc.)
282
if (Header.sh_type != ELF::SHT_PROGBITS &&
283
Header.sh_type != ELF::SHT_X86_64_UNWIND)
284
continue;
285
if (!(Header.sh_flags & ELF::SHF_ALLOC))
286
continue;
287
288
auto Wrapped = std::make_unique<ELFDebugObjectSection<ELFT>>(&Header);
289
if (Error Err = DebugObj->recordSection(*Name, std::move(Wrapped)))
290
return std::move(Err);
291
}
292
293
return std::move(DebugObj);
294
}
295
296
Expected<std::unique_ptr<DebugObject>>
297
ELFDebugObject::Create(MemoryBufferRef Buffer, JITLinkContext &Ctx,
298
ExecutionSession &ES) {
299
unsigned char Class, Endian;
300
std::tie(Class, Endian) = getElfArchType(Buffer.getBuffer());
301
302
if (Class == ELF::ELFCLASS32) {
303
if (Endian == ELF::ELFDATA2LSB)
304
return CreateArchType<ELF32LE>(Buffer, Ctx.getMemoryManager(),
305
Ctx.getJITLinkDylib(), ES);
306
if (Endian == ELF::ELFDATA2MSB)
307
return CreateArchType<ELF32BE>(Buffer, Ctx.getMemoryManager(),
308
Ctx.getJITLinkDylib(), ES);
309
return nullptr;
310
}
311
if (Class == ELF::ELFCLASS64) {
312
if (Endian == ELF::ELFDATA2LSB)
313
return CreateArchType<ELF64LE>(Buffer, Ctx.getMemoryManager(),
314
Ctx.getJITLinkDylib(), ES);
315
if (Endian == ELF::ELFDATA2MSB)
316
return CreateArchType<ELF64BE>(Buffer, Ctx.getMemoryManager(),
317
Ctx.getJITLinkDylib(), ES);
318
return nullptr;
319
}
320
return nullptr;
321
}
322
323
Expected<SimpleSegmentAlloc> ELFDebugObject::finalizeWorkingMemory() {
324
LLVM_DEBUG({
325
dbgs() << "Section load-addresses in debug object for \""
326
<< Buffer->getBufferIdentifier() << "\":\n";
327
for (const auto &KV : Sections)
328
KV.second->dump(dbgs(), KV.first());
329
});
330
331
// TODO: This works, but what actual alignment requirements do we have?
332
unsigned PageSize = sys::Process::getPageSizeEstimate();
333
size_t Size = Buffer->getBufferSize();
334
335
// Allocate working memory for debug object in read-only segment.
336
auto Alloc = SimpleSegmentAlloc::Create(
337
MemMgr, JD, {{MemProt::Read, {Size, Align(PageSize)}}});
338
if (!Alloc)
339
return Alloc;
340
341
// Initialize working memory with a copy of our object buffer.
342
auto SegInfo = Alloc->getSegInfo(MemProt::Read);
343
memcpy(SegInfo.WorkingMem.data(), Buffer->getBufferStart(), Size);
344
Buffer.reset();
345
346
return Alloc;
347
}
348
349
void ELFDebugObject::reportSectionTargetMemoryRange(StringRef Name,
350
SectionRange TargetMem) {
351
if (auto *DebugObjSection = getSection(Name))
352
DebugObjSection->setTargetMemoryRange(TargetMem);
353
}
354
355
template <typename ELFT>
356
Error ELFDebugObject::recordSection(
357
StringRef Name, std::unique_ptr<ELFDebugObjectSection<ELFT>> Section) {
358
if (Error Err = Section->validateInBounds(this->getBuffer(), Name.data()))
359
return Err;
360
bool Inserted = Sections.try_emplace(Name, std::move(Section)).second;
361
if (!Inserted)
362
LLVM_DEBUG(dbgs() << "Skipping debug registration for section '" << Name
363
<< "' in object " << Buffer->getBufferIdentifier()
364
<< " (duplicate name)\n");
365
return Error::success();
366
}
367
368
DebugObjectSection *ELFDebugObject::getSection(StringRef Name) {
369
auto It = Sections.find(Name);
370
return It == Sections.end() ? nullptr : It->second.get();
371
}
372
373
/// Creates a debug object based on the input object file from
374
/// ObjectLinkingLayerJITLinkContext.
375
///
376
static Expected<std::unique_ptr<DebugObject>>
377
createDebugObjectFromBuffer(ExecutionSession &ES, LinkGraph &G,
378
JITLinkContext &Ctx, MemoryBufferRef ObjBuffer) {
379
switch (G.getTargetTriple().getObjectFormat()) {
380
case Triple::ELF:
381
return ELFDebugObject::Create(ObjBuffer, Ctx, ES);
382
383
default:
384
// TODO: Once we add support for other formats, we might want to split this
385
// into multiple files.
386
return nullptr;
387
}
388
}
389
390
DebugObjectManagerPlugin::DebugObjectManagerPlugin(
391
ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target,
392
bool RequireDebugSections, bool AutoRegisterCode)
393
: ES(ES), Target(std::move(Target)),
394
RequireDebugSections(RequireDebugSections),
395
AutoRegisterCode(AutoRegisterCode) {}
396
397
DebugObjectManagerPlugin::DebugObjectManagerPlugin(
398
ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target)
399
: DebugObjectManagerPlugin(ES, std::move(Target), true, true) {}
400
401
DebugObjectManagerPlugin::~DebugObjectManagerPlugin() = default;
402
403
void DebugObjectManagerPlugin::notifyMaterializing(
404
MaterializationResponsibility &MR, LinkGraph &G, JITLinkContext &Ctx,
405
MemoryBufferRef ObjBuffer) {
406
std::lock_guard<std::mutex> Lock(PendingObjsLock);
407
assert(PendingObjs.count(&MR) == 0 &&
408
"Cannot have more than one pending debug object per "
409
"MaterializationResponsibility");
410
411
if (auto DebugObj = createDebugObjectFromBuffer(ES, G, Ctx, ObjBuffer)) {
412
// Not all link artifacts allow debugging.
413
if (*DebugObj == nullptr)
414
return;
415
if (RequireDebugSections && !(**DebugObj).hasFlags(HasDebugSections)) {
416
LLVM_DEBUG(dbgs() << "Skipping debug registration for LinkGraph '"
417
<< G.getName() << "': no debug info\n");
418
return;
419
}
420
PendingObjs[&MR] = std::move(*DebugObj);
421
} else {
422
ES.reportError(DebugObj.takeError());
423
}
424
}
425
426
void DebugObjectManagerPlugin::modifyPassConfig(
427
MaterializationResponsibility &MR, LinkGraph &G,
428
PassConfiguration &PassConfig) {
429
// Not all link artifacts have associated debug objects.
430
std::lock_guard<std::mutex> Lock(PendingObjsLock);
431
auto It = PendingObjs.find(&MR);
432
if (It == PendingObjs.end())
433
return;
434
435
DebugObject &DebugObj = *It->second;
436
if (DebugObj.hasFlags(ReportFinalSectionLoadAddresses)) {
437
PassConfig.PostAllocationPasses.push_back(
438
[&DebugObj](LinkGraph &Graph) -> Error {
439
for (const Section &GraphSection : Graph.sections())
440
DebugObj.reportSectionTargetMemoryRange(GraphSection.getName(),
441
SectionRange(GraphSection));
442
return Error::success();
443
});
444
}
445
}
446
447
Error DebugObjectManagerPlugin::notifyEmitted(
448
MaterializationResponsibility &MR) {
449
std::lock_guard<std::mutex> Lock(PendingObjsLock);
450
auto It = PendingObjs.find(&MR);
451
if (It == PendingObjs.end())
452
return Error::success();
453
454
// During finalization the debug object is registered with the target.
455
// Materialization must wait for this process to finish. Otherwise we might
456
// start running code before the debugger processed the corresponding debug
457
// info.
458
std::promise<MSVCPError> FinalizePromise;
459
std::future<MSVCPError> FinalizeErr = FinalizePromise.get_future();
460
461
It->second->finalizeAsync(
462
[this, &FinalizePromise, &MR](Expected<ExecutorAddrRange> TargetMem) {
463
// Any failure here will fail materialization.
464
if (!TargetMem) {
465
FinalizePromise.set_value(TargetMem.takeError());
466
return;
467
}
468
if (Error Err =
469
Target->registerDebugObject(*TargetMem, AutoRegisterCode)) {
470
FinalizePromise.set_value(std::move(Err));
471
return;
472
}
473
474
// Once our tracking info is updated, notifyEmitted() can return and
475
// finish materialization.
476
FinalizePromise.set_value(MR.withResourceKeyDo([&](ResourceKey K) {
477
assert(PendingObjs.count(&MR) && "We still hold PendingObjsLock");
478
std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
479
RegisteredObjs[K].push_back(std::move(PendingObjs[&MR]));
480
PendingObjs.erase(&MR);
481
}));
482
});
483
484
return FinalizeErr.get();
485
}
486
487
Error DebugObjectManagerPlugin::notifyFailed(
488
MaterializationResponsibility &MR) {
489
std::lock_guard<std::mutex> Lock(PendingObjsLock);
490
PendingObjs.erase(&MR);
491
return Error::success();
492
}
493
494
void DebugObjectManagerPlugin::notifyTransferringResources(JITDylib &JD,
495
ResourceKey DstKey,
496
ResourceKey SrcKey) {
497
// Debug objects are stored by ResourceKey only after registration.
498
// Thus, pending objects don't need to be updated here.
499
std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
500
auto SrcIt = RegisteredObjs.find(SrcKey);
501
if (SrcIt != RegisteredObjs.end()) {
502
// Resources from distinct MaterializationResponsibilitys can get merged
503
// after emission, so we can have multiple debug objects per resource key.
504
for (std::unique_ptr<DebugObject> &DebugObj : SrcIt->second)
505
RegisteredObjs[DstKey].push_back(std::move(DebugObj));
506
RegisteredObjs.erase(SrcIt);
507
}
508
}
509
510
Error DebugObjectManagerPlugin::notifyRemovingResources(JITDylib &JD,
511
ResourceKey Key) {
512
// Removing the resource for a pending object fails materialization, so they
513
// get cleaned up in the notifyFailed() handler.
514
std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
515
RegisteredObjs.erase(Key);
516
517
// TODO: Implement unregister notifications.
518
return Error::success();
519
}
520
521
} // namespace orc
522
} // namespace llvm
523
524