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