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/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
57 assert(!FinalizeFuture.valid());
58 if (Alloc) {
59 std::vector<FinalizedAlloc> Allocs;
60 Allocs.push_back(std::move(Alloc));
61 if (Error Err = MemMgr.deallocate(std::move(Allocs)))
62 ES.reportError(std::move(Err));
63 }
64 }
65
67 auto SegInfo = WorkingMem.getSegInfo(MemProt::Read);
68 return SegInfo.WorkingMem;
69 }
70
72 FinalizeFuture = FinalizePromise.get_future();
73 return std::move(WorkingMem);
74 }
75
76 void trackFinalizedAlloc(FinalizedAlloc FA) { Alloc = std::move(FA); }
77
78 bool hasPendingTargetMem() const { return FinalizeFuture.valid(); }
79
81 assert(FinalizeFuture.valid() &&
82 "FinalizeFuture is not valid. Perhaps there is no pending target "
83 "memory transaction?");
84 return FinalizeFuture.get();
85 }
86
88 FinalizePromise.set_value(TargetMem);
89 }
90
92 FinalizePromise.set_value(std::move(Err));
93 }
94
96 if (FinalizeFuture.valid()) {
97 // Error before step 4: Finalization error was not reported
98 Expected<ExecutorAddrRange> TargetMem = FinalizeFuture.get();
99 if (!TargetMem)
100 ES.reportError(TargetMem.takeError());
101 } else {
102 // Error before step 3: WorkingMem was not collected
103 WorkingMem.abandon(
104 [ES = &this->ES](Error Err) { ES->reportError(std::move(Err)); });
105 }
106 }
107
110
111 template <typename ELFT>
113
114private:
115 std::string Name;
116 SimpleSegmentAlloc WorkingMem;
117 JITLinkMemoryManager &MemMgr;
119
120 std::promise<MSVCPExpected<ExecutorAddrRange>> FinalizePromise;
121 std::future<MSVCPExpected<ExecutorAddrRange>> FinalizeFuture;
122
123 FinalizedAlloc Alloc;
124};
125
126template <typename ELFT>
128 using SectionHeader = typename ELFT::Shdr;
129
131 StringRef BufferRef(Buffer.data(), Buffer.size());
133 if (!ObjRef)
134 return ObjRef.takeError();
135
136 Expected<ArrayRef<SectionHeader>> Sections = ObjRef->sections();
137 if (!Sections)
138 return Sections.takeError();
139
140 for (const SectionHeader &Header : *Sections) {
141 Expected<StringRef> Name = ObjRef->getSectionName(Header);
142 if (!Name)
143 return Name.takeError();
144 if (Name->empty())
145 continue;
146 ExecutorAddr LoadAddress = Callback(*Name);
147 if (LoadAddress)
148 const_cast<SectionHeader &>(Header).sh_addr =
149 static_cast<typename ELFT::uint>(LoadAddress.getValue());
150 }
151
152 LLVM_DEBUG({
153 dbgs() << "Section load-addresses in debug object for \"" << Name
154 << "\":\n";
155 for (const SectionHeader &Header : *Sections) {
156 StringRef Name = cantFail(ObjRef->getSectionName(Header));
157 if (uint64_t Addr = Header.sh_addr) {
158 dbgs() << formatv(" {0:x16} {1}\n", Addr, Name);
159 } else {
160 dbgs() << formatv(" {0}\n", Name);
161 }
162 }
163 });
164
165 return Error::success();
166}
167
169 unsigned char Class, Endian;
171 std::tie(Class, Endian) = getElfArchType(StringRef(Buf.data(), Buf.size()));
172
173 switch (Class) {
174 case ELF::ELFCLASS32:
175 if (Endian == ELF::ELFDATA2LSB)
176 return visitSectionLoadAddresses<ELF32LE>(std::move(Callback));
177 if (Endian == ELF::ELFDATA2MSB)
178 return visitSectionLoadAddresses<ELF32BE>(std::move(Callback));
179 break;
180
181 case ELF::ELFCLASS64:
182 if (Endian == ELF::ELFDATA2LSB)
183 return visitSectionLoadAddresses<ELF64LE>(std::move(Callback));
184 if (Endian == ELF::ELFDATA2MSB)
185 return visitSectionLoadAddresses<ELF64BE>(std::move(Callback));
186 break;
187
188 default:
189 break;
190 }
191 llvm_unreachable("Checked class and endian in notifyMaterializing()");
192}
193
195 bool RequireDebugSections,
196 Error &Err)
197 : ES(ES), RequireDebugSections(RequireDebugSections) {
198 // Pass bootstrap symbol for registration function to enable debugging
200 Err = ES.getExecutorProcessControl().getBootstrapSymbols(
201 {{RegistrationAction, rt::RegisterJITLoaderGDBAllocActionName}});
202}
203
205
206static const std::set<StringRef> DwarfSectionNames = {
207#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \
208 ELF_NAME,
209#include "llvm/BinaryFormat/Dwarf.def"
210#undef HANDLE_DWARF_SECTION
211};
212
214 return DwarfSectionNames.count(SectionName) == 1;
215}
216
219 MemoryBufferRef InputObj) {
220 if (InputObj.getBufferSize() == 0)
221 return;
222 if (G.getTargetTriple().getObjectFormat() != Triple::ELF)
223 return;
224
225 unsigned char Class, Endian;
226 std::tie(Class, Endian) = getElfArchType(InputObj.getBuffer());
227 if (Class != ELF::ELFCLASS64 && Class != ELF::ELFCLASS32)
228 return ES.reportError(
230 "Skipping debug object registration: Invalid arch "
231 "0x%02x in ELF LinkGraph %s",
232 Class, G.getName().c_str()));
233 if (Endian != ELF::ELFDATA2LSB && Endian != ELF::ELFDATA2MSB)
234 return ES.reportError(
236 "Skipping debug object registration: Invalid endian "
237 "0x%02x in ELF LinkGraph %s",
238 Endian, G.getName().c_str()));
239
240 // Step 1: We copy the raw input object into the working memory of a
241 // single-segment read-only allocation
242 size_t Size = InputObj.getBufferSize();
243 auto Alignment = sys::Process::getPageSizeEstimate();
244 SimpleSegmentAlloc::Segment Segment{Size, Align(Alignment)};
245
247 Ctx.getMemoryManager(), ES.getSymbolStringPool(), ES.getTargetTriple(),
248 Ctx.getJITLinkDylib(), {{MemProt::Read, Segment}});
249 if (!Alloc) {
250 ES.reportError(Alloc.takeError());
251 return;
252 }
253
254 std::lock_guard<std::mutex> Lock(PendingObjsLock);
255 assert(PendingObjs.count(&MR) == 0 && "One debug object per materialization");
256 PendingObjs[&MR] = std::make_unique<DebugObject>(
257 InputObj.getBufferIdentifier(), std::move(*Alloc), Ctx, ES);
258
259 MutableArrayRef<char> Buffer = PendingObjs[&MR]->getBuffer();
260 memcpy(Buffer.data(), InputObj.getBufferStart(), Size);
261}
262
263DebugObject *
264ELFDebugObjectPlugin::getPendingDebugObj(MaterializationResponsibility &MR) {
265 std::lock_guard<std::mutex> Lock(PendingObjsLock);
266 auto It = PendingObjs.find(&MR);
267 return It == PendingObjs.end() ? nullptr : It->second.get();
268}
269
271 LinkGraph &G,
272 PassConfiguration &PassConfig) {
273 if (!getPendingDebugObj(MR))
274 return;
275
276 PassConfig.PostAllocationPasses.push_back([this, &MR](LinkGraph &G) -> Error {
277 size_t SectionsPatched = 0;
278 bool HasDebugSections = false;
279 DebugObject *DebugObj = getPendingDebugObj(MR);
280 assert(DebugObj && "Don't inject passes if we have no debug object");
281
282 // Step 2: Once the target memory layout is ready, we write the
283 // addresses of the LinkGraph sections into the load-address fields of the
284 // section headers in our debug object allocation
285 Error Err = DebugObj->visitSections(
286 [&G, &SectionsPatched, &HasDebugSections](StringRef Name) {
287 Section *S = G.findSectionByName(Name);
288 if (!S) {
289 // The section may have been merged into a different one during
290 // linking, ignore it.
291 return ExecutorAddr();
292 }
293
294 SectionsPatched += 1;
295 if (isDwarfSection(Name))
296 HasDebugSections = true;
297 return SectionRange(*S).getStart();
298 });
299
300 if (Err)
301 return Err;
302 if (!SectionsPatched) {
303 LLVM_DEBUG(dbgs() << "Skipping debug registration for LinkGraph '"
304 << G.getName() << "': no debug info\n");
305 return Error::success();
306 }
307
308 if (RequireDebugSections && !HasDebugSections) {
309 LLVM_DEBUG(dbgs() << "Skipping debug registration for LinkGraph '"
310 << G.getName() << "': no debug info\n");
311 return Error::success();
312 }
313
314 // Step 3: We start copying the debug object into target memory
316
317 // FIXME: FA->getAddress() below is supposed to be the address of the memory
318 // range on the target, but InProcessMemoryManager returns the address of a
319 // FinalizedAllocInfo helper instead
320 auto ROSeg = Alloc.getSegInfo(MemProt::Read);
321 ExecutorAddrRange R(ROSeg.Addr, ROSeg.WorkingMem.size());
322 Alloc.finalize([this, R, &MR](Expected<DebugObject::FinalizedAlloc> FA) {
323 // Bail out if materialization failed in the meantime
324 std::lock_guard<std::mutex> Lock(PendingObjsLock);
325 auto It = PendingObjs.find(&MR);
326 if (It == PendingObjs.end()) {
327 if (!FA)
328 ES.reportError(FA.takeError());
329 return;
330 }
331
332 DebugObject *DebugObj = It->second.get();
333 if (!FA)
334 DebugObj->failMaterialization(FA.takeError());
335
336 // Keep allocation alive until the corresponding code is removed
337 DebugObj->trackFinalizedAlloc(std::move(*FA));
338
339 // Unblock post-fixup pass
340 DebugObj->reportTargetMem(R);
341 });
342
343 return Error::success();
344 });
345
346 PassConfig.PostFixupPasses.push_back([this, &MR](LinkGraph &G) -> Error {
347 // Step 4: We wait for the debug object copy to finish, so we can
348 // register the memory range with the GDB JIT Interface in an allocation
349 // action of the LinkGraph's own allocation
350 DebugObject *DebugObj = getPendingDebugObj(MR);
351 assert(DebugObj && "Don't inject passes if we have no debug object");
352 // Post-allocation phases would bail out if there is no debug section,
353 // in which case we wouldn't collect target memory and therefore shouldn't
354 // wait for the transaction to finish.
355 if (!DebugObj->hasPendingTargetMem())
356 return Error::success();
358 if (!R)
359 return R.takeError();
360
361 // Step 5: We have to keep the allocation alive until the corresponding
362 // code is removed
363 Error Err = MR.withResourceKeyDo([&](ResourceKey K) {
364 std::lock_guard<std::mutex> LockPending(PendingObjsLock);
365 std::lock_guard<std::mutex> LockRegistered(RegisteredObjsLock);
366 auto It = PendingObjs.find(&MR);
367 RegisteredObjs[K].push_back(std::move(It->second));
368 PendingObjs.erase(It);
369 });
370
371 if (Err)
372 return Err;
373
374 if (R->empty())
375 return Error::success();
376
377 using namespace shared;
378 G.allocActions().push_back(
379 {cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddrRange>>(
380 RegistrationAction, *R)),
381 {/* no deregistration */}});
382 return Error::success();
383 });
384}
385
387 std::lock_guard<std::mutex> Lock(PendingObjsLock);
388 auto It = PendingObjs.find(&MR);
389 It->second->releasePendingResources();
390 PendingObjs.erase(It);
391 return Error::success();
392}
393
395 ResourceKey DstKey,
396 ResourceKey SrcKey) {
397 // Debug objects are stored by ResourceKey only after registration.
398 // Thus, pending objects don't need to be updated here.
399 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
400 auto SrcIt = RegisteredObjs.find(SrcKey);
401 if (SrcIt != RegisteredObjs.end()) {
402 // Resources from distinct MaterializationResponsibilitys can get merged
403 // after emission, so we can have multiple debug objects per resource key.
404 for (std::unique_ptr<DebugObject> &DebugObj : SrcIt->second)
405 RegisteredObjs[DstKey].push_back(std::move(DebugObj));
406 RegisteredObjs.erase(SrcIt);
407 }
408}
409
412 // Removing the resource for a pending object fails materialization, so they
413 // get cleaned up in the notifyFailed() handler.
414 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
415 RegisteredObjs.erase(Key);
416
417 // TODO: Implement unregister notifications.
418 return Error::success();
419}
420
421} // namespace orc
422} // 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: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.