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 | 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 | |
292 | private: |
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 | |
320 | namespace llvm { |
321 | namespace orc { |
322 | |
323 | Expected<std::unique_ptr<GDBJITDebugInfoRegistrationPlugin>> |
324 | GDBJITDebugInfoRegistrationPlugin::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 | |
339 | Error GDBJITDebugInfoRegistrationPlugin::notifyFailed( |
340 | MaterializationResponsibility &MR) { |
341 | return Error::success(); |
342 | } |
343 | |
344 | Error GDBJITDebugInfoRegistrationPlugin::notifyRemovingResources( |
345 | JITDylib &JD, ResourceKey K) { |
346 | return Error::success(); |
347 | } |
348 | |
349 | void GDBJITDebugInfoRegistrationPlugin::notifyTransferringResources( |
350 | JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) {} |
351 | |
352 | void 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 | |
367 | void 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 | |