123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471 |
- //===------- DebuggerSupportPlugin.cpp - Utils for debugger support -------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- //
- //===----------------------------------------------------------------------===//
- #include "llvm/ExecutionEngine/Orc/DebuggerSupportPlugin.h"
- #include "llvm/ADT/SmallSet.h"
- #include "llvm/ADT/SmallVector.h"
- #include "llvm/ADT/StringSet.h"
- #include "llvm/BinaryFormat/MachO.h"
- #define DEBUG_TYPE "orc"
- using namespace llvm;
- using namespace llvm::jitlink;
- using namespace llvm::orc;
- static const char *SynthDebugSectionName = "__jitlink_synth_debug_object";
- namespace {
- struct MachO64LE {
- using UIntPtr = uint64_t;
- using Header = MachO::mach_header_64;
- using SegmentLC = MachO::segment_command_64;
- using Section = MachO::section_64;
- using NList = MachO::nlist_64;
- static constexpr support::endianness Endianness = support::little;
- static constexpr const uint32_t Magic = MachO::MH_MAGIC_64;
- static constexpr const uint32_t SegmentCmd = MachO::LC_SEGMENT_64;
- };
- class MachODebugObjectSynthesizerBase
- : public GDBJITDebugInfoRegistrationPlugin::DebugSectionSynthesizer {
- public:
- static bool isDebugSection(Section &Sec) {
- return Sec.getName().startswith("__DWARF,");
- }
- MachODebugObjectSynthesizerBase(LinkGraph &G, ExecutorAddr RegisterActionAddr)
- : G(G), RegisterActionAddr(RegisterActionAddr) {}
- virtual ~MachODebugObjectSynthesizerBase() = default;
- Error preserveDebugSections() {
- if (G.findSectionByName(SynthDebugSectionName)) {
- LLVM_DEBUG({
- dbgs() << "MachODebugObjectSynthesizer skipping graph " << G.getName()
- << " which contains an unexpected existing "
- << SynthDebugSectionName << " section.\n";
- });
- return Error::success();
- }
- LLVM_DEBUG({
- dbgs() << "MachODebugObjectSynthesizer visiting graph " << G.getName()
- << "\n";
- });
- for (auto &Sec : G.sections()) {
- if (!isDebugSection(Sec))
- continue;
- // Preserve blocks in this debug section by marking one existing symbol
- // live for each block, and introducing a new live, anonymous symbol for
- // each currently unreferenced block.
- LLVM_DEBUG({
- dbgs() << " Preserving debug section " << Sec.getName() << "\n";
- });
- SmallSet<Block *, 8> PreservedBlocks;
- for (auto *Sym : Sec.symbols()) {
- bool NewPreservedBlock =
- PreservedBlocks.insert(&Sym->getBlock()).second;
- if (NewPreservedBlock)
- Sym->setLive(true);
- }
- for (auto *B : Sec.blocks())
- if (!PreservedBlocks.count(B))
- G.addAnonymousSymbol(*B, 0, 0, false, true);
- }
- return Error::success();
- }
- protected:
- LinkGraph &G;
- ExecutorAddr RegisterActionAddr;
- };
- template <typename MachOTraits>
- class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase {
- private:
- class MachOStructWriter {
- public:
- MachOStructWriter(MutableArrayRef<char> Buffer) : Buffer(Buffer) {}
- size_t getOffset() const { return Offset; }
- template <typename MachOStruct> void write(MachOStruct S) {
- assert(Offset + sizeof(S) <= Buffer.size() &&
- "Container block overflow while constructing debug MachO");
- if (MachOTraits::Endianness != support::endian::system_endianness())
- MachO::swapStruct(S);
- memcpy(Buffer.data() + Offset, &S, sizeof(S));
- Offset += sizeof(S);
- }
- private:
- MutableArrayRef<char> Buffer;
- size_t Offset = 0;
- };
- public:
- using MachODebugObjectSynthesizerBase::MachODebugObjectSynthesizerBase;
- Error startSynthesis() override {
- LLVM_DEBUG({
- dbgs() << "Creating " << SynthDebugSectionName << " for " << G.getName()
- << "\n";
- });
- auto &SDOSec = G.createSection(SynthDebugSectionName, MemProt::Read);
- struct DebugSectionInfo {
- Section *Sec = nullptr;
- StringRef SegName;
- StringRef SecName;
- uint64_t Alignment = 0;
- orc::ExecutorAddr StartAddr;
- uint64_t Size = 0;
- };
- SmallVector<DebugSectionInfo, 12> DebugSecInfos;
- size_t NumSections = 0;
- for (auto &Sec : G.sections()) {
- if (Sec.blocks().empty())
- continue;
- ++NumSections;
- if (isDebugSection(Sec)) {
- size_t SepPos = Sec.getName().find(',');
- if (SepPos > 16 || (Sec.getName().size() - (SepPos + 1) > 16)) {
- LLVM_DEBUG({
- dbgs() << "Skipping debug object synthesis for graph "
- << G.getName()
- << ": encountered non-standard DWARF section name \""
- << Sec.getName() << "\"\n";
- });
- return Error::success();
- }
- DebugSecInfos.push_back({&Sec, Sec.getName().substr(0, SepPos),
- Sec.getName().substr(SepPos + 1), 0,
- orc::ExecutorAddr(), 0});
- } else {
- NonDebugSections.push_back(&Sec);
- // If the first block in the section has a non-zero alignment offset
- // then we need to add a padding block, since the section command in
- // the header doesn't allow for aligment offsets.
- SectionRange R(Sec);
- if (!R.empty()) {
- auto &FB = *R.getFirstBlock();
- if (FB.getAlignmentOffset() != 0) {
- auto Padding = G.allocateBuffer(FB.getAlignmentOffset());
- memset(Padding.data(), 0, Padding.size());
- G.createContentBlock(Sec, Padding,
- FB.getAddress() - FB.getAlignmentOffset(),
- FB.getAlignment(), 0);
- }
- }
- }
- }
- // Create container block.
- size_t SectionsCmdSize =
- sizeof(typename MachOTraits::Section) * NumSections;
- size_t SegmentLCSize =
- sizeof(typename MachOTraits::SegmentLC) + SectionsCmdSize;
- size_t ContainerBlockSize =
- sizeof(typename MachOTraits::Header) + SegmentLCSize;
- auto ContainerBlockContent = G.allocateBuffer(ContainerBlockSize);
- MachOContainerBlock = &G.createMutableContentBlock(
- SDOSec, ContainerBlockContent, orc::ExecutorAddr(), 8, 0);
- // Copy debug section blocks and symbols.
- orc::ExecutorAddr NextBlockAddr(MachOContainerBlock->getSize());
- for (auto &SI : DebugSecInfos) {
- assert(!SI.Sec->blocks().empty() && "Empty debug info section?");
- // Update addresses in debug section.
- LLVM_DEBUG({
- dbgs() << " Appending " << SI.Sec->getName() << " ("
- << SI.Sec->blocks_size() << " block(s)) at "
- << formatv("{0:x8}", NextBlockAddr) << "\n";
- });
- for (auto *B : SI.Sec->blocks()) {
- NextBlockAddr = alignToBlock(NextBlockAddr, *B);
- B->setAddress(NextBlockAddr);
- NextBlockAddr += B->getSize();
- }
- auto &FirstBlock = **SI.Sec->blocks().begin();
- if (FirstBlock.getAlignmentOffset() != 0)
- return make_error<StringError>(
- "First block in " + SI.Sec->getName() +
- " section has non-zero alignment offset",
- inconvertibleErrorCode());
- if (FirstBlock.getAlignment() > std::numeric_limits<uint32_t>::max())
- return make_error<StringError>("First block in " + SI.Sec->getName() +
- " has alignment >4Gb",
- inconvertibleErrorCode());
- SI.Alignment = FirstBlock.getAlignment();
- SI.StartAddr = FirstBlock.getAddress();
- SI.Size = NextBlockAddr - SI.StartAddr;
- G.mergeSections(SDOSec, *SI.Sec);
- SI.Sec = nullptr;
- }
- size_t DebugSectionsSize =
- NextBlockAddr - orc::ExecutorAddr(MachOContainerBlock->getSize());
- // Write MachO header and debug section load commands.
- MachOStructWriter Writer(MachOContainerBlock->getAlreadyMutableContent());
- typename MachOTraits::Header Hdr;
- memset(&Hdr, 0, sizeof(Hdr));
- Hdr.magic = MachOTraits::Magic;
- switch (G.getTargetTriple().getArch()) {
- case Triple::x86_64:
- Hdr.cputype = MachO::CPU_TYPE_X86_64;
- Hdr.cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL;
- break;
- case Triple::aarch64:
- Hdr.cputype = MachO::CPU_TYPE_ARM64;
- Hdr.cpusubtype = MachO::CPU_SUBTYPE_ARM64_ALL;
- break;
- default:
- llvm_unreachable("Unsupported architecture");
- }
- Hdr.filetype = MachO::MH_OBJECT;
- Hdr.ncmds = 1;
- Hdr.sizeofcmds = SegmentLCSize;
- Hdr.flags = 0;
- Writer.write(Hdr);
- typename MachOTraits::SegmentLC SegLC;
- memset(&SegLC, 0, sizeof(SegLC));
- SegLC.cmd = MachOTraits::SegmentCmd;
- SegLC.cmdsize = SegmentLCSize;
- SegLC.vmaddr = ContainerBlockSize;
- SegLC.vmsize = DebugSectionsSize;
- SegLC.fileoff = ContainerBlockSize;
- SegLC.filesize = DebugSectionsSize;
- SegLC.maxprot =
- MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE;
- SegLC.initprot =
- MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE;
- SegLC.nsects = NumSections;
- SegLC.flags = 0;
- Writer.write(SegLC);
- StringSet<> ExistingLongNames;
- for (auto &SI : DebugSecInfos) {
- typename MachOTraits::Section Sec;
- memset(&Sec, 0, sizeof(Sec));
- memcpy(Sec.sectname, SI.SecName.data(), SI.SecName.size());
- memcpy(Sec.segname, SI.SegName.data(), SI.SegName.size());
- Sec.addr = SI.StartAddr.getValue();
- Sec.size = SI.Size;
- Sec.offset = SI.StartAddr.getValue();
- Sec.align = SI.Alignment;
- Sec.reloff = 0;
- Sec.nreloc = 0;
- Sec.flags = MachO::S_ATTR_DEBUG;
- Writer.write(Sec);
- }
- // Set MachOContainerBlock to indicate success to
- // completeSynthesisAndRegister.
- NonDebugSectionsStart = Writer.getOffset();
- return Error::success();
- }
- Error completeSynthesisAndRegister() override {
- if (!MachOContainerBlock) {
- LLVM_DEBUG({
- dbgs() << "Not writing MachO debug object header for " << G.getName()
- << " since createDebugSection failed\n";
- });
- return Error::success();
- }
- LLVM_DEBUG({
- dbgs() << "Writing MachO debug object header for " << G.getName() << "\n";
- });
- MachOStructWriter Writer(
- MachOContainerBlock->getAlreadyMutableContent().drop_front(
- NonDebugSectionsStart));
- unsigned LongSectionNameIdx = 0;
- for (auto *Sec : NonDebugSections) {
- size_t SepPos = Sec->getName().find(',');
- StringRef SegName, SecName;
- std::string CustomSecName;
- if ((SepPos == StringRef::npos && Sec->getName().size() <= 16)) {
- // No embedded segment name, short section name.
- SegName = "__JITLINK_CUSTOM";
- SecName = Sec->getName();
- } else if (SepPos < 16 && (Sec->getName().size() - (SepPos + 1) <= 16)) {
- // Canonical embedded segment and section name.
- SegName = Sec->getName().substr(0, SepPos);
- SecName = Sec->getName().substr(SepPos + 1);
- } else {
- // Long section name that needs to be truncated.
- assert(Sec->getName().size() > 16 &&
- "Short section name should have been handled above");
- SegName = "__JITLINK_CUSTOM";
- auto IdxStr = std::to_string(++LongSectionNameIdx);
- CustomSecName = Sec->getName().substr(0, 15 - IdxStr.size()).str();
- CustomSecName += ".";
- CustomSecName += IdxStr;
- SecName = StringRef(CustomSecName.data(), 16);
- }
- SectionRange R(*Sec);
- if (R.getFirstBlock()->getAlignmentOffset() != 0)
- return make_error<StringError>(
- "While building MachO debug object for " + G.getName() +
- " first block has non-zero alignment offset",
- inconvertibleErrorCode());
- typename MachOTraits::Section SecCmd;
- memset(&SecCmd, 0, sizeof(SecCmd));
- memcpy(SecCmd.sectname, SecName.data(), SecName.size());
- memcpy(SecCmd.segname, SegName.data(), SegName.size());
- SecCmd.addr = R.getStart().getValue();
- SecCmd.size = R.getSize();
- SecCmd.offset = 0;
- SecCmd.align = R.getFirstBlock()->getAlignment();
- SecCmd.reloff = 0;
- SecCmd.nreloc = 0;
- SecCmd.flags = 0;
- Writer.write(SecCmd);
- }
- SectionRange R(MachOContainerBlock->getSection());
- G.allocActions().push_back(
- {cantFail(shared::WrapperFunctionCall::Create<
- shared::SPSArgList<shared::SPSExecutorAddrRange>>(
- RegisterActionAddr, R.getRange())),
- {}});
- return Error::success();
- }
- private:
- Block *MachOContainerBlock = nullptr;
- SmallVector<Section *, 16> NonDebugSections;
- size_t NonDebugSectionsStart = 0;
- };
- } // end anonymous namespace
- namespace llvm {
- namespace orc {
- Expected<std::unique_ptr<GDBJITDebugInfoRegistrationPlugin>>
- GDBJITDebugInfoRegistrationPlugin::Create(ExecutionSession &ES,
- JITDylib &ProcessJD,
- const Triple &TT) {
- auto RegisterActionAddr =
- TT.isOSBinFormatMachO()
- ? ES.intern("_llvm_orc_registerJITLoaderGDBAllocAction")
- : ES.intern("llvm_orc_registerJITLoaderGDBAllocAction");
- if (auto Addr = ES.lookup({&ProcessJD}, RegisterActionAddr))
- return std::make_unique<GDBJITDebugInfoRegistrationPlugin>(
- ExecutorAddr(Addr->getAddress()));
- else
- return Addr.takeError();
- }
- Error GDBJITDebugInfoRegistrationPlugin::notifyFailed(
- MaterializationResponsibility &MR) {
- return Error::success();
- }
- Error GDBJITDebugInfoRegistrationPlugin::notifyRemovingResources(
- JITDylib &JD, ResourceKey K) {
- return Error::success();
- }
- void GDBJITDebugInfoRegistrationPlugin::notifyTransferringResources(
- JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) {}
- void GDBJITDebugInfoRegistrationPlugin::modifyPassConfig(
- MaterializationResponsibility &MR, LinkGraph &LG,
- PassConfiguration &PassConfig) {
- if (LG.getTargetTriple().getObjectFormat() == Triple::MachO)
- modifyPassConfigForMachO(MR, LG, PassConfig);
- else {
- LLVM_DEBUG({
- dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph "
- << LG.getName() << "(triple = " << LG.getTargetTriple().str()
- << "\n";
- });
- }
- }
- void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO(
- MaterializationResponsibility &MR, jitlink::LinkGraph &LG,
- jitlink::PassConfiguration &PassConfig) {
- switch (LG.getTargetTriple().getArch()) {
- case Triple::x86_64:
- case Triple::aarch64:
- // Supported, continue.
- assert(LG.getPointerSize() == 8 && "Graph has incorrect pointer size");
- assert(LG.getEndianness() == support::little &&
- "Graph has incorrect endianness");
- break;
- default:
- // Unsupported.
- LLVM_DEBUG({
- dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported "
- << "MachO graph " << LG.getName()
- << "(triple = " << LG.getTargetTriple().str()
- << ", pointer size = " << LG.getPointerSize() << ", endianness = "
- << (LG.getEndianness() == support::big ? "big" : "little")
- << ")\n";
- });
- return;
- }
- // Scan for debug sections. If we find one then install passes.
- bool HasDebugSections = false;
- for (auto &Sec : LG.sections())
- if (MachODebugObjectSynthesizerBase::isDebugSection(Sec)) {
- HasDebugSections = true;
- break;
- }
- if (HasDebugSections) {
- LLVM_DEBUG({
- dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
- << " contains debug info. Installing debugger support passes.\n";
- });
- auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>(
- LG, RegisterActionAddr);
- PassConfig.PrePrunePasses.push_back(
- [=](LinkGraph &G) { return MDOS->preserveDebugSections(); });
- PassConfig.PostPrunePasses.push_back(
- [=](LinkGraph &G) { return MDOS->startSynthesis(); });
- PassConfig.PreFixupPasses.push_back(
- [=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); });
- } else {
- LLVM_DEBUG({
- dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
- << " contains no debug info. Skipping.\n";
- });
- }
- }
- } // namespace orc
- } // namespace llvm
|