LLVM 17.0.0git
DebugObjectManagerPlugin.cpp
Go to the documentation of this file.
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
15
16#include "llvm/ADT/ArrayRef.h"
17#include "llvm/ADT/StringMap.h"
18#include "llvm/ADT/StringRef.h"
25#include "llvm/Support/Errc.h"
30
31#include <set>
32
33#define DEBUG_TYPE "orc"
34
35using namespace llvm::jitlink;
36using namespace llvm::object;
37
38namespace llvm {
39namespace orc {
40
42public:
43 virtual void setTargetMemoryRange(SectionRange Range) = 0;
44 virtual void dump(raw_ostream &OS, StringRef Name) {}
45 virtual ~DebugObjectSection() = default;
46};
47
48template <typename ELFT>
50public:
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
61private:
62 typename ELFT::Shdr *Header;
63
64 bool isTextOrDataSection() const;
65};
66
67template <typename ELFT>
69 // Only patch load-addresses for executable and data sections.
70 if (isTextOrDataSection())
71 Header->sh_addr =
72 static_cast<typename ELFT::uint>(Range.getStart().getValue());
73}
74
75template <typename ELFT>
77 switch (Header->sh_type) {
80 return Header->sh_flags & (ELF::SHF_EXECINSTR | ELF::SHF_ALLOC);
81 }
82 return false;
83}
84
85template <typename ELFT>
87 const char *Name) const {
88 const uint8_t *Start = Buffer.bytes_begin();
89 const uint8_t *End = Buffer.bytes_end();
90 const uint8_t *HeaderPtr = reinterpret_cast<uint8_t *>(Header);
91 if (HeaderPtr < Start || HeaderPtr + sizeof(typename ELFT::Shdr) > End)
92 return make_error<StringError>(
93 formatv("{0} section header at {1:x16} not within bounds of the "
94 "given debug object buffer [{2:x16} - {3:x16}]",
95 Name, &Header->sh_addr, Start, End),
97 if (Header->sh_offset + Header->sh_size > Buffer.size())
98 return make_error<StringError>(
99 formatv("{0} section data [{1:x16} - {2:x16}] not within bounds of "
100 "the given debug object buffer [{3:x16} - {4:x16}]",
101 Name, Start + Header->sh_offset,
102 Start + Header->sh_offset + Header->sh_size, Start, End),
104 return Error::success();
105}
106
107template <typename ELFT>
109 if (Header->sh_addr) {
110 OS << formatv(" {0:x16} {1}\n", Header->sh_addr, Name);
111 } else {
112 OS << formatv(" {0}\n", Name);
113 }
114}
115
116enum class Requirement {
117 // Request final target memory load-addresses for all sections.
119};
120
121/// The plugin creates a debug object from when JITLink starts processing the
122/// corresponding LinkGraph. It provides access to the pass configuration of
123/// the LinkGraph and calls the finalization function, once the resulting link
124/// artifact was emitted.
125///
127public:
130 : MemMgr(MemMgr), JD(JD), ES(ES) {}
131
132 void set(Requirement Req) { Reqs.insert(Req); }
133 bool has(Requirement Req) const { return Reqs.count(Req) > 0; }
134
136
137 void finalizeAsync(FinalizeContinuation OnFinalize);
138
139 virtual ~DebugObject() {
140 if (Alloc) {
141 std::vector<FinalizedAlloc> Allocs;
142 Allocs.push_back(std::move(Alloc));
143 if (Error Err = MemMgr.deallocate(std::move(Allocs)))
144 ES.reportError(std::move(Err));
145 }
146 }
147
149 SectionRange TargetMem) {}
150
151protected:
154
156
158 const JITLinkDylib *JD = nullptr;
159
160private:
162 std::set<Requirement> Reqs;
163 FinalizedAlloc Alloc;
164};
165
166// Finalize working memory and take ownership of the resulting allocation. Start
167// copying memory over to the target and pass on the result once we're done.
168// Ownership of the allocation remains with us for the rest of our lifetime.
170 assert(!Alloc && "Cannot finalize more than once");
171
172 if (auto SimpleSegAlloc = finalizeWorkingMemory()) {
173 auto ROSeg = SimpleSegAlloc->getSegInfo(MemProt::Read);
174 ExecutorAddrRange DebugObjRange(ExecutorAddr(ROSeg.Addr),
175 ExecutorAddrDiff(ROSeg.WorkingMem.size()));
176 SimpleSegAlloc->finalize(
177 [this, DebugObjRange,
178 OnFinalize = std::move(OnFinalize)](Expected<FinalizedAlloc> FA) {
179 if (FA) {
180 Alloc = std::move(*FA);
181 OnFinalize(DebugObjRange);
182 } else
183 OnFinalize(FA.takeError());
184 });
185 } else
186 OnFinalize(SimpleSegAlloc.takeError());
187}
188
189/// The current implementation of ELFDebugObject replicates the approach used in
190/// RuntimeDyld: It patches executable and data section headers in the given
191/// object buffer with load-addresses of their corresponding sections in target
192/// memory.
193///
195public:
198
200 SectionRange TargetMem) override;
201
202 StringRef getBuffer() const { return Buffer->getMemBufferRef().getBuffer(); }
203
204protected:
206
207 template <typename ELFT>
209 std::unique_ptr<ELFDebugObjectSection<ELFT>> Section);
211
212private:
213 template <typename ELFT>
215 CreateArchType(MemoryBufferRef Buffer, JITLinkMemoryManager &MemMgr,
216 const JITLinkDylib *JD, ExecutionSession &ES);
217
218 static std::unique_ptr<WritableMemoryBuffer>
219 CopyBuffer(MemoryBufferRef Buffer, Error &Err);
220
221 ELFDebugObject(std::unique_ptr<WritableMemoryBuffer> Buffer,
224 : DebugObject(MemMgr, JD, ES), Buffer(std::move(Buffer)) {
226 }
227
228 std::unique_ptr<WritableMemoryBuffer> Buffer;
230};
231
232static const std::set<StringRef> DwarfSectionNames = {
233#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \
234 ELF_NAME,
235#include "llvm/BinaryFormat/Dwarf.def"
236#undef HANDLE_DWARF_SECTION
237};
238
240 return DwarfSectionNames.count(SectionName) == 1;
241}
242
243std::unique_ptr<WritableMemoryBuffer>
244ELFDebugObject::CopyBuffer(MemoryBufferRef Buffer, Error &Err) {
246 size_t Size = Buffer.getBufferSize();
249 memcpy(Copy->getBufferStart(), Buffer.getBufferStart(), Size);
250 return Copy;
251 }
252
254 return nullptr;
255}
256
257template <typename ELFT>
259ELFDebugObject::CreateArchType(MemoryBufferRef Buffer,
260 JITLinkMemoryManager &MemMgr,
261 const JITLinkDylib *JD, ExecutionSession &ES) {
262 using SectionHeader = typename ELFT::Shdr;
263
264 Error Err = Error::success();
265 std::unique_ptr<ELFDebugObject> DebugObj(
266 new ELFDebugObject(CopyBuffer(Buffer, Err), MemMgr, JD, ES));
267 if (Err)
268 return std::move(Err);
269
270 Expected<ELFFile<ELFT>> ObjRef = ELFFile<ELFT>::create(DebugObj->getBuffer());
271 if (!ObjRef)
272 return ObjRef.takeError();
273
274 // TODO: Add support for other architectures.
275 uint16_t TargetMachineArch = ObjRef->getHeader().e_machine;
276 if (TargetMachineArch != ELF::EM_X86_64)
277 return nullptr;
278
279 Expected<ArrayRef<SectionHeader>> Sections = ObjRef->sections();
280 if (!Sections)
281 return Sections.takeError();
282
283 bool HasDwarfSection = false;
284 for (const SectionHeader &Header : *Sections) {
285 Expected<StringRef> Name = ObjRef->getSectionName(Header);
286 if (!Name)
287 return Name.takeError();
288 if (Name->empty())
289 continue;
290 HasDwarfSection |= isDwarfSection(*Name);
291
292 if (!(Header.sh_flags & ELF::SHF_ALLOC))
293 continue;
294
295 auto Wrapped = std::make_unique<ELFDebugObjectSection<ELFT>>(&Header);
296 if (Error Err = DebugObj->recordSection(*Name, std::move(Wrapped)))
297 return std::move(Err);
298 }
299
300 if (!HasDwarfSection) {
301 LLVM_DEBUG(dbgs() << "Aborting debug registration for LinkGraph \""
302 << DebugObj->Buffer->getBufferIdentifier()
303 << "\": input object contains no debug info\n");
304 return nullptr;
305 }
306
307 return std::move(DebugObj);
308}
309
312 ExecutionSession &ES) {
313 unsigned char Class, Endian;
314 std::tie(Class, Endian) = getElfArchType(Buffer.getBuffer());
315
316 if (Class == ELF::ELFCLASS32) {
318 return CreateArchType<ELF32LE>(Buffer, Ctx.getMemoryManager(),
319 Ctx.getJITLinkDylib(), ES);
321 return CreateArchType<ELF32BE>(Buffer, Ctx.getMemoryManager(),
322 Ctx.getJITLinkDylib(), ES);
323 return nullptr;
324 }
325 if (Class == ELF::ELFCLASS64) {
327 return CreateArchType<ELF64LE>(Buffer, Ctx.getMemoryManager(),
328 Ctx.getJITLinkDylib(), ES);
330 return CreateArchType<ELF64BE>(Buffer, Ctx.getMemoryManager(),
331 Ctx.getJITLinkDylib(), ES);
332 return nullptr;
333 }
334 return nullptr;
335}
336
338 LLVM_DEBUG({
339 dbgs() << "Section load-addresses in debug object for \""
340 << Buffer->getBufferIdentifier() << "\":\n";
341 for (const auto &KV : Sections)
342 KV.second->dump(dbgs(), KV.first());
343 });
344
345 // TODO: This works, but what actual alignment requirements do we have?
347 size_t Size = Buffer->getBufferSize();
348
349 // Allocate working memory for debug object in read-only segment.
350 auto Alloc = SimpleSegmentAlloc::Create(
352 if (!Alloc)
353 return Alloc;
354
355 // Initialize working memory with a copy of our object buffer.
356 auto SegInfo = Alloc->getSegInfo(MemProt::Read);
357 memcpy(SegInfo.WorkingMem.data(), Buffer->getBufferStart(), Size);
358 Buffer.reset();
359
360 return Alloc;
361}
362
364 SectionRange TargetMem) {
365 if (auto *DebugObjSection = getSection(Name))
366 DebugObjSection->setTargetMemoryRange(TargetMem);
367}
368
369template <typename ELFT>
372 if (Error Err = Section->validateInBounds(this->getBuffer(), Name.data()))
373 return Err;
374 auto ItInserted = Sections.try_emplace(Name, std::move(Section));
375 if (!ItInserted.second)
376 return make_error<StringError>("In " + Buffer->getBufferIdentifier() +
377 ", encountered duplicate section \"" +
378 Name + "\" while building debug object",
380 return Error::success();
381}
382
384 auto It = Sections.find(Name);
385 return It == Sections.end() ? nullptr : It->second.get();
386}
387
388/// Creates a debug object based on the input object file from
389/// ObjectLinkingLayerJITLinkContext.
390///
393 JITLinkContext &Ctx, MemoryBufferRef ObjBuffer) {
394 switch (G.getTargetTriple().getObjectFormat()) {
395 case Triple::ELF:
396 return ELFDebugObject::Create(ObjBuffer, Ctx, ES);
397
398 default:
399 // TODO: Once we add support for other formats, we might want to split this
400 // into multiple files.
401 return nullptr;
402 }
403}
404
406 ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target)
407 : ES(ES), Target(std::move(Target)) {}
408
410
413 MemoryBufferRef ObjBuffer) {
414 std::lock_guard<std::mutex> Lock(PendingObjsLock);
415 assert(PendingObjs.count(&MR) == 0 &&
416 "Cannot have more than one pending debug object per "
417 "MaterializationResponsibility");
418
419 if (auto DebugObj = createDebugObjectFromBuffer(ES, G, Ctx, ObjBuffer)) {
420 // Not all link artifacts allow debugging.
421 if (*DebugObj != nullptr)
422 PendingObjs[&MR] = std::move(*DebugObj);
423 } else {
424 ES.reportError(DebugObj.takeError());
425 }
426}
427
430 PassConfiguration &PassConfig) {
431 // Not all link artifacts have associated debug objects.
432 std::lock_guard<std::mutex> Lock(PendingObjsLock);
433 auto It = PendingObjs.find(&MR);
434 if (It == PendingObjs.end())
435 return;
436
437 DebugObject &DebugObj = *It->second;
439 PassConfig.PostAllocationPasses.push_back(
440 [&DebugObj](LinkGraph &Graph) -> Error {
441 for (const Section &GraphSection : Graph.sections())
442 DebugObj.reportSectionTargetMemoryRange(GraphSection.getName(),
443 SectionRange(GraphSection));
444 return Error::success();
445 });
446 }
447}
448
451 std::lock_guard<std::mutex> Lock(PendingObjsLock);
452 auto It = PendingObjs.find(&MR);
453 if (It == PendingObjs.end())
454 return Error::success();
455
456 // During finalization the debug object is registered with the target.
457 // Materialization must wait for this process to finish. Otherwise we might
458 // start running code before the debugger processed the corresponding debug
459 // info.
460 std::promise<MSVCPError> FinalizePromise;
461 std::future<MSVCPError> FinalizeErr = FinalizePromise.get_future();
462
463 It->second->finalizeAsync(
464 [this, &FinalizePromise, &MR](Expected<ExecutorAddrRange> TargetMem) {
465 // Any failure here will fail materialization.
466 if (!TargetMem) {
467 FinalizePromise.set_value(TargetMem.takeError());
468 return;
469 }
470 if (Error Err = Target->registerDebugObject(*TargetMem)) {
471 FinalizePromise.set_value(std::move(Err));
472 return;
473 }
474
475 // Once our tracking info is updated, notifyEmitted() can return and
476 // finish materialization.
477 FinalizePromise.set_value(MR.withResourceKeyDo([&](ResourceKey K) {
478 assert(PendingObjs.count(&MR) && "We still hold PendingObjsLock");
479 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
480 RegisteredObjs[K].push_back(std::move(PendingObjs[&MR]));
481 PendingObjs.erase(&MR);
482 }));
483 });
484
485 return FinalizeErr.get();
486}
487
490 std::lock_guard<std::mutex> Lock(PendingObjsLock);
491 PendingObjs.erase(&MR);
492 return Error::success();
493}
494
496 ResourceKey DstKey,
497 ResourceKey SrcKey) {
498 // Debug objects are stored by ResourceKey only after registration.
499 // Thus, pending objects don't need to be updated here.
500 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
501 auto SrcIt = RegisteredObjs.find(SrcKey);
502 if (SrcIt != RegisteredObjs.end()) {
503 // Resources from distinct MaterializationResponsibilitys can get merged
504 // after emission, so we can have multiple debug objects per resource key.
505 for (std::unique_ptr<DebugObject> &DebugObj : SrcIt->second)
506 RegisteredObjs[DstKey].push_back(std::move(DebugObj));
507 RegisteredObjs.erase(SrcIt);
508 }
509}
510
512 ResourceKey Key) {
513 // Removing the resource for a pending object fails materialization, so they
514 // get cleaned up in the notifyFailed() handler.
515 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
516 RegisteredObjs.erase(Key);
517
518 // TODO: Implement unregister notifications.
519 return Error::success();
520}
521
522} // namespace orc
523} // namespace llvm
This file defines the StringMap class.
#define LLVM_DEBUG(X)
Definition: Debug.h:101
Elf_Shdr Shdr
std::string Name
uint64_t Size
#define _
static cl::opt< int > PageSize("imp-null-check-page-size", cl::desc("The page size of the target in bytes"), cl::init(4096), cl::Hidden)
#define G(x, y, z)
Definition: MD5.cpp:56
Provides a library for accessing information about this process and other processes on the operating ...
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
endianness Endian
raw_pwrite_stream & OS
Helper for Errors used as out-parameters.
Definition: Error.h:1104
Lightweight error class with error context and mandatory checking.
Definition: Error.h:156
static ErrorSuccess success()
Create a success value.
Definition: Error.h:330
Tagged union holding either a T or a Error.
Definition: Error.h:470
Error takeError()
Take ownership of the stored error.
Definition: Error.h:597
size_t getBufferSize() const
StringRef getBufferIdentifier() const
const char * getBufferStart() const
StringRef getBuffer() const
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition: StringMap.h:111
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
const unsigned char * bytes_end() const
Definition: StringRef.h:118
constexpr size_t size() const
size - Get the string size.
Definition: StringRef.h:137
const unsigned char * bytes_begin() const
Definition: StringRef.h:115
Target - Wrapper for Target specific information.
static std::unique_ptr< WritableMemoryBuffer > getNewUninitMemBuffer(size_t Size, const Twine &BufferName="", std::optional< Align > Alignment=std::nullopt)
Allocate a new MemoryBuffer of the specified size that is not initialized.
static Expected< ELFFile > create(StringRef Object)
Definition: ELF.h:775
Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override
void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) override
Error notifyFailed(MaterializationResponsibility &MR) override
Error notifyEmitted(MaterializationResponsibility &MR) override
void notifyMaterializing(MaterializationResponsibility &MR, jitlink::LinkGraph &G, jitlink::JITLinkContext &Ctx, MemoryBufferRef InputObject) override
void modifyPassConfig(MaterializationResponsibility &MR, jitlink::LinkGraph &LG, jitlink::PassConfiguration &PassConfig) override
DebugObjectManagerPlugin(ExecutionSession &ES, std::unique_ptr< DebugObjectRegistrar > Target)
virtual void setTargetMemoryRange(SectionRange Range)=0
virtual void dump(raw_ostream &OS, StringRef Name)
virtual ~DebugObjectSection()=default
The plugin creates a debug object from when JITLink starts processing the corresponding LinkGraph.
bool has(Requirement Req) const
void finalizeAsync(FinalizeContinuation OnFinalize)
virtual Expected< SimpleSegmentAlloc > finalizeWorkingMemory()=0
std::function< void(Expected< ExecutorAddrRange >)> FinalizeContinuation
virtual void reportSectionTargetMemoryRange(StringRef Name, SectionRange TargetMem)
DebugObject(JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD, ExecutionSession &ES)
Error validateInBounds(StringRef Buffer, const char *Name) const
void setTargetMemoryRange(SectionRange Range) override
void dump(raw_ostream &OS, StringRef Name) override
ELFDebugObjectSection(const typename ELFT::Shdr *Header)
The current implementation of ELFDebugObject replicates the approach used in RuntimeDyld: It patches ...
Error recordSection(StringRef Name, std::unique_ptr< ELFDebugObjectSection< ELFT > > Section)
DebugObjectSection * getSection(StringRef Name)
Expected< SimpleSegmentAlloc > finalizeWorkingMemory() override
void reportSectionTargetMemoryRange(StringRef Name, SectionRange TargetMem) override
static Expected< std::unique_ptr< DebugObject > > Create(MemoryBufferRef Buffer, JITLinkContext &Ctx, ExecutionSession &ES)
An ExecutionSession represents a running JIT program.
Definition: Core.h:1373
void reportError(Error Err)
Report a error for this execution session.
Definition: Core.h:1496
Represents an address in the executor process.
Represents a JIT'd dynamic library.
Definition: Core.h:964
Tracks responsibility for materialization, and mediates interactions between MaterializationUnits and...
Definition: Core.h:526
Error withResourceKeyDo(Func &&F) const
Runs the given callback under the session lock, passing in the associated ResourceKey.
Definition: Core.h:542
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:52
static unsigned getPageSizeEstimate()
Get the process's estimated page size.
Definition: Process.h:61
@ SHF_ALLOC
Definition: ELF.h:1083
@ SHF_EXECINSTR
Definition: ELF.h:1086
@ ELFCLASS64
Definition: ELF.h:329
@ ELFCLASS32
Definition: ELF.h:328
@ EM_X86_64
Definition: ELF.h:178
@ ELFDATA2MSB
Definition: ELF.h:336
@ ELFDATA2LSB
Definition: ELF.h:335
@ SHT_PROGBITS
Definition: ELF.h:995
@ SHT_X86_64_UNWIND
Definition: ELF.h:1059
std::pair< unsigned char, unsigned char > getElfArchType(StringRef Object)
Definition: ELF.h:78
static const std::set< StringRef > DwarfSectionNames
static Expected< std::unique_ptr< DebugObject > > createDebugObjectFromBuffer(ExecutionSession &ES, LinkGraph &G, JITLinkContext &Ctx, MemoryBufferRef ObjBuffer)
Creates a debug object based on the input object file from ObjectLinkingLayerJITLinkContext.
static bool isDwarfSection(StringRef SectionName)
uintptr_t ResourceKey
Definition: Core.h:52
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
auto formatv(const char *Fmt, Ts &&... Vals) -> formatv_object< decltype(std::make_tuple(detail::build_format_adapter(std::forward< Ts >(Vals))...))>
std::error_code make_error_code(BitcodeError E)
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition: Error.cpp:79
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1946
Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition: Error.cpp:92
Definition: BitVector.h:858
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39
Represents an address range in the exceutor process.