//===------------ 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 #include #include namespace llvm { namespace orc { template size_t writeMachOStruct(MutableArrayRef 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(&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 Buf, size_t Offset, bool SwapStruct) = 0; }; /// MachOBuilder load command wrapper type. template struct MachOBuilderLoadCommandImplBase; #define HANDLE_LOAD_COMMAND(Name, Value, LCStruct) \ template <> \ struct MachOBuilderLoadCommandImplBase \ : public MachO::LCStruct, public MachOBuilderLoadCommandBase { \ using CmdStruct = LCStruct; \ MachOBuilderLoadCommandImplBase() { \ memset(&rawStruct(), 0, sizeof(CmdStruct)); \ cmd = Value; \ cmdsize = sizeof(CmdStruct); \ } \ template \ MachOBuilderLoadCommandImplBase(ArgTs &&...Args) \ : CmdStruct{Value, sizeof(CmdStruct), std::forward(Args)...} {} \ CmdStruct &rawStruct() { return static_cast(*this); } \ size_t size() const override { return cmdsize; } \ size_t write(MutableArrayRef Buf, size_t Offset, \ bool SwapStruct) override { \ return writeMachOStruct(Buf, Offset, rawStruct(), SwapStruct); \ } \ }; #include "llvm/BinaryFormat/MachO.def" #undef HANDLE_LOAD_COMMAND template struct MachOBuilderLoadCommand : public MachOBuilderLoadCommandImplBase { public: MachOBuilderLoadCommand() = default; template MachOBuilderLoadCommand(ArgTs &&...Args) : MachOBuilderLoadCommand(std::forward(Args)...) {} }; template <> struct MachOBuilderLoadCommand : public MachOBuilderLoadCommandImplBase { MachOBuilderLoadCommand(std::string Name, uint32_t Timestamp, uint32_t CurrentVersion, uint32_t CompatibilityVersion) : MachOBuilderLoadCommandImplBase( MachO::dylib{24, Timestamp, CurrentVersion, CompatibilityVersion}), Name(std::move(Name)) { cmdsize += (this->Name.size() + 1 + 3) & ~0x3; } size_t write(MutableArrayRef Buf, size_t Offset, bool SwapStruct) override { Offset = writeMachOStruct(Buf, Offset, rawStruct(), SwapStruct); strcpy(Buf.data() + Offset, Name.data()); return Offset + ((Name.size() + 1 + 3) & ~0x3); } std::string Name; }; template <> struct MachOBuilderLoadCommand : public MachOBuilderLoadCommandImplBase { MachOBuilderLoadCommand(std::string Name, uint32_t Timestamp, uint32_t CurrentVersion, uint32_t CompatibilityVersion) : MachOBuilderLoadCommandImplBase( MachO::dylib{24, Timestamp, CurrentVersion, CompatibilityVersion}), Name(std::move(Name)) { cmdsize += (this->Name.size() + 1 + 3) & ~0x3; } size_t write(MutableArrayRef Buf, size_t Offset, bool SwapStruct) override { Offset = writeMachOStruct(Buf, Offset, rawStruct(), SwapStruct); strcpy(Buf.data() + Offset, Name.data()); return Offset + ((Name.size() + 1 + 3) & ~0x3); } std::string Name; }; template <> struct MachOBuilderLoadCommand : public MachOBuilderLoadCommandImplBase { MachOBuilderLoadCommand(std::string Path) : MachOBuilderLoadCommandImplBase(12u), Path(std::move(Path)) { cmdsize += (this->Path.size() + 1 + 3) & ~0x3; } size_t write(MutableArrayRef Buf, size_t Offset, bool SwapStruct) override { Offset = writeMachOStruct(Buf, Offset, rawStruct(), SwapStruct); strcpy(Buf.data() + Offset, Path.data()); return Offset + ((Path.size() + 1 + 3) & ~0x3); } std::string Path; }; // Builds MachO objects. template class MachOBuilder { private: struct SymbolContainer { size_t SymbolIndexBase = 0; std::vector Symbols; }; struct StringTableEntry { StringRef S; size_t Offset; }; using StringTable = std::vector; static bool swapStruct() { return MachOTraits::Endianness != llvm::endianness::native; } 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(*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 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(*this); } }; struct Segment : public MachOBuilderLoadCommand { MachOBuilder &Builder; std::vector> Sections; Segment(MachOBuilder &Builder, StringRef SegName) : MachOBuilderLoadCommand(), 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
(Builder, SecName, SegName)); return *Sections.back(); } size_t write(MutableArrayRef Buf, size_t Offset, bool SwapStruct) override { Offset = MachOBuilderLoadCommand::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 MachOBuilderLoadCommand &addLoadCommand(ArgTs &&...Args) { static_assert(LCType != MachOTraits::SegmentCmd, "Use addSegment to add segment load command"); auto LC = std::make_unique>( std::forward(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 *SymTabLC = nullptr; if (!StrTab.empty()) SymTabLC = &addLoadCommand(); // 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, size_t{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 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 Buf, size_t Offset) { Header.ncmds = Segments.size() + LoadCommands.size(); return writeMachOStruct(Buf, Offset, Header, swapStruct()); } size_t writeSegments(MutableArrayRef Buf, size_t Offset) { for (auto &Seg : Segments) Offset = Seg.write(Buf, Offset, swapStruct()); return Offset; } size_t writeLoadCommands(MutableArrayRef Buf, size_t Offset) { for (auto &LC : LoadCommands) Offset = LC->write(Buf, Offset, swapStruct()); return Offset; } size_t writeSectionContent(MutableArrayRef 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 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(&R.rawStruct()), sizeof(MachO::relocation_info)); Offset += sizeof(MachO::relocation_info); } } } return Offset; } size_t writeSymbols(MutableArrayRef 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 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 Segments; std::vector> LoadCommands; SymbolContainer SC; // Maps strings to their "id" (addition order). std::map 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 llvm::endianness Endianness = llvm::endianness::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