LLVM 17.0.0git
DebuggerSupportPlugin.cpp
Go to the documentation of this file.
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
13
14#include "llvm/ADT/SmallSet.h"
16#include "llvm/ADT/StringSet.h"
18
19#define DEBUG_TYPE "orc"
20
21using namespace llvm;
22using namespace llvm::jitlink;
23using namespace llvm::orc;
24
25static const char *SynthDebugSectionName = "__jitlink_synth_debug_object";
26
27namespace {
28
29struct MachO64LE {
30 using UIntPtr = uint64_t;
31
32 using Header = MachO::mach_header_64;
33 using SegmentLC = MachO::segment_command_64;
35 using NList = MachO::nlist_64;
36
37 static constexpr support::endianness Endianness = support::little;
38 static constexpr const uint32_t Magic = MachO::MH_MAGIC_64;
39 static constexpr const uint32_t SegmentCmd = MachO::LC_SEGMENT_64;
40};
41
42class MachODebugObjectSynthesizerBase
44public:
45 static bool isDebugSection(Section &Sec) {
46 return Sec.getName().startswith("__DWARF,");
47 }
48
49 MachODebugObjectSynthesizerBase(LinkGraph &G, ExecutorAddr RegisterActionAddr)
50 : G(G), RegisterActionAddr(RegisterActionAddr) {}
51 virtual ~MachODebugObjectSynthesizerBase() = default;
52
53 Error preserveDebugSections() {
54 if (G.findSectionByName(SynthDebugSectionName)) {
56 dbgs() << "MachODebugObjectSynthesizer skipping graph " << G.getName()
57 << " which contains an unexpected existing "
58 << SynthDebugSectionName << " section.\n";
59 });
60 return Error::success();
61 }
62
64 dbgs() << "MachODebugObjectSynthesizer visiting graph " << G.getName()
65 << "\n";
66 });
67 for (auto &Sec : G.sections()) {
68 if (!isDebugSection(Sec))
69 continue;
70 // Preserve blocks in this debug section by marking one existing symbol
71 // live for each block, and introducing a new live, anonymous symbol for
72 // each currently unreferenced block.
74 dbgs() << " Preserving debug section " << Sec.getName() << "\n";
75 });
76 SmallSet<Block *, 8> PreservedBlocks;
77 for (auto *Sym : Sec.symbols()) {
78 bool NewPreservedBlock =
79 PreservedBlocks.insert(&Sym->getBlock()).second;
80 if (NewPreservedBlock)
81 Sym->setLive(true);
82 }
83 for (auto *B : Sec.blocks())
84 if (!PreservedBlocks.count(B))
85 G.addAnonymousSymbol(*B, 0, 0, false, true);
86 }
87 return Error::success();
88 }
89
90protected:
91 LinkGraph &G;
92 ExecutorAddr RegisterActionAddr;
93};
94
95template <typename MachOTraits>
96class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase {
97private:
98 class MachOStructWriter {
99 public:
100 MachOStructWriter(MutableArrayRef<char> Buffer) : Buffer(Buffer) {}
101
102 size_t getOffset() const { return Offset; }
103
104 template <typename MachOStruct> void write(MachOStruct S) {
105 assert(Offset + sizeof(S) <= Buffer.size() &&
106 "Container block overflow while constructing debug MachO");
107 if (MachOTraits::Endianness != support::endian::system_endianness())
109 memcpy(Buffer.data() + Offset, &S, sizeof(S));
110 Offset += sizeof(S);
111 }
112
113 private:
115 size_t Offset = 0;
116 };
117
118public:
119 using MachODebugObjectSynthesizerBase::MachODebugObjectSynthesizerBase;
120
121 Error startSynthesis() override {
122 LLVM_DEBUG({
123 dbgs() << "Creating " << SynthDebugSectionName << " for " << G.getName()
124 << "\n";
125 });
126 auto &SDOSec = G.createSection(SynthDebugSectionName, MemProt::Read);
127
128 struct DebugSectionInfo {
129 Section *Sec = nullptr;
130 StringRef SegName;
131 StringRef SecName;
132 uint64_t Alignment = 0;
133 orc::ExecutorAddr StartAddr;
134 uint64_t Size = 0;
135 };
136
138 size_t NumSections = 0;
139 for (auto &Sec : G.sections()) {
140 if (Sec.blocks().empty())
141 continue;
142
143 ++NumSections;
144 if (isDebugSection(Sec)) {
145 size_t SepPos = Sec.getName().find(',');
146 if (SepPos > 16 || (Sec.getName().size() - (SepPos + 1) > 16)) {
147 LLVM_DEBUG({
148 dbgs() << "Skipping debug object synthesis for graph "
149 << G.getName()
150 << ": encountered non-standard DWARF section name \""
151 << Sec.getName() << "\"\n";
152 });
153 return Error::success();
154 }
155 DebugSecInfos.push_back({&Sec, Sec.getName().substr(0, SepPos),
156 Sec.getName().substr(SepPos + 1), 0,
157 orc::ExecutorAddr(), 0});
158 } else {
159 NonDebugSections.push_back(&Sec);
160
161 // If the first block in the section has a non-zero alignment offset
162 // then we need to add a padding block, since the section command in
163 // the header doesn't allow for aligment offsets.
164 SectionRange R(Sec);
165 if (!R.empty()) {
166 auto &FB = *R.getFirstBlock();
167 if (FB.getAlignmentOffset() != 0) {
168 auto Padding = G.allocateBuffer(FB.getAlignmentOffset());
169 memset(Padding.data(), 0, Padding.size());
170 G.createContentBlock(Sec, Padding,
171 FB.getAddress() - FB.getAlignmentOffset(),
172 FB.getAlignment(), 0);
173 }
174 }
175 }
176 }
177
178 // Create container block.
179 size_t SectionsCmdSize =
180 sizeof(typename MachOTraits::Section) * NumSections;
181 size_t SegmentLCSize =
182 sizeof(typename MachOTraits::SegmentLC) + SectionsCmdSize;
183 size_t ContainerBlockSize =
184 sizeof(typename MachOTraits::Header) + SegmentLCSize;
185 auto ContainerBlockContent = G.allocateBuffer(ContainerBlockSize);
186 MachOContainerBlock = &G.createMutableContentBlock(
187 SDOSec, ContainerBlockContent, orc::ExecutorAddr(), 8, 0);
188
189 // Copy debug section blocks and symbols.
190 orc::ExecutorAddr NextBlockAddr(MachOContainerBlock->getSize());
191 for (auto &SI : DebugSecInfos) {
192 assert(!SI.Sec->blocks().empty() && "Empty debug info section?");
193
194 // Update addresses in debug section.
195 LLVM_DEBUG({
196 dbgs() << " Appending " << SI.Sec->getName() << " ("
197 << SI.Sec->blocks_size() << " block(s)) at "
198 << formatv("{0:x8}", NextBlockAddr) << "\n";
199 });
200 for (auto *B : SI.Sec->blocks()) {
201 NextBlockAddr = alignToBlock(NextBlockAddr, *B);
202 B->setAddress(NextBlockAddr);
203 NextBlockAddr += B->getSize();
204 }
205
206 auto &FirstBlock = **SI.Sec->blocks().begin();
207 if (FirstBlock.getAlignmentOffset() != 0)
208 return make_error<StringError>(
209 "First block in " + SI.Sec->getName() +
210 " section has non-zero alignment offset",
212 if (FirstBlock.getAlignment() > std::numeric_limits<uint32_t>::max())
213 return make_error<StringError>("First block in " + SI.Sec->getName() +
214 " has alignment >4Gb",
216
217 SI.Alignment = FirstBlock.getAlignment();
218 SI.StartAddr = FirstBlock.getAddress();
219 SI.Size = NextBlockAddr - SI.StartAddr;
220 G.mergeSections(SDOSec, *SI.Sec);
221 SI.Sec = nullptr;
222 }
223 size_t DebugSectionsSize =
224 NextBlockAddr - orc::ExecutorAddr(MachOContainerBlock->getSize());
225
226 // Write MachO header and debug section load commands.
227 MachOStructWriter Writer(MachOContainerBlock->getAlreadyMutableContent());
228 typename MachOTraits::Header Hdr;
229 memset(&Hdr, 0, sizeof(Hdr));
230 Hdr.magic = MachOTraits::Magic;
231 switch (G.getTargetTriple().getArch()) {
232 case Triple::x86_64:
233 Hdr.cputype = MachO::CPU_TYPE_X86_64;
234 Hdr.cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL;
235 break;
236 case Triple::aarch64:
237 Hdr.cputype = MachO::CPU_TYPE_ARM64;
238 Hdr.cpusubtype = MachO::CPU_SUBTYPE_ARM64_ALL;
239 break;
240 default:
241 llvm_unreachable("Unsupported architecture");
242 }
243 Hdr.filetype = MachO::MH_OBJECT;
244 Hdr.ncmds = 1;
245 Hdr.sizeofcmds = SegmentLCSize;
246 Hdr.flags = 0;
247 Writer.write(Hdr);
248
249 typename MachOTraits::SegmentLC SegLC;
250 memset(&SegLC, 0, sizeof(SegLC));
251 SegLC.cmd = MachOTraits::SegmentCmd;
252 SegLC.cmdsize = SegmentLCSize;
253 SegLC.vmaddr = ContainerBlockSize;
254 SegLC.vmsize = DebugSectionsSize;
255 SegLC.fileoff = ContainerBlockSize;
256 SegLC.filesize = DebugSectionsSize;
257 SegLC.maxprot =
259 SegLC.initprot =
261 SegLC.nsects = NumSections;
262 SegLC.flags = 0;
263 Writer.write(SegLC);
264
265 StringSet<> ExistingLongNames;
266 for (auto &SI : DebugSecInfos) {
267 typename MachOTraits::Section Sec;
268 memset(&Sec, 0, sizeof(Sec));
269 memcpy(Sec.sectname, SI.SecName.data(), SI.SecName.size());
270 memcpy(Sec.segname, SI.SegName.data(), SI.SegName.size());
271 Sec.addr = SI.StartAddr.getValue();
272 Sec.size = SI.Size;
273 Sec.offset = SI.StartAddr.getValue();
274 Sec.align = SI.Alignment;
275 Sec.reloff = 0;
276 Sec.nreloc = 0;
277 Sec.flags = MachO::S_ATTR_DEBUG;
278 Writer.write(Sec);
279 }
280
281 // Set MachOContainerBlock to indicate success to
282 // completeSynthesisAndRegister.
283 NonDebugSectionsStart = Writer.getOffset();
284 return Error::success();
285 }
286
287 Error completeSynthesisAndRegister() override {
288 if (!MachOContainerBlock) {
289 LLVM_DEBUG({
290 dbgs() << "Not writing MachO debug object header for " << G.getName()
291 << " since createDebugSection failed\n";
292 });
293 return Error::success();
294 }
295
296 LLVM_DEBUG({
297 dbgs() << "Writing MachO debug object header for " << G.getName() << "\n";
298 });
299
300 MachOStructWriter Writer(
301 MachOContainerBlock->getAlreadyMutableContent().drop_front(
302 NonDebugSectionsStart));
303
304 unsigned LongSectionNameIdx = 0;
305 for (auto *Sec : NonDebugSections) {
306 size_t SepPos = Sec->getName().find(',');
307 StringRef SegName, SecName;
308 std::string CustomSecName;
309
310 if ((SepPos == StringRef::npos && Sec->getName().size() <= 16)) {
311 // No embedded segment name, short section name.
312 SegName = "__JITLINK_CUSTOM";
313 SecName = Sec->getName();
314 } else if (SepPos < 16 && (Sec->getName().size() - (SepPos + 1) <= 16)) {
315 // Canonical embedded segment and section name.
316 SegName = Sec->getName().substr(0, SepPos);
317 SecName = Sec->getName().substr(SepPos + 1);
318 } else {
319 // Long section name that needs to be truncated.
320 assert(Sec->getName().size() > 16 &&
321 "Short section name should have been handled above");
322 SegName = "__JITLINK_CUSTOM";
323 auto IdxStr = std::to_string(++LongSectionNameIdx);
324 CustomSecName = Sec->getName().substr(0, 15 - IdxStr.size()).str();
325 CustomSecName += ".";
326 CustomSecName += IdxStr;
327 SecName = StringRef(CustomSecName.data(), 16);
328 }
329
330 SectionRange R(*Sec);
331 if (R.getFirstBlock()->getAlignmentOffset() != 0)
332 return make_error<StringError>(
333 "While building MachO debug object for " + G.getName() +
334 " first block has non-zero alignment offset",
336
337 typename MachOTraits::Section SecCmd;
338 memset(&SecCmd, 0, sizeof(SecCmd));
339 memcpy(SecCmd.sectname, SecName.data(), SecName.size());
340 memcpy(SecCmd.segname, SegName.data(), SegName.size());
341 SecCmd.addr = R.getStart().getValue();
342 SecCmd.size = R.getSize();
343 SecCmd.offset = 0;
344 SecCmd.align = R.getFirstBlock()->getAlignment();
345 SecCmd.reloff = 0;
346 SecCmd.nreloc = 0;
347 SecCmd.flags = 0;
348 Writer.write(SecCmd);
349 }
350
351 SectionRange R(MachOContainerBlock->getSection());
352 G.allocActions().push_back(
355 RegisterActionAddr, R.getRange())),
356 {}});
357 return Error::success();
358 }
359
360private:
361 Block *MachOContainerBlock = nullptr;
362 SmallVector<Section *, 16> NonDebugSections;
363 size_t NonDebugSectionsStart = 0;
364};
365
366} // end anonymous namespace
367
368namespace llvm {
369namespace orc {
370
373 JITDylib &ProcessJD,
374 const Triple &TT) {
375 auto RegisterActionAddr =
376 TT.isOSBinFormatMachO()
377 ? ES.intern("_llvm_orc_registerJITLoaderGDBAllocAction")
378 : ES.intern("llvm_orc_registerJITLoaderGDBAllocAction");
379
380 if (auto RegisterSym = ES.lookup({&ProcessJD}, RegisterActionAddr))
381 return std::make_unique<GDBJITDebugInfoRegistrationPlugin>(
382 RegisterSym->getAddress());
383 else
384 return RegisterSym.takeError();
385}
386
389 return Error::success();
390}
391
393 JITDylib &JD, ResourceKey K) {
394 return Error::success();
395}
396
398 JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) {}
399
402 PassConfiguration &PassConfig) {
403
405 modifyPassConfigForMachO(MR, LG, PassConfig);
406 else {
407 LLVM_DEBUG({
408 dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph "
409 << LG.getName() << "(triple = " << LG.getTargetTriple().str()
410 << "\n";
411 });
412 }
413}
414
415void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO(
417 jitlink::PassConfiguration &PassConfig) {
418
419 switch (LG.getTargetTriple().getArch()) {
420 case Triple::x86_64:
421 case Triple::aarch64:
422 // Supported, continue.
423 assert(LG.getPointerSize() == 8 && "Graph has incorrect pointer size");
425 "Graph has incorrect endianness");
426 break;
427 default:
428 // Unsupported.
429 LLVM_DEBUG({
430 dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported "
431 << "MachO graph " << LG.getName()
432 << "(triple = " << LG.getTargetTriple().str()
433 << ", pointer size = " << LG.getPointerSize() << ", endianness = "
434 << (LG.getEndianness() == support::big ? "big" : "little")
435 << ")\n";
436 });
437 return;
438 }
439
440 // Scan for debug sections. If we find one then install passes.
441 bool HasDebugSections = false;
442 for (auto &Sec : LG.sections())
443 if (MachODebugObjectSynthesizerBase::isDebugSection(Sec)) {
444 HasDebugSections = true;
445 break;
446 }
447
448 if (HasDebugSections) {
449 LLVM_DEBUG({
450 dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
451 << " contains debug info. Installing debugger support passes.\n";
452 });
453
454 auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>(
455 LG, RegisterActionAddr);
456 PassConfig.PrePrunePasses.push_back(
457 [=](LinkGraph &G) { return MDOS->preserveDebugSections(); });
458 PassConfig.PostPrunePasses.push_back(
459 [=](LinkGraph &G) { return MDOS->startSynthesis(); });
460 PassConfig.PreFixupPasses.push_back(
461 [=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); });
462 } else {
463 LLVM_DEBUG({
464 dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
465 << " contains no debug info. Skipping.\n";
466 });
467 }
468}
469
470} // namespace orc
471} // namespace llvm
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
#define LLVM_DEBUG(X)
Definition: Debug.h:101
static const char * SynthDebugSectionName
uint64_t Size
static bool isDebugSection(const SectionBase &Sec)
Definition: ELFObjcopy.cpp:54
#define G(x, y, z)
Definition: MD5.cpp:56
@ SI
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
static StringRef substr(StringRef Str, uint64_t Len)
This file defines the SmallSet class.
This file defines the SmallVector class.
StringSet - A set-like wrapper for the StringMap.
size_t size() const
size - Get the array size.
Definition: ArrayRef.h:163
Lightweight error class with error context and mandatory checking.
Definition: Error.h:156
static ErrorSuccess success()
Create a success value.
Definition: Error.h:330
Tagged union holding either a T or a Error.
Definition: Error.h:470
MutableArrayRef - Represent a mutable reference to an array (0 or more elements consecutively in memo...
Definition: ArrayRef.h:305
T * data() const
Definition: ArrayRef.h:352
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition: SmallSet.h:135
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
Definition: SmallSet.h:179
void push_back(const T &Elt)
Definition: SmallVector.h:416
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1200
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
Definition: StringRef.h:558
constexpr size_t size() const
size - Get the string size.
Definition: StringRef.h:137
bool startswith(StringRef Prefix) const
Definition: StringRef.h:261
static constexpr size_t npos
Definition: StringRef.h:52
const char * data() const
data - Get a pointer to the start of the string (which may not be null terminated).
Definition: StringRef.h:131
StringSet - A wrapper for StringMap that provides set-like functionality.
Definition: StringSet.h:23
Triple - Helper class for working with autoconf configuration names.
Definition: Triple.h:44
ObjectFormatType getObjectFormat() const
Get the object format for this triple.
Definition: Triple.h:382
ArchType getArch() const
Get the parsed architecture type of this triple.
Definition: Triple.h:356
const std::string & str() const
Definition: Triple.h:415
An ExecutionSession represents a running JIT program.
Definition: Core.h:1373
SymbolStringPtr intern(StringRef SymName)
Add a symbol name to the SymbolStringPool and return a pointer to it.
Definition: Core.h:1427
void lookup(LookupKind K, const JITDylibSearchOrder &SearchOrder, SymbolLookupSet Symbols, SymbolState RequiredState, SymbolsResolvedCallback NotifyComplete, RegisterDependenciesFunction RegisterDependencies)
Search the given JITDylibs for the given symbols.
Definition: Core.cpp:2084
Represents an address in the executor process.
void modifyPassConfig(MaterializationResponsibility &MR, jitlink::LinkGraph &LG, jitlink::PassConfiguration &PassConfig) override
static Expected< std::unique_ptr< GDBJITDebugInfoRegistrationPlugin > > Create(ExecutionSession &ES, JITDylib &ProcessJD, const Triple &TT)
void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) override
Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override
Error notifyFailed(MaterializationResponsibility &MR) override
Represents a JIT'd dynamic library.
Definition: Core.h:964
Tracks responsibility for materialization, and mediates interactions between MaterializationUnits and...
Definition: Core.h:526
A utility class for serializing to a blob from a variadic list.
static Expected< WrapperFunctionCall > Create(ExecutorAddr FnAddr, const ArgTs &...Args)
Create a WrapperFunctionCall using the given SPS serializer to serialize the arguments.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ MH_MAGIC_64
Definition: MachO.h:32
@ CPU_SUBTYPE_ARM64_ALL
Definition: MachO.h:1639
@ S_ATTR_DEBUG
S_ATTR_DEBUG - A debug section.
Definition: MachO.h:207
@ MH_OBJECT
Definition: MachO.h:43
void swapStruct(fat_header &mh)
Definition: MachO.h:1139
@ VM_PROT_EXECUTE
Definition: MachO.h:494
@ VM_PROT_READ
Definition: MachO.h:494
@ VM_PROT_WRITE
Definition: MachO.h:494
@ CPU_SUBTYPE_X86_64_ALL
Definition: MachO.h:1609
@ CPU_TYPE_ARM64
Definition: MachO.h:1568
@ CPU_TYPE_X86_64
Definition: MachO.h:1564
uintptr_t ResourceKey
Definition: Core.h:52
constexpr endianness system_endianness()
Definition: Endian.h:44
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
auto formatv(const char *Fmt, Ts &&... Vals) -> formatv_object< decltype(std::make_tuple(detail::build_format_adapter(std::forward< Ts >(Vals))...))>
Error write(MCStreamer &Out, ArrayRef< std::string > Inputs)
Definition: DWP.cpp:551
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition: Error.cpp:79
static Error getOffset(const SymbolRef &Sym, SectionRef Sec, uint64_t &Result)
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition: Error.h:745