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;
dump(raw_ostream & OS,StringRef Name)39   virtual void dump(raw_ostream &OS, StringRef Name) {}
~DebugObjectSection()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.
ELFDebugObjectSection(const typename ELFT::Shdr * Header)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>
setTargetMemoryRange(SectionRange Range)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>
isTextOrDataSection() const71 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>
validateInBounds(StringRef Buffer,const char * Name) const81 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>
dump(raw_ostream & OS,StringRef Name)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:
DebugObject(JITLinkContext & Ctx,ExecutionSession & ES)126   DebugObject(JITLinkContext &Ctx, ExecutionSession &ES) : Ctx(Ctx), ES(ES) {}
127 
set(Requirement Req)128   void set(Requirement Req) { Reqs.insert(Req); }
has(Requirement Req) const129   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 
~DebugObject()134   virtual ~DebugObject() {
135     if (Alloc)
136       if (Error Err = Alloc->deallocate())
137         ES.reportError(std::move(Err));
138   }
139 
reportSectionTargetMemoryRange(StringRef Name,SectionRange TargetMem)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   ExecutionSession &ES;
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.
finalizeAsync(FinalizeContinuation OnFinalize)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>>
185   Create(MemoryBufferRef Buffer, JITLinkContext &Ctx, ExecutionSession &ES);
186 
187   void reportSectionTargetMemoryRange(StringRef Name,
188                                       SectionRange TargetMem) override;
189 
getBuffer() const190   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                  ExecutionSession &ES);
206 
207   static std::unique_ptr<WritableMemoryBuffer>
208   CopyBuffer(MemoryBufferRef Buffer, Error &Err);
209 
ELFDebugObject(std::unique_ptr<WritableMemoryBuffer> Buffer,JITLinkContext & Ctx,ExecutionSession & ES)210   ELFDebugObject(std::unique_ptr<WritableMemoryBuffer> Buffer,
211                  JITLinkContext &Ctx, ExecutionSession &ES)
212       : DebugObject(Ctx, ES), Buffer(std::move(Buffer)) {
213     set(Requirement::ReportFinalSectionLoadAddresses);
214   }
215 
216   std::unique_ptr<WritableMemoryBuffer> Buffer;
217   StringMap<std::unique_ptr<DebugObjectSection>> Sections;
218 };
219 
220 static const std::set<StringRef> DwarfSectionNames = {
221 #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION)        \
222   ELF_NAME,
223 #include "llvm/BinaryFormat/Dwarf.def"
224 #undef HANDLE_DWARF_SECTION
225 };
226 
isDwarfSection(StringRef SectionName)227 static bool isDwarfSection(StringRef SectionName) {
228   return DwarfSectionNames.count(SectionName) == 1;
229 }
230 
231 std::unique_ptr<WritableMemoryBuffer>
CopyBuffer(MemoryBufferRef Buffer,Error & Err)232 ELFDebugObject::CopyBuffer(MemoryBufferRef Buffer, Error &Err) {
233   ErrorAsOutParameter _(&Err);
234   size_t Size = Buffer.getBufferSize();
235   StringRef Name = Buffer.getBufferIdentifier();
236   if (auto Copy = WritableMemoryBuffer::getNewUninitMemBuffer(Size, Name)) {
237     memcpy(Copy->getBufferStart(), Buffer.getBufferStart(), Size);
238     return Copy;
239   }
240 
241   Err = errorCodeToError(make_error_code(errc::not_enough_memory));
242   return nullptr;
243 }
244 
245 template <typename ELFT>
246 Expected<std::unique_ptr<ELFDebugObject>>
CreateArchType(MemoryBufferRef Buffer,JITLinkContext & Ctx,ExecutionSession & ES)247 ELFDebugObject::CreateArchType(MemoryBufferRef Buffer, JITLinkContext &Ctx,
248                                ExecutionSession &ES) {
249   using SectionHeader = typename ELFT::Shdr;
250 
251   Error Err = Error::success();
252   std::unique_ptr<ELFDebugObject> DebugObj(
253       new ELFDebugObject(CopyBuffer(Buffer, Err), Ctx, ES));
254   if (Err)
255     return std::move(Err);
256 
257   Expected<ELFFile<ELFT>> ObjRef = ELFFile<ELFT>::create(DebugObj->getBuffer());
258   if (!ObjRef)
259     return ObjRef.takeError();
260 
261   // TODO: Add support for other architectures.
262   uint16_t TargetMachineArch = ObjRef->getHeader().e_machine;
263   if (TargetMachineArch != ELF::EM_X86_64)
264     return nullptr;
265 
266   Expected<ArrayRef<SectionHeader>> Sections = ObjRef->sections();
267   if (!Sections)
268     return Sections.takeError();
269 
270   bool HasDwarfSection = false;
271   for (const SectionHeader &Header : *Sections) {
272     Expected<StringRef> Name = ObjRef->getSectionName(Header);
273     if (!Name)
274       return Name.takeError();
275     if (Name->empty())
276       continue;
277     HasDwarfSection |= isDwarfSection(*Name);
278 
279     auto Wrapped = std::make_unique<ELFDebugObjectSection<ELFT>>(&Header);
280     if (Error Err = DebugObj->recordSection(*Name, std::move(Wrapped)))
281       return std::move(Err);
282   }
283 
284   if (!HasDwarfSection) {
285     LLVM_DEBUG(dbgs() << "Aborting debug registration for LinkGraph \""
286                       << DebugObj->Buffer->getBufferIdentifier()
287                       << "\": input object contains no debug info\n");
288     return nullptr;
289   }
290 
291   return std::move(DebugObj);
292 }
293 
294 Expected<std::unique_ptr<DebugObject>>
Create(MemoryBufferRef Buffer,JITLinkContext & Ctx,ExecutionSession & ES)295 ELFDebugObject::Create(MemoryBufferRef Buffer, JITLinkContext &Ctx,
296                        ExecutionSession &ES) {
297   unsigned char Class, Endian;
298   std::tie(Class, Endian) = getElfArchType(Buffer.getBuffer());
299 
300   if (Class == ELF::ELFCLASS32) {
301     if (Endian == ELF::ELFDATA2LSB)
302       return CreateArchType<ELF32LE>(Buffer, Ctx, ES);
303     if (Endian == ELF::ELFDATA2MSB)
304       return CreateArchType<ELF32BE>(Buffer, Ctx, ES);
305     return nullptr;
306   }
307   if (Class == ELF::ELFCLASS64) {
308     if (Endian == ELF::ELFDATA2LSB)
309       return CreateArchType<ELF64LE>(Buffer, Ctx, ES);
310     if (Endian == ELF::ELFDATA2MSB)
311       return CreateArchType<ELF64BE>(Buffer, Ctx, ES);
312     return nullptr;
313   }
314   return nullptr;
315 }
316 
317 Expected<std::unique_ptr<DebugObject::Allocation>>
finalizeWorkingMemory(JITLinkContext & Ctx)318 ELFDebugObject::finalizeWorkingMemory(JITLinkContext &Ctx) {
319   LLVM_DEBUG({
320     dbgs() << "Section load-addresses in debug object for \""
321            << Buffer->getBufferIdentifier() << "\":\n";
322     for (const auto &KV : Sections)
323       KV.second->dump(dbgs(), KV.first());
324   });
325 
326   // TODO: This works, but what actual alignment requirements do we have?
327   unsigned Alignment = sys::Process::getPageSizeEstimate();
328   JITLinkMemoryManager &MemMgr = Ctx.getMemoryManager();
329   const JITLinkDylib *JD = Ctx.getJITLinkDylib();
330   size_t Size = Buffer->getBufferSize();
331 
332   // Allocate working memory for debug object in read-only segment.
333   JITLinkMemoryManager::SegmentsRequestMap SingleReadOnlySegment;
334   SingleReadOnlySegment[ReadOnly] =
335       JITLinkMemoryManager::SegmentRequest(Alignment, Size, 0);
336 
337   auto AllocOrErr = MemMgr.allocate(JD, SingleReadOnlySegment);
338   if (!AllocOrErr)
339     return AllocOrErr.takeError();
340 
341   // Initialize working memory with a copy of our object buffer.
342   // TODO: Use our buffer as working memory directly.
343   std::unique_ptr<Allocation> Alloc = std::move(*AllocOrErr);
344   MutableArrayRef<char> WorkingMem = Alloc->getWorkingMemory(ReadOnly);
345   memcpy(WorkingMem.data(), Buffer->getBufferStart(), Size);
346   Buffer.reset();
347 
348   return std::move(Alloc);
349 }
350 
reportSectionTargetMemoryRange(StringRef Name,SectionRange TargetMem)351 void ELFDebugObject::reportSectionTargetMemoryRange(StringRef Name,
352                                                     SectionRange TargetMem) {
353   if (auto *DebugObjSection = getSection(Name))
354     DebugObjSection->setTargetMemoryRange(TargetMem);
355 }
356 
357 template <typename ELFT>
recordSection(StringRef Name,std::unique_ptr<ELFDebugObjectSection<ELFT>> Section)358 Error ELFDebugObject::recordSection(
359     StringRef Name, std::unique_ptr<ELFDebugObjectSection<ELFT>> Section) {
360   if (Error Err = Section->validateInBounds(this->getBuffer(), Name.data()))
361     return Err;
362   auto ItInserted = Sections.try_emplace(Name, std::move(Section));
363   if (!ItInserted.second)
364     return make_error<StringError>("Duplicate section",
365                                    inconvertibleErrorCode());
366   return Error::success();
367 }
368 
getSection(StringRef Name)369 DebugObjectSection *ELFDebugObject::getSection(StringRef Name) {
370   auto It = Sections.find(Name);
371   return It == Sections.end() ? nullptr : It->second.get();
372 }
373 
374 /// Creates a debug object based on the input object file from
375 /// ObjectLinkingLayerJITLinkContext.
376 ///
377 static Expected<std::unique_ptr<DebugObject>>
createDebugObjectFromBuffer(ExecutionSession & ES,LinkGraph & G,JITLinkContext & Ctx,MemoryBufferRef ObjBuffer)378 createDebugObjectFromBuffer(ExecutionSession &ES, LinkGraph &G,
379                             JITLinkContext &Ctx, MemoryBufferRef ObjBuffer) {
380   switch (G.getTargetTriple().getObjectFormat()) {
381   case Triple::ELF:
382     return ELFDebugObject::Create(ObjBuffer, Ctx, ES);
383 
384   default:
385     // TODO: Once we add support for other formats, we might want to split this
386     // into multiple files.
387     return nullptr;
388   }
389 }
390 
DebugObjectManagerPlugin(ExecutionSession & ES,std::unique_ptr<DebugObjectRegistrar> Target)391 DebugObjectManagerPlugin::DebugObjectManagerPlugin(
392     ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target)
393     : ES(ES), Target(std::move(Target)) {}
394 
395 DebugObjectManagerPlugin::~DebugObjectManagerPlugin() = default;
396 
notifyMaterializing(MaterializationResponsibility & MR,LinkGraph & G,JITLinkContext & Ctx,MemoryBufferRef ObjBuffer)397 void DebugObjectManagerPlugin::notifyMaterializing(
398     MaterializationResponsibility &MR, LinkGraph &G, JITLinkContext &Ctx,
399     MemoryBufferRef ObjBuffer) {
400   std::lock_guard<std::mutex> Lock(PendingObjsLock);
401   assert(PendingObjs.count(&MR) == 0 &&
402          "Cannot have more than one pending debug object per "
403          "MaterializationResponsibility");
404 
405   if (auto DebugObj = createDebugObjectFromBuffer(ES, G, Ctx, ObjBuffer)) {
406     // Not all link artifacts allow debugging.
407     if (*DebugObj != nullptr)
408       PendingObjs[&MR] = std::move(*DebugObj);
409   } else {
410     ES.reportError(DebugObj.takeError());
411   }
412 }
413 
modifyPassConfig(MaterializationResponsibility & MR,LinkGraph & G,PassConfiguration & PassConfig)414 void DebugObjectManagerPlugin::modifyPassConfig(
415     MaterializationResponsibility &MR, LinkGraph &G,
416     PassConfiguration &PassConfig) {
417   // Not all link artifacts have associated debug objects.
418   std::lock_guard<std::mutex> Lock(PendingObjsLock);
419   auto It = PendingObjs.find(&MR);
420   if (It == PendingObjs.end())
421     return;
422 
423   DebugObject &DebugObj = *It->second;
424   if (DebugObj.has(Requirement::ReportFinalSectionLoadAddresses)) {
425     PassConfig.PostAllocationPasses.push_back(
426         [&DebugObj](LinkGraph &Graph) -> Error {
427           for (const Section &GraphSection : Graph.sections())
428             DebugObj.reportSectionTargetMemoryRange(GraphSection.getName(),
429                                                     SectionRange(GraphSection));
430           return Error::success();
431         });
432   }
433 }
434 
notifyEmitted(MaterializationResponsibility & MR)435 Error DebugObjectManagerPlugin::notifyEmitted(
436     MaterializationResponsibility &MR) {
437   std::lock_guard<std::mutex> Lock(PendingObjsLock);
438   auto It = PendingObjs.find(&MR);
439   if (It == PendingObjs.end())
440     return Error::success();
441 
442   // During finalization the debug object is registered with the target.
443   // Materialization must wait for this process to finish. Otherwise we might
444   // start running code before the debugger processed the corresponding debug
445   // info.
446   std::promise<MSVCPError> FinalizePromise;
447   std::future<MSVCPError> FinalizeErr = FinalizePromise.get_future();
448 
449   It->second->finalizeAsync(
450       [this, &FinalizePromise, &MR](Expected<sys::MemoryBlock> TargetMem) {
451         // Any failure here will fail materialization.
452         if (!TargetMem) {
453           FinalizePromise.set_value(TargetMem.takeError());
454           return;
455         }
456         if (Error Err = Target->registerDebugObject(*TargetMem)) {
457           FinalizePromise.set_value(std::move(Err));
458           return;
459         }
460 
461         // Once our tracking info is updated, notifyEmitted() can return and
462         // finish materialization.
463         FinalizePromise.set_value(MR.withResourceKeyDo([&](ResourceKey K) {
464           assert(PendingObjs.count(&MR) && "We still hold PendingObjsLock");
465           std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
466           RegisteredObjs[K].push_back(std::move(PendingObjs[&MR]));
467           PendingObjs.erase(&MR);
468         }));
469       });
470 
471   return FinalizeErr.get();
472 }
473 
notifyFailed(MaterializationResponsibility & MR)474 Error DebugObjectManagerPlugin::notifyFailed(
475     MaterializationResponsibility &MR) {
476   std::lock_guard<std::mutex> Lock(PendingObjsLock);
477   PendingObjs.erase(&MR);
478   return Error::success();
479 }
480 
notifyTransferringResources(ResourceKey DstKey,ResourceKey SrcKey)481 void DebugObjectManagerPlugin::notifyTransferringResources(ResourceKey DstKey,
482                                                            ResourceKey SrcKey) {
483   // Debug objects are stored by ResourceKey only after registration.
484   // Thus, pending objects don't need to be updated here.
485   std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
486   auto SrcIt = RegisteredObjs.find(SrcKey);
487   if (SrcIt != RegisteredObjs.end()) {
488     // Resources from distinct MaterializationResponsibilitys can get merged
489     // after emission, so we can have multiple debug objects per resource key.
490     for (std::unique_ptr<DebugObject> &DebugObj : SrcIt->second)
491       RegisteredObjs[DstKey].push_back(std::move(DebugObj));
492     RegisteredObjs.erase(SrcIt);
493   }
494 }
495 
notifyRemovingResources(ResourceKey Key)496 Error DebugObjectManagerPlugin::notifyRemovingResources(ResourceKey Key) {
497   // Removing the resource for a pending object fails materialization, so they
498   // get cleaned up in the notifyFailed() handler.
499   std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
500   RegisteredObjs.erase(Key);
501 
502   // TODO: Implement unregister notifications.
503   return Error::success();
504 }
505 
506 } // namespace orc
507 } // namespace llvm
508