LLVM 24.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/StringRef.h"
27#include "llvm/Object/Error.h"
28#include "llvm/Support/Error.h"
32
33#include <set>
34
35#define DEBUG_TYPE "orc"
36
37using namespace llvm::jitlink;
38using namespace llvm::object;
39
40namespace llvm {
41namespace orc {
42
43// Helper class to emit and fixup an individual debug object
45public:
47
50 : Name(Name), WorkingMem(std::move(Alloc)),
51 MemMgr(Ctx.getMemoryManager()), ES(ES) {}
52
54 assert(!FinalizeFuture.valid());
55 if (Alloc) {
56 std::vector<FinalizedAlloc> Allocs;
57 Allocs.push_back(std::move(Alloc));
58 if (Error Err = MemMgr.deallocate(std::move(Allocs)))
59 ES.reportError(std::move(Err));
60 }
61 }
62
64 auto SegInfo = WorkingMem.getSegInfo(MemProt::Read);
65 return SegInfo.WorkingMem;
66 }
67
69 FinalizeFuture = FinalizePromise.get_future();
70 return std::move(WorkingMem);
71 }
72
73 void trackFinalizedAlloc(FinalizedAlloc FA) { Alloc = std::move(FA); }
74
75 bool hasPendingTargetMem() const { return FinalizeFuture.valid(); }
76
78 assert(FinalizeFuture.valid() &&
79 "FinalizeFuture is not valid. Perhaps there is no pending target "
80 "memory transaction?");
81 return FinalizeFuture.get();
82 }
83
85 FinalizePromise.set_value(TargetMem);
86 }
87
89 FinalizePromise.set_value(std::move(Err));
90 }
91
93 if (FinalizeFuture.valid()) {
94 // Error before step 4: Finalization error was not reported
95 Expected<ExecutorAddrRange> TargetMem = FinalizeFuture.get();
96 if (!TargetMem)
97 ES.reportError(TargetMem.takeError());
98 } else {
99 // Error before step 3: WorkingMem was not collected
100 WorkingMem.abandon(
101 [ES = &this->ES](Error Err) { ES->reportError(std::move(Err)); });
102 }
103 }
104
107
108 template <typename ELFT>
110
111private:
112 std::string Name;
113 SimpleSegmentAlloc WorkingMem;
114 JITLinkMemoryManager &MemMgr;
116
117 std::promise<MSVCPExpected<ExecutorAddrRange>> FinalizePromise;
118 std::future<MSVCPExpected<ExecutorAddrRange>> FinalizeFuture;
119
120 FinalizedAlloc Alloc;
121};
122
123template <typename ELFT>
125 using SectionHeader = typename ELFT::Shdr;
126
128 StringRef BufferRef(Buffer.data(), Buffer.size());
130 if (!ObjRef)
131 return ObjRef.takeError();
132
133 Expected<ArrayRef<SectionHeader>> Sections = ObjRef->sections();
134 if (!Sections)
135 return Sections.takeError();
136
137 for (const SectionHeader &Header : *Sections) {
138 Expected<StringRef> Name = ObjRef->getSectionName(Header);
139 if (!Name)
140 return Name.takeError();
141 if (Name->empty())
142 continue;
143 ExecutorAddr LoadAddress = Callback(*Name);
144 if (LoadAddress)
145 const_cast<SectionHeader &>(Header).sh_addr =
146 static_cast<typename ELFT::uint>(LoadAddress.getValue());
147 }
148
149 LLVM_DEBUG({
150 dbgs() << "Section load-addresses in debug object for \"" << Name
151 << "\":\n";
152 for (const SectionHeader &Header : *Sections) {
153 StringRef Name = cantFail(ObjRef->getSectionName(Header));
154 if (uint64_t Addr = Header.sh_addr) {
155 dbgs() << formatv(" {0:x16} {1}\n", Addr, Name);
156 } else {
157 dbgs() << formatv(" {0}\n", Name);
158 }
159 }
160 });
161
162 return Error::success();
163}
164
166 unsigned char Class, Endian;
168 std::tie(Class, Endian) = getElfArchType(StringRef(Buf.data(), Buf.size()));
169
170 switch (Class) {
171 case ELF::ELFCLASS32:
172 if (Endian == ELF::ELFDATA2LSB)
173 return visitSectionLoadAddresses<ELF32LE>(std::move(Callback));
174 if (Endian == ELF::ELFDATA2MSB)
175 return visitSectionLoadAddresses<ELF32BE>(std::move(Callback));
176 break;
177
178 case ELF::ELFCLASS64:
179 if (Endian == ELF::ELFDATA2LSB)
180 return visitSectionLoadAddresses<ELF64LE>(std::move(Callback));
181 if (Endian == ELF::ELFDATA2MSB)
182 return visitSectionLoadAddresses<ELF64BE>(std::move(Callback));
183 break;
184
185 default:
186 break;
187 }
188 llvm_unreachable("Checked class and endian in notifyMaterializing()");
189}
190
192 bool RequireDebugSections,
193 Error &Err)
194 : ES(ES), RequireDebugSections(RequireDebugSections) {
195 // Pass bootstrap symbol for registration function to enable debugging
197 Err = ES.getExecutorProcessControl().getBootstrapSymbols(
198 {{RegistrationAction, rt::RegisterJITLoaderGDBAllocActionName}});
199}
200
202
203static const std::set<StringRef> DwarfSectionNames = {
204#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \
205 ELF_NAME,
206#include "llvm/BinaryFormat/Dwarf.def"
207#undef HANDLE_DWARF_SECTION
208};
209
211 return DwarfSectionNames.count(SectionName) == 1;
212}
213
216 MemoryBufferRef InputObj) {
217 if (InputObj.getBufferSize() == 0)
218 return;
219 if (G.getTargetTriple().getObjectFormat() != Triple::ELF)
220 return;
221
222 unsigned char Class, Endian;
223 std::tie(Class, Endian) = getElfArchType(InputObj.getBuffer());
224 if (Class != ELF::ELFCLASS64 && Class != ELF::ELFCLASS32)
225 return ES.reportError(
227 "Skipping debug object registration: Invalid arch "
228 "0x%02x in ELF LinkGraph %s",
229 Class, G.getName().c_str()));
230 if (Endian != ELF::ELFDATA2LSB && Endian != ELF::ELFDATA2MSB)
231 return ES.reportError(
233 "Skipping debug object registration: Invalid endian "
234 "0x%02x in ELF LinkGraph %s",
235 Endian, G.getName().c_str()));
236
237 // Step 1: We copy the raw input object into the working memory of a
238 // single-segment read-only allocation
239 size_t Size = InputObj.getBufferSize();
240 auto Alignment = sys::Process::getPageSizeEstimate();
241 SimpleSegmentAlloc::Segment Segment{Size, Align(Alignment)};
242
244 Ctx.getMemoryManager(), ES.getSymbolStringPool(), ES.getTargetTriple(),
245 Ctx.getJITLinkDylib(), {{MemProt::Read, Segment}});
246 if (!Alloc) {
247 ES.reportError(Alloc.takeError());
248 return;
249 }
250
251 std::lock_guard<std::mutex> Lock(PendingObjsLock);
252 assert(PendingObjs.count(&MR) == 0 && "One debug object per materialization");
253 PendingObjs[&MR] = std::make_unique<DebugObject>(
254 InputObj.getBufferIdentifier(), std::move(*Alloc), Ctx, ES);
255
256 MutableArrayRef<char> Buffer = PendingObjs[&MR]->getBuffer();
257 memcpy(Buffer.data(), InputObj.getBufferStart(), Size);
258}
259
260DebugObject *
261ELFDebugObjectPlugin::getPendingDebugObj(MaterializationResponsibility &MR) {
262 std::lock_guard<std::mutex> Lock(PendingObjsLock);
263 auto It = PendingObjs.find(&MR);
264 return It == PendingObjs.end() ? nullptr : It->second.get();
265}
266
268 LinkGraph &G,
269 PassConfiguration &PassConfig) {
270 if (!getPendingDebugObj(MR))
271 return;
272
273 PassConfig.PostAllocationPasses.push_back([this, &MR](LinkGraph &G) -> Error {
274 size_t SectionsPatched = 0;
275 bool HasDebugSections = false;
276 DebugObject *DebugObj = getPendingDebugObj(MR);
277 assert(DebugObj && "Don't inject passes if we have no debug object");
278
279 // Step 2: Once the target memory layout is ready, we write the
280 // addresses of the LinkGraph sections into the load-address fields of the
281 // section headers in our debug object allocation
282 Error Err = DebugObj->visitSections(
283 [&G, &SectionsPatched, &HasDebugSections](StringRef Name) {
284 Section *S = G.findSectionByName(Name);
285 if (!S) {
286 // The section may have been merged into a different one during
287 // linking, ignore it.
288 return ExecutorAddr();
289 }
290
291 SectionsPatched += 1;
292 if (isDwarfSection(Name))
293 HasDebugSections = true;
294 return SectionRange(*S).getStart();
295 });
296
297 if (Err)
298 return Err;
299 if (!SectionsPatched) {
300 LLVM_DEBUG(dbgs() << "Skipping debug registration for LinkGraph '"
301 << G.getName() << "': no debug info\n");
302 return Error::success();
303 }
304
305 if (RequireDebugSections && !HasDebugSections) {
306 LLVM_DEBUG(dbgs() << "Skipping debug registration for LinkGraph '"
307 << G.getName() << "': no debug info\n");
308 return Error::success();
309 }
310
311 // Step 3: We start copying the debug object into target memory
313
314 // FIXME: FA->getAddress() below is supposed to be the address of the memory
315 // range on the target, but InProcessMemoryManager returns the address of a
316 // FinalizedAllocInfo helper instead
317 auto ROSeg = Alloc.getSegInfo(MemProt::Read);
318 ExecutorAddrRange R(ROSeg.Addr, ROSeg.WorkingMem.size());
319 Alloc.finalize([this, R, &MR](Expected<DebugObject::FinalizedAlloc> FA) {
320 // Bail out if materialization failed in the meantime
321 std::lock_guard<std::mutex> Lock(PendingObjsLock);
322 auto It = PendingObjs.find(&MR);
323 if (It == PendingObjs.end()) {
324 if (!FA)
325 ES.reportError(FA.takeError());
326 return;
327 }
328
329 DebugObject *DebugObj = It->second.get();
330 if (!FA)
331 DebugObj->failMaterialization(FA.takeError());
332
333 // Keep allocation alive until the corresponding code is removed
334 DebugObj->trackFinalizedAlloc(std::move(*FA));
335
336 // Unblock post-fixup pass
337 DebugObj->reportTargetMem(R);
338 });
339
340 return Error::success();
341 });
342
343 PassConfig.PostFixupPasses.push_back([this, &MR](LinkGraph &G) -> Error {
344 // Step 4: We wait for the debug object copy to finish, so we can
345 // register the memory range with the GDB JIT Interface in an allocation
346 // action of the LinkGraph's own allocation
347 DebugObject *DebugObj = getPendingDebugObj(MR);
348 assert(DebugObj && "Don't inject passes if we have no debug object");
349 // Post-allocation phases would bail out if there is no debug section,
350 // in which case we wouldn't collect target memory and therefore shouldn't
351 // wait for the transaction to finish.
352 if (!DebugObj->hasPendingTargetMem())
353 return Error::success();
355 if (!R)
356 return R.takeError();
357
358 // Step 5: We have to keep the allocation alive until the corresponding
359 // code is removed
360 Error Err = MR.withResourceKeyDo([&](ResourceKey K) {
361 std::lock_guard<std::mutex> LockPending(PendingObjsLock);
362 std::lock_guard<std::mutex> LockRegistered(RegisteredObjsLock);
363 auto It = PendingObjs.find(&MR);
364 RegisteredObjs[K].push_back(std::move(It->second));
365 PendingObjs.erase(It);
366 });
367
368 if (Err)
369 return Err;
370
371 if (R->empty())
372 return Error::success();
373
374 using namespace shared;
375 G.allocActions().push_back(
376 {cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddrRange>>(
377 RegistrationAction, *R)),
378 {/* no deregistration */}});
379 return Error::success();
380 });
381}
382
384 std::lock_guard<std::mutex> Lock(PendingObjsLock);
385 auto It = PendingObjs.find(&MR);
386 It->second->releasePendingResources();
387 PendingObjs.erase(It);
388 return Error::success();
389}
390
392 ResourceKey DstKey,
393 ResourceKey SrcKey) {
394 // Debug objects are stored by ResourceKey only after registration.
395 // Thus, pending objects don't need to be updated here.
396 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
397 auto SrcIt = RegisteredObjs.find(SrcKey);
398 if (SrcIt != RegisteredObjs.end()) {
399 // Resources from distinct MaterializationResponsibilitys can get merged
400 // after emission, so we can have multiple debug objects per resource key.
401 for (std::unique_ptr<DebugObject> &DebugObj : SrcIt->second)
402 RegisteredObjs[DstKey].push_back(std::move(DebugObj));
403 RegisteredObjs.erase(SrcIt);
404 }
405}
406
409 // Removing the resource for a pending object fails materialization, so they
410 // get cleaned up in the notifyFailed() handler.
411 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
412 RegisteredObjs.erase(Key);
413
414 // TODO: Implement unregister notifications.
415 return Error::success();
416}
417
418} // namespace orc
419} // namespace llvm
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
#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:119
size_t size() const
Get the array size.
Definition ArrayRef.h:141
Helper for Errors used as out-parameters.
Definition Error.h:1160
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
reference get()
Returns a reference to the stored T value.
Definition Error.h:582
size_t getBufferSize() const
StringRef getBuffer() const
Represent a mutable reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:294
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
static Expected< ELFFile > create(StringRef Object)
Definition ELF.h:1000
MutableArrayRef< char > getBuffer()
Error visitSectionLoadAddresses(GetLoadAddressFn Callback)
Expected< ExecutorAddrRange > awaitTargetMem()
void reportTargetMem(ExecutorAddrRange TargetMem)
SimpleSegmentAlloc collectTargetAlloc()
DebugObject(StringRef Name, SimpleSegmentAlloc Alloc, JITLinkContext &Ctx, ExecutionSession &ES)
llvm::unique_function< ExecutorAddr(StringRef)> GetLoadAddressFn
Error visitSections(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
Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override
void modifyPassConfig(MaterializationResponsibility &MR, jitlink::LinkGraph &LG, jitlink::PassConfiguration &PassConfig) override
ELFDebugObjectPlugin(ExecutionSession &ES, bool RequireDebugSections, Error &Err)
Create the plugin for the given session and set additional options.
An ExecutionSession represents a running JIT program.
Definition Core.h:1111
Represents an address in the executor process.
uint64_t getValue() const
Represents a JIT'd dynamic library.
Definition Core.h:675
Tracks responsibility for materialization, and mediates interactions between MaterializationUnits and...
Definition Core.h:349
Error withResourceKeyDo(Func &&F) const
Runs the given callback under the session lock, passing in the associated ResourceKey.
Definition Core.h:368
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.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ 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:60
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:1321
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:209
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:1917
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:878
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.