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; |
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. |
53 | ELFDebugObjectSection(const typename ELFT::Shdr *) |
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 *; |
63 | }; |
64 | |
65 | template <typename ELFT> |
66 | void ELFDebugObjectSection<ELFT>::setTargetMemoryRange(SectionRange Range) { |
67 | // All recorded sections are candidates for load-address patching. |
68 | Header->sh_addr = |
69 | static_cast<typename ELFT::uint>(Range.getStart().getValue()); |
70 | } |
71 | |
72 | template <typename ELFT> |
73 | Error ELFDebugObjectSection<ELFT>::validateInBounds(StringRef Buffer, |
74 | const char *Name) const { |
75 | const uint8_t *Start = Buffer.bytes_begin(); |
76 | const uint8_t *End = Buffer.bytes_end(); |
77 | const uint8_t * = reinterpret_cast<uint8_t *>(Header); |
78 | if (HeaderPtr < Start || HeaderPtr + sizeof(typename ELFT::Shdr) > End) |
79 | return make_error<StringError>( |
80 | formatv("{0} section header at {1:x16} not within bounds of the " |
81 | "given debug object buffer [{2:x16} - {3:x16}]" , |
82 | Name, &Header->sh_addr, Start, End), |
83 | inconvertibleErrorCode()); |
84 | if (Header->sh_offset + Header->sh_size > Buffer.size()) |
85 | return make_error<StringError>( |
86 | formatv("{0} section data [{1:x16} - {2:x16}] not within bounds of " |
87 | "the given debug object buffer [{3:x16} - {4:x16}]" , |
88 | Name, Start + Header->sh_offset, |
89 | Start + Header->sh_offset + Header->sh_size, Start, End), |
90 | inconvertibleErrorCode()); |
91 | return Error::success(); |
92 | } |
93 | |
94 | template <typename ELFT> |
95 | void ELFDebugObjectSection<ELFT>::dump(raw_ostream &OS, StringRef Name) { |
96 | if (uint64_t Addr = Header->sh_addr) { |
97 | OS << formatv(Fmt: " {0:x16} {1}\n" , Vals&: Addr, Vals&: Name); |
98 | } else { |
99 | OS << formatv(Fmt: " {0}\n" , Vals&: Name); |
100 | } |
101 | } |
102 | |
103 | enum DebugObjectFlags : int { |
104 | // Request final target memory load-addresses for all sections. |
105 | ReportFinalSectionLoadAddresses = 1 << 0, |
106 | |
107 | // We found sections with debug information when processing the input object. |
108 | HasDebugSections = 1 << 1, |
109 | }; |
110 | |
111 | /// The plugin creates a debug object from when JITLink starts processing the |
112 | /// corresponding LinkGraph. It provides access to the pass configuration of |
113 | /// the LinkGraph and calls the finalization function, once the resulting link |
114 | /// artifact was emitted. |
115 | /// |
116 | class DebugObject { |
117 | public: |
118 | DebugObject(JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD, |
119 | ExecutionSession &ES) |
120 | : MemMgr(MemMgr), JD(JD), ES(ES), Flags(DebugObjectFlags{}) {} |
121 | |
122 | bool hasFlags(DebugObjectFlags F) const { return Flags & F; } |
123 | void setFlags(DebugObjectFlags F) { |
124 | Flags = static_cast<DebugObjectFlags>(Flags | F); |
125 | } |
126 | void clearFlags(DebugObjectFlags F) { |
127 | Flags = static_cast<DebugObjectFlags>(Flags & ~F); |
128 | } |
129 | |
130 | using FinalizeContinuation = std::function<void(Expected<ExecutorAddrRange>)>; |
131 | |
132 | void finalizeAsync(FinalizeContinuation OnFinalize); |
133 | |
134 | virtual ~DebugObject() { |
135 | if (Alloc) { |
136 | std::vector<FinalizedAlloc> Allocs; |
137 | Allocs.push_back(x: std::move(Alloc)); |
138 | if (Error Err = MemMgr.deallocate(Allocs: std::move(Allocs))) |
139 | ES.reportError(Err: std::move(Err)); |
140 | } |
141 | } |
142 | |
143 | virtual void reportSectionTargetMemoryRange(StringRef Name, |
144 | SectionRange TargetMem) {} |
145 | |
146 | protected: |
147 | using InFlightAlloc = JITLinkMemoryManager::InFlightAlloc; |
148 | using FinalizedAlloc = JITLinkMemoryManager::FinalizedAlloc; |
149 | |
150 | virtual Expected<SimpleSegmentAlloc> finalizeWorkingMemory() = 0; |
151 | |
152 | JITLinkMemoryManager &MemMgr; |
153 | const JITLinkDylib *JD = nullptr; |
154 | |
155 | private: |
156 | ExecutionSession &ES; |
157 | DebugObjectFlags Flags; |
158 | FinalizedAlloc Alloc; |
159 | }; |
160 | |
161 | // Finalize working memory and take ownership of the resulting allocation. Start |
162 | // copying memory over to the target and pass on the result once we're done. |
163 | // Ownership of the allocation remains with us for the rest of our lifetime. |
164 | void DebugObject::finalizeAsync(FinalizeContinuation OnFinalize) { |
165 | assert(!Alloc && "Cannot finalize more than once" ); |
166 | |
167 | if (auto SimpleSegAlloc = finalizeWorkingMemory()) { |
168 | auto ROSeg = SimpleSegAlloc->getSegInfo(AG: MemProt::Read); |
169 | ExecutorAddrRange DebugObjRange(ROSeg.Addr, ROSeg.WorkingMem.size()); |
170 | SimpleSegAlloc->finalize( |
171 | OnFinalized: [this, DebugObjRange, |
172 | OnFinalize = std::move(OnFinalize)](Expected<FinalizedAlloc> FA) { |
173 | if (FA) { |
174 | Alloc = std::move(*FA); |
175 | OnFinalize(DebugObjRange); |
176 | } else |
177 | OnFinalize(FA.takeError()); |
178 | }); |
179 | } else |
180 | OnFinalize(SimpleSegAlloc.takeError()); |
181 | } |
182 | |
183 | /// The current implementation of ELFDebugObject replicates the approach used in |
184 | /// RuntimeDyld: It patches executable and data section headers in the given |
185 | /// object buffer with load-addresses of their corresponding sections in target |
186 | /// memory. |
187 | /// |
188 | class ELFDebugObject : public DebugObject { |
189 | public: |
190 | static Expected<std::unique_ptr<DebugObject>> |
191 | Create(MemoryBufferRef Buffer, JITLinkContext &Ctx, ExecutionSession &ES); |
192 | |
193 | void reportSectionTargetMemoryRange(StringRef Name, |
194 | SectionRange TargetMem) override; |
195 | |
196 | StringRef getBuffer() const { return Buffer->getMemBufferRef().getBuffer(); } |
197 | |
198 | protected: |
199 | Expected<SimpleSegmentAlloc> finalizeWorkingMemory() override; |
200 | |
201 | template <typename ELFT> |
202 | Error recordSection(StringRef Name, |
203 | std::unique_ptr<ELFDebugObjectSection<ELFT>> Section); |
204 | DebugObjectSection *getSection(StringRef Name); |
205 | |
206 | private: |
207 | template <typename ELFT> |
208 | static Expected<std::unique_ptr<ELFDebugObject>> |
209 | CreateArchType(MemoryBufferRef Buffer, JITLinkMemoryManager &MemMgr, |
210 | const JITLinkDylib *JD, ExecutionSession &ES); |
211 | |
212 | static std::unique_ptr<WritableMemoryBuffer> |
213 | CopyBuffer(MemoryBufferRef Buffer, Error &Err); |
214 | |
215 | ELFDebugObject(std::unique_ptr<WritableMemoryBuffer> Buffer, |
216 | JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD, |
217 | ExecutionSession &ES) |
218 | : DebugObject(MemMgr, JD, ES), Buffer(std::move(Buffer)) { |
219 | setFlags(ReportFinalSectionLoadAddresses); |
220 | } |
221 | |
222 | std::unique_ptr<WritableMemoryBuffer> Buffer; |
223 | StringMap<std::unique_ptr<DebugObjectSection>> Sections; |
224 | }; |
225 | |
226 | static const std::set<StringRef> DwarfSectionNames = { |
227 | #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \ |
228 | ELF_NAME, |
229 | #include "llvm/BinaryFormat/Dwarf.def" |
230 | #undef HANDLE_DWARF_SECTION |
231 | }; |
232 | |
233 | static bool isDwarfSection(StringRef SectionName) { |
234 | return DwarfSectionNames.count(x: SectionName) == 1; |
235 | } |
236 | |
237 | std::unique_ptr<WritableMemoryBuffer> |
238 | ELFDebugObject::CopyBuffer(MemoryBufferRef Buffer, Error &Err) { |
239 | ErrorAsOutParameter _(&Err); |
240 | size_t Size = Buffer.getBufferSize(); |
241 | StringRef Name = Buffer.getBufferIdentifier(); |
242 | if (auto Copy = WritableMemoryBuffer::getNewUninitMemBuffer(Size, BufferName: Name)) { |
243 | memcpy(dest: Copy->getBufferStart(), src: Buffer.getBufferStart(), n: Size); |
244 | return Copy; |
245 | } |
246 | |
247 | Err = errorCodeToError(EC: make_error_code(E: errc::not_enough_memory)); |
248 | return nullptr; |
249 | } |
250 | |
251 | template <typename ELFT> |
252 | Expected<std::unique_ptr<ELFDebugObject>> |
253 | ELFDebugObject::CreateArchType(MemoryBufferRef Buffer, |
254 | JITLinkMemoryManager &MemMgr, |
255 | const JITLinkDylib *JD, ExecutionSession &ES) { |
256 | using = typename ELFT::Shdr; |
257 | |
258 | Error Err = Error::success(); |
259 | std::unique_ptr<ELFDebugObject> DebugObj( |
260 | new ELFDebugObject(CopyBuffer(Buffer, Err), MemMgr, JD, ES)); |
261 | if (Err) |
262 | return std::move(Err); |
263 | |
264 | Expected<ELFFile<ELFT>> ObjRef = ELFFile<ELFT>::create(DebugObj->getBuffer()); |
265 | if (!ObjRef) |
266 | return ObjRef.takeError(); |
267 | |
268 | Expected<ArrayRef<SectionHeader>> Sections = ObjRef->sections(); |
269 | if (!Sections) |
270 | return Sections.takeError(); |
271 | |
272 | for (const SectionHeader & : *Sections) { |
273 | Expected<StringRef> Name = ObjRef->getSectionName(Header); |
274 | if (!Name) |
275 | return Name.takeError(); |
276 | if (Name->empty()) |
277 | continue; |
278 | if (isDwarfSection(SectionName: *Name)) |
279 | DebugObj->setFlags(HasDebugSections); |
280 | |
281 | // Only record text and data sections (i.e. no bss, comments, rel, etc.) |
282 | if (Header.sh_type != ELF::SHT_PROGBITS && |
283 | Header.sh_type != ELF::SHT_X86_64_UNWIND) |
284 | continue; |
285 | if (!(Header.sh_flags & ELF::SHF_ALLOC)) |
286 | continue; |
287 | |
288 | auto Wrapped = std::make_unique<ELFDebugObjectSection<ELFT>>(&Header); |
289 | if (Error Err = DebugObj->recordSection(*Name, std::move(Wrapped))) |
290 | return std::move(Err); |
291 | } |
292 | |
293 | return std::move(DebugObj); |
294 | } |
295 | |
296 | Expected<std::unique_ptr<DebugObject>> |
297 | ELFDebugObject::Create(MemoryBufferRef Buffer, JITLinkContext &Ctx, |
298 | ExecutionSession &ES) { |
299 | unsigned char Class, Endian; |
300 | std::tie(args&: Class, args&: Endian) = getElfArchType(Object: Buffer.getBuffer()); |
301 | |
302 | if (Class == ELF::ELFCLASS32) { |
303 | if (Endian == ELF::ELFDATA2LSB) |
304 | return CreateArchType<ELF32LE>(Buffer, MemMgr&: Ctx.getMemoryManager(), |
305 | JD: Ctx.getJITLinkDylib(), ES); |
306 | if (Endian == ELF::ELFDATA2MSB) |
307 | return CreateArchType<ELF32BE>(Buffer, MemMgr&: Ctx.getMemoryManager(), |
308 | JD: Ctx.getJITLinkDylib(), ES); |
309 | return nullptr; |
310 | } |
311 | if (Class == ELF::ELFCLASS64) { |
312 | if (Endian == ELF::ELFDATA2LSB) |
313 | return CreateArchType<ELF64LE>(Buffer, MemMgr&: Ctx.getMemoryManager(), |
314 | JD: Ctx.getJITLinkDylib(), ES); |
315 | if (Endian == ELF::ELFDATA2MSB) |
316 | return CreateArchType<ELF64BE>(Buffer, MemMgr&: Ctx.getMemoryManager(), |
317 | JD: Ctx.getJITLinkDylib(), ES); |
318 | return nullptr; |
319 | } |
320 | return nullptr; |
321 | } |
322 | |
323 | Expected<SimpleSegmentAlloc> ELFDebugObject::finalizeWorkingMemory() { |
324 | LLVM_DEBUG({ |
325 | dbgs() << "Section load-addresses in debug object for \"" |
326 | << Buffer->getBufferIdentifier() << "\":\n" ; |
327 | for (const auto &KV : Sections) |
328 | KV.second->dump(dbgs(), KV.first()); |
329 | }); |
330 | |
331 | // TODO: This works, but what actual alignment requirements do we have? |
332 | unsigned PageSize = sys::Process::getPageSizeEstimate(); |
333 | size_t Size = Buffer->getBufferSize(); |
334 | |
335 | // Allocate working memory for debug object in read-only segment. |
336 | auto Alloc = SimpleSegmentAlloc::Create( |
337 | MemMgr, JD, Segments: {{MemProt::Read, {Size, Align(PageSize)}}}); |
338 | if (!Alloc) |
339 | return Alloc; |
340 | |
341 | // Initialize working memory with a copy of our object buffer. |
342 | auto SegInfo = Alloc->getSegInfo(AG: MemProt::Read); |
343 | memcpy(dest: SegInfo.WorkingMem.data(), src: Buffer->getBufferStart(), n: Size); |
344 | Buffer.reset(); |
345 | |
346 | return Alloc; |
347 | } |
348 | |
349 | void ELFDebugObject::reportSectionTargetMemoryRange(StringRef Name, |
350 | SectionRange TargetMem) { |
351 | if (auto *DebugObjSection = getSection(Name)) |
352 | DebugObjSection->setTargetMemoryRange(TargetMem); |
353 | } |
354 | |
355 | template <typename ELFT> |
356 | Error ELFDebugObject::recordSection( |
357 | StringRef Name, std::unique_ptr<ELFDebugObjectSection<ELFT>> Section) { |
358 | if (Error Err = Section->validateInBounds(this->getBuffer(), Name.data())) |
359 | return Err; |
360 | bool Inserted = Sections.try_emplace(Name, std::move(Section)).second; |
361 | if (!Inserted) |
362 | LLVM_DEBUG(dbgs() << "Skipping debug registration for section '" << Name |
363 | << "' in object " << Buffer->getBufferIdentifier() |
364 | << " (duplicate name)\n" ); |
365 | return Error::success(); |
366 | } |
367 | |
368 | DebugObjectSection *ELFDebugObject::getSection(StringRef Name) { |
369 | auto It = Sections.find(Key: Name); |
370 | return It == Sections.end() ? nullptr : It->second.get(); |
371 | } |
372 | |
373 | /// Creates a debug object based on the input object file from |
374 | /// ObjectLinkingLayerJITLinkContext. |
375 | /// |
376 | static Expected<std::unique_ptr<DebugObject>> |
377 | createDebugObjectFromBuffer(ExecutionSession &ES, LinkGraph &G, |
378 | JITLinkContext &Ctx, MemoryBufferRef ObjBuffer) { |
379 | switch (G.getTargetTriple().getObjectFormat()) { |
380 | case Triple::ELF: |
381 | return ELFDebugObject::Create(Buffer: ObjBuffer, Ctx, ES); |
382 | |
383 | default: |
384 | // TODO: Once we add support for other formats, we might want to split this |
385 | // into multiple files. |
386 | return nullptr; |
387 | } |
388 | } |
389 | |
390 | DebugObjectManagerPlugin::DebugObjectManagerPlugin( |
391 | ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target, |
392 | bool RequireDebugSections, bool AutoRegisterCode) |
393 | : ES(ES), Target(std::move(Target)), |
394 | RequireDebugSections(RequireDebugSections), |
395 | AutoRegisterCode(AutoRegisterCode) {} |
396 | |
397 | DebugObjectManagerPlugin::DebugObjectManagerPlugin( |
398 | ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target) |
399 | : DebugObjectManagerPlugin(ES, std::move(Target), true, true) {} |
400 | |
401 | DebugObjectManagerPlugin::~DebugObjectManagerPlugin() = default; |
402 | |
403 | void DebugObjectManagerPlugin::notifyMaterializing( |
404 | MaterializationResponsibility &MR, LinkGraph &G, JITLinkContext &Ctx, |
405 | MemoryBufferRef ObjBuffer) { |
406 | std::lock_guard<std::mutex> Lock(PendingObjsLock); |
407 | assert(PendingObjs.count(&MR) == 0 && |
408 | "Cannot have more than one pending debug object per " |
409 | "MaterializationResponsibility" ); |
410 | |
411 | if (auto DebugObj = createDebugObjectFromBuffer(ES, G, Ctx, ObjBuffer)) { |
412 | // Not all link artifacts allow debugging. |
413 | if (*DebugObj == nullptr) |
414 | return; |
415 | if (RequireDebugSections && !(**DebugObj).hasFlags(F: HasDebugSections)) { |
416 | LLVM_DEBUG(dbgs() << "Skipping debug registration for LinkGraph '" |
417 | << G.getName() << "': no debug info\n" ); |
418 | return; |
419 | } |
420 | PendingObjs[&MR] = std::move(*DebugObj); |
421 | } else { |
422 | ES.reportError(Err: DebugObj.takeError()); |
423 | } |
424 | } |
425 | |
426 | void DebugObjectManagerPlugin::modifyPassConfig( |
427 | MaterializationResponsibility &MR, LinkGraph &G, |
428 | PassConfiguration &PassConfig) { |
429 | // Not all link artifacts have associated debug objects. |
430 | std::lock_guard<std::mutex> Lock(PendingObjsLock); |
431 | auto It = PendingObjs.find(x: &MR); |
432 | if (It == PendingObjs.end()) |
433 | return; |
434 | |
435 | DebugObject &DebugObj = *It->second; |
436 | if (DebugObj.hasFlags(F: ReportFinalSectionLoadAddresses)) { |
437 | PassConfig.PostAllocationPasses.push_back( |
438 | x: [&DebugObj](LinkGraph &Graph) -> Error { |
439 | for (const Section &GraphSection : Graph.sections()) |
440 | DebugObj.reportSectionTargetMemoryRange(Name: GraphSection.getName(), |
441 | TargetMem: SectionRange(GraphSection)); |
442 | return Error::success(); |
443 | }); |
444 | } |
445 | } |
446 | |
447 | Error DebugObjectManagerPlugin::notifyEmitted( |
448 | MaterializationResponsibility &MR) { |
449 | std::lock_guard<std::mutex> Lock(PendingObjsLock); |
450 | auto It = PendingObjs.find(x: &MR); |
451 | if (It == PendingObjs.end()) |
452 | return Error::success(); |
453 | |
454 | // During finalization the debug object is registered with the target. |
455 | // Materialization must wait for this process to finish. Otherwise we might |
456 | // start running code before the debugger processed the corresponding debug |
457 | // info. |
458 | std::promise<MSVCPError> FinalizePromise; |
459 | std::future<MSVCPError> FinalizeErr = FinalizePromise.get_future(); |
460 | |
461 | It->second->finalizeAsync( |
462 | OnFinalize: [this, &FinalizePromise, &MR](Expected<ExecutorAddrRange> TargetMem) { |
463 | // Any failure here will fail materialization. |
464 | if (!TargetMem) { |
465 | FinalizePromise.set_value(TargetMem.takeError()); |
466 | return; |
467 | } |
468 | if (Error Err = |
469 | Target->registerDebugObject(TargetMem: *TargetMem, AutoRegisterCode)) { |
470 | FinalizePromise.set_value(std::move(Err)); |
471 | return; |
472 | } |
473 | |
474 | // Once our tracking info is updated, notifyEmitted() can return and |
475 | // finish materialization. |
476 | FinalizePromise.set_value(MR.withResourceKeyDo(F: [&](ResourceKey K) { |
477 | assert(PendingObjs.count(&MR) && "We still hold PendingObjsLock" ); |
478 | std::lock_guard<std::mutex> Lock(RegisteredObjsLock); |
479 | RegisteredObjs[K].push_back(x: std::move(PendingObjs[&MR])); |
480 | PendingObjs.erase(x: &MR); |
481 | })); |
482 | }); |
483 | |
484 | return FinalizeErr.get(); |
485 | } |
486 | |
487 | Error DebugObjectManagerPlugin::notifyFailed( |
488 | MaterializationResponsibility &MR) { |
489 | std::lock_guard<std::mutex> Lock(PendingObjsLock); |
490 | PendingObjs.erase(x: &MR); |
491 | return Error::success(); |
492 | } |
493 | |
494 | void DebugObjectManagerPlugin::notifyTransferringResources(JITDylib &JD, |
495 | ResourceKey DstKey, |
496 | ResourceKey SrcKey) { |
497 | // Debug objects are stored by ResourceKey only after registration. |
498 | // Thus, pending objects don't need to be updated here. |
499 | std::lock_guard<std::mutex> Lock(RegisteredObjsLock); |
500 | auto SrcIt = RegisteredObjs.find(x: SrcKey); |
501 | if (SrcIt != RegisteredObjs.end()) { |
502 | // Resources from distinct MaterializationResponsibilitys can get merged |
503 | // after emission, so we can have multiple debug objects per resource key. |
504 | for (std::unique_ptr<DebugObject> &DebugObj : SrcIt->second) |
505 | RegisteredObjs[DstKey].push_back(x: std::move(DebugObj)); |
506 | RegisteredObjs.erase(position: SrcIt); |
507 | } |
508 | } |
509 | |
510 | Error DebugObjectManagerPlugin::notifyRemovingResources(JITDylib &JD, |
511 | ResourceKey Key) { |
512 | // Removing the resource for a pending object fails materialization, so they |
513 | // get cleaned up in the notifyFailed() handler. |
514 | std::lock_guard<std::mutex> Lock(RegisteredObjsLock); |
515 | RegisteredObjs.erase(x: Key); |
516 | |
517 | // TODO: Implement unregister notifications. |
518 | return Error::success(); |
519 | } |
520 | |
521 | } // namespace orc |
522 | } // namespace llvm |
523 | |