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
35using namespace llvm::jitlink;
36using namespace llvm::object;
37
38namespace llvm {
39namespace orc {
40
41class DebugObjectSection {
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>
49class ELFDebugObjectSection : public DebugObjectSection {
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
65template <typename ELFT>
66void 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
72template <typename ELFT>
73Error 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
94template <typename ELFT>
95void ELFDebugObjectSection<ELFT>::dump(raw_ostream &OS, StringRef Name) {
96 if (uint64_t Addr = Header->sh_addr) {
97 OS << formatv(Fmt: " {0:x16} {1}\n", Vals&: Addr, Vals&: Name);
98 } else {
99 OS << formatv(Fmt: " {0}\n", Vals&: Name);
100 }
101}
102
103enum 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///
116class DebugObject {
117public:
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(x: std::move(Alloc));
138 if (Error Err = MemMgr.deallocate(Allocs: std::move(Allocs)))
139 ES.reportError(Err: std::move(Err));
140 }
141 }
142
143 virtual void reportSectionTargetMemoryRange(StringRef Name,
144 SectionRange TargetMem) {}
145
146protected:
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
155private:
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.
164void DebugObject::finalizeAsync(FinalizeContinuation OnFinalize) {
165 assert(!Alloc && "Cannot finalize more than once");
166
167 if (auto SimpleSegAlloc = finalizeWorkingMemory()) {
168 auto ROSeg = SimpleSegAlloc->getSegInfo(AG: MemProt::Read);
169 ExecutorAddrRange DebugObjRange(ROSeg.Addr, ROSeg.WorkingMem.size());
170 SimpleSegAlloc->finalize(
171 OnFinalized: [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///
188class ELFDebugObject : public DebugObject {
189public:
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
198protected:
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
206private:
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
226static 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
233static bool isDwarfSection(StringRef SectionName) {
234 return DwarfSectionNames.count(x: SectionName) == 1;
235}
236
237std::unique_ptr<WritableMemoryBuffer>
238ELFDebugObject::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, BufferName: Name)) {
243 memcpy(dest: Copy->getBufferStart(), src: Buffer.getBufferStart(), n: Size);
244 return Copy;
245 }
246
247 Err = errorCodeToError(EC: make_error_code(E: errc::not_enough_memory));
248 return nullptr;
249}
250
251template <typename ELFT>
252Expected<std::unique_ptr<ELFDebugObject>>
253ELFDebugObject::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(SectionName: *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
296Expected<std::unique_ptr<DebugObject>>
297ELFDebugObject::Create(MemoryBufferRef Buffer, JITLinkContext &Ctx,
298 ExecutionSession &ES) {
299 unsigned char Class, Endian;
300 std::tie(args&: Class, args&: Endian) = getElfArchType(Object: Buffer.getBuffer());
301
302 if (Class == ELF::ELFCLASS32) {
303 if (Endian == ELF::ELFDATA2LSB)
304 return CreateArchType<ELF32LE>(Buffer, MemMgr&: Ctx.getMemoryManager(),
305 JD: Ctx.getJITLinkDylib(), ES);
306 if (Endian == ELF::ELFDATA2MSB)
307 return CreateArchType<ELF32BE>(Buffer, MemMgr&: Ctx.getMemoryManager(),
308 JD: Ctx.getJITLinkDylib(), ES);
309 return nullptr;
310 }
311 if (Class == ELF::ELFCLASS64) {
312 if (Endian == ELF::ELFDATA2LSB)
313 return CreateArchType<ELF64LE>(Buffer, MemMgr&: Ctx.getMemoryManager(),
314 JD: Ctx.getJITLinkDylib(), ES);
315 if (Endian == ELF::ELFDATA2MSB)
316 return CreateArchType<ELF64BE>(Buffer, MemMgr&: Ctx.getMemoryManager(),
317 JD: Ctx.getJITLinkDylib(), ES);
318 return nullptr;
319 }
320 return nullptr;
321}
322
323Expected<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, Segments: {{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(AG: MemProt::Read);
343 memcpy(dest: SegInfo.WorkingMem.data(), src: Buffer->getBufferStart(), n: Size);
344 Buffer.reset();
345
346 return Alloc;
347}
348
349void ELFDebugObject::reportSectionTargetMemoryRange(StringRef Name,
350 SectionRange TargetMem) {
351 if (auto *DebugObjSection = getSection(Name))
352 DebugObjSection->setTargetMemoryRange(TargetMem);
353}
354
355template <typename ELFT>
356Error 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
368DebugObjectSection *ELFDebugObject::getSection(StringRef Name) {
369 auto It = Sections.find(Key: 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///
376static Expected<std::unique_ptr<DebugObject>>
377createDebugObjectFromBuffer(ExecutionSession &ES, LinkGraph &G,
378 JITLinkContext &Ctx, MemoryBufferRef ObjBuffer) {
379 switch (G.getTargetTriple().getObjectFormat()) {
380 case Triple::ELF:
381 return ELFDebugObject::Create(Buffer: 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
390DebugObjectManagerPlugin::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
397DebugObjectManagerPlugin::DebugObjectManagerPlugin(
398 ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target)
399 : DebugObjectManagerPlugin(ES, std::move(Target), true, true) {}
400
401DebugObjectManagerPlugin::~DebugObjectManagerPlugin() = default;
402
403void 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(F: 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(Err: DebugObj.takeError());
423 }
424}
425
426void 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(x: &MR);
432 if (It == PendingObjs.end())
433 return;
434
435 DebugObject &DebugObj = *It->second;
436 if (DebugObj.hasFlags(F: ReportFinalSectionLoadAddresses)) {
437 PassConfig.PostAllocationPasses.push_back(
438 x: [&DebugObj](LinkGraph &Graph) -> Error {
439 for (const Section &GraphSection : Graph.sections())
440 DebugObj.reportSectionTargetMemoryRange(Name: GraphSection.getName(),
441 TargetMem: SectionRange(GraphSection));
442 return Error::success();
443 });
444 }
445}
446
447Error DebugObjectManagerPlugin::notifyEmitted(
448 MaterializationResponsibility &MR) {
449 std::lock_guard<std::mutex> Lock(PendingObjsLock);
450 auto It = PendingObjs.find(x: &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 OnFinalize: [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: *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(F: [&](ResourceKey K) {
477 assert(PendingObjs.count(&MR) && "We still hold PendingObjsLock");
478 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
479 RegisteredObjs[K].push_back(x: std::move(PendingObjs[&MR]));
480 PendingObjs.erase(x: &MR);
481 }));
482 });
483
484 return FinalizeErr.get();
485}
486
487Error DebugObjectManagerPlugin::notifyFailed(
488 MaterializationResponsibility &MR) {
489 std::lock_guard<std::mutex> Lock(PendingObjsLock);
490 PendingObjs.erase(x: &MR);
491 return Error::success();
492}
493
494void 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(x: 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(x: std::move(DebugObj));
506 RegisteredObjs.erase(position: SrcIt);
507 }
508}
509
510Error 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(x: Key);
516
517 // TODO: Implement unregister notifications.
518 return Error::success();
519}
520
521} // namespace orc
522} // namespace llvm
523

source code of llvm/lib/ExecutionEngine/Orc/DebugObjectManagerPlugin.cpp