LLVM 22.0.0git
ELFDebugObjectPlugin.cpp
Go to the documentation of this file.
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-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
15
16#include "llvm/ADT/ArrayRef.h"
17#include "llvm/ADT/StringMap.h"
18#include "llvm/ADT/StringRef.h"
28#include "llvm/Object/Error.h"
29#include "llvm/Support/Errc.h"
30#include "llvm/Support/Error.h"
35
36#include <set>
37
38#define DEBUG_TYPE "orc"
39
40using namespace llvm::jitlink;
41using namespace llvm::object;
42
43namespace llvm {
44namespace orc {
45
46// Helper class to emit and fixup an individual debug object
48public:
50
53 : Name(Name), WorkingMem(std::move(Alloc)),
54 MemMgr(Ctx.getMemoryManager()), ES(ES) {
55 FinalizeFuture = FinalizePromise.get_future();
56 }
57
59 if (Alloc) {
60 std::vector<FinalizedAlloc> Allocs;
61 Allocs.push_back(std::move(Alloc));
62 if (Error Err = MemMgr.deallocate(std::move(Allocs)))
63 ES.reportError(std::move(Err));
64 }
65 }
66
67 StringRef getName() const { return Name; }
68
71 return StringRef(Buffer.data(), Buffer.size());
72 }
73
75 auto SegInfo = WorkingMem.getSegInfo(MemProt::Read);
76 return SegInfo.WorkingMem;
77 }
78
79 SimpleSegmentAlloc &getTargetAlloc() { return WorkingMem; }
80
81 void trackFinalizedAlloc(FinalizedAlloc FA) { Alloc = std::move(FA); }
82
83 Expected<ExecutorAddrRange> awaitTargetMem() { return FinalizeFuture.get(); }
84
86 FinalizePromise.set_value(TargetMem);
87 }
88
90 FinalizePromise.set_value(std::move(Err));
91 }
92
93 void reportError(Error Err) { ES.reportError(std::move(Err)); }
94
96 void visitSections(GetLoadAddressFn Callback);
97
98 template <typename ELFT>
100
101private:
102 std::string Name;
103 SimpleSegmentAlloc WorkingMem;
104 JITLinkMemoryManager &MemMgr;
106
107 std::promise<MSVCPExpected<ExecutorAddrRange>> FinalizePromise;
108 std::future<MSVCPExpected<ExecutorAddrRange>> FinalizeFuture;
109
110 FinalizedAlloc Alloc;
111};
112
113template <typename ELFT>
115 using SectionHeader = typename ELFT::Shdr;
116
118 if (!ObjRef) {
119 reportError(ObjRef.takeError());
120 return;
121 }
122
123 Expected<ArrayRef<SectionHeader>> Sections = ObjRef->sections();
124 if (!Sections) {
125 reportError(Sections.takeError());
126 return;
127 }
128
129 for (const SectionHeader &Header : *Sections) {
130 Expected<StringRef> Name = ObjRef->getSectionName(Header);
131 if (!Name) {
132 reportError(Name.takeError());
133 return;
134 }
135 if (Name->empty())
136 continue;
137 ExecutorAddr LoadAddress = Callback(*Name);
138 const_cast<SectionHeader &>(Header).sh_addr =
139 static_cast<typename ELFT::uint>(LoadAddress.getValue());
140 }
141
142 LLVM_DEBUG({
143 dbgs() << "Section load-addresses in debug object for \"" << getName()
144 << "\":\n";
145 for (const SectionHeader &Header : *Sections) {
146 StringRef Name = cantFail(ObjRef->getSectionName(Header));
147 if (uint64_t Addr = Header.sh_addr) {
148 dbgs() << formatv(" {0:x16} {1}\n", Addr, Name);
149 } else {
150 dbgs() << formatv(" {0}\n", Name);
151 }
152 }
153 });
154}
155
157 unsigned char Class, Endian;
158 std::tie(Class, Endian) = getElfArchType(getBuffer());
159
160 switch (Class) {
161 case ELF::ELFCLASS32:
162 if (Endian == ELF::ELFDATA2LSB)
163 return visitSectionLoadAddresses<ELF32LE>(std::move(Callback));
164 if (Endian == ELF::ELFDATA2MSB)
165 return visitSectionLoadAddresses<ELF32BE>(std::move(Callback));
168 "Invalid endian in 32-bit ELF object file: %x", Endian));
169
170 case ELF::ELFCLASS64:
171 if (Endian == ELF::ELFDATA2LSB)
172 return visitSectionLoadAddresses<ELF64LE>(std::move(Callback));
173 if (Endian == ELF::ELFDATA2MSB)
174 return visitSectionLoadAddresses<ELF64BE>(std::move(Callback));
177 "Invalid endian in 64-bit ELF object file: %x", Endian));
178
179 default:
181 "Invalid arch in ELF object file: %x",
182 Class));
183 }
184}
185
187 bool RequireDebugSections,
188 bool AutoRegisterCode, Error &Err)
189 : ES(ES), RequireDebugSections(RequireDebugSections),
190 AutoRegisterCode(AutoRegisterCode) {
191 // Pass bootstrap symbol for registration function to enable debugging
193 Err = ES.getExecutorProcessControl().getBootstrapSymbols(
194 {{RegistrationAction, rt::RegisterJITLoaderGDBAllocActionName}});
195}
196
198
199static const std::set<StringRef> DwarfSectionNames = {
200#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \
201 ELF_NAME,
202#include "llvm/BinaryFormat/Dwarf.def"
203#undef HANDLE_DWARF_SECTION
204};
205
207 return DwarfSectionNames.count(SectionName) == 1;
208}
209
212 MemoryBufferRef InputObj) {
213 if (G.getTargetTriple().getObjectFormat() != Triple::ELF)
214 return;
215
216 // Step 1: We copy the raw input object into the working memory of a
217 // single-segment read-only allocation
218 size_t Size = InputObj.getBufferSize();
219 auto Alignment = sys::Process::getPageSizeEstimate();
220 SimpleSegmentAlloc::Segment Segment{Size, Align(Alignment)};
221
223 Ctx.getMemoryManager(), ES.getSymbolStringPool(), ES.getTargetTriple(),
224 Ctx.getJITLinkDylib(), {{MemProt::Read, Segment}});
225 if (!Alloc) {
226 ES.reportError(Alloc.takeError());
227 return;
228 }
229
230 std::lock_guard<std::mutex> Lock(PendingObjsLock);
231 assert(PendingObjs.count(&MR) == 0 && "One debug object per materialization");
232 PendingObjs[&MR] = std::make_unique<DebugObject>(
233 InputObj.getBufferIdentifier(), std::move(*Alloc), Ctx, ES);
234
235 MutableArrayRef<char> Buffer = PendingObjs[&MR]->getMutBuffer();
236 memcpy(Buffer.data(), InputObj.getBufferStart(), Size);
237}
238
239DebugObject *
240ELFDebugObjectPlugin::getPendingDebugObj(MaterializationResponsibility &MR) {
241 std::lock_guard<std::mutex> Lock(PendingObjsLock);
242 auto It = PendingObjs.find(&MR);
243 return It == PendingObjs.end() ? nullptr : It->second.get();
244}
245
247 LinkGraph &G,
248 PassConfiguration &PassConfig) {
249 if (!getPendingDebugObj(MR))
250 return;
251
252 PassConfig.PostAllocationPasses.push_back([this, &MR](LinkGraph &G) -> Error {
253 size_t SectionsPatched = 0;
254 bool HasDebugSections = false;
255 DebugObject *DebugObj = getPendingDebugObj(MR);
256 assert(DebugObj && "Don't inject passes if we have no debug object");
257
258 // Step 2: Once the target memory layout is ready, we write the
259 // addresses of the LinkGraph sections into the load-address fields of the
260 // section headers in our debug object allocation
261 DebugObj->visitSections(
262 [&G, &SectionsPatched, &HasDebugSections](StringRef Name) {
263 SectionsPatched += 1;
264 if (isDwarfSection(Name))
265 HasDebugSections = true;
266 Section *S = G.findSectionByName(Name);
267 assert(S && "No graph section for object section");
268 return SectionRange(*S).getStart();
269 });
270
271 if (!SectionsPatched) {
272 LLVM_DEBUG(dbgs() << "Skipping debug registration for LinkGraph '"
273 << G.getName() << "': no debug info\n");
274 return Error::success();
275 }
276
277 if (RequireDebugSections && !HasDebugSections) {
278 LLVM_DEBUG(dbgs() << "Skipping debug registration for LinkGraph '"
279 << G.getName() << "': no debug info\n");
280 return Error::success();
281 }
282
283 // Step 3: We start copying the debug object into target memory
284 auto &Alloc = DebugObj->getTargetAlloc();
285
286 // FIXME: FA->getAddress() below is supposed to be the address of the memory
287 // range on the target, but InProcessMemoryManager returns the address of a
288 // FinalizedAllocInfo helper instead
289 auto ROSeg = Alloc.getSegInfo(MemProt::Read);
290 ExecutorAddrRange R(ROSeg.Addr, ROSeg.WorkingMem.size());
291 Alloc.finalize([this, R, &MR](Expected<DebugObject::FinalizedAlloc> FA) {
292 DebugObject *DebugObj = getPendingDebugObj(MR);
293 if (!FA)
294 DebugObj->failMaterialization(FA.takeError());
295
296 // Keep allocation alive until the corresponding code is removed
297 DebugObj->trackFinalizedAlloc(std::move(*FA));
298
299 // Unblock post-fixup pass
300 DebugObj->reportTargetMem(R);
301 });
302
303 return Error::success();
304 });
305
306 PassConfig.PostFixupPasses.push_back([this, &MR](LinkGraph &G) -> Error {
307 // Step 4: We wait for the debug object copy to finish, so we can
308 // register the memory range with the GDB JIT Interface in an allocation
309 // action of the LinkGraph's own allocation
310 DebugObject *DebugObj = getPendingDebugObj(MR);
312 if (!R)
313 return R.takeError();
314
315 // Step 5: We have to keep the allocation alive until the corresponding
316 // code is removed
317 Error Err = MR.withResourceKeyDo([&](ResourceKey K) {
318 std::lock_guard<std::mutex> LockPending(PendingObjsLock);
319 std::lock_guard<std::mutex> LockRegistered(RegisteredObjsLock);
320 auto It = PendingObjs.find(&MR);
321 RegisteredObjs[K].push_back(std::move(It->second));
322 PendingObjs.erase(It);
323 });
324
325 if (Err)
326 return Err;
327
328 if (R->empty())
329 return Error::success();
330
331 using namespace shared;
332 G.allocActions().push_back(
333 {cantFail(WrapperFunctionCall::Create<
334 SPSArgList<SPSExecutorAddrRange, bool>>(
335 RegistrationAction, *R, AutoRegisterCode)),
336 {/* no deregistration */}});
337 return Error::success();
338 });
339}
340
342 std::lock_guard<std::mutex> Lock(PendingObjsLock);
343 PendingObjs.erase(&MR);
344 return Error::success();
345}
346
348 ResourceKey DstKey,
349 ResourceKey SrcKey) {
350 // Debug objects are stored by ResourceKey only after registration.
351 // Thus, pending objects don't need to be updated here.
352 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
353 auto SrcIt = RegisteredObjs.find(SrcKey);
354 if (SrcIt != RegisteredObjs.end()) {
355 // Resources from distinct MaterializationResponsibilitys can get merged
356 // after emission, so we can have multiple debug objects per resource key.
357 for (std::unique_ptr<DebugObject> &DebugObj : SrcIt->second)
358 RegisteredObjs[DstKey].push_back(std::move(DebugObj));
359 RegisteredObjs.erase(SrcIt);
360 }
361}
362
365 // Removing the resource for a pending object fails materialization, so they
366 // get cleaned up in the notifyFailed() handler.
367 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
368 RegisteredObjs.erase(Key);
369
370 // TODO: Implement unregister notifications.
371 return Error::success();
372}
373
374} // namespace orc
375} // namespace llvm
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file defines the StringMap class.
#define _
#define G(x, y, z)
Definition MD5.cpp:55
static bool isDwarfSection(const MCObjectFileInfo *FI, const MCSection *Section)
Provides a library for accessing information about this process and other processes on the operating ...
#define LLVM_DEBUG(...)
Definition Debug.h:114
size_t size() const
size - Get the array size.
Definition ArrayRef.h:142
Helper for Errors used as out-parameters.
Definition Error.h:1144
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
size_t getBufferSize() const
MutableArrayRef - Represent a mutable reference to an array (0 or more elements consecutively in memo...
Definition ArrayRef.h:298
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
static Expected< ELFFile > create(StringRef Object)
Definition ELF.h:965
void visitSections(GetLoadAddressFn Callback)
Expected< ExecutorAddrRange > awaitTargetMem()
void reportTargetMem(ExecutorAddrRange TargetMem)
SimpleSegmentAlloc & getTargetAlloc()
MutableArrayRef< char > getMutBuffer()
DebugObject(StringRef Name, SimpleSegmentAlloc Alloc, JITLinkContext &Ctx, ExecutionSession &ES)
llvm::unique_function< ExecutorAddr(StringRef)> GetLoadAddressFn
void visitSectionLoadAddresses(GetLoadAddressFn Callback)
void trackFinalizedAlloc(FinalizedAlloc FA)
JITLinkMemoryManager::FinalizedAlloc FinalizedAlloc
Error notifyFailed(MaterializationResponsibility &MR) override
void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) override
void notifyMaterializing(MaterializationResponsibility &MR, jitlink::LinkGraph &G, jitlink::JITLinkContext &Ctx, MemoryBufferRef InputObj) override
ELFDebugObjectPlugin(ExecutionSession &ES, bool RequireDebugSections, bool AutoRegisterCode, Error &Err)
Create the plugin for the given session and set additional options.
Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override
void modifyPassConfig(MaterializationResponsibility &MR, jitlink::LinkGraph &LG, jitlink::PassConfiguration &PassConfig) override
An ExecutionSession represents a running JIT program.
Definition Core.h:1342
Represents an address in the executor process.
uint64_t getValue() const
Represents a JIT'd dynamic library.
Definition Core.h:906
Tracks responsibility for materialization, and mediates interactions between MaterializationUnits and...
Definition Core.h:580
Error withResourceKeyDo(Func &&F) const
Runs the given callback under the session lock, passing in the associated ResourceKey.
Definition Core.h:599
static unsigned getPageSizeEstimate()
Get the process's estimated page size.
Definition Process.h:62
unique_function is a type-erasing functor similar to std::function.
@ ELFDATA2MSB
Definition ELF.h:341
@ ELFDATA2LSB
Definition ELF.h:340
@ ELFCLASS64
Definition ELF.h:334
@ ELFCLASS32
Definition ELF.h:333
std::pair< unsigned char, unsigned char > getElfArchType(StringRef Object)
Definition ELF.h:82
LLVM_ABI const char * RegisterJITLoaderGDBAllocActionName
static const std::set< StringRef > DwarfSectionNames
uintptr_t ResourceKey
Definition Core.h:79
static bool isDwarfSection(StringRef SectionName)
This is an optimization pass for GlobalISel generic memory operations.
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1305
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:207
LLVM_ATTRIBUTE_VISIBILITY_DEFAULT AnalysisKey InnerAnalysisManagerProxy< AnalysisManagerT, IRUnitT, ExtraArgTs... >::Key
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition Error.h:769
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1867
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:867
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
Represents an address range in the exceutor process.