1//===------- DebugObjectManagerPlugin.cpp - JITLink debug objects ---------===// 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 7//===----------------------------------------------------------------------===// 9// FIXME: Update Plugin to poke the debug object into a new JITLink section, 10// rather than creating a new allocation. 12//===----------------------------------------------------------------------===// 31#define DEBUG_TYPE "orc" 46template <
typename ELFT>
49// BinaryFormat ELF is not meant as a mutable format. We can only make changes 50// that don't invalidate the file structure. 52 : Header(const_cast<typename ELFT::
Shdr *>(Header)) {}
60typename ELFT::Shdr *Header;
63template <
typename ELFT>
65// All recorded sections are candidates for load-address patching. 67static_cast<typename ELFT::uint
>(
Range.getStart().getValue());
70template <
typename ELFT>
72constchar *
Name)
const{
76if (HeaderPtr < Start || HeaderPtr +
sizeof(
typename ELFT::Shdr) >
End)
77return make_error<StringError>(
78formatv(
"{0} section header at {1:x16} not within bounds of the " 79"given debug object buffer [{2:x16} - {3:x16}]",
80Name, &Header->sh_addr, Start,
End),
82if (Header->sh_offset + Header->sh_size > Buffer.
size())
83return make_error<StringError>(
84formatv(
"{0} section data [{1:x16} - {2:x16}] not within bounds of " 85"the given debug object buffer [{3:x16} - {4:x16}]",
86Name, Start + Header->sh_offset,
87 Start + Header->sh_offset + Header->sh_size, Start,
End),
92template <
typename ELFT>
102// Request final target memory load-addresses for all sections. 105// We found sections with debug information when processing the input object. 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. 134 std::vector<FinalizedAlloc> Allocs;
135 Allocs.push_back(std::move(Alloc));
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. 163assert(!Alloc &&
"Cannot finalize more than once");
168 SimpleSegAlloc->finalize(
169 [
this, DebugObjRange,
172 Alloc = std::move(*FA);
173 OnFinalize(DebugObjRange);
178 OnFinalize(SimpleSegAlloc.takeError());
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 199template <
typename ELFT>
205template <
typename ELFT>
210static std::unique_ptr<WritableMemoryBuffer>
220 std::unique_ptr<WritableMemoryBuffer> Buffer;
225#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \ 227#include "llvm/BinaryFormat/Dwarf.def" 228#undef HANDLE_DWARF_SECTION 235std::unique_ptr<WritableMemoryBuffer>
249template <
typename ELFT>
257 std::unique_ptr<ELFDebugObject> DebugObj(
258new ELFDebugObject(CopyBuffer(Buffer, Err),
MemMgr,
JD,
ES));
260return std::move(Err);
270for (
const SectionHeader &Header : *Sections) {
273returnName.takeError();
279// Only record text and data sections (i.e. no bss, comments, rel, etc.) 286auto Wrapped = std::make_unique<ELFDebugObjectSection<ELFT>>(&Header);
287if (
Error Err = DebugObj->recordSection(*
Name, std::move(Wrapped)))
288return std::move(Err);
291return std::move(DebugObj);
323dbgs() <<
"Section load-addresses in debug object for \"" 325for (
constauto &KV : Sections)
326 KV.second->dump(
dbgs(), KV.first());
329// TODO: This works, but what actual alignment requirements do we have? 333// Allocate working memory for debug object in read-only segment. 336 {{MemProt::Read, {Size, Align(PageSize)}}});
340// Initialize working memory with a copy of our object buffer. 351 DebugObjSection->setTargetMemoryRange(TargetMem);
354template <
typename ELFT>
359bool Inserted = Sections.try_emplace(
Name, std::move(
Section)).second;
363 <<
" (duplicate name)\n");
368auto It = Sections.find(
Name);
369return It == Sections.end() ? nullptr : It->second.get();
372/// Creates a debug object based on the input object file from 373/// ObjectLinkingLayerJITLinkContext. 378switch (
G.getTargetTriple().getObjectFormat()) {
380return ELFDebugObject::Create(ObjBuffer, Ctx, ES);
383// TODO: Once we add support for other formats, we might want to split this 384// into multiple files. 389DebugObjectManagerPlugin::DebugObjectManagerPlugin(
391bool RequireDebugSections,
bool AutoRegisterCode)
393 RequireDebugSections(RequireDebugSections),
394 AutoRegisterCode(AutoRegisterCode) {}
405 std::lock_guard<std::mutex> Lock(PendingObjsLock);
406assert(PendingObjs.count(&MR) == 0 &&
407"Cannot have more than one pending debug object per " 408"MaterializationResponsibility");
411// Not all link artifacts allow debugging. 412if (*DebugObj ==
nullptr)
416 <<
G.getName() <<
"': no debug info\n");
419 PendingObjs[&MR] = std::move(*DebugObj);
421 ES.reportError(DebugObj.takeError());
428// Not all link artifacts have associated debug objects. 429 std::lock_guard<std::mutex> Lock(PendingObjsLock);
430auto It = PendingObjs.find(&MR);
431if (It == PendingObjs.end())
448 std::lock_guard<std::mutex> Lock(PendingObjsLock);
449auto It = PendingObjs.find(&MR);
450if (It == PendingObjs.end())
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 457 std::promise<MSVCPError> FinalizePromise;
458 std::future<MSVCPError> FinalizeErr = FinalizePromise.get_future();
460 It->second->finalizeAsync(
462// Any failure here will fail materialization. 464 FinalizePromise.set_value(TargetMem.
takeError());
468Target->registerDebugObject(*TargetMem, AutoRegisterCode)) {
469 FinalizePromise.set_value(std::move(Err));
473// Once our tracking info is updated, notifyEmitted() can return and 474// finish materialization. 476 assert(PendingObjs.count(&MR) &&
"We still hold PendingObjsLock");
477 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
478 RegisteredObjs[K].push_back(std::move(PendingObjs[&MR]));
479 PendingObjs.erase(&MR);
483return FinalizeErr.get();
488 std::lock_guard<std::mutex> Lock(PendingObjsLock);
489 PendingObjs.erase(&MR);
496// Debug objects are stored by ResourceKey only after registration. 497// Thus, pending objects don't need to be updated here. 498 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
499auto SrcIt = RegisteredObjs.find(SrcKey);
500if (SrcIt != RegisteredObjs.end()) {
501// Resources from distinct MaterializationResponsibilitys can get merged 502// after emission, so we can have multiple debug objects per resource key. 503for (std::unique_ptr<DebugObject> &DebugObj : SrcIt->second)
504 RegisteredObjs[DstKey].push_back(std::move(DebugObj));
505 RegisteredObjs.erase(SrcIt);
511// Removing the resource for a pending object fails materialization, so they 512// get cleaned up in the notifyFailed() handler. 513 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
514 RegisteredObjs.erase(Key);
516// TODO: Implement unregister notifications. This file defines the StringMap class.
static cl::opt< int > PageSize("imp-null-check-page-size", cl::desc("The page size of the target in bytes"), cl::init(4096), cl::Hidden)
ConstantRange Range(APInt(BitWidth, Low), APInt(BitWidth, High))
Provides a library for accessing information about this process and other processes on the operating ...
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
Helper for Errors used as out-parameters.
Lightweight error class with error context and mandatory checking.
static ErrorSuccess success()
Create a success value.
Tagged union holding either a T or a Error.
Error takeError()
Take ownership of the stored error.
size_t getBufferSize() const
StringRef getBufferIdentifier() const
const char * getBufferStart() const
StringRef getBuffer() const
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
StringRef - Represent a constant reference to a string, i.e.
const unsigned char * bytes_end() const
constexpr size_t size() const
size - Get the string size.
const unsigned char * bytes_begin() const
Target - Wrapper for Target specific information.
static std::unique_ptr< WritableMemoryBuffer > getNewUninitMemBuffer(size_t Size, const Twine &BufferName="", std::optional< Align > Alignment=std::nullopt)
Allocate a new MemoryBuffer of the specified size that is not initialized.
Holds context for a single jitLink invocation.
const JITLinkDylib * getJITLinkDylib() const
Return the JITLinkDylib that this link is targeting, if any.
virtual JITLinkMemoryManager & getMemoryManager()=0
Return the MemoryManager to be used for this link.
Represents a finalized allocation.
Represents an allocation which has not been finalized yet.
Manages allocations of JIT memory.
virtual void deallocate(std::vector< FinalizedAlloc > Allocs, OnDeallocatedFunction OnDeallocated)=0
Deallocate a list of allocation objects.
iterator_range< section_iterator > sections()
Represents a section address range via a pair of Block pointers to the first and last Blocks in the s...
Represents an object file section.
static void Create(JITLinkMemoryManager &MemMgr, std::shared_ptr< orc::SymbolStringPool > SSP, Triple TT, const JITLinkDylib *JD, SegmentMap Segments, OnCreatedFunction OnCreated)
static Expected< ELFFile > create(StringRef Object)
Creates and manages DebugObjects for JITLink artifacts.
Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override
void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) override
Error notifyFailed(MaterializationResponsibility &MR) override
Error notifyEmitted(MaterializationResponsibility &MR) override
~DebugObjectManagerPlugin()
void notifyMaterializing(MaterializationResponsibility &MR, jitlink::LinkGraph &G, jitlink::JITLinkContext &Ctx, MemoryBufferRef InputObject) override
void modifyPassConfig(MaterializationResponsibility &MR, jitlink::LinkGraph &LG, jitlink::PassConfiguration &PassConfig) override
DebugObjectManagerPlugin(ExecutionSession &ES, std::unique_ptr< DebugObjectRegistrar > Target)
virtual void setTargetMemoryRange(SectionRange Range)=0
virtual void dump(raw_ostream &OS, StringRef Name)
virtual ~DebugObjectSection()=default
The plugin creates a debug object from when JITLink starts processing the corresponding LinkGraph.
JITLinkMemoryManager & MemMgr
bool hasFlags(DebugObjectFlags F) const
void finalizeAsync(FinalizeContinuation OnFinalize)
virtual Expected< SimpleSegmentAlloc > finalizeWorkingMemory()=0
std::function< void(Expected< ExecutorAddrRange >)> FinalizeContinuation
void clearFlags(DebugObjectFlags F)
virtual void reportSectionTargetMemoryRange(StringRef Name, SectionRange TargetMem)
void setFlags(DebugObjectFlags F)
DebugObject(JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD, ExecutionSession &ES)
Error validateInBounds(StringRef Buffer, const char *Name) const
void setTargetMemoryRange(SectionRange Range) override
void dump(raw_ostream &OS, StringRef Name) override
ELFDebugObjectSection(const typename ELFT::Shdr *Header)
The current implementation of ELFDebugObject replicates the approach used in RuntimeDyld: It patches ...
Error recordSection(StringRef Name, std::unique_ptr< ELFDebugObjectSection< ELFT > > Section)
DebugObjectSection * getSection(StringRef Name)
Expected< SimpleSegmentAlloc > finalizeWorkingMemory() override
void reportSectionTargetMemoryRange(StringRef Name, SectionRange TargetMem) override
StringRef getBuffer() const
static Expected< std::unique_ptr< DebugObject > > Create(MemoryBufferRef Buffer, JITLinkContext &Ctx, ExecutionSession &ES)
An ExecutionSession represents a running JIT program.
void reportError(Error Err)
Report a error for this execution session.
const Triple & getTargetTriple() const
Return the triple for the executor.
std::shared_ptr< SymbolStringPool > getSymbolStringPool()
Get the SymbolStringPool for this instance.
Represents a JIT'd dynamic library.
Tracks responsibility for materialization, and mediates interactions between MaterializationUnits and...
Error withResourceKeyDo(Func &&F) const
Runs the given callback under the session lock, passing in the associated ResourceKey.
This class implements an extremely fast bulk output stream that can only output to a stream.
static unsigned getPageSizeEstimate()
Get the process's estimated page size.
Expected< const typename ELFT::Shdr * > getSection(typename ELFT::ShdrRange Sections, uint32_t Index)
std::pair< unsigned char, unsigned char > getElfArchType(StringRef Object)
static const std::set< StringRef > DwarfSectionNames
static Expected< std::unique_ptr< DebugObject > > createDebugObjectFromBuffer(ExecutionSession &ES, LinkGraph &G, JITLinkContext &Ctx, MemoryBufferRef ObjBuffer)
Creates a debug object based on the input object file from ObjectLinkingLayerJITLinkContext.
static bool isDwarfSection(StringRef SectionName)
@ ReportFinalSectionLoadAddresses
This is an optimization pass for GlobalISel generic memory operations.
std::error_code make_error_code(BitcodeError E)
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Implement std::hash so that hash_code can be used in STL containers.
An LinkGraph pass configuration, consisting of a list of pre-prune, post-prune, and post-fixup passes...
LinkGraphPassList PostAllocationPasses
Post-allocation passes.
Represents an address range in the exceutor process.