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;
dump(raw_ostream & OS,StringRef Name)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.
ELFDebugObjectSection(const typename ELFT::Shdr * Header)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 bool isTextOrDataSection() const;
65 };
66
67 template <typename ELFT>
setTargetMemoryRange(SectionRange Range)68 void ELFDebugObjectSection<ELFT>::setTargetMemoryRange(SectionRange Range) {
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
75 template <typename ELFT>
isTextOrDataSection() const76 bool ELFDebugObjectSection<ELFT>::isTextOrDataSection() const {
77 switch (Header->sh_type) {
78 case ELF::SHT_PROGBITS:
79 case ELF::SHT_X86_64_UNWIND:
80 return Header->sh_flags & (ELF::SHF_EXECINSTR | ELF::SHF_ALLOC);
81 }
82 return false;
83 }
84
85 template <typename ELFT>
validateInBounds(StringRef Buffer,const char * Name) const86 Error ELFDebugObjectSection<ELFT>::validateInBounds(StringRef Buffer,
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),
96 inconvertibleErrorCode());
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),
103 inconvertibleErrorCode());
104 return Error::success();
105 }
106
107 template <typename ELFT>
dump(raw_ostream & OS,StringRef Name)108 void ELFDebugObjectSection<ELFT>::dump(raw_ostream &OS, StringRef Name) {
109 if (auto Addr = static_cast<JITTargetAddress>(Header->sh_addr)) {
110 OS << formatv(" {0:x16} {1}\n", Addr, Name);
111 } else {
112 OS << formatv(" {0}\n", Name);
113 }
114 }
115
116 enum class Requirement {
117 // Request final target memory load-addresses for all sections.
118 ReportFinalSectionLoadAddresses,
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 ///
126 class DebugObject {
127 public:
DebugObject(JITLinkMemoryManager & MemMgr,const JITLinkDylib * JD,ExecutionSession & ES)128 DebugObject(JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD,
129 ExecutionSession &ES)
130 : MemMgr(MemMgr), JD(JD), ES(ES) {}
131
set(Requirement Req)132 void set(Requirement Req) { Reqs.insert(Req); }
has(Requirement Req) const133 bool has(Requirement Req) const { return Reqs.count(Req) > 0; }
134
135 using FinalizeContinuation = std::function<void(Expected<ExecutorAddrRange>)>;
136
137 void finalizeAsync(FinalizeContinuation OnFinalize);
138
~DebugObject()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
reportSectionTargetMemoryRange(StringRef Name,SectionRange TargetMem)148 virtual void reportSectionTargetMemoryRange(StringRef Name,
149 SectionRange TargetMem) {}
150
151 protected:
152 using InFlightAlloc = JITLinkMemoryManager::InFlightAlloc;
153 using FinalizedAlloc = JITLinkMemoryManager::FinalizedAlloc;
154
155 virtual Expected<SimpleSegmentAlloc> finalizeWorkingMemory() = 0;
156
157 JITLinkMemoryManager &MemMgr;
158 const JITLinkDylib *JD = nullptr;
159
160 private:
161 ExecutionSession &ES;
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.
finalizeAsync(FinalizeContinuation OnFinalize)169 void DebugObject::finalizeAsync(FinalizeContinuation OnFinalize) {
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 ///
194 class ELFDebugObject : public DebugObject {
195 public:
196 static Expected<std::unique_ptr<DebugObject>>
197 Create(MemoryBufferRef Buffer, JITLinkContext &Ctx, ExecutionSession &ES);
198
199 void reportSectionTargetMemoryRange(StringRef Name,
200 SectionRange TargetMem) override;
201
getBuffer() const202 StringRef getBuffer() const { return Buffer->getMemBufferRef().getBuffer(); }
203
204 protected:
205 Expected<SimpleSegmentAlloc> finalizeWorkingMemory() override;
206
207 template <typename ELFT>
208 Error recordSection(StringRef Name,
209 std::unique_ptr<ELFDebugObjectSection<ELFT>> Section);
210 DebugObjectSection *getSection(StringRef Name);
211
212 private:
213 template <typename ELFT>
214 static Expected<std::unique_ptr<ELFDebugObject>>
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
ELFDebugObject(std::unique_ptr<WritableMemoryBuffer> Buffer,JITLinkMemoryManager & MemMgr,const JITLinkDylib * JD,ExecutionSession & ES)221 ELFDebugObject(std::unique_ptr<WritableMemoryBuffer> Buffer,
222 JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD,
223 ExecutionSession &ES)
224 : DebugObject(MemMgr, JD, ES), Buffer(std::move(Buffer)) {
225 set(Requirement::ReportFinalSectionLoadAddresses);
226 }
227
228 std::unique_ptr<WritableMemoryBuffer> Buffer;
229 StringMap<std::unique_ptr<DebugObjectSection>> Sections;
230 };
231
232 static 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
isDwarfSection(StringRef SectionName)239 static bool isDwarfSection(StringRef SectionName) {
240 return DwarfSectionNames.count(SectionName) == 1;
241 }
242
243 std::unique_ptr<WritableMemoryBuffer>
CopyBuffer(MemoryBufferRef Buffer,Error & Err)244 ELFDebugObject::CopyBuffer(MemoryBufferRef Buffer, Error &Err) {
245 ErrorAsOutParameter _(&Err);
246 size_t Size = Buffer.getBufferSize();
247 StringRef Name = Buffer.getBufferIdentifier();
248 if (auto Copy = WritableMemoryBuffer::getNewUninitMemBuffer(Size, Name)) {
249 memcpy(Copy->getBufferStart(), Buffer.getBufferStart(), Size);
250 return Copy;
251 }
252
253 Err = errorCodeToError(make_error_code(errc::not_enough_memory));
254 return nullptr;
255 }
256
257 template <typename ELFT>
258 Expected<std::unique_ptr<ELFDebugObject>>
CreateArchType(MemoryBufferRef Buffer,JITLinkMemoryManager & MemMgr,const JITLinkDylib * JD,ExecutionSession & ES)259 ELFDebugObject::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 auto Wrapped = std::make_unique<ELFDebugObjectSection<ELFT>>(&Header);
293 if (Error Err = DebugObj->recordSection(*Name, std::move(Wrapped)))
294 return std::move(Err);
295 }
296
297 if (!HasDwarfSection) {
298 LLVM_DEBUG(dbgs() << "Aborting debug registration for LinkGraph \""
299 << DebugObj->Buffer->getBufferIdentifier()
300 << "\": input object contains no debug info\n");
301 return nullptr;
302 }
303
304 return std::move(DebugObj);
305 }
306
307 Expected<std::unique_ptr<DebugObject>>
Create(MemoryBufferRef Buffer,JITLinkContext & Ctx,ExecutionSession & ES)308 ELFDebugObject::Create(MemoryBufferRef Buffer, JITLinkContext &Ctx,
309 ExecutionSession &ES) {
310 unsigned char Class, Endian;
311 std::tie(Class, Endian) = getElfArchType(Buffer.getBuffer());
312
313 if (Class == ELF::ELFCLASS32) {
314 if (Endian == ELF::ELFDATA2LSB)
315 return CreateArchType<ELF32LE>(Buffer, Ctx.getMemoryManager(),
316 Ctx.getJITLinkDylib(), ES);
317 if (Endian == ELF::ELFDATA2MSB)
318 return CreateArchType<ELF32BE>(Buffer, Ctx.getMemoryManager(),
319 Ctx.getJITLinkDylib(), ES);
320 return nullptr;
321 }
322 if (Class == ELF::ELFCLASS64) {
323 if (Endian == ELF::ELFDATA2LSB)
324 return CreateArchType<ELF64LE>(Buffer, Ctx.getMemoryManager(),
325 Ctx.getJITLinkDylib(), ES);
326 if (Endian == ELF::ELFDATA2MSB)
327 return CreateArchType<ELF64BE>(Buffer, Ctx.getMemoryManager(),
328 Ctx.getJITLinkDylib(), ES);
329 return nullptr;
330 }
331 return nullptr;
332 }
333
finalizeWorkingMemory()334 Expected<SimpleSegmentAlloc> ELFDebugObject::finalizeWorkingMemory() {
335 LLVM_DEBUG({
336 dbgs() << "Section load-addresses in debug object for \""
337 << Buffer->getBufferIdentifier() << "\":\n";
338 for (const auto &KV : Sections)
339 KV.second->dump(dbgs(), KV.first());
340 });
341
342 // TODO: This works, but what actual alignment requirements do we have?
343 unsigned PageSize = sys::Process::getPageSizeEstimate();
344 size_t Size = Buffer->getBufferSize();
345
346 // Allocate working memory for debug object in read-only segment.
347 auto Alloc = SimpleSegmentAlloc::Create(
348 MemMgr, JD, {{MemProt::Read, {Size, Align(PageSize)}}});
349 if (!Alloc)
350 return Alloc;
351
352 // Initialize working memory with a copy of our object buffer.
353 auto SegInfo = Alloc->getSegInfo(MemProt::Read);
354 memcpy(SegInfo.WorkingMem.data(), Buffer->getBufferStart(), Size);
355 Buffer.reset();
356
357 return Alloc;
358 }
359
reportSectionTargetMemoryRange(StringRef Name,SectionRange TargetMem)360 void ELFDebugObject::reportSectionTargetMemoryRange(StringRef Name,
361 SectionRange TargetMem) {
362 if (auto *DebugObjSection = getSection(Name))
363 DebugObjSection->setTargetMemoryRange(TargetMem);
364 }
365
366 template <typename ELFT>
recordSection(StringRef Name,std::unique_ptr<ELFDebugObjectSection<ELFT>> Section)367 Error ELFDebugObject::recordSection(
368 StringRef Name, std::unique_ptr<ELFDebugObjectSection<ELFT>> Section) {
369 if (Error Err = Section->validateInBounds(this->getBuffer(), Name.data()))
370 return Err;
371 auto ItInserted = Sections.try_emplace(Name, std::move(Section));
372 if (!ItInserted.second)
373 return make_error<StringError>("Duplicate section",
374 inconvertibleErrorCode());
375 return Error::success();
376 }
377
getSection(StringRef Name)378 DebugObjectSection *ELFDebugObject::getSection(StringRef Name) {
379 auto It = Sections.find(Name);
380 return It == Sections.end() ? nullptr : It->second.get();
381 }
382
383 /// Creates a debug object based on the input object file from
384 /// ObjectLinkingLayerJITLinkContext.
385 ///
386 static Expected<std::unique_ptr<DebugObject>>
createDebugObjectFromBuffer(ExecutionSession & ES,LinkGraph & G,JITLinkContext & Ctx,MemoryBufferRef ObjBuffer)387 createDebugObjectFromBuffer(ExecutionSession &ES, LinkGraph &G,
388 JITLinkContext &Ctx, MemoryBufferRef ObjBuffer) {
389 switch (G.getTargetTriple().getObjectFormat()) {
390 case Triple::ELF:
391 return ELFDebugObject::Create(ObjBuffer, Ctx, ES);
392
393 default:
394 // TODO: Once we add support for other formats, we might want to split this
395 // into multiple files.
396 return nullptr;
397 }
398 }
399
DebugObjectManagerPlugin(ExecutionSession & ES,std::unique_ptr<DebugObjectRegistrar> Target)400 DebugObjectManagerPlugin::DebugObjectManagerPlugin(
401 ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target)
402 : ES(ES), Target(std::move(Target)) {}
403
404 DebugObjectManagerPlugin::~DebugObjectManagerPlugin() = default;
405
notifyMaterializing(MaterializationResponsibility & MR,LinkGraph & G,JITLinkContext & Ctx,MemoryBufferRef ObjBuffer)406 void DebugObjectManagerPlugin::notifyMaterializing(
407 MaterializationResponsibility &MR, LinkGraph &G, JITLinkContext &Ctx,
408 MemoryBufferRef ObjBuffer) {
409 std::lock_guard<std::mutex> Lock(PendingObjsLock);
410 assert(PendingObjs.count(&MR) == 0 &&
411 "Cannot have more than one pending debug object per "
412 "MaterializationResponsibility");
413
414 if (auto DebugObj = createDebugObjectFromBuffer(ES, G, Ctx, ObjBuffer)) {
415 // Not all link artifacts allow debugging.
416 if (*DebugObj != nullptr)
417 PendingObjs[&MR] = std::move(*DebugObj);
418 } else {
419 ES.reportError(DebugObj.takeError());
420 }
421 }
422
modifyPassConfig(MaterializationResponsibility & MR,LinkGraph & G,PassConfiguration & PassConfig)423 void DebugObjectManagerPlugin::modifyPassConfig(
424 MaterializationResponsibility &MR, LinkGraph &G,
425 PassConfiguration &PassConfig) {
426 // Not all link artifacts have associated debug objects.
427 std::lock_guard<std::mutex> Lock(PendingObjsLock);
428 auto It = PendingObjs.find(&MR);
429 if (It == PendingObjs.end())
430 return;
431
432 DebugObject &DebugObj = *It->second;
433 if (DebugObj.has(Requirement::ReportFinalSectionLoadAddresses)) {
434 PassConfig.PostAllocationPasses.push_back(
435 [&DebugObj](LinkGraph &Graph) -> Error {
436 for (const Section &GraphSection : Graph.sections())
437 DebugObj.reportSectionTargetMemoryRange(GraphSection.getName(),
438 SectionRange(GraphSection));
439 return Error::success();
440 });
441 }
442 }
443
notifyEmitted(MaterializationResponsibility & MR)444 Error DebugObjectManagerPlugin::notifyEmitted(
445 MaterializationResponsibility &MR) {
446 std::lock_guard<std::mutex> Lock(PendingObjsLock);
447 auto It = PendingObjs.find(&MR);
448 if (It == PendingObjs.end())
449 return Error::success();
450
451 // During finalization the debug object is registered with the target.
452 // Materialization must wait for this process to finish. Otherwise we might
453 // start running code before the debugger processed the corresponding debug
454 // info.
455 std::promise<MSVCPError> FinalizePromise;
456 std::future<MSVCPError> FinalizeErr = FinalizePromise.get_future();
457
458 It->second->finalizeAsync(
459 [this, &FinalizePromise, &MR](Expected<ExecutorAddrRange> TargetMem) {
460 // Any failure here will fail materialization.
461 if (!TargetMem) {
462 FinalizePromise.set_value(TargetMem.takeError());
463 return;
464 }
465 if (Error Err = Target->registerDebugObject(*TargetMem)) {
466 FinalizePromise.set_value(std::move(Err));
467 return;
468 }
469
470 // Once our tracking info is updated, notifyEmitted() can return and
471 // finish materialization.
472 FinalizePromise.set_value(MR.withResourceKeyDo([&](ResourceKey K) {
473 assert(PendingObjs.count(&MR) && "We still hold PendingObjsLock");
474 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
475 RegisteredObjs[K].push_back(std::move(PendingObjs[&MR]));
476 PendingObjs.erase(&MR);
477 }));
478 });
479
480 return FinalizeErr.get();
481 }
482
notifyFailed(MaterializationResponsibility & MR)483 Error DebugObjectManagerPlugin::notifyFailed(
484 MaterializationResponsibility &MR) {
485 std::lock_guard<std::mutex> Lock(PendingObjsLock);
486 PendingObjs.erase(&MR);
487 return Error::success();
488 }
489
notifyTransferringResources(ResourceKey DstKey,ResourceKey SrcKey)490 void DebugObjectManagerPlugin::notifyTransferringResources(ResourceKey DstKey,
491 ResourceKey SrcKey) {
492 // Debug objects are stored by ResourceKey only after registration.
493 // Thus, pending objects don't need to be updated here.
494 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
495 auto SrcIt = RegisteredObjs.find(SrcKey);
496 if (SrcIt != RegisteredObjs.end()) {
497 // Resources from distinct MaterializationResponsibilitys can get merged
498 // after emission, so we can have multiple debug objects per resource key.
499 for (std::unique_ptr<DebugObject> &DebugObj : SrcIt->second)
500 RegisteredObjs[DstKey].push_back(std::move(DebugObj));
501 RegisteredObjs.erase(SrcIt);
502 }
503 }
504
notifyRemovingResources(ResourceKey Key)505 Error DebugObjectManagerPlugin::notifyRemovingResources(ResourceKey Key) {
506 // Removing the resource for a pending object fails materialization, so they
507 // get cleaned up in the notifyFailed() handler.
508 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
509 RegisteredObjs.erase(Key);
510
511 // TODO: Implement unregister notifications.
512 return Error::success();
513 }
514
515 } // namespace orc
516 } // namespace llvm
517