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