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
14#include "llvm/ExecutionEngine/Orc/Debugging/ELFDebugObjectPlugin.h"
15
16#include "llvm/ADT/ArrayRef.h"
17#include "llvm/ADT/StringMap.h"
18#include "llvm/ADT/StringRef.h"
19#include "llvm/BinaryFormat/ELF.h"
20#include "llvm/ExecutionEngine/JITLink/JITLink.h"
21#include "llvm/ExecutionEngine/JITLink/JITLinkDylib.h"
22#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
23#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
24#include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h"
25#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
26#include "llvm/IR/Instructions.h"
27#include "llvm/Object/ELFObjectFile.h"
28#include "llvm/Object/Error.h"
29#include "llvm/Support/Errc.h"
30#include "llvm/Support/Error.h"
31#include "llvm/Support/MSVCErrorWorkarounds.h"
32#include "llvm/Support/MemoryBuffer.h"
33#include "llvm/Support/Process.h"
34#include "llvm/Support/raw_ostream.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
47class DebugObject {
48public:
49 using FinalizedAlloc = JITLinkMemoryManager::FinalizedAlloc;
50
51 DebugObject(StringRef Name, SimpleSegmentAlloc Alloc, JITLinkContext &Ctx,
52 ExecutionSession &ES)
53 : Name(Name), WorkingMem(std::move(Alloc)),
54 MemMgr(Ctx.getMemoryManager()), ES(ES) {}
55
56 ~DebugObject() {
57 assert(!FinalizeFuture.valid());
58 if (Alloc) {
59 std::vector<FinalizedAlloc> Allocs;
60 Allocs.push_back(x: std::move(Alloc));
61 if (Error Err = MemMgr.deallocate(Allocs: std::move(Allocs)))
62 ES.reportError(Err: std::move(Err));
63 }
64 }
65
66 MutableArrayRef<char> getBuffer() {
67 auto SegInfo = WorkingMem.getSegInfo(AG: MemProt::Read);
68 return SegInfo.WorkingMem;
69 }
70
71 SimpleSegmentAlloc collectTargetAlloc() {
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
80 Expected<ExecutorAddrRange> awaitTargetMem() {
81 assert(FinalizeFuture.valid() &&
82 "FinalizeFuture is not valid. Perhaps there is no pending target "
83 "memory transaction?");
84 return FinalizeFuture.get();
85 }
86
87 void reportTargetMem(ExecutorAddrRange TargetMem) {
88 FinalizePromise.set_value(TargetMem);
89 }
90
91 void failMaterialization(Error Err) {
92 FinalizePromise.set_value(std::move(Err));
93 }
94
95 void releasePendingResources() {
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(Err: TargetMem.takeError());
101 } else {
102 // Error before step 3: WorkingMem was not collected
103 WorkingMem.abandon(
104 OnAbandoned: [ES = &this->ES](Error Err) { ES->reportError(Err: std::move(Err)); });
105 }
106 }
107
108 using GetLoadAddressFn = llvm::unique_function<ExecutorAddr(StringRef)>;
109 Error visitSections(GetLoadAddressFn Callback);
110
111 template <typename ELFT>
112 Error visitSectionLoadAddresses(GetLoadAddressFn Callback);
113
114private:
115 std::string Name;
116 SimpleSegmentAlloc WorkingMem;
117 JITLinkMemoryManager &MemMgr;
118 ExecutionSession &ES;
119
120 std::promise<MSVCPExpected<ExecutorAddrRange>> FinalizePromise;
121 std::future<MSVCPExpected<ExecutorAddrRange>> FinalizeFuture;
122
123 FinalizedAlloc Alloc;
124};
125
126template <typename ELFT>
127Error DebugObject::visitSectionLoadAddresses(GetLoadAddressFn Callback) {
128 using SectionHeader = typename ELFT::Shdr;
129
130 MutableArrayRef<char> Buffer = getBuffer();
131 StringRef BufferRef(Buffer.data(), Buffer.size());
132 Expected<ELFFile<ELFT>> ObjRef = ELFFile<ELFT>::create(BufferRef);
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
168Error DebugObject::visitSections(GetLoadAddressFn Callback) {
169 unsigned char Class, Endian;
170 MutableArrayRef<char> Buf = getBuffer();
171 std::tie(args&: Class, args&: Endian) = getElfArchType(Object: StringRef(Buf.data(), Buf.size()));
172
173 switch (Class) {
174 case ELF::ELFCLASS32:
175 if (Endian == ELF::ELFDATA2LSB)
176 return visitSectionLoadAddresses<ELF32LE>(Callback: std::move(Callback));
177 if (Endian == ELF::ELFDATA2MSB)
178 return visitSectionLoadAddresses<ELF32BE>(Callback: std::move(Callback));
179 break;
180
181 case ELF::ELFCLASS64:
182 if (Endian == ELF::ELFDATA2LSB)
183 return visitSectionLoadAddresses<ELF64LE>(Callback: std::move(Callback));
184 if (Endian == ELF::ELFDATA2MSB)
185 return visitSectionLoadAddresses<ELF64BE>(Callback: std::move(Callback));
186 break;
187
188 default:
189 break;
190 }
191 llvm_unreachable("Checked class and endian in notifyMaterializing()");
192}
193
194ELFDebugObjectPlugin::ELFDebugObjectPlugin(ExecutionSession &ES,
195 bool RequireDebugSections,
196 bool AutoRegisterCode, Error &Err)
197 : ES(ES), RequireDebugSections(RequireDebugSections),
198 AutoRegisterCode(AutoRegisterCode) {
199 // Pass bootstrap symbol for registration function to enable debugging
200 ErrorAsOutParameter _(&Err);
201 Err = ES.getExecutorProcessControl().getBootstrapSymbols(
202 Pairs: {{RegistrationAction, rt::RegisterJITLoaderGDBAllocActionName}});
203}
204
205ELFDebugObjectPlugin::~ELFDebugObjectPlugin() = default;
206
207static const std::set<StringRef> DwarfSectionNames = {
208#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \
209 ELF_NAME,
210#include "llvm/BinaryFormat/Dwarf.def"
211#undef HANDLE_DWARF_SECTION
212};
213
214static bool isDwarfSection(StringRef SectionName) {
215 return DwarfSectionNames.count(x: SectionName) == 1;
216}
217
218void ELFDebugObjectPlugin::notifyMaterializing(
219 MaterializationResponsibility &MR, LinkGraph &G, JITLinkContext &Ctx,
220 MemoryBufferRef InputObj) {
221 if (InputObj.getBufferSize() == 0)
222 return;
223 if (G.getTargetTriple().getObjectFormat() != Triple::ELF)
224 return;
225
226 unsigned char Class, Endian;
227 std::tie(args&: Class, args&: Endian) = getElfArchType(Object: InputObj.getBuffer());
228 if (Class != ELF::ELFCLASS64 && Class != ELF::ELFCLASS32)
229 return ES.reportError(
230 Err: createStringError(EC: object_error::invalid_file_type,
231 Fmt: "Skipping debug object registration: Invalid arch "
232 "0x%02x in ELF LinkGraph %s",
233 Vals: Class, Vals: G.getName().c_str()));
234 if (Endian != ELF::ELFDATA2LSB && Endian != ELF::ELFDATA2MSB)
235 return ES.reportError(
236 Err: createStringError(EC: object_error::invalid_file_type,
237 Fmt: "Skipping debug object registration: Invalid endian "
238 "0x%02x in ELF LinkGraph %s",
239 Vals: Endian, Vals: G.getName().c_str()));
240
241 // Step 1: We copy the raw input object into the working memory of a
242 // single-segment read-only allocation
243 size_t Size = InputObj.getBufferSize();
244 auto Alignment = sys::Process::getPageSizeEstimate();
245 SimpleSegmentAlloc::Segment Segment{Size, Align(Alignment)};
246
247 auto Alloc = SimpleSegmentAlloc::Create(
248 MemMgr&: Ctx.getMemoryManager(), SSP: ES.getSymbolStringPool(), TT: ES.getTargetTriple(),
249 JD: Ctx.getJITLinkDylib(), Segments: {{MemProt::Read, Segment}});
250 if (!Alloc) {
251 ES.reportError(Err: Alloc.takeError());
252 return;
253 }
254
255 std::lock_guard<std::mutex> Lock(PendingObjsLock);
256 assert(PendingObjs.count(&MR) == 0 && "One debug object per materialization");
257 PendingObjs[&MR] = std::make_unique<DebugObject>(
258 args: InputObj.getBufferIdentifier(), args: std::move(*Alloc), args&: Ctx, args&: ES);
259
260 MutableArrayRef<char> Buffer = PendingObjs[&MR]->getBuffer();
261 memcpy(dest: Buffer.data(), src: InputObj.getBufferStart(), n: Size);
262}
263
264DebugObject *
265ELFDebugObjectPlugin::getPendingDebugObj(MaterializationResponsibility &MR) {
266 std::lock_guard<std::mutex> Lock(PendingObjsLock);
267 auto It = PendingObjs.find(x: &MR);
268 return It == PendingObjs.end() ? nullptr : It->second.get();
269}
270
271void ELFDebugObjectPlugin::modifyPassConfig(MaterializationResponsibility &MR,
272 LinkGraph &G,
273 PassConfiguration &PassConfig) {
274 if (!getPendingDebugObj(MR))
275 return;
276
277 PassConfig.PostAllocationPasses.push_back(x: [this, &MR](LinkGraph &G) -> Error {
278 size_t SectionsPatched = 0;
279 bool HasDebugSections = false;
280 DebugObject *DebugObj = getPendingDebugObj(MR);
281 assert(DebugObj && "Don't inject passes if we have no debug object");
282
283 // Step 2: Once the target memory layout is ready, we write the
284 // addresses of the LinkGraph sections into the load-address fields of the
285 // section headers in our debug object allocation
286 Error Err = DebugObj->visitSections(
287 Callback: [&G, &SectionsPatched, &HasDebugSections](StringRef Name) {
288 Section *S = G.findSectionByName(Name);
289 if (!S) {
290 // The section may have been merged into a different one during
291 // linking, ignore it.
292 return ExecutorAddr();
293 }
294
295 SectionsPatched += 1;
296 if (isDwarfSection(SectionName: Name))
297 HasDebugSections = true;
298 return SectionRange(*S).getStart();
299 });
300
301 if (Err)
302 return Err;
303 if (!SectionsPatched) {
304 LLVM_DEBUG(dbgs() << "Skipping debug registration for LinkGraph '"
305 << G.getName() << "': no debug info\n");
306 return Error::success();
307 }
308
309 if (RequireDebugSections && !HasDebugSections) {
310 LLVM_DEBUG(dbgs() << "Skipping debug registration for LinkGraph '"
311 << G.getName() << "': no debug info\n");
312 return Error::success();
313 }
314
315 // Step 3: We start copying the debug object into target memory
316 SimpleSegmentAlloc Alloc = DebugObj->collectTargetAlloc();
317
318 // FIXME: FA->getAddress() below is supposed to be the address of the memory
319 // range on the target, but InProcessMemoryManager returns the address of a
320 // FinalizedAllocInfo helper instead
321 auto ROSeg = Alloc.getSegInfo(AG: MemProt::Read);
322 ExecutorAddrRange R(ROSeg.Addr, ROSeg.WorkingMem.size());
323 Alloc.finalize(OnFinalized: [this, R, &MR](Expected<DebugObject::FinalizedAlloc> FA) {
324 // Bail out if materialization failed in the meantime
325 std::lock_guard<std::mutex> Lock(PendingObjsLock);
326 auto It = PendingObjs.find(x: &MR);
327 if (It == PendingObjs.end()) {
328 if (!FA)
329 ES.reportError(Err: FA.takeError());
330 return;
331 }
332
333 DebugObject *DebugObj = It->second.get();
334 if (!FA)
335 DebugObj->failMaterialization(Err: FA.takeError());
336
337 // Keep allocation alive until the corresponding code is removed
338 DebugObj->trackFinalizedAlloc(FA: std::move(*FA));
339
340 // Unblock post-fixup pass
341 DebugObj->reportTargetMem(TargetMem: R);
342 });
343
344 return Error::success();
345 });
346
347 PassConfig.PostFixupPasses.push_back(x: [this, &MR](LinkGraph &G) -> Error {
348 // Step 4: We wait for the debug object copy to finish, so we can
349 // register the memory range with the GDB JIT Interface in an allocation
350 // action of the LinkGraph's own allocation
351 DebugObject *DebugObj = getPendingDebugObj(MR);
352 assert(DebugObj && "Don't inject passes if we have no debug object");
353 // Post-allocation phases would bail out if there is no debug section,
354 // in which case we wouldn't collect target memory and therefore shouldn't
355 // wait for the transaction to finish.
356 if (!DebugObj->hasPendingTargetMem())
357 return Error::success();
358 Expected<ExecutorAddrRange> R = DebugObj->awaitTargetMem();
359 if (!R)
360 return R.takeError();
361
362 // Step 5: We have to keep the allocation alive until the corresponding
363 // code is removed
364 Error Err = MR.withResourceKeyDo(F: [&](ResourceKey K) {
365 std::lock_guard<std::mutex> LockPending(PendingObjsLock);
366 std::lock_guard<std::mutex> LockRegistered(RegisteredObjsLock);
367 auto It = PendingObjs.find(x: &MR);
368 RegisteredObjs[K].push_back(x: std::move(It->second));
369 PendingObjs.erase(position: It);
370 });
371
372 if (Err)
373 return Err;
374
375 if (R->empty())
376 return Error::success();
377
378 using namespace shared;
379 G.allocActions().push_back(
380 x: {.Finalize: cantFail(ValOrErr: WrapperFunctionCall::Create<
381 SPSArgList<SPSExecutorAddrRange, bool>>(
382 FnAddr: RegistrationAction, Args: *R, Args: AutoRegisterCode)),
383 .Dealloc: {/* no deregistration */}});
384 return Error::success();
385 });
386}
387
388Error ELFDebugObjectPlugin::notifyFailed(MaterializationResponsibility &MR) {
389 std::lock_guard<std::mutex> Lock(PendingObjsLock);
390 auto It = PendingObjs.find(x: &MR);
391 It->second->releasePendingResources();
392 PendingObjs.erase(position: It);
393 return Error::success();
394}
395
396void ELFDebugObjectPlugin::notifyTransferringResources(JITDylib &JD,
397 ResourceKey DstKey,
398 ResourceKey SrcKey) {
399 // Debug objects are stored by ResourceKey only after registration.
400 // Thus, pending objects don't need to be updated here.
401 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
402 auto SrcIt = RegisteredObjs.find(x: SrcKey);
403 if (SrcIt != RegisteredObjs.end()) {
404 // Resources from distinct MaterializationResponsibilitys can get merged
405 // after emission, so we can have multiple debug objects per resource key.
406 for (std::unique_ptr<DebugObject> &DebugObj : SrcIt->second)
407 RegisteredObjs[DstKey].push_back(x: std::move(DebugObj));
408 RegisteredObjs.erase(position: SrcIt);
409 }
410}
411
412Error ELFDebugObjectPlugin::notifyRemovingResources(JITDylib &JD,
413 ResourceKey Key) {
414 // Removing the resource for a pending object fails materialization, so they
415 // get cleaned up in the notifyFailed() handler.
416 std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
417 RegisteredObjs.erase(x: Key);
418
419 // TODO: Implement unregister notifications.
420 return Error::success();
421}
422
423} // namespace orc
424} // namespace llvm
425