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