Re-apply 75c487602a "[ORC] Add a MachOBuilder utility, use it to..." with fixes.

This re-applies 75c487602a ([ORC] Add a MachOBuilder utility, use it to build
MachO debug objects), which was reverted in 99e70cc3a5 due to build
failures. The MachoBuilder class has been refactored to fix the errors.
This commit is contained in:
Lang Hames 2023-09-11 08:28:18 -07:00
parent 65a15a56d5
commit 5293109774
2 changed files with 633 additions and 223 deletions

View File

@ -0,0 +1,519 @@
//===------------ 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 {
template <typename MachOStruct>
size_t writeMachOStruct(MutableArrayRef<char> Buf, size_t Offset, MachOStruct S,
bool SwapStruct) {
if (SwapStruct)
MachO::swapStruct(S);
assert(Offset + sizeof(MachOStruct) <= Buf.size() && "Buffer overflow");
memcpy(&Buf[Offset], reinterpret_cast<const char *>(&S), sizeof(MachOStruct));
return Offset + sizeof(MachOStruct);
}
/// Base type for MachOBuilder load command wrappers.
struct MachOBuilderLoadCommandBase {
virtual ~MachOBuilderLoadCommandBase() {}
virtual size_t size() const = 0;
virtual size_t write(MutableArrayRef<char> Buf, size_t Offset,
bool SwapStruct) = 0;
};
/// MachOBuilder load command wrapper type.
template <MachO::LoadCommandType LCType> struct MachOBuilderLoadCommand;
#define HANDLE_LOAD_COMMAND(Name, Value, LCStruct) \
template <> \
struct MachOBuilderLoadCommand<MachO::Name> \
: public MachO::LCStruct, public MachOBuilderLoadCommandBase { \
using CmdStruct = LCStruct; \
MachOBuilderLoadCommand() { \
memset(&rawStruct(), 0, sizeof(CmdStruct)); \
cmd = Value; \
cmdsize = sizeof(CmdStruct); \
} \
template <typename... ArgTs> \
MachOBuilderLoadCommand(ArgTs &&...Args) \
: CmdStruct{Value, sizeof(CmdStruct), std::forward<ArgTs>(Args)...} {} \
CmdStruct &rawStruct() { return static_cast<CmdStruct &>(*this); } \
size_t size() const override { return cmdsize; } \
size_t write(MutableArrayRef<char> Buf, size_t Offset, \
bool SwapStruct) override { \
return writeMachOStruct(Buf, Offset, rawStruct(), SwapStruct); \
} \
};
#include "llvm/BinaryFormat/MachO.def"
#undef HANDLE_LOAD_COMMAND
// Builds MachO objects.
template <typename MachOTraits> class MachOBuilder {
private:
struct SymbolContainer {
size_t SymbolIndexBase = 0;
std::vector<typename MachOTraits::NList> Symbols;
};
struct StringTableEntry {
StringRef S;
size_t Offset;
};
using StringTable = std::vector<StringTableEntry>;
static bool swapStruct() {
return MachOTraits::Endianness != support::endian::system_endianness();
}
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 MachOBuilderLoadCommand<MachOTraits::SegmentCmd> {
MachOBuilder &Builder;
std::vector<std::unique_ptr<Section>> Sections;
Segment(MachOBuilder &Builder, StringRef SegName)
: MachOBuilderLoadCommand<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();
}
size_t write(MutableArrayRef<char> Buf, size_t Offset,
bool SwapStruct) override {
Offset = MachOBuilderLoadCommand<MachOTraits::SegmentCmd>::write(
Buf, Offset, SwapStruct);
for (auto &Sec : Sections)
Offset = writeMachOStruct(Buf, Offset, Sec->rawStruct(), SwapStruct);
return Offset;
}
};
MachOBuilder(size_t PageSize) : PageSize(PageSize) {
memset((char *)&Header, 0, sizeof(Header));
Header.magic = MachOTraits::Magic;
}
template <MachO::LoadCommandType LCType, typename... ArgTs>
MachOBuilderLoadCommand<LCType> &addLoadCommand(ArgTs &&...Args) {
static_assert(LCType != MachOTraits::SegmentCmd,
"Use addSegment to add segment load command");
auto LC = std::make_unique<MachOBuilderLoadCommand<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();
MachOBuilderLoadCommand<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) {
size_t Offset = 0;
Offset = writeHeader(Buffer, Offset);
Offset = writeSegments(Buffer, Offset);
Offset = writeLoadCommands(Buffer, Offset);
Offset = writeSectionContent(Buffer, Offset);
Offset = writeRelocations(Buffer, Offset);
Offset = writeSymbols(Buffer, Offset);
Offset = writeStrings(Buffer, Offset);
}
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;
}
}
size_t writeHeader(MutableArrayRef<char> Buf, size_t Offset) {
Header.ncmds = Segments.size() + LoadCommands.size();
return writeMachOStruct(Buf, Offset, Header, swapStruct());
}
size_t writeSegments(MutableArrayRef<char> Buf, size_t Offset) {
for (auto &Seg : Segments)
Offset = Seg.write(Buf, Offset, swapStruct());
return Offset;
}
size_t writeLoadCommands(MutableArrayRef<char> Buf, size_t Offset) {
for (auto &LC : LoadCommands)
Offset = LC->write(Buf, Offset, swapStruct());
return Offset;
}
size_t writeSectionContent(MutableArrayRef<char> Buf, size_t Offset) {
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;
}
while (Offset != Sec->offset)
Buf[Offset++] = '\0';
assert(Offset + Sec->Content.Size <= Buf.size() && "Buffer overflow");
memcpy(&Buf[Offset], Sec->Content.Data, Sec->Content.Size);
Offset += Sec->Content.Size;
}
}
return Offset;
}
size_t writeRelocations(MutableArrayRef<char> Buf, size_t Offset) {
for (auto &Seg : Segments) {
for (auto &Sec : Seg.Sections) {
if (!Sec->Relocs.empty()) {
while (Offset % sizeof(MachO::relocation_info))
Buf[Offset++] = '\0';
}
for (auto &R : Sec->Relocs) {
assert(Offset + sizeof(MachO::relocation_info) <= Buf.size() &&
"Buffer overflow");
memcpy(&Buf[Offset], reinterpret_cast<const char *>(&R.rawStruct()),
sizeof(MachO::relocation_info));
Offset += sizeof(MachO::relocation_info);
}
}
}
return Offset;
}
size_t writeSymbols(MutableArrayRef<char> Buf, size_t Offset) {
// 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 Offset;
// Align to nlist entry size.
while (Offset % sizeof(typename MachOTraits::NList))
Buf[Offset++] = '\0';
// Write non-section symbols.
for (auto &Sym : SC.Symbols)
Offset = writeMachOStruct(Buf, Offset, Sym, swapStruct());
// Write section symbols.
for (auto &Seg : Segments) {
for (auto &Sec : Seg.Sections) {
for (auto &Sym : Sec->SC.Symbols) {
Offset = writeMachOStruct(Buf, Offset, Sym, swapStruct());
}
}
}
return Offset;
}
size_t writeStrings(MutableArrayRef<char> Buf, size_t Offset) {
for (auto &Elem : StrTab) {
assert(Offset + Elem.S.size() + 1 <= Buf.size() && "Buffer overflow");
memcpy(&Buf[Offset], Elem.S.data(), Elem.S.size());
Offset += Elem.S.size();
Buf[Offset++] = '\0';
}
return Offset;
}
size_t PageSize;
std::list<Segment> Segments;
std::vector<std::unique_ptr<MachOBuilderLoadCommandBase>> 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

View File

@ -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 @@ public:
if (!PreservedBlocks.count(B))
G.addAnonymousSymbol(*B, 0, 0, false, true);
}
return Error::success();
}
@ -94,28 +83,12 @@ protected:
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 @@ public:
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);
// Skip sections whose name's don't fit the MachO standard.
if (Sec.getName().empty() || Sec.getName().size() > 33 ||
Sec.getName().find(',') > 16)
continue;
// 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);
}
}
}
if (isDebugSection(Sec))
DebugSections.push_back({&Sec, nullptr});
else if (Sec.getMemLifetimePolicy() != MemLifetimePolicy::NoAlloc)
NonDebugSections.push_back({&Sec, nullptr});
}
// 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;
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);
Seg = &Builder.addSegment("");
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);
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 @@ public:
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 @@ public:
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({