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 | |
25 | using namespace llvm; |
26 | using namespace llvm::jitlink; |
27 | using namespace llvm::orc; |
28 | |
29 | static const char *SynthDebugSectionName = "__jitlink_synth_debug_object" ; |
30 | |
31 | namespace { |
32 | |
33 | class MachODebugObjectSynthesizerBase |
34 | : public GDBJITDebugInfoRegistrationPlugin::DebugSectionSynthesizer { |
35 | public: |
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 | |
82 | protected: |
83 | LinkGraph &G; |
84 | ExecutorAddr RegisterActionAddr; |
85 | }; |
86 | |
87 | template <typename MachOTraits> |
88 | class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase { |
89 | public: |
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 | if (auto CPUType = MachO::getCPUType(T: G.getTargetTriple())) |
125 | Builder.Header.cputype = *CPUType; |
126 | else |
127 | return CPUType.takeError(); |
128 | if (auto CPUSubType = MachO::getCPUSubType(G.getTargetTriple())) |
129 | Builder.Header.cpusubtype = *CPUSubType; |
130 | else |
131 | return CPUSubType.takeError(); |
132 | |
133 | Seg = &Builder.addSegment("" ); |
134 | |
135 | StringMap<std::unique_ptr<MemoryBuffer>> DebugSectionMap; |
136 | StringRef DebugLineSectionData; |
137 | for (auto &DSec : DebugSections) { |
138 | auto [SegName, SecName] = DSec.GraphSec->getName().split(','); |
139 | DSec.BuilderSec = &Seg->addSection(SecName, SegName); |
140 | |
141 | SectionRange SR(*DSec.GraphSec); |
142 | DSec.BuilderSec->Content.Size = SR.getSize(); |
143 | if (!SR.empty()) { |
144 | DSec.BuilderSec->align = Log2_64(Value: SR.getFirstBlock()->getAlignment()); |
145 | StringRef SectionData(SR.getFirstBlock()->getContent().data(), |
146 | SR.getFirstBlock()->getSize()); |
147 | DebugSectionMap[SecName.drop_front(2)] = // drop "__" prefix. |
148 | MemoryBuffer::getMemBuffer(SectionData, G.getName(), false); |
149 | if (SecName == "__debug_line" ) |
150 | DebugLineSectionData = SectionData; |
151 | } |
152 | } |
153 | |
154 | std::optional<StringRef> FileName; |
155 | if (!DebugLineSectionData.empty()) { |
156 | assert((G.getEndianness() == llvm::endianness::big || |
157 | G.getEndianness() == llvm::endianness::little) && |
158 | "G.getEndianness() must be either big or little" ); |
159 | auto DWARFCtx = |
160 | DWARFContext::create(DebugSectionMap, G.getPointerSize(), |
161 | G.getEndianness() == llvm::endianness::little); |
162 | DWARFDataExtractor DebugLineData( |
163 | DebugLineSectionData, G.getEndianness() == llvm::endianness::little, |
164 | G.getPointerSize()); |
165 | uint64_t Offset = 0; |
166 | DWARFDebugLine::Prologue P; |
167 | |
168 | // Try to parse line data. Consume error on failure. |
169 | if (auto Err = P.parse(Data: DebugLineData, OffsetPtr: &Offset, RecoverableErrorHandler: consumeError, Ctx: *DWARFCtx)) { |
170 | handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) { |
171 | LLVM_DEBUG({ |
172 | dbgs() << "Cannot parse line table for \"" << G.getName() << "\": " ; |
173 | EIB.log(dbgs()); |
174 | dbgs() << "\n" ; |
175 | }); |
176 | }); |
177 | } else { |
178 | for (auto &FN : P.FileNames) |
179 | if ((FileName = dwarf::toString(V: FN.Name))) { |
180 | LLVM_DEBUG({ |
181 | dbgs() << "Using FileName = \"" << *FileName |
182 | << "\" from DWARF line table\n" ; |
183 | }); |
184 | break; |
185 | } |
186 | } |
187 | } |
188 | |
189 | // If no line table (or unable to use) then use graph name. |
190 | // FIXME: There are probably other debug sections we should look in first. |
191 | if (!FileName) { |
192 | LLVM_DEBUG({ |
193 | dbgs() << "Could not find source name from DWARF line table. " |
194 | "Using FileName = \"\"\n" ; |
195 | }); |
196 | FileName = "" ; |
197 | } |
198 | |
199 | Builder.addSymbol("" , MachO::N_SO, 0, 0, 0); |
200 | Builder.addSymbol(*FileName, MachO::N_SO, 0, 0, 0); |
201 | auto TimeStamp = std::chrono::duration_cast<std::chrono::seconds>( |
202 | d: std::chrono::system_clock::now().time_since_epoch()) |
203 | .count(); |
204 | Builder.addSymbol("" , MachO::N_OSO, 3, 1, TimeStamp); |
205 | |
206 | for (auto &NDSP : NonDebugSections) { |
207 | auto [SegName, SecName] = NDSP.GraphSec->getName().split(','); |
208 | NDSP.BuilderSec = &Seg->addSection(SecName, SegName); |
209 | SectionRange SR(*NDSP.GraphSec); |
210 | if (!SR.empty()) |
211 | NDSP.BuilderSec->align = Log2_64(Value: SR.getFirstBlock()->getAlignment()); |
212 | |
213 | // Add stabs. |
214 | for (auto *Sym : NDSP.GraphSec->symbols()) { |
215 | // Skip anonymous symbols. |
216 | if (!Sym->hasName()) |
217 | continue; |
218 | |
219 | uint8_t SymType = Sym->isCallable() ? MachO::N_FUN : MachO::N_GSYM; |
220 | |
221 | Builder.addSymbol("" , MachO::N_BNSYM, 1, 0, 0); |
222 | StabSymbols.push_back( |
223 | {*Sym, Builder.addSymbol(*Sym->getName(), SymType, 1, 0, 0), |
224 | Builder.addSymbol(*Sym->getName(), SymType, 0, 0, 0)}); |
225 | Builder.addSymbol("" , MachO::N_ENSYM, 1, 0, 0); |
226 | } |
227 | } |
228 | |
229 | Builder.addSymbol("" , MachO::N_SO, 1, 0, 0); |
230 | |
231 | // Lay out the debug object, create a section and block for it. |
232 | size_t DebugObjectSize = Builder.layout(); |
233 | |
234 | auto &SDOSec = G.createSection(Name: SynthDebugSectionName, Prot: MemProt::Read); |
235 | MachOContainerBlock = &G.createMutableContentBlock( |
236 | SDOSec, G.allocateBuffer(Size: DebugObjectSize), orc::ExecutorAddr(), 8, 0); |
237 | |
238 | return Error::success(); |
239 | } |
240 | |
241 | Error completeSynthesisAndRegister() override { |
242 | if (!MachOContainerBlock) { |
243 | LLVM_DEBUG({ |
244 | dbgs() << "Not writing MachO debug object header for " << G.getName() |
245 | << " since createDebugSection failed\n" ; |
246 | }); |
247 | |
248 | return Error::success(); |
249 | } |
250 | ExecutorAddr MaxAddr; |
251 | for (auto &NDSec : NonDebugSections) { |
252 | SectionRange SR(*NDSec.GraphSec); |
253 | NDSec.BuilderSec->addr = SR.getStart().getValue(); |
254 | NDSec.BuilderSec->size = SR.getSize(); |
255 | NDSec.BuilderSec->offset = SR.getStart().getValue(); |
256 | if (SR.getEnd() > MaxAddr) |
257 | MaxAddr = SR.getEnd(); |
258 | } |
259 | |
260 | for (auto &DSec : DebugSections) { |
261 | if (DSec.GraphSec->blocks_size() != 1) |
262 | return make_error<StringError>( |
263 | Args: "Unexpected number of blocks in debug info section" , |
264 | Args: inconvertibleErrorCode()); |
265 | |
266 | if (ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size > MaxAddr) |
267 | MaxAddr = ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size; |
268 | |
269 | auto &B = **DSec.GraphSec->blocks().begin(); |
270 | DSec.BuilderSec->Content.Data = B.getContent().data(); |
271 | DSec.BuilderSec->Content.Size = B.getContent().size(); |
272 | DSec.BuilderSec->flags |= MachO::S_ATTR_DEBUG; |
273 | } |
274 | |
275 | LLVM_DEBUG({ |
276 | dbgs() << "Writing MachO debug object header for " << G.getName() << "\n" ; |
277 | }); |
278 | |
279 | // Update stab symbol addresses. |
280 | for (auto &SS : StabSymbols) { |
281 | SS.StartStab.nlist().n_value = SS.Sym.getAddress().getValue(); |
282 | SS.EndStab.nlist().n_value = SS.Sym.getSize(); |
283 | } |
284 | |
285 | Builder.write(MachOContainerBlock->getAlreadyMutableContent()); |
286 | |
287 | static constexpr bool AutoRegisterCode = true; |
288 | SectionRange R(MachOContainerBlock->getSection()); |
289 | G.allocActions().push_back( |
290 | {cantFail(shared::WrapperFunctionCall::Create< |
291 | shared::SPSArgList<shared::SPSExecutorAddrRange, bool>>( |
292 | RegisterActionAddr, R.getRange(), AutoRegisterCode)), |
293 | {}}); |
294 | |
295 | return Error::success(); |
296 | } |
297 | |
298 | private: |
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 | |
326 | namespace llvm { |
327 | namespace orc { |
328 | |
329 | Expected<std::unique_ptr<GDBJITDebugInfoRegistrationPlugin>> |
330 | GDBJITDebugInfoRegistrationPlugin::Create(ExecutionSession &ES, |
331 | JITDylib &ProcessJD, |
332 | const Triple &TT) { |
333 | auto RegisterActionAddr = |
334 | TT.isOSBinFormatMachO() |
335 | ? ES.intern(SymName: "_llvm_orc_registerJITLoaderGDBAllocAction" ) |
336 | : ES.intern(SymName: "llvm_orc_registerJITLoaderGDBAllocAction" ); |
337 | |
338 | if (auto RegisterSym = ES.lookup(SearchOrder: {&ProcessJD}, Symbol: RegisterActionAddr)) |
339 | return std::make_unique<GDBJITDebugInfoRegistrationPlugin>( |
340 | args: RegisterSym->getAddress()); |
341 | else |
342 | return RegisterSym.takeError(); |
343 | } |
344 | |
345 | Error GDBJITDebugInfoRegistrationPlugin::notifyFailed( |
346 | MaterializationResponsibility &MR) { |
347 | return Error::success(); |
348 | } |
349 | |
350 | Error GDBJITDebugInfoRegistrationPlugin::notifyRemovingResources( |
351 | JITDylib &JD, ResourceKey K) { |
352 | return Error::success(); |
353 | } |
354 | |
355 | void GDBJITDebugInfoRegistrationPlugin::notifyTransferringResources( |
356 | JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) {} |
357 | |
358 | void GDBJITDebugInfoRegistrationPlugin::modifyPassConfig( |
359 | MaterializationResponsibility &MR, LinkGraph &LG, |
360 | PassConfiguration &PassConfig) { |
361 | |
362 | if (LG.getTargetTriple().getObjectFormat() == Triple::MachO) |
363 | modifyPassConfigForMachO(MR, LG, PassConfig); |
364 | else { |
365 | LLVM_DEBUG({ |
366 | dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph " |
367 | << LG.getName() << "(triple = " << LG.getTargetTriple().str() |
368 | << "\n" ; |
369 | }); |
370 | } |
371 | } |
372 | |
373 | void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO( |
374 | MaterializationResponsibility &MR, jitlink::LinkGraph &LG, |
375 | jitlink::PassConfiguration &PassConfig) { |
376 | |
377 | switch (LG.getTargetTriple().getArch()) { |
378 | case Triple::x86_64: |
379 | case Triple::aarch64: |
380 | // Supported, continue. |
381 | assert(LG.getPointerSize() == 8 && "Graph has incorrect pointer size" ); |
382 | assert(LG.getEndianness() == llvm::endianness::little && |
383 | "Graph has incorrect endianness" ); |
384 | break; |
385 | default: |
386 | // Unsupported. |
387 | LLVM_DEBUG({ |
388 | dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported " |
389 | << "MachO graph " << LG.getName() |
390 | << "(triple = " << LG.getTargetTriple().str() |
391 | << ", pointer size = " << LG.getPointerSize() << ", endianness = " |
392 | << (LG.getEndianness() == llvm::endianness::big ? "big" : "little" ) |
393 | << ")\n" ; |
394 | }); |
395 | return; |
396 | } |
397 | |
398 | // Scan for debug sections. If we find one then install passes. |
399 | bool HasDebugSections = false; |
400 | for (auto &Sec : LG.sections()) |
401 | if (MachODebugObjectSynthesizerBase::isDebugSection(Sec)) { |
402 | HasDebugSections = true; |
403 | break; |
404 | } |
405 | |
406 | if (HasDebugSections) { |
407 | LLVM_DEBUG({ |
408 | dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName() |
409 | << " contains debug info. Installing debugger support passes.\n" ; |
410 | }); |
411 | |
412 | auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>( |
413 | args&: MR.getTargetJITDylib().getExecutionSession(), args&: LG, args&: RegisterActionAddr); |
414 | PassConfig.PrePrunePasses.push_back( |
415 | x: [=](LinkGraph &G) { return MDOS->preserveDebugSections(); }); |
416 | PassConfig.PostPrunePasses.push_back( |
417 | x: [=](LinkGraph &G) { return MDOS->startSynthesis(); }); |
418 | PassConfig.PostFixupPasses.push_back( |
419 | x: [=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); }); |
420 | } else { |
421 | LLVM_DEBUG({ |
422 | dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName() |
423 | << " contains no debug info. Skipping.\n" ; |
424 | }); |
425 | } |
426 | } |
427 | |
428 | } // namespace orc |
429 | } // namespace llvm |
430 | |