[llvm] 75c4876 - [ORC] Add a MachOBuilder utility, use it to build MachO debug objects.
Lang Hames via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 7 10:35:02 PDT 2023
Author: Lang Hames
Date: 2023-09-07T10:34:57-07:00
New Revision: 75c487602a8af130ec0aedf398e318cb57063f2d
URL: https://github.com/llvm/llvm-project/commit/75c487602a8af130ec0aedf398e318cb57063f2d
DIFF: https://github.com/llvm/llvm-project/commit/75c487602a8af130ec0aedf398e318cb57063f2d.diff
LOG: [ORC] Add a MachOBuilder utility, use it to build MachO debug objects.
Adds a utility for creating MachO objects. In this patch we use this utility to
construct "debug objects" for the debugger support plugin; however, this utility
should be reusable for other purposes, e.g. constructing object files for
registration with the ObjC runtime.
The debug objects in this patch are created by the MachO JIT debugging-support
plugin. The debug objects hold the debug info for object files loaded under the
JIT, and are registered with the debugger via the GDB-JIT interface. Previously
the plugin created these objects manually using direct writes to a buffer, but
this was difficult to read and modify and had become an impediment to improving
debugger support (e.g. getting source debugging working in the presence of
dead-stripping). Using the MachOBuilder utility should make the debugger
support plugin easier to maintain and improve.
This initial change to MachO JIT'd code debugging aims for feature parity with
the existing plugin (though the debug objects produced are slightly different).
Improvements to JIT'd code debugging will be tackled in follow-up patches.
Added:
llvm/include/llvm/ExecutionEngine/Orc/MachOBuilder.h
Modified:
llvm/lib/ExecutionEngine/Orc/DebuggerSupportPlugin.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/MachOBuilder.h b/llvm/include/llvm/ExecutionEngine/Orc/MachOBuilder.h
new file mode 100644
index 000000000000000..ed123b766d6634b
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/MachOBuilder.h
@@ -0,0 +1,525 @@
+//===------------ MachOBuilder.h -- Build MachO Objects ---------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Build MachO object files for interaction with the ObjC runtime and debugger.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_MACHOBUILDER_H
+#define LLVM_EXECUTIONENGINE_ORC_MACHOBUILDER_H
+
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/MathExtras.h"
+
+#include <list>
+#include <map>
+#include <vector>
+
+namespace llvm {
+namespace orc {
+
+// Builds MachO objects.
+template <typename MachOTraits> class MachOBuilder {
+private:
+ struct BufferWriter {
+ public:
+ BufferWriter(MutableArrayRef<char> Buffer)
+ : Data(Buffer.data()), Size(Buffer.size()) {}
+
+ size_t tell() const { return Offset; }
+
+ void write(char C) {
+ assert(Offset < Size && "Buffer overflow");
+ Data[Offset++] = C;
+ }
+
+ void write(const char *Src, size_t SrcSize) {
+ assert(Offset + SrcSize <= Size && "Buffer overflow");
+ memcpy(&Data[Offset], Src, SrcSize);
+ Offset += SrcSize;
+ }
+
+ template <typename T> void write(const T &Value) {
+ assert(Offset + sizeof(T) <= Size && "Buffer overflow");
+ memcpy(&Data[Offset], reinterpret_cast<const char *>(&Value), sizeof(T));
+ Offset += sizeof(T);
+ }
+
+ private:
+ char *Data = 0;
+ size_t Offset = 0;
+ size_t Size = 0;
+ };
+
+ struct SymbolContainer {
+ size_t SymbolIndexBase = 0;
+ std::vector<typename MachOTraits::NList> Symbols;
+ };
+
+ static void writeMachOStruct(BufferWriter &BW, MachO::relocation_info RI) {
+ BW.write(RI);
+ }
+
+ template <typename MachOStruct>
+ static void writeMachOStruct(BufferWriter &BW, MachOStruct S) {
+ if (MachOTraits::Endianness != support::endian::system_endianness())
+ MachO::swapStruct(S);
+ BW.write(S);
+ }
+
+ struct LoadCommandBase {
+ virtual ~LoadCommandBase() {}
+ virtual size_t size() const = 0;
+ virtual void write(BufferWriter &BW) = 0;
+ };
+
+ template <MachO::LoadCommandType LCType> struct LoadCommand;
+
+#define HANDLE_LOAD_COMMAND(Name, Value, LCStruct) \
+ template <> \
+ struct LoadCommand<MachO::Name> : public MachO::LCStruct, \
+ public LoadCommandBase { \
+ using CmdStruct = LCStruct; \
+ LoadCommand() { \
+ memset(&rawStruct(), 0, sizeof(CmdStruct)); \
+ cmd = Value; \
+ cmdsize = sizeof(CmdStruct); \
+ } \
+ template <typename... ArgTs> \
+ LoadCommand(ArgTs &&...Args) \
+ : CmdStruct{Value, sizeof(CmdStruct), std::forward<ArgTs>(Args)...} {} \
+ CmdStruct &rawStruct() { return static_cast<CmdStruct &>(*this); } \
+ size_t size() const override { return cmdsize; } \
+ void write(BufferWriter &BW) override { \
+ writeMachOStruct(BW, rawStruct()); \
+ } \
+ };
+
+#include "llvm/BinaryFormat/MachO.def"
+
+#undef HANDLE_LOAD_COMMAND
+
+ struct StringTableEntry {
+ StringRef S;
+ size_t Offset;
+ };
+
+ using StringTable = std::vector<StringTableEntry>;
+
+public:
+ using StringId = size_t;
+
+ struct Section;
+
+ // Points to either an nlist entry (as a (symbol-container, index) pair), or
+ // a section.
+ class RelocTarget {
+ public:
+ RelocTarget(const Section &S) : S(&S), Idx(~0U) {}
+ RelocTarget(SymbolContainer &SC, size_t Idx) : SC(&SC), Idx(Idx) {}
+
+ bool isSymbol() { return Idx != ~0U; }
+
+ uint32_t getSymbolNum() {
+ assert(isSymbol() && "Target is not a symbol");
+ return SC->SymbolIndexBase + Idx;
+ }
+
+ uint32_t getSectionId() {
+ assert(!isSymbol() && "Target is not a section");
+ return S->SectionNumber;
+ }
+
+ typename MachOTraits::NList &nlist() {
+ assert(isSymbol() && "Target is not a symbol");
+ return SC->Symbols[Idx];
+ }
+
+ private:
+ union {
+ const Section *S;
+ SymbolContainer *SC;
+ };
+ size_t Idx;
+ };
+
+ struct Reloc : public MachO::relocation_info {
+ RelocTarget Target;
+
+ Reloc(int32_t Offset, RelocTarget Target, bool PCRel, unsigned Length,
+ unsigned Type)
+ : Target(Target) {
+ assert(Type < 16 && "Relocation type out of range");
+ r_address = Offset; // Will slide to account for sec addr during layout
+ r_symbolnum = 0;
+ r_pcrel = PCRel;
+ r_length = Length;
+ r_extern = Target.isSymbol();
+ r_type = Type;
+ }
+
+ MachO::relocation_info &rawStruct() {
+ return static_cast<MachO::relocation_info &>(*this);
+ }
+ };
+
+ struct SectionContent {
+ const char *Data = nullptr;
+ size_t Size = 0;
+ };
+
+ struct Section : public MachOTraits::Section, public RelocTarget {
+ MachOBuilder &Builder;
+ SectionContent Content;
+ size_t SectionNumber = 0;
+ SymbolContainer SC;
+ std::vector<Reloc> Relocs;
+
+ Section(MachOBuilder &Builder, StringRef SecName, StringRef SegName)
+ : RelocTarget(*this), Builder(Builder) {
+ memset(&rawStruct(), 0, sizeof(typename MachOTraits::Section));
+ assert(SecName.size() <= 16 && "SecName too long");
+ assert(SegName.size() <= 16 && "SegName too long");
+ memcpy(this->sectname, SecName.data(), SecName.size());
+ memcpy(this->segname, SegName.data(), SegName.size());
+ }
+
+ RelocTarget addSymbol(int32_t Offset, StringRef Name, uint8_t Type,
+ uint16_t Desc) {
+ StringId SI = Builder.addString(Name);
+ typename MachOTraits::NList Sym;
+ Sym.n_strx = SI;
+ Sym.n_type = Type | MachO::N_SECT;
+ Sym.n_sect = MachO::NO_SECT; // Will be filled in later.
+ Sym.n_desc = Desc;
+ Sym.n_value = Offset;
+ SC.Symbols.push_back(Sym);
+ return {SC, SC.Symbols.size() - 1};
+ }
+
+ void addReloc(int32_t Offset, RelocTarget Target, bool PCRel,
+ unsigned Length, unsigned Type) {
+ Relocs.push_back({Offset, Target, PCRel, Length, Type});
+ }
+
+ auto &rawStruct() {
+ return static_cast<typename MachOTraits::Section &>(*this);
+ }
+ };
+
+ struct Segment : public LoadCommand<MachOTraits::SegmentCmd> {
+ MachOBuilder &Builder;
+ std::vector<std::unique_ptr<Section>> Sections;
+
+ Segment(MachOBuilder &Builder, StringRef SegName)
+ : LoadCommand<MachOTraits::SegmentCmd>(), Builder(Builder) {
+ assert(SegName.size() <= 16 && "SegName too long");
+ memcpy(this->segname, SegName.data(), SegName.size());
+ this->maxprot =
+ MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE;
+ this->initprot = this->maxprot;
+ }
+
+ Section &addSection(StringRef SecName, StringRef SegName) {
+ Sections.push_back(std::make_unique<Section>(Builder, SecName, SegName));
+ return *Sections.back();
+ }
+
+ void write(BufferWriter &BW) override {
+ writeMachOStruct(BW, this->rawStruct());
+ for (auto &Sec : Sections)
+ writeMachOStruct(BW, Sec->rawStruct());
+ }
+ };
+
+ MachOBuilder(size_t PageSize) : PageSize(PageSize) {
+ memset((char *)&Header, 0, sizeof(Header));
+ Header.magic = MachOTraits::Magic;
+ }
+
+ template <MachO::LoadCommandType LCType, typename... ArgTs>
+ LoadCommand<LCType> &addLoadCommand(ArgTs &&...Args) {
+ static_assert(LCType != MachOTraits::SegmentCmd,
+ "Use addSegment to add segment load command");
+ auto LC =
+ std::make_unique<LoadCommand<LCType>>(std::forward<ArgTs>(Args)...);
+ auto &Tmp = *LC;
+ LoadCommands.push_back(std::move(LC));
+ return Tmp;
+ }
+
+ StringId addString(StringRef Str) {
+ if (Strings.empty() && !Str.empty())
+ addString("");
+ return Strings.insert(std::make_pair(Str, Strings.size())).first->second;
+ }
+
+ Segment &addSegment(StringRef SegName) {
+ Segments.push_back(Segment(*this, SegName));
+ return Segments.back();
+ }
+
+ RelocTarget addSymbol(StringRef Name, uint8_t Type, uint8_t Sect,
+ uint16_t Desc, typename MachOTraits::UIntPtr Value) {
+ StringId SI = addString(Name);
+ typename MachOTraits::NList Sym;
+ Sym.n_strx = SI;
+ Sym.n_type = Type;
+ Sym.n_sect = Sect;
+ Sym.n_desc = Desc;
+ Sym.n_value = Value;
+ SC.Symbols.push_back(Sym);
+ return {SC, SC.Symbols.size() - 1};
+ }
+
+ // Call to perform layout on the MachO. Returns the total size of the
+ // resulting file.
+ // This method will automatically insert some load commands (e.g.
+ // LC_SYMTAB) and fill in load command fields.
+ size_t layout() {
+
+ // Build symbol table and add LC_SYMTAB command.
+ makeStringTable();
+ LoadCommand<MachOTraits::SymTabCmd> *SymTabLC = nullptr;
+ if (!StrTab.empty())
+ SymTabLC = &addLoadCommand<MachOTraits::SymTabCmd>();
+
+ // Lay out header, segment load command, and other load commands.
+ size_t Offset = sizeof(Header);
+ for (auto &Seg : Segments) {
+ Seg.cmdsize +=
+ Seg.Sections.size() * sizeof(typename MachOTraits::Section);
+ Seg.nsects = Seg.Sections.size();
+ Offset += Seg.cmdsize;
+ }
+ for (auto &LC : LoadCommands)
+ Offset += LC->size();
+
+ Header.sizeofcmds = Offset - sizeof(Header);
+
+ // Lay out content, set segment / section addrs and offsets.
+ size_t SegVMAddr = 0;
+ for (auto &Seg : Segments) {
+ Seg.vmaddr = SegVMAddr;
+ Seg.fileoff = Offset;
+ for (auto &Sec : Seg.Sections) {
+ Offset = alignTo(Offset, 1 << Sec->align);
+ if (Sec->Content.Size)
+ Sec->offset = Offset;
+ Sec->size = Sec->Content.Size;
+ Sec->addr = SegVMAddr + Sec->offset - Seg.fileoff;
+ Offset += Sec->Content.Size;
+ }
+ size_t SegContentSize = Offset - Seg.fileoff;
+ Seg.filesize = SegContentSize;
+ Seg.vmsize = Header.filetype == MachO::MH_OBJECT
+ ? SegContentSize
+ : alignTo(SegContentSize, PageSize);
+ SegVMAddr += Seg.vmsize;
+ }
+
+ // Set string table offsets for non-section symbols.
+ for (auto &Sym : SC.Symbols)
+ Sym.n_strx = StrTab[Sym.n_strx].Offset;
+
+ // Number sections, set symbol section numbers and string table offsets,
+ // count relocations.
+ size_t NumSymbols = SC.Symbols.size();
+ size_t SectionNumber = 0;
+ for (auto &Seg : Segments) {
+ for (auto &Sec : Seg.Sections) {
+ ++SectionNumber;
+ Sec->SectionNumber = SectionNumber;
+ Sec->SC.SymbolIndexBase = NumSymbols;
+ NumSymbols += Sec->SC.Symbols.size();
+ for (auto &Sym : Sec->SC.Symbols) {
+ Sym.n_sect = SectionNumber;
+ Sym.n_strx = StrTab[Sym.n_strx].Offset;
+ Sym.n_value += Sec->addr;
+ }
+ }
+ }
+
+ // Handle relocations
+ bool OffsetAlignedForRelocs = false;
+ for (auto &Seg : Segments) {
+ for (auto &Sec : Seg.Sections) {
+ if (!Sec->Relocs.empty()) {
+ if (!OffsetAlignedForRelocs) {
+ Offset = alignTo(Offset, sizeof(MachO::relocation_info));
+ OffsetAlignedForRelocs = true;
+ }
+ Sec->reloff = Offset;
+ Sec->nreloc = Sec->Relocs.size();
+ Offset += Sec->Relocs.size() * sizeof(MachO::relocation_info);
+ for (auto &R : Sec->Relocs)
+ R.r_symbolnum = R.Target.isSymbol() ? R.Target.getSymbolNum()
+ : R.Target.getSectionId();
+ }
+ }
+ }
+
+ // Calculate offset to start of nlist and update symtab command.
+ if (NumSymbols > 0) {
+ Offset = alignTo(Offset, sizeof(typename MachOTraits::NList));
+ SymTabLC->symoff = Offset;
+ SymTabLC->nsyms = NumSymbols;
+
+ // Calculate string table bounds and update symtab command.
+ if (!StrTab.empty()) {
+ Offset += NumSymbols * sizeof(typename MachOTraits::NList);
+ size_t StringTableSize =
+ StrTab.back().Offset + StrTab.back().S.size() + 1;
+
+ SymTabLC->stroff = Offset;
+ SymTabLC->strsize = StringTableSize;
+ Offset += StringTableSize;
+ }
+ }
+
+ return Offset;
+ }
+
+ void write(MutableArrayRef<char> Buffer) {
+ BufferWriter BW(Buffer);
+ writeHeader(BW);
+ writeSegments(BW);
+ writeLoadCommands(BW);
+ writeSectionContent(BW);
+ writeRelocations(BW);
+ writeSymbols(BW);
+ writeStrings(BW);
+ }
+
+ typename MachOTraits::Header Header;
+
+private:
+ void makeStringTable() {
+ if (Strings.empty())
+ return;
+
+ StrTab.resize(Strings.size());
+ for (auto &KV : Strings)
+ StrTab[KV.second] = {KV.first, 0};
+ size_t Offset = 0;
+ for (auto &Elem : StrTab) {
+ Elem.Offset = Offset;
+ Offset += Elem.S.size() + 1;
+ }
+ }
+
+ void writeHeader(BufferWriter &BW) {
+ Header.ncmds = Segments.size() + LoadCommands.size();
+ writeMachOStruct(BW, Header);
+ }
+
+ void writeSegments(BufferWriter &BW) {
+ for (auto &Seg : Segments)
+ Seg.write(BW);
+ }
+
+ void writeLoadCommands(BufferWriter &BW) {
+ for (auto &LC : LoadCommands)
+ LC->write(BW);
+ }
+
+ void writeSectionContent(BufferWriter &BW) {
+ for (auto &Seg : Segments) {
+ for (auto &Sec : Seg.Sections) {
+ if (!Sec->Content.Data) {
+ assert(Sec->Relocs.empty() &&
+ "Cant' have relocs for zero-fill segment");
+ continue;
+ }
+ size_t ZeroPad = Sec->offset - BW.tell();
+ while (ZeroPad--)
+ BW.write('\0');
+ BW.write(Sec->Content.Data, Sec->Content.Size);
+ }
+ }
+ }
+
+ void writeRelocations(BufferWriter &BW) {
+ for (auto &Seg : Segments) {
+ for (auto &Sec : Seg.Sections) {
+ if (!Sec->Relocs.empty()) {
+ while (BW.tell() % sizeof(MachO::relocation_info))
+ BW.write('\0');
+ }
+ for (auto &R : Sec->Relocs)
+ writeMachOStruct(BW, R.rawStruct());
+ }
+ }
+ }
+
+ void writeSymbols(BufferWriter &BW) {
+
+ // Count symbols.
+ size_t NumSymbols = SC.Symbols.size();
+ for (auto &Seg : Segments)
+ for (auto &Sec : Seg.Sections)
+ NumSymbols += Sec->SC.Symbols.size();
+
+ // If none then return.
+ if (NumSymbols == 0)
+ return;
+
+ size_t ZeroPad =
+ alignTo(BW.tell(), sizeof(typename MachOTraits::NList)) - BW.tell();
+ while (ZeroPad--)
+ BW.write('\0');
+
+ // Write non-section symbols.
+ for (auto &Sym : SC.Symbols)
+ writeMachOStruct(BW, Sym);
+
+ // Write section symbols.
+ for (auto &Seg : Segments) {
+ for (auto &Sec : Seg.Sections) {
+ for (auto &Sym : Sec->SC.Symbols) {
+ writeMachOStruct(BW, Sym);
+ }
+ }
+ }
+ }
+
+ void writeStrings(BufferWriter &BW) {
+ for (auto &Elem : StrTab) {
+ BW.write(Elem.S.data(), Elem.S.size());
+ BW.write('\0');
+ }
+ }
+
+ size_t PageSize;
+ std::list<Segment> Segments;
+ std::vector<std::unique_ptr<LoadCommandBase>> LoadCommands;
+ SymbolContainer SC;
+
+ // Maps strings to their "id" (addition order).
+ std::map<StringRef, size_t> Strings;
+ StringTable StrTab;
+};
+
+struct MachO64LE {
+ using UIntPtr = uint64_t;
+ using Header = MachO::mach_header_64;
+ using Section = MachO::section_64;
+ using NList = MachO::nlist_64;
+ using Relocation = MachO::relocation_info;
+
+ static constexpr support::endianness Endianness = support::little;
+ static constexpr uint32_t Magic = MachO::MH_MAGIC_64;
+ static constexpr MachO::LoadCommandType SegmentCmd = MachO::LC_SEGMENT_64;
+ static constexpr MachO::LoadCommandType SymTabCmd = MachO::LC_SYMTAB;
+};
+
+} // namespace orc
+} // namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_ORC_MACHOBUILDER_H
diff --git a/llvm/lib/ExecutionEngine/Orc/DebuggerSupportPlugin.cpp b/llvm/lib/ExecutionEngine/Orc/DebuggerSupportPlugin.cpp
index 830582bb3649115..63c188676d197b4 100644
--- a/llvm/lib/ExecutionEngine/Orc/DebuggerSupportPlugin.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/DebuggerSupportPlugin.cpp
@@ -10,6 +10,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/Orc/DebuggerSupportPlugin.h"
+#include "llvm/ExecutionEngine/Orc/MachOBuilder.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
@@ -26,19 +27,6 @@ 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:
@@ -84,6 +72,7 @@ class MachODebugObjectSynthesizerBase
if (!PreservedBlocks.count(B))
G.addAnonymousSymbol(*B, 0, 0, false, true);
}
+
return Error::success();
}
@@ -94,28 +83,12 @@ class MachODebugObjectSynthesizerBase
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:
+ MachODebugObjectSynthesizer(ExecutionSession &ES, LinkGraph &G,
+ ExecutorAddr RegisterActionAddr)
+ : MachODebugObjectSynthesizerBase(G, RegisterActionAddr),
+ Builder(ES.getPageSize()) {}
+
using MachODebugObjectSynthesizerBase::MachODebugObjectSynthesizerBase;
Error startSynthesis() override {
@@ -123,164 +96,79 @@ class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase {
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();
- }
+ // Skip sections whose name's don't fit the MachO standard.
+ if (Sec.getName().empty() || Sec.getName().size() > 33 ||
+ Sec.getName().find(',') > 16)
+ continue;
- 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;
+ if (isDebugSection(Sec))
+ DebugSections.push_back({&Sec, nullptr});
+ else if (Sec.getMemLifetimePolicy() != MemLifetimePolicy::NoAlloc)
+ NonDebugSections.push_back({&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;
+ Builder.Header.filetype = MachO::MH_OBJECT;
switch (G.getTargetTriple().getArch()) {
case Triple::x86_64:
- Hdr.cputype = MachO::CPU_TYPE_X86_64;
- Hdr.cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL;
+ Builder.Header.cputype = MachO::CPU_TYPE_X86_64;
+ Builder.Header.cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL;
break;
case Triple::aarch64:
- Hdr.cputype = MachO::CPU_TYPE_ARM64;
- Hdr.cpusubtype = MachO::CPU_SUBTYPE_ARM64_ALL;
+ Builder.Header.cputype = MachO::CPU_TYPE_ARM64;
+ Builder.Header.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);
+
+ Seg = &Builder.addSegment("");
+
+ for (auto &DSec : DebugSections) {
+ auto [SegName, SecName] = DSec.GraphSec->getName().split(',');
+ DSec.BuilderSec = &Seg->addSection(SecName, SegName);
+
+ SectionRange SR(*DSec.GraphSec);
+ DSec.BuilderSec->Content.Size = SR.getSize();
+ if (!SR.empty())
+ DSec.BuilderSec->align = Log2_64(SR.getFirstBlock()->getAlignment());
}
- // Set MachOContainerBlock to indicate success to
- // completeSynthesisAndRegister.
- NonDebugSectionsStart = Writer.getOffset();
+ for (auto &NDSP : NonDebugSections) {
+ auto [SegName, SecName] = NDSP.GraphSec->getName().split(',');
+ NDSP.BuilderSec = &Seg->addSection(SecName, SegName);
+ SectionRange SR(*NDSP.GraphSec);
+ if (!SR.empty())
+ NDSP.BuilderSec->align = Log2_64(SR.getFirstBlock()->getAlignment());
+
+ // Add stabs.
+ for (auto *Sym : NDSP.GraphSec->symbols()) {
+ // Skip anonymous symbols.
+ if (!Sym->hasName())
+ continue;
+
+ uint8_t SymType = Sym->isCallable() ? MachO::N_FUN : MachO::N_GSYM;
+
+ Builder.addSymbol("", MachO::N_BNSYM, 1, 0, 0);
+ StabSymbols.push_back(
+ {*Sym, Builder.addSymbol(Sym->getName(), SymType, 1, 0, 0),
+ Builder.addSymbol(Sym->getName(), SymType, 0, 0, 0)});
+ Builder.addSymbol("", MachO::N_ENSYM, 1, 0, 0);
+ }
+ }
+
+ size_t DebugObjectSize = Builder.layout();
+
+ MachOContainerBlock = &G.createMutableContentBlock(
+ SDOSec, G.allocateBuffer(DebugObjectSize), orc::ExecutorAddr(), 8, 0);
+
return Error::success();
}
@@ -290,64 +178,46 @@ class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase {
dbgs() << "Not writing MachO debug object header for " << G.getName()
<< " since createDebugSection failed\n";
});
+
return Error::success();
}
+ ExecutorAddr MaxAddr;
+ for (auto &NDSec : NonDebugSections) {
+ SectionRange SR(*NDSec.GraphSec);
+ NDSec.BuilderSec->addr = SR.getStart().getValue();
+ NDSec.BuilderSec->size = SR.getSize();
+ NDSec.BuilderSec->offset = SR.getStart().getValue();
+ if (SR.getEnd() > MaxAddr)
+ MaxAddr = SR.getEnd();
+ }
+
+ for (auto &DSec : DebugSections) {
+ if (DSec.GraphSec->blocks_size() != 1)
+ return make_error<StringError>(
+ "Unexpected number of blocks in debug info section",
+ inconvertibleErrorCode());
+
+ if (ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size > MaxAddr)
+ MaxAddr = ExecutorAddr(DSec.BuilderSec->addr) + DSec.BuilderSec->size;
+
+ auto &B = **DSec.GraphSec->blocks().begin();
+ DSec.BuilderSec->Content.Data = B.getContent().data();
+ DSec.BuilderSec->Content.Size = B.getContent().size();
+ DSec.BuilderSec->flags |= MachO::S_ATTR_DEBUG;
+ }
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);
+ // Update stab symbol addresses.
+ for (auto &SS : StabSymbols) {
+ SS.StartStab.nlist().n_value = SS.Sym.getAddress().getValue();
+ SS.EndStab.nlist().n_value = SS.Sym.getSize();
}
+ Builder.write(MachOContainerBlock->getAlreadyMutableContent());
+
static constexpr bool AutoRegisterCode = true;
SectionRange R(MachOContainerBlock->getSection());
G.allocActions().push_back(
@@ -355,13 +225,34 @@ class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase {
shared::SPSArgList<shared::SPSExecutorAddrRange, bool>>(
RegisterActionAddr, R.getRange(), AutoRegisterCode)),
{}});
+
return Error::success();
}
private:
+ struct SectionPair {
+ Section *GraphSec = nullptr;
+ typename MachOBuilder<MachOTraits>::Section *BuilderSec = nullptr;
+ };
+
+ struct StabSymbolsEntry {
+ using RelocTarget = typename MachOBuilder<MachOTraits>::RelocTarget;
+
+ StabSymbolsEntry(Symbol &Sym, RelocTarget StartStab, RelocTarget EndStab)
+ : Sym(Sym), StartStab(StartStab), EndStab(EndStab) {}
+
+ Symbol &Sym;
+ RelocTarget StartStab, EndStab;
+ };
+
+ using BuilderType = MachOBuilder<MachOTraits>;
+
Block *MachOContainerBlock = nullptr;
- SmallVector<Section *, 16> NonDebugSections;
- size_t NonDebugSectionsStart = 0;
+ MachOBuilder<MachOTraits> Builder;
+ typename MachOBuilder<MachOTraits>::Segment *Seg = nullptr;
+ std::vector<StabSymbolsEntry> StabSymbols;
+ SmallVector<SectionPair, 16> DebugSections;
+ SmallVector<SectionPair, 16> NonDebugSections;
};
} // end anonymous namespace
@@ -453,12 +344,12 @@ void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO(
});
auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>(
- LG, RegisterActionAddr);
+ MR.getTargetJITDylib().getExecutionSession(), LG, RegisterActionAddr);
PassConfig.PrePrunePasses.push_back(
[=](LinkGraph &G) { return MDOS->preserveDebugSections(); });
PassConfig.PostPrunePasses.push_back(
[=](LinkGraph &G) { return MDOS->startSynthesis(); });
- PassConfig.PreFixupPasses.push_back(
+ PassConfig.PostFixupPasses.push_back(
[=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); });
} else {
LLVM_DEBUG({
More information about the llvm-commits
mailing list