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