Path: blob/main/contrib/llvm-project/llvm/lib/ExecutionEngine/Orc/DebugObjectManagerPlugin.cpp
35266 views
//===------- DebugObjectManagerPlugin.cpp - JITLink debug objects ---------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// FIXME: Update Plugin to poke the debug object into a new JITLink section,9// rather than creating a new allocation.10//11//===----------------------------------------------------------------------===//1213#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"1415#include "llvm/ADT/ArrayRef.h"16#include "llvm/ADT/StringMap.h"17#include "llvm/ADT/StringRef.h"18#include "llvm/BinaryFormat/ELF.h"19#include "llvm/ExecutionEngine/JITLink/JITLinkDylib.h"20#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"21#include "llvm/ExecutionEngine/JITSymbol.h"22#include "llvm/Object/ELFObjectFile.h"23#include "llvm/Object/ObjectFile.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"2930#include <set>3132#define DEBUG_TYPE "orc"3334using namespace llvm::jitlink;35using namespace llvm::object;3637namespace llvm {38namespace orc {3940class DebugObjectSection {41public:42virtual void setTargetMemoryRange(SectionRange Range) = 0;43virtual void dump(raw_ostream &OS, StringRef Name) {}44virtual ~DebugObjectSection() = default;45};4647template <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.52ELFDebugObjectSection(const typename ELFT::Shdr *Header)53: Header(const_cast<typename ELFT::Shdr *>(Header)) {}5455void setTargetMemoryRange(SectionRange Range) override;56void dump(raw_ostream &OS, StringRef Name) override;5758Error validateInBounds(StringRef Buffer, const char *Name) const;5960private:61typename ELFT::Shdr *Header;62};6364template <typename ELFT>65void ELFDebugObjectSection<ELFT>::setTargetMemoryRange(SectionRange Range) {66// All recorded sections are candidates for load-address patching.67Header->sh_addr =68static_cast<typename ELFT::uint>(Range.getStart().getValue());69}7071template <typename ELFT>72Error ELFDebugObjectSection<ELFT>::validateInBounds(StringRef Buffer,73const char *Name) const {74const uint8_t *Start = Buffer.bytes_begin();75const uint8_t *End = Buffer.bytes_end();76const uint8_t *HeaderPtr = reinterpret_cast<uint8_t *>(Header);77if (HeaderPtr < Start || HeaderPtr + sizeof(typename ELFT::Shdr) > End)78return make_error<StringError>(79formatv("{0} section header at {1:x16} not within bounds of the "80"given debug object buffer [{2:x16} - {3:x16}]",81Name, &Header->sh_addr, Start, End),82inconvertibleErrorCode());83if (Header->sh_offset + Header->sh_size > Buffer.size())84return make_error<StringError>(85formatv("{0} section data [{1:x16} - {2:x16}] not within bounds of "86"the given debug object buffer [{3:x16} - {4:x16}]",87Name, Start + Header->sh_offset,88Start + Header->sh_offset + Header->sh_size, Start, End),89inconvertibleErrorCode());90return Error::success();91}9293template <typename ELFT>94void ELFDebugObjectSection<ELFT>::dump(raw_ostream &OS, StringRef Name) {95if (uint64_t Addr = Header->sh_addr) {96OS << formatv(" {0:x16} {1}\n", Addr, Name);97} else {98OS << formatv(" {0}\n", Name);99}100}101102enum DebugObjectFlags : int {103// Request final target memory load-addresses for all sections.104ReportFinalSectionLoadAddresses = 1 << 0,105106// We found sections with debug information when processing the input object.107HasDebugSections = 1 << 1,108};109110/// 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:117DebugObject(JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD,118ExecutionSession &ES)119: MemMgr(MemMgr), JD(JD), ES(ES), Flags(DebugObjectFlags{}) {}120121bool hasFlags(DebugObjectFlags F) const { return Flags & F; }122void setFlags(DebugObjectFlags F) {123Flags = static_cast<DebugObjectFlags>(Flags | F);124}125void clearFlags(DebugObjectFlags F) {126Flags = static_cast<DebugObjectFlags>(Flags & ~F);127}128129using FinalizeContinuation = std::function<void(Expected<ExecutorAddrRange>)>;130131void finalizeAsync(FinalizeContinuation OnFinalize);132133virtual ~DebugObject() {134if (Alloc) {135std::vector<FinalizedAlloc> Allocs;136Allocs.push_back(std::move(Alloc));137if (Error Err = MemMgr.deallocate(std::move(Allocs)))138ES.reportError(std::move(Err));139}140}141142virtual void reportSectionTargetMemoryRange(StringRef Name,143SectionRange TargetMem) {}144145protected:146using InFlightAlloc = JITLinkMemoryManager::InFlightAlloc;147using FinalizedAlloc = JITLinkMemoryManager::FinalizedAlloc;148149virtual Expected<SimpleSegmentAlloc> finalizeWorkingMemory() = 0;150151JITLinkMemoryManager &MemMgr;152const JITLinkDylib *JD = nullptr;153154private:155ExecutionSession &ES;156DebugObjectFlags Flags;157FinalizedAlloc Alloc;158};159160// Finalize working memory and take ownership of the resulting allocation. Start161// copying memory over to the target and pass on the result once we're done.162// Ownership of the allocation remains with us for the rest of our lifetime.163void DebugObject::finalizeAsync(FinalizeContinuation OnFinalize) {164assert(!Alloc && "Cannot finalize more than once");165166if (auto SimpleSegAlloc = finalizeWorkingMemory()) {167auto ROSeg = SimpleSegAlloc->getSegInfo(MemProt::Read);168ExecutorAddrRange DebugObjRange(ROSeg.Addr, ROSeg.WorkingMem.size());169SimpleSegAlloc->finalize(170[this, DebugObjRange,171OnFinalize = std::move(OnFinalize)](Expected<FinalizedAlloc> FA) {172if (FA) {173Alloc = std::move(*FA);174OnFinalize(DebugObjRange);175} else176OnFinalize(FA.takeError());177});178} else179OnFinalize(SimpleSegAlloc.takeError());180}181182/// The current implementation of ELFDebugObject replicates the approach used in183/// RuntimeDyld: It patches executable and data section headers in the given184/// object buffer with load-addresses of their corresponding sections in target185/// memory.186///187class ELFDebugObject : public DebugObject {188public:189static Expected<std::unique_ptr<DebugObject>>190Create(MemoryBufferRef Buffer, JITLinkContext &Ctx, ExecutionSession &ES);191192void reportSectionTargetMemoryRange(StringRef Name,193SectionRange TargetMem) override;194195StringRef getBuffer() const { return Buffer->getMemBufferRef().getBuffer(); }196197protected:198Expected<SimpleSegmentAlloc> finalizeWorkingMemory() override;199200template <typename ELFT>201Error recordSection(StringRef Name,202std::unique_ptr<ELFDebugObjectSection<ELFT>> Section);203DebugObjectSection *getSection(StringRef Name);204205private:206template <typename ELFT>207static Expected<std::unique_ptr<ELFDebugObject>>208CreateArchType(MemoryBufferRef Buffer, JITLinkMemoryManager &MemMgr,209const JITLinkDylib *JD, ExecutionSession &ES);210211static std::unique_ptr<WritableMemoryBuffer>212CopyBuffer(MemoryBufferRef Buffer, Error &Err);213214ELFDebugObject(std::unique_ptr<WritableMemoryBuffer> Buffer,215JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD,216ExecutionSession &ES)217: DebugObject(MemMgr, JD, ES), Buffer(std::move(Buffer)) {218setFlags(ReportFinalSectionLoadAddresses);219}220221std::unique_ptr<WritableMemoryBuffer> Buffer;222StringMap<std::unique_ptr<DebugObjectSection>> Sections;223};224225static const std::set<StringRef> DwarfSectionNames = {226#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \227ELF_NAME,228#include "llvm/BinaryFormat/Dwarf.def"229#undef HANDLE_DWARF_SECTION230};231232static bool isDwarfSection(StringRef SectionName) {233return DwarfSectionNames.count(SectionName) == 1;234}235236std::unique_ptr<WritableMemoryBuffer>237ELFDebugObject::CopyBuffer(MemoryBufferRef Buffer, Error &Err) {238ErrorAsOutParameter _(&Err);239size_t Size = Buffer.getBufferSize();240StringRef Name = Buffer.getBufferIdentifier();241if (auto Copy = WritableMemoryBuffer::getNewUninitMemBuffer(Size, Name)) {242memcpy(Copy->getBufferStart(), Buffer.getBufferStart(), Size);243return Copy;244}245246Err = errorCodeToError(make_error_code(errc::not_enough_memory));247return nullptr;248}249250template <typename ELFT>251Expected<std::unique_ptr<ELFDebugObject>>252ELFDebugObject::CreateArchType(MemoryBufferRef Buffer,253JITLinkMemoryManager &MemMgr,254const JITLinkDylib *JD, ExecutionSession &ES) {255using SectionHeader = typename ELFT::Shdr;256257Error Err = Error::success();258std::unique_ptr<ELFDebugObject> DebugObj(259new ELFDebugObject(CopyBuffer(Buffer, Err), MemMgr, JD, ES));260if (Err)261return std::move(Err);262263Expected<ELFFile<ELFT>> ObjRef = ELFFile<ELFT>::create(DebugObj->getBuffer());264if (!ObjRef)265return ObjRef.takeError();266267Expected<ArrayRef<SectionHeader>> Sections = ObjRef->sections();268if (!Sections)269return Sections.takeError();270271for (const SectionHeader &Header : *Sections) {272Expected<StringRef> Name = ObjRef->getSectionName(Header);273if (!Name)274return Name.takeError();275if (Name->empty())276continue;277if (isDwarfSection(*Name))278DebugObj->setFlags(HasDebugSections);279280// Only record text and data sections (i.e. no bss, comments, rel, etc.)281if (Header.sh_type != ELF::SHT_PROGBITS &&282Header.sh_type != ELF::SHT_X86_64_UNWIND)283continue;284if (!(Header.sh_flags & ELF::SHF_ALLOC))285continue;286287auto Wrapped = std::make_unique<ELFDebugObjectSection<ELFT>>(&Header);288if (Error Err = DebugObj->recordSection(*Name, std::move(Wrapped)))289return std::move(Err);290}291292return std::move(DebugObj);293}294295Expected<std::unique_ptr<DebugObject>>296ELFDebugObject::Create(MemoryBufferRef Buffer, JITLinkContext &Ctx,297ExecutionSession &ES) {298unsigned char Class, Endian;299std::tie(Class, Endian) = getElfArchType(Buffer.getBuffer());300301if (Class == ELF::ELFCLASS32) {302if (Endian == ELF::ELFDATA2LSB)303return CreateArchType<ELF32LE>(Buffer, Ctx.getMemoryManager(),304Ctx.getJITLinkDylib(), ES);305if (Endian == ELF::ELFDATA2MSB)306return CreateArchType<ELF32BE>(Buffer, Ctx.getMemoryManager(),307Ctx.getJITLinkDylib(), ES);308return nullptr;309}310if (Class == ELF::ELFCLASS64) {311if (Endian == ELF::ELFDATA2LSB)312return CreateArchType<ELF64LE>(Buffer, Ctx.getMemoryManager(),313Ctx.getJITLinkDylib(), ES);314if (Endian == ELF::ELFDATA2MSB)315return CreateArchType<ELF64BE>(Buffer, Ctx.getMemoryManager(),316Ctx.getJITLinkDylib(), ES);317return nullptr;318}319return nullptr;320}321322Expected<SimpleSegmentAlloc> ELFDebugObject::finalizeWorkingMemory() {323LLVM_DEBUG({324dbgs() << "Section load-addresses in debug object for \""325<< Buffer->getBufferIdentifier() << "\":\n";326for (const auto &KV : Sections)327KV.second->dump(dbgs(), KV.first());328});329330// TODO: This works, but what actual alignment requirements do we have?331unsigned PageSize = sys::Process::getPageSizeEstimate();332size_t Size = Buffer->getBufferSize();333334// Allocate working memory for debug object in read-only segment.335auto Alloc = SimpleSegmentAlloc::Create(336MemMgr, JD, {{MemProt::Read, {Size, Align(PageSize)}}});337if (!Alloc)338return Alloc;339340// Initialize working memory with a copy of our object buffer.341auto SegInfo = Alloc->getSegInfo(MemProt::Read);342memcpy(SegInfo.WorkingMem.data(), Buffer->getBufferStart(), Size);343Buffer.reset();344345return Alloc;346}347348void ELFDebugObject::reportSectionTargetMemoryRange(StringRef Name,349SectionRange TargetMem) {350if (auto *DebugObjSection = getSection(Name))351DebugObjSection->setTargetMemoryRange(TargetMem);352}353354template <typename ELFT>355Error ELFDebugObject::recordSection(356StringRef Name, std::unique_ptr<ELFDebugObjectSection<ELFT>> Section) {357if (Error Err = Section->validateInBounds(this->getBuffer(), Name.data()))358return Err;359bool Inserted = Sections.try_emplace(Name, std::move(Section)).second;360if (!Inserted)361LLVM_DEBUG(dbgs() << "Skipping debug registration for section '" << Name362<< "' in object " << Buffer->getBufferIdentifier()363<< " (duplicate name)\n");364return Error::success();365}366367DebugObjectSection *ELFDebugObject::getSection(StringRef Name) {368auto It = Sections.find(Name);369return It == Sections.end() ? nullptr : It->second.get();370}371372/// Creates a debug object based on the input object file from373/// ObjectLinkingLayerJITLinkContext.374///375static Expected<std::unique_ptr<DebugObject>>376createDebugObjectFromBuffer(ExecutionSession &ES, LinkGraph &G,377JITLinkContext &Ctx, MemoryBufferRef ObjBuffer) {378switch (G.getTargetTriple().getObjectFormat()) {379case Triple::ELF:380return ELFDebugObject::Create(ObjBuffer, Ctx, ES);381382default:383// TODO: Once we add support for other formats, we might want to split this384// into multiple files.385return nullptr;386}387}388389DebugObjectManagerPlugin::DebugObjectManagerPlugin(390ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target,391bool RequireDebugSections, bool AutoRegisterCode)392: ES(ES), Target(std::move(Target)),393RequireDebugSections(RequireDebugSections),394AutoRegisterCode(AutoRegisterCode) {}395396DebugObjectManagerPlugin::DebugObjectManagerPlugin(397ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target)398: DebugObjectManagerPlugin(ES, std::move(Target), true, true) {}399400DebugObjectManagerPlugin::~DebugObjectManagerPlugin() = default;401402void DebugObjectManagerPlugin::notifyMaterializing(403MaterializationResponsibility &MR, LinkGraph &G, JITLinkContext &Ctx,404MemoryBufferRef ObjBuffer) {405std::lock_guard<std::mutex> Lock(PendingObjsLock);406assert(PendingObjs.count(&MR) == 0 &&407"Cannot have more than one pending debug object per "408"MaterializationResponsibility");409410if (auto DebugObj = createDebugObjectFromBuffer(ES, G, Ctx, ObjBuffer)) {411// Not all link artifacts allow debugging.412if (*DebugObj == nullptr)413return;414if (RequireDebugSections && !(**DebugObj).hasFlags(HasDebugSections)) {415LLVM_DEBUG(dbgs() << "Skipping debug registration for LinkGraph '"416<< G.getName() << "': no debug info\n");417return;418}419PendingObjs[&MR] = std::move(*DebugObj);420} else {421ES.reportError(DebugObj.takeError());422}423}424425void DebugObjectManagerPlugin::modifyPassConfig(426MaterializationResponsibility &MR, LinkGraph &G,427PassConfiguration &PassConfig) {428// Not all link artifacts have associated debug objects.429std::lock_guard<std::mutex> Lock(PendingObjsLock);430auto It = PendingObjs.find(&MR);431if (It == PendingObjs.end())432return;433434DebugObject &DebugObj = *It->second;435if (DebugObj.hasFlags(ReportFinalSectionLoadAddresses)) {436PassConfig.PostAllocationPasses.push_back(437[&DebugObj](LinkGraph &Graph) -> Error {438for (const Section &GraphSection : Graph.sections())439DebugObj.reportSectionTargetMemoryRange(GraphSection.getName(),440SectionRange(GraphSection));441return Error::success();442});443}444}445446Error DebugObjectManagerPlugin::notifyEmitted(447MaterializationResponsibility &MR) {448std::lock_guard<std::mutex> Lock(PendingObjsLock);449auto It = PendingObjs.find(&MR);450if (It == PendingObjs.end())451return Error::success();452453// During finalization the debug object is registered with the target.454// Materialization must wait for this process to finish. Otherwise we might455// start running code before the debugger processed the corresponding debug456// info.457std::promise<MSVCPError> FinalizePromise;458std::future<MSVCPError> FinalizeErr = FinalizePromise.get_future();459460It->second->finalizeAsync(461[this, &FinalizePromise, &MR](Expected<ExecutorAddrRange> TargetMem) {462// Any failure here will fail materialization.463if (!TargetMem) {464FinalizePromise.set_value(TargetMem.takeError());465return;466}467if (Error Err =468Target->registerDebugObject(*TargetMem, AutoRegisterCode)) {469FinalizePromise.set_value(std::move(Err));470return;471}472473// Once our tracking info is updated, notifyEmitted() can return and474// finish materialization.475FinalizePromise.set_value(MR.withResourceKeyDo([&](ResourceKey K) {476assert(PendingObjs.count(&MR) && "We still hold PendingObjsLock");477std::lock_guard<std::mutex> Lock(RegisteredObjsLock);478RegisteredObjs[K].push_back(std::move(PendingObjs[&MR]));479PendingObjs.erase(&MR);480}));481});482483return FinalizeErr.get();484}485486Error DebugObjectManagerPlugin::notifyFailed(487MaterializationResponsibility &MR) {488std::lock_guard<std::mutex> Lock(PendingObjsLock);489PendingObjs.erase(&MR);490return Error::success();491}492493void DebugObjectManagerPlugin::notifyTransferringResources(JITDylib &JD,494ResourceKey DstKey,495ResourceKey SrcKey) {496// Debug objects are stored by ResourceKey only after registration.497// Thus, pending objects don't need to be updated here.498std::lock_guard<std::mutex> Lock(RegisteredObjsLock);499auto SrcIt = RegisteredObjs.find(SrcKey);500if (SrcIt != RegisteredObjs.end()) {501// Resources from distinct MaterializationResponsibilitys can get merged502// after emission, so we can have multiple debug objects per resource key.503for (std::unique_ptr<DebugObject> &DebugObj : SrcIt->second)504RegisteredObjs[DstKey].push_back(std::move(DebugObj));505RegisteredObjs.erase(SrcIt);506}507}508509Error DebugObjectManagerPlugin::notifyRemovingResources(JITDylib &JD,510ResourceKey Key) {511// Removing the resource for a pending object fails materialization, so they512// get cleaned up in the notifyFailed() handler.513std::lock_guard<std::mutex> Lock(RegisteredObjsLock);514RegisteredObjs.erase(Key);515516// TODO: Implement unregister notifications.517return Error::success();518}519520} // namespace orc521} // namespace llvm522523524