1 //===------- DebuggerSupportPlugin.cpp - Utils for debugger support -------===//
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
7 //===----------------------------------------------------------------------===//
10 //===----------------------------------------------------------------------===//
12 #include "llvm/ExecutionEngine/Orc/DebuggerSupportPlugin.h"
14 #include "llvm/ADT/SmallSet.h"
15 #include "llvm/ADT/SmallVector.h"
16 #include "llvm/ADT/StringSet.h"
17 #include "llvm/BinaryFormat/MachO.h"
19 #define DEBUG_TYPE "orc"
22 using namespace llvm::jitlink
;
23 using namespace llvm::orc
;
25 static const char *SynthDebugSectionName
= "__jitlink_synth_debug_object";
30 using UIntPtr
= uint64_t;
32 using Header
= MachO::mach_header_64
;
33 using SegmentLC
= MachO::segment_command_64
;
34 using Section
= MachO::section_64
;
35 using NList
= MachO::nlist_64
;
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
;
42 class MachODebugObjectSynthesizerBase
43 : public GDBJITDebugInfoRegistrationPlugin::DebugSectionSynthesizer
{
45 static bool isDebugSection(Section
&Sec
) {
46 return Sec
.getName().startswith("__DWARF,");
49 MachODebugObjectSynthesizerBase(LinkGraph
&G
, ExecutorAddr RegisterActionAddr
)
50 : G(G
), RegisterActionAddr(RegisterActionAddr
) {}
51 virtual ~MachODebugObjectSynthesizerBase() {}
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";
60 return Error::success();
64 dbgs() << "MachODebugObjectSynthesizer visiting graph " << G
.getName()
67 for (auto &Sec
: G
.sections()) {
68 if (!isDebugSection(Sec
))
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";
76 SmallSet
<Block
*, 8> PreservedBlocks
;
77 for (auto *Sym
: Sec
.symbols()) {
78 bool NewPreservedBlock
=
79 PreservedBlocks
.insert(&Sym
->getBlock()).second
;
80 if (NewPreservedBlock
)
83 for (auto *B
: Sec
.blocks())
84 if (!PreservedBlocks
.count(B
))
85 G
.addAnonymousSymbol(*B
, 0, 0, false, true);
87 return Error::success();
92 ExecutorAddr RegisterActionAddr
;
95 template <typename MachOTraits
>
96 class MachODebugObjectSynthesizer
: public MachODebugObjectSynthesizerBase
{
98 class MachOStructWriter
{
100 MachOStructWriter(MutableArrayRef
<char> Buffer
) : Buffer(Buffer
) {}
102 size_t getOffset() const { return Offset
; }
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())
108 MachO::swapStruct(S
);
109 memcpy(Buffer
.data() + Offset
, &S
, sizeof(S
));
114 MutableArrayRef
<char> Buffer
;
119 using MachODebugObjectSynthesizerBase::MachODebugObjectSynthesizerBase
;
121 Error
startSynthesis() override
{
123 dbgs() << "Creating " << SynthDebugSectionName
<< " for " << G
.getName()
126 auto &SDOSec
= G
.createSection(SynthDebugSectionName
, MemProt::Read
);
128 struct DebugSectionInfo
{
129 Section
*Sec
= nullptr;
132 uint64_t Alignment
= 0;
133 orc::ExecutorAddr StartAddr
;
137 SmallVector
<DebugSectionInfo
, 12> DebugSecInfos
;
138 size_t NumSections
= 0;
139 for (auto &Sec
: G
.sections()) {
140 if (llvm::empty(Sec
.blocks()))
144 if (isDebugSection(Sec
)) {
145 size_t SepPos
= Sec
.getName().find(',');
146 if (SepPos
> 16 || (Sec
.getName().size() - (SepPos
+ 1) > 16)) {
148 dbgs() << "Skipping debug object synthesis for graph "
150 << ": encountered non-standard DWARF section name \""
151 << Sec
.getName() << "\"\n";
153 return Error::success();
155 DebugSecInfos
.push_back({&Sec
, Sec
.getName().substr(0, SepPos
),
156 Sec
.getName().substr(SepPos
+ 1), 0,
157 orc::ExecutorAddr(), 0});
159 NonDebugSections
.push_back(&Sec
);
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.
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);
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);
189 // Copy debug section blocks and symbols.
190 orc::ExecutorAddr
NextBlockAddr(MachOContainerBlock
->getSize());
191 for (auto &SI
: DebugSecInfos
) {
192 assert(!llvm::empty(SI
.Sec
->blocks()) && "Empty debug info section?");
194 // Update addresses in debug section.
196 dbgs() << " Appending " << SI
.Sec
->getName() << " ("
197 << SI
.Sec
->blocks_size() << " block(s)) at "
198 << formatv("{0:x8}", NextBlockAddr
) << "\n";
200 for (auto *B
: SI
.Sec
->blocks()) {
201 NextBlockAddr
= alignToBlock(NextBlockAddr
, *B
);
202 B
->setAddress(NextBlockAddr
);
203 NextBlockAddr
+= B
->getSize();
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",
211 inconvertibleErrorCode());
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",
215 inconvertibleErrorCode());
217 SI
.Alignment
= FirstBlock
.getAlignment();
218 SI
.StartAddr
= FirstBlock
.getAddress();
219 SI
.Size
= NextBlockAddr
- SI
.StartAddr
;
220 G
.mergeSections(SDOSec
, *SI
.Sec
);
223 size_t DebugSectionsSize
=
224 NextBlockAddr
- orc::ExecutorAddr(MachOContainerBlock
->getSize());
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()) {
233 Hdr
.cputype
= MachO::CPU_TYPE_X86_64
;
234 Hdr
.cpusubtype
= MachO::CPU_SUBTYPE_X86_64_ALL
;
236 case Triple::aarch64
:
237 Hdr
.cputype
= MachO::CPU_TYPE_ARM64
;
238 Hdr
.cpusubtype
= MachO::CPU_SUBTYPE_ARM64_ALL
;
241 llvm_unreachable("Unsupported architecture");
243 Hdr
.filetype
= MachO::MH_OBJECT
;
245 Hdr
.sizeofcmds
= SegmentLCSize
;
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
;
258 MachO::VM_PROT_READ
| MachO::VM_PROT_WRITE
| MachO::VM_PROT_EXECUTE
;
260 MachO::VM_PROT_READ
| MachO::VM_PROT_WRITE
| MachO::VM_PROT_EXECUTE
;
261 SegLC
.nsects
= NumSections
;
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();
273 Sec
.offset
= SI
.StartAddr
.getValue();
274 Sec
.align
= SI
.Alignment
;
277 Sec
.flags
= MachO::S_ATTR_DEBUG
;
281 // Set MachOContainerBlock to indicate success to
282 // completeSynthesisAndRegister.
283 NonDebugSectionsStart
= Writer
.getOffset();
284 return Error::success();
287 Error
completeSynthesisAndRegister() override
{
288 if (!MachOContainerBlock
) {
290 dbgs() << "Not writing MachO debug object header for " << G
.getName()
291 << " since createDebugSection failed\n";
293 return Error::success();
297 dbgs() << "Writing MachO debug object header for " << G
.getName() << "\n";
300 MachOStructWriter
Writer(
301 MachOContainerBlock
->getAlreadyMutableContent().drop_front(
302 NonDebugSectionsStart
));
304 unsigned LongSectionNameIdx
= 0;
305 for (auto *Sec
: NonDebugSections
) {
306 size_t SepPos
= Sec
->getName().find(',');
307 StringRef SegName
, SecName
;
308 std::string CustomSecName
;
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);
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);
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",
335 inconvertibleErrorCode());
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();
344 SecCmd
.align
= R
.getFirstBlock()->getAlignment();
348 Writer
.write(SecCmd
);
351 SectionRange
R(MachOContainerBlock
->getSection());
352 G
.allocActions().push_back({cantFail(shared::WrapperFunctionCall::Create
<
353 SPSArgList
<SPSExecutorAddrRange
>>(
354 RegisterActionAddr
, R
.getRange())),
356 return Error::success();
360 Block
*MachOContainerBlock
= nullptr;
361 SmallVector
<Section
*, 16> NonDebugSections
;
362 size_t NonDebugSectionsStart
= 0;
365 } // end anonymous namespace
370 Expected
<std::unique_ptr
<GDBJITDebugInfoRegistrationPlugin
>>
371 GDBJITDebugInfoRegistrationPlugin::Create(ExecutionSession
&ES
,
374 auto RegisterActionAddr
=
375 TT
.isOSBinFormatMachO()
376 ? ES
.intern("_llvm_orc_registerJITLoaderGDBAllocAction")
377 : ES
.intern("llvm_orc_registerJITLoaderGDBAllocAction");
379 if (auto Addr
= ES
.lookup({&ProcessJD
}, RegisterActionAddr
))
380 return std::make_unique
<GDBJITDebugInfoRegistrationPlugin
>(
381 ExecutorAddr(Addr
->getAddress()));
383 return Addr
.takeError();
386 Error
GDBJITDebugInfoRegistrationPlugin::notifyFailed(
387 MaterializationResponsibility
&MR
) {
388 return Error::success();
391 Error
GDBJITDebugInfoRegistrationPlugin::notifyRemovingResources(
393 return Error::success();
396 void GDBJITDebugInfoRegistrationPlugin::notifyTransferringResources(
397 ResourceKey DstKey
, ResourceKey SrcKey
) {}
399 void GDBJITDebugInfoRegistrationPlugin::modifyPassConfig(
400 MaterializationResponsibility
&MR
, LinkGraph
&LG
,
401 PassConfiguration
&PassConfig
) {
403 if (LG
.getTargetTriple().getObjectFormat() == Triple::MachO
)
404 modifyPassConfigForMachO(MR
, LG
, PassConfig
);
407 dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph "
408 << LG
.getName() << "(triple = " << LG
.getTargetTriple().str()
414 void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO(
415 MaterializationResponsibility
&MR
, jitlink::LinkGraph
&LG
,
416 jitlink::PassConfiguration
&PassConfig
) {
418 switch (LG
.getTargetTriple().getArch()) {
420 case Triple::aarch64
:
421 // Supported, continue.
422 assert(LG
.getPointerSize() == 8 && "Graph has incorrect pointer size");
423 assert(LG
.getEndianness() == support::little
&&
424 "Graph has incorrect endianness");
429 dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported "
430 << "MachO graph " << LG
.getName()
431 << "(triple = " << LG
.getTargetTriple().str()
432 << ", pointer size = " << LG
.getPointerSize() << ", endianness = "
433 << (LG
.getEndianness() == support::big
? "big" : "little")
439 // Scan for debug sections. If we find one then install passes.
440 bool HasDebugSections
= false;
441 for (auto &Sec
: LG
.sections())
442 if (MachODebugObjectSynthesizerBase::isDebugSection(Sec
)) {
443 HasDebugSections
= true;
447 if (HasDebugSections
) {
449 dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG
.getName()
450 << " contains debug info. Installing debugger support passes.\n";
453 auto MDOS
= std::make_shared
<MachODebugObjectSynthesizer
<MachO64LE
>>(
454 LG
, RegisterActionAddr
);
455 PassConfig
.PrePrunePasses
.push_back(
456 [=](LinkGraph
&G
) { return MDOS
->preserveDebugSections(); });
457 PassConfig
.PostPrunePasses
.push_back(
458 [=](LinkGraph
&G
) { return MDOS
->startSynthesis(); });
459 PassConfig
.PreFixupPasses
.push_back(
460 [=](LinkGraph
&G
) { return MDOS
->completeSynthesisAndRegister(); });
463 dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG
.getName()
464 << " contains no debug info. Skipping.\n";