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