1//===------- DebuggerSupportPlugin.cpp - Utils for debugger support -------===//
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//
10//===----------------------------------------------------------------------===//
11
12#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupportPlugin.h"
13#include "llvm/ExecutionEngine/Orc/MachOBuilder.h"
14
15#include "llvm/ADT/SmallSet.h"
16#include "llvm/ADT/SmallVector.h"
17#include "llvm/BinaryFormat/MachO.h"
18#include "llvm/DebugInfo/DWARF/DWARFContext.h"
19#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
20
21#include <chrono>
22
23#define DEBUG_TYPE "orc"
24
25using namespace llvm;
26using namespace llvm::jitlink;
27using namespace llvm::orc;
28
29static const char *SynthDebugSectionName = "__jitlink_synth_debug_object";
30
31namespace {
32
33class MachODebugObjectSynthesizerBase
34 : public GDBJITDebugInfoRegistrationPlugin::DebugSectionSynthesizer {
35public:
36 static bool isDebugSection(Section &Sec) {
37 return Sec.getName().starts_with(Prefix: "__DWARF,");
38 }
39
40 MachODebugObjectSynthesizerBase(LinkGraph &G, ExecutorAddr RegisterActionAddr)
41 : G(G), RegisterActionAddr(RegisterActionAddr) {}
42 virtual ~MachODebugObjectSynthesizerBase() = default;
43
44 Error preserveDebugSections() {
45 if (G.findSectionByName(Name: SynthDebugSectionName)) {
46 LLVM_DEBUG({
47 dbgs() << "MachODebugObjectSynthesizer skipping graph " << G.getName()
48 << " which contains an unexpected existing "
49 << SynthDebugSectionName << " section.\n";
50 });
51 return Error::success();
52 }
53
54 LLVM_DEBUG({
55 dbgs() << "MachODebugObjectSynthesizer visiting graph " << G.getName()
56 << "\n";
57 });
58 for (auto &Sec : G.sections()) {
59 if (!isDebugSection(Sec))
60 continue;
61 // Preserve blocks in this debug section by marking one existing symbol
62 // live for each block, and introducing a new live, anonymous symbol for
63 // each currently unreferenced block.
64 LLVM_DEBUG({
65 dbgs() << " Preserving debug section " << Sec.getName() << "\n";
66 });
67 SmallSet<Block *, 8> PreservedBlocks;
68 for (auto *Sym : Sec.symbols()) {
69 bool NewPreservedBlock =
70 PreservedBlocks.insert(Ptr: &Sym->getBlock()).second;
71 if (NewPreservedBlock)
72 Sym->setLive(true);
73 }
74 for (auto *B : Sec.blocks())
75 if (!PreservedBlocks.count(Ptr: B))
76 G.addAnonymousSymbol(Content&: *B, Offset: 0, Size: 0, IsCallable: false, IsLive: true);
77 }
78
79 return Error::success();
80 }
81
82protected:
83 LinkGraph &G;
84 ExecutorAddr RegisterActionAddr;
85};
86
87template <typename MachOTraits>
88class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase {
89public:
90 MachODebugObjectSynthesizer(ExecutionSession &ES, LinkGraph &G,
91 ExecutorAddr RegisterActionAddr)
92 : MachODebugObjectSynthesizerBase(G, RegisterActionAddr),
93 Builder(ES.getPageSize()) {}
94
95 using MachODebugObjectSynthesizerBase::MachODebugObjectSynthesizerBase;
96
97 Error startSynthesis() override {
98 LLVM_DEBUG({
99 dbgs() << "Creating " << SynthDebugSectionName << " for " << G.getName()
100 << "\n";
101 });
102
103 for (auto &Sec : G.sections()) {
104 if (Sec.blocks().empty())
105 continue;
106
107 // Skip sections whose name's don't fit the MachO standard.
108 if (Sec.getName().empty() || Sec.getName().size() > 33 ||
109 Sec.getName().find(',') > 16)
110 continue;
111
112 if (isDebugSection(Sec))
113 DebugSections.push_back({&Sec, nullptr});
114 else if (Sec.getMemLifetime() != MemLifetime::NoAlloc)
115 NonDebugSections.push_back({&Sec, nullptr});
116 }
117
118 // Bail out early if no debug sections.
119 if (DebugSections.empty())
120 return Error::success();
121
122 // Write MachO header and debug section load commands.
123 Builder.Header.filetype = MachO::MH_OBJECT;
124 switch (G.getTargetTriple().getArch()) {
125 case Triple::x86_64:
126 Builder.Header.cputype = MachO::CPU_TYPE_X86_64;
127 Builder.Header.cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL;
128 break;
129 case Triple::aarch64:
130 Builder.Header.cputype = MachO::CPU_TYPE_ARM64;
131 Builder.Header.cpusubtype = MachO::CPU_SUBTYPE_ARM64_ALL;
132 break;
133 default:
134 llvm_unreachable("Unsupported architecture");
135 }
136
137 Seg = &Builder.addSegment("");
138
139 StringMap<std::unique_ptr<MemoryBuffer>> DebugSectionMap;
140 StringRef DebugLineSectionData;
141 for (auto &DSec : DebugSections) {
142 auto [SegName, SecName] = DSec.GraphSec->getName().split(',');
143 DSec.BuilderSec = &Seg->addSection(SecName, SegName);
144
145 SectionRange SR(*DSec.GraphSec);
146 DSec.BuilderSec->Content.Size = SR.getSize();
147 if (!SR.empty()) {
148 DSec.BuilderSec->align = Log2_64(Value: SR.getFirstBlock()->getAlignment());
149 StringRef SectionData(SR.getFirstBlock()->getContent().data(),
150 SR.getFirstBlock()->getSize());
151 DebugSectionMap[SecName] =
152 MemoryBuffer::getMemBuffer(SectionData, G.getName(), false);
153 if (SecName == "__debug_line")
154 DebugLineSectionData = SectionData;
155 }
156 }
157
158 std::optional<StringRef> FileName;
159 if (!DebugLineSectionData.empty()) {
160 assert((G.getEndianness() == llvm::endianness::big ||
161 G.getEndianness() == llvm::endianness::little) &&
162 "G.getEndianness() must be either big or little");
163 auto DWARFCtx =
164 DWARFContext::create(DebugSectionMap, G.getPointerSize(),
165 G.getEndianness() == llvm::endianness::little);
166 DWARFDataExtractor DebugLineData(
167 DebugLineSectionData, G.getEndianness() == llvm::endianness::little,
168 G.getPointerSize());
169 uint64_t Offset = 0;
170 DWARFDebugLine::LineTable LineTable;
171
172 // Try to parse line data. Consume error on failure.
173 if (auto Err = LineTable.parse(DebugLineData, OffsetPtr: &Offset, Ctx: *DWARFCtx, U: nullptr,
174 RecoverableErrorHandler: consumeError)) {
175 handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
176 LLVM_DEBUG({
177 dbgs() << "Cannot parse line table for \"" << G.getName() << "\": ";
178 EIB.log(dbgs());
179 dbgs() << "\n";
180 });
181 });
182 } else {
183 if (!LineTable.Prologue.FileNames.empty())
184 FileName = *dwarf::toString(V: LineTable.Prologue.FileNames[0].Name);
185 }
186 }
187
188 // If no line table (or unable to use) then use graph name.
189 // FIXME: There are probably other debug sections we should look in first.
190 if (!FileName)
191 FileName = StringRef(G.getName());
192
193 Builder.addSymbol("", MachO::N_SO, 0, 0, 0);
194 Builder.addSymbol(*FileName, MachO::N_SO, 0, 0, 0);
195 auto TimeStamp = std::chrono::duration_cast<std::chrono::seconds>(
196 d: std::chrono::system_clock::now().time_since_epoch())
197 .count();
198 Builder.addSymbol("", MachO::N_OSO, 3, 1, TimeStamp);
199
200 for (auto &NDSP : NonDebugSections) {
201 auto [SegName, SecName] = NDSP.GraphSec->getName().split(',');
202 NDSP.BuilderSec = &Seg->addSection(SecName, SegName);
203 SectionRange SR(*NDSP.GraphSec);
204 if (!SR.empty())
205 NDSP.BuilderSec->align = Log2_64(Value: SR.getFirstBlock()->getAlignment());
206
207 // Add stabs.
208 for (auto *Sym : NDSP.GraphSec->symbols()) {
209 // Skip anonymous symbols.
210 if (!Sym->hasName())
211 continue;
212
213 uint8_t SymType = Sym->isCallable() ? MachO::N_FUN : MachO::N_GSYM;
214
215 Builder.addSymbol("", MachO::N_BNSYM, 1, 0, 0);
216 StabSymbols.push_back(
217 {*Sym, Builder.addSymbol(Sym->getName(), SymType, 1, 0, 0),
218 Builder.addSymbol(Sym->getName(), SymType, 0, 0, 0)});
219 Builder.addSymbol("", MachO::N_ENSYM, 1, 0, 0);
220 }
221 }
222
223 Builder.addSymbol("", MachO::N_SO, 1, 0, 0);
224
225 // Lay out the debug object, create a section and block for it.
226 size_t DebugObjectSize = Builder.layout();
227
228 auto &SDOSec = G.createSection(Name: SynthDebugSectionName, Prot: MemProt::Read);
229 MachOContainerBlock = &G.createMutableContentBlock(
230 SDOSec, G.allocateBuffer(Size: DebugObjectSize), orc::ExecutorAddr(), 8, 0);
231
232 return Error::success();
233 }
234
235 Error completeSynthesisAndRegister() override {
236 if (!MachOContainerBlock) {
237 LLVM_DEBUG({
238 dbgs() << "Not writing MachO debug object header for " << G.getName()
239 << " since createDebugSection failed\n";
240 });
241
242 return Error::success();
243 }
244 ExecutorAddr MaxAddr;
245 for (auto &NDSec : NonDebugSections) {
246 SectionRange SR(*NDSec.GraphSec);
247 NDSec.BuilderSec->addr = SR.getStart().getValue();
248 NDSec.BuilderSec->size = SR.getSize();
249 NDSec.BuilderSec->offset = SR.getStart().getValue();
250 if (SR.getEnd() > MaxAddr)
251 MaxAddr = SR.getEnd();
252 }
253
254 for (auto &DSec : DebugSections) {
255 if (DSec.GraphSec->blocks_size() != 1)
256 return make_error<StringError>(
257 Args: "Unexpected number of blocks in debug info section",
258 Args: inconvertibleErrorCode());
259
260 if (ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size > MaxAddr)
261 MaxAddr = ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size;
262
263 auto &B = **DSec.GraphSec->blocks().begin();
264 DSec.BuilderSec->Content.Data = B.getContent().data();
265 DSec.BuilderSec->Content.Size = B.getContent().size();
266 DSec.BuilderSec->flags |= MachO::S_ATTR_DEBUG;
267 }
268
269 LLVM_DEBUG({
270 dbgs() << "Writing MachO debug object header for " << G.getName() << "\n";
271 });
272
273 // Update stab symbol addresses.
274 for (auto &SS : StabSymbols) {
275 SS.StartStab.nlist().n_value = SS.Sym.getAddress().getValue();
276 SS.EndStab.nlist().n_value = SS.Sym.getSize();
277 }
278
279 Builder.write(MachOContainerBlock->getAlreadyMutableContent());
280
281 static constexpr bool AutoRegisterCode = true;
282 SectionRange R(MachOContainerBlock->getSection());
283 G.allocActions().push_back(
284 {cantFail(shared::WrapperFunctionCall::Create<
285 shared::SPSArgList<shared::SPSExecutorAddrRange, bool>>(
286 RegisterActionAddr, R.getRange(), AutoRegisterCode)),
287 {}});
288
289 return Error::success();
290 }
291
292private:
293 struct SectionPair {
294 Section *GraphSec = nullptr;
295 typename MachOBuilder<MachOTraits>::Section *BuilderSec = nullptr;
296 };
297
298 struct StabSymbolsEntry {
299 using RelocTarget = typename MachOBuilder<MachOTraits>::RelocTarget;
300
301 StabSymbolsEntry(Symbol &Sym, RelocTarget StartStab, RelocTarget EndStab)
302 : Sym(Sym), StartStab(StartStab), EndStab(EndStab) {}
303
304 Symbol &Sym;
305 RelocTarget StartStab, EndStab;
306 };
307
308 using BuilderType = MachOBuilder<MachOTraits>;
309
310 Block *MachOContainerBlock = nullptr;
311 MachOBuilder<MachOTraits> Builder;
312 typename MachOBuilder<MachOTraits>::Segment *Seg = nullptr;
313 std::vector<StabSymbolsEntry> StabSymbols;
314 SmallVector<SectionPair, 16> DebugSections;
315 SmallVector<SectionPair, 16> NonDebugSections;
316};
317
318} // end anonymous namespace
319
320namespace llvm {
321namespace orc {
322
323Expected<std::unique_ptr<GDBJITDebugInfoRegistrationPlugin>>
324GDBJITDebugInfoRegistrationPlugin::Create(ExecutionSession &ES,
325 JITDylib &ProcessJD,
326 const Triple &TT) {
327 auto RegisterActionAddr =
328 TT.isOSBinFormatMachO()
329 ? ES.intern(SymName: "_llvm_orc_registerJITLoaderGDBAllocAction")
330 : ES.intern(SymName: "llvm_orc_registerJITLoaderGDBAllocAction");
331
332 if (auto RegisterSym = ES.lookup(SearchOrder: {&ProcessJD}, Symbol: RegisterActionAddr))
333 return std::make_unique<GDBJITDebugInfoRegistrationPlugin>(
334 args: RegisterSym->getAddress());
335 else
336 return RegisterSym.takeError();
337}
338
339Error GDBJITDebugInfoRegistrationPlugin::notifyFailed(
340 MaterializationResponsibility &MR) {
341 return Error::success();
342}
343
344Error GDBJITDebugInfoRegistrationPlugin::notifyRemovingResources(
345 JITDylib &JD, ResourceKey K) {
346 return Error::success();
347}
348
349void GDBJITDebugInfoRegistrationPlugin::notifyTransferringResources(
350 JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) {}
351
352void GDBJITDebugInfoRegistrationPlugin::modifyPassConfig(
353 MaterializationResponsibility &MR, LinkGraph &LG,
354 PassConfiguration &PassConfig) {
355
356 if (LG.getTargetTriple().getObjectFormat() == Triple::MachO)
357 modifyPassConfigForMachO(MR, LG, PassConfig);
358 else {
359 LLVM_DEBUG({
360 dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph "
361 << LG.getName() << "(triple = " << LG.getTargetTriple().str()
362 << "\n";
363 });
364 }
365}
366
367void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO(
368 MaterializationResponsibility &MR, jitlink::LinkGraph &LG,
369 jitlink::PassConfiguration &PassConfig) {
370
371 switch (LG.getTargetTriple().getArch()) {
372 case Triple::x86_64:
373 case Triple::aarch64:
374 // Supported, continue.
375 assert(LG.getPointerSize() == 8 && "Graph has incorrect pointer size");
376 assert(LG.getEndianness() == llvm::endianness::little &&
377 "Graph has incorrect endianness");
378 break;
379 default:
380 // Unsupported.
381 LLVM_DEBUG({
382 dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported "
383 << "MachO graph " << LG.getName()
384 << "(triple = " << LG.getTargetTriple().str()
385 << ", pointer size = " << LG.getPointerSize() << ", endianness = "
386 << (LG.getEndianness() == llvm::endianness::big ? "big" : "little")
387 << ")\n";
388 });
389 return;
390 }
391
392 // Scan for debug sections. If we find one then install passes.
393 bool HasDebugSections = false;
394 for (auto &Sec : LG.sections())
395 if (MachODebugObjectSynthesizerBase::isDebugSection(Sec)) {
396 HasDebugSections = true;
397 break;
398 }
399
400 if (HasDebugSections) {
401 LLVM_DEBUG({
402 dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
403 << " contains debug info. Installing debugger support passes.\n";
404 });
405
406 auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>(
407 args&: MR.getTargetJITDylib().getExecutionSession(), args&: LG, args&: RegisterActionAddr);
408 PassConfig.PrePrunePasses.push_back(
409 x: [=](LinkGraph &G) { return MDOS->preserveDebugSections(); });
410 PassConfig.PostPrunePasses.push_back(
411 x: [=](LinkGraph &G) { return MDOS->startSynthesis(); });
412 PassConfig.PostFixupPasses.push_back(
413 x: [=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); });
414 } else {
415 LLVM_DEBUG({
416 dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
417 << " contains no debug info. Skipping.\n";
418 });
419 }
420}
421
422} // namespace orc
423} // namespace llvm
424