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