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