diff options
Diffstat (limited to 'lld/lib/ReaderWriter/MachO/WriterMachO.cpp')
-rw-r--r-- | lld/lib/ReaderWriter/MachO/WriterMachO.cpp | 1533 |
1 files changed, 1533 insertions, 0 deletions
diff --git a/lld/lib/ReaderWriter/MachO/WriterMachO.cpp b/lld/lib/ReaderWriter/MachO/WriterMachO.cpp new file mode 100644 index 00000000000..3d91a8a5040 --- /dev/null +++ b/lld/lib/ReaderWriter/MachO/WriterMachO.cpp @@ -0,0 +1,1533 @@ +//===- lib/ReaderWriter/MachO/WriterMachO.cpp -----------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/ReaderWriter/WriterMachO.h" + +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +//#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/system_error.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/SmallVector.h" + +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/File.h" +#include "lld/Core/InputFiles.h" +#include "lld/Core/Reference.h" +#include "lld/Core/SharedLibraryAtom.h" + +#include <vector> +#include <map> +#include <string.h> + +#include "MachOFormat.hpp" +#include "ReferenceKinds.h" +#include "ExecutableAtoms.hpp" +#include "GOTPass.hpp" +#include "StubsPass.hpp" + + +namespace lld { +namespace mach_o { + +// +// A mach-o file consists of some meta data (header and load commands), +// then atom content (e.g. function instructions), then more meta data +// (symbol table, etc). Before you can write a mach-o file, you need to +// compute what will be the file offsets and "addresses" of various things +// in the file. +// +// The design here is to break up what will be the mach-o file into chunks. +// Each Chunk has an object to manage its size and content. There is a +// chunk for the mach_header, one for the load commands, and one for each +// part of the LINKEDIT segment. There is also one chunk for each traditional +// mach-o section. The MachOWriter manages the list of chunks. And +// asks each to determine its size in the correct order. Many chunks +// cannot be sized until other chunks are sized (e.g. the dyld info +// in the LINKEDIT cannot be sized until all atoms have been assigned +// addresses). +// +// Once all chunks have a size, the MachOWriter iterates through them and +// asks each to write out their content. +// + + + +// +// A Chunk is an abstrace contiguous range of a generated +// mach-o executable file. +// +class Chunk { +public: + virtual StringRef segmentName() const = 0; + virtual bool occupiesNoDiskSpace(); + virtual void write(uint8_t *fileBuffer) = 0; + void assignFileOffset(uint64_t &curOff, uint64_t &curAddr); + virtual const char* info() = 0; + uint64_t size() const; + uint64_t address() const; + uint64_t fileOffset() const; + uint64_t align2() const; + static uint64_t alignTo(uint64_t value, uint8_t align2); + +protected: + Chunk(); + + uint64_t _size; + uint64_t _address; + uint64_t _fileOffset; + uint32_t _align2; +}; + + + +// +// A SectionChunk represents a set of Atoms assigned to a specific +// mach-o section (which is a subrange of a mach-o segment). +// For example, there is one SectionChunk for the __TEXT,__text section. +// +class SectionChunk : public Chunk { +public: + static SectionChunk* make(DefinedAtom::ContentType, + const WriterOptionsMachO &options, + class MachOWriter &writer); + virtual StringRef segmentName() const; + virtual bool occupiesNoDiskSpace(); + virtual void write(uint8_t *fileBuffer); + virtual const char* info(); + StringRef sectionName(); + uint32_t flags() const; + uint32_t permissions(); + void appendAtom(const DefinedAtom*); + + struct AtomInfo { + const DefinedAtom *atom; + uint64_t offsetInSection; + }; + + const std::vector<AtomInfo>& atoms() const; + +private: + SectionChunk(StringRef seg, + StringRef sect, + uint32_t flags, + const WriterOptionsMachO &options, + class MachOWriter &writer); + + void applyFixup(Reference::Kind kind, uint64_t addend, + uint8_t* location, uint64_t fixupAddress, + uint64_t targetAddress); + + StringRef _segmentName; + StringRef _sectionName; + const WriterOptionsMachO &_options; + class MachOWriter &_writer; + uint32_t _flags; + uint32_t _permissions; + std::vector<AtomInfo> _atoms; +}; + + + +// +// A MachHeaderChunk represents the mach_header struct at the start +// of a mach-o executable file. +// +class MachHeaderChunk : public Chunk { +public: + MachHeaderChunk(const WriterOptionsMachO &options, + const File &file); + virtual StringRef segmentName() const; + virtual void write(uint8_t *fileBuffer); + virtual const char* info(); + void recordLoadCommand(load_command*); + uint64_t loadCommandsSize(); + +private: + uint32_t filetype(WriterOptionsMachO::OutputKind kind); + + mach_header _mh; +}; + + + +// +// A LoadCommandsChunk represents the variable length list of +// of load commands in a mach-o executable file right after the +// mach_header. +// +class LoadCommandsChunk : public Chunk { +public: + LoadCommandsChunk(MachHeaderChunk&, + const WriterOptionsMachO &options, + class MachOWriter&); + virtual StringRef segmentName() const; + virtual void write(uint8_t *fileBuffer); + virtual const char* info(); + void computeSize(const lld::File &file); + void addSection(SectionChunk*); + void updateLoadCommandContent(const lld::File &file); + +private: + friend class LoadCommandPaddingChunk; + + void addLoadCommand(load_command* lc); + void setMachOSection(SectionChunk *chunk, + segment_command_64 *seg, uint32_t index); + uint32_t permissionsFromSections( + const SmallVector<SectionChunk*,16> &); + + struct ChunkSegInfo { + SectionChunk* chunk; + segment_command_64* segment; + section_64* section; + }; + + MachHeaderChunk &_mh; + const WriterOptionsMachO &_options; + class MachOWriter &_writer; + segment_command_64 *_linkEditSegment; + symtab_command *_symbolTableLoadCommand; + entry_point_command *_entryPointLoadCommand; + dyld_info_command *_dyldInfoLoadCommand; + std::vector<load_command*> _loadCmds; + std::vector<ChunkSegInfo> _sectionInfo; +}; + + + +// +// A LoadCommandPaddingChunk represents the padding space between the last +// load commmand and the first section (usually __text) in the __TEXT +// segment. +// +class LoadCommandPaddingChunk : public Chunk { +public: + LoadCommandPaddingChunk(LoadCommandsChunk&); + virtual StringRef segmentName() const; + virtual void write(uint8_t *fileBuffer); + virtual const char* info(); + void computeSize(); +private: + LoadCommandsChunk& _loadCommandsChunk; +}; + + + +// +// LinkEditChunk is the base class for all chunks in the +// __LINKEDIT segment at the end of a mach-o executable. +// +class LinkEditChunk : public Chunk { +public: + LinkEditChunk(); + virtual StringRef segmentName() const; + virtual void computeSize(const lld::File &file, + const std::vector<SectionChunk*>&) = 0; +}; + + + +// +// A DyldInfoChunk represents the bytes for any of the dyld info areas +// in the __LINKEDIT segment at the end of a mach-o executable. +// +class DyldInfoChunk : public LinkEditChunk { +public: + DyldInfoChunk(class MachOWriter &); + virtual void write(uint8_t *fileBuffer); + +protected: + void append_byte(uint8_t); + void append_uleb128(uint64_t); + void append_string(StringRef); + + class MachOWriter &_writer; + std::vector<uint8_t> _bytes; +}; + + + +// +// A BindingInfoChunk represents the bytes containing binding info +// in the __LINKEDIT segment at the end of a mach-o executable. +// +class BindingInfoChunk : public DyldInfoChunk { +public: + BindingInfoChunk(class MachOWriter &); + virtual void computeSize(const lld::File &file, + const std::vector<SectionChunk*>&); + virtual const char* info(); +}; + + + +// +// A LazyBindingInfoChunk represents the bytes containing lazy binding info +// in the __LINKEDIT segment at the end of a mach-o executable. +// +class LazyBindingInfoChunk : public DyldInfoChunk { +public: + LazyBindingInfoChunk(class MachOWriter &); + virtual void computeSize(const lld::File &file, + const std::vector<SectionChunk*>&); + virtual const char* info(); +private: + void updateHelper(const DefinedAtom *, uint32_t ); +}; + + +// +// A SymbolTableChunk represents the array of nlist structs in the +// __LINKEDIT segment at the end of a mach-o executable. +// +class SymbolTableChunk : public LinkEditChunk { +public: + SymbolTableChunk(class SymbolStringsChunk&); + virtual void write(uint8_t *fileBuffer); + virtual void computeSize(const lld::File &file, + const std::vector<SectionChunk*>&); + virtual const char* info(); + uint32_t count(); + +private: + uint8_t nType(const DefinedAtom*); + + SymbolStringsChunk &_stringsChunk; + std::vector<nlist_64> _globalDefinedsymbols; + std::vector<nlist_64> _localDefinedsymbols; + std::vector<nlist_64> _undefinedsymbols; +}; + + +// +// A SymbolStringsChunk represents the strings pointed to +// by nlist structs in the __LINKEDIT segment at the end +// of a mach-o executable. +// +class SymbolStringsChunk : public LinkEditChunk { +public: + SymbolStringsChunk(); + virtual void write(uint8_t *fileBuffer); + virtual void computeSize(const lld::File &file, + const std::vector<SectionChunk*>&); + virtual const char* info(); + uint32_t stringIndex(StringRef); + +private: + std::vector<char> _strings; +}; + + +// +// A MachOWriter manages all the Chunks that comprise a mach-o executable. +// +class MachOWriter : public Writer { +public: + MachOWriter(const WriterOptionsMachO &options); + + virtual error_code writeFile(const lld::File &file, StringRef path); + virtual StubsPass *stubPass(); + virtual GOTPass *gotPass(); + virtual void addFiles(InputFiles&); + + uint64_t addressOfAtom(const Atom *atom); + void findSegment(StringRef segmentName, uint32_t *segIndex, + uint64_t *segStartAddr, uint64_t *segEndAddr); + + const std::vector<Chunk*> chunks() { return _chunks; } + +private: + friend class LoadCommandsChunk; + friend class LazyBindingInfoChunk; + + void build(const lld::File &file); + void createChunks(const lld::File &file); + void buildAtomToAddressMap(); + void assignFileOffsets(); + void addLinkEditChunk(LinkEditChunk *chunk); + void buildLinkEdit(const lld::File &file); + void assignLinkEditFileOffsets(); + void dump(); + + + typedef llvm::DenseMap<const Atom*, uint64_t> AtomToAddress; + + const WriterOptionsMachO &_options; + StubsPass _stubsPass; + GOTPass _gotPass; + CRuntimeFile _cRuntimeFile; + LoadCommandsChunk *_loadCommandsChunk; + LoadCommandPaddingChunk *_paddingChunk; + AtomToAddress _atomToAddress; + std::vector<Chunk*> _chunks; + std::vector<SectionChunk*> _sectionChunks; + std::vector<LinkEditChunk*> _linkEditChunks; + BindingInfoChunk *_bindingInfo; + LazyBindingInfoChunk *_lazyBindingInfo; + SymbolTableChunk *_symbolTableChunk; + SymbolStringsChunk *_stringsChunk; + const DefinedAtom *_mainAtom; + uint64_t _linkEditStartOffset; + uint64_t _linkEditStartAddress; +}; + + + +//===----------------------------------------------------------------------===// +// Chunk +//===----------------------------------------------------------------------===// + +Chunk::Chunk() + : _size(0), _address(0), _fileOffset(0), _align2(0) { +} + +bool Chunk::occupiesNoDiskSpace() { + return false; +} + +uint64_t Chunk::size() const { + return _size; +} + +uint64_t Chunk::align2() const { + return _align2; +} + +uint64_t Chunk::address() const { + return _address; +} + +uint64_t Chunk::fileOffset() const { + return _fileOffset; +} + +uint64_t Chunk::alignTo(uint64_t value, uint8_t align2) { + uint64_t align = 1 << align2; + return ( (value + (align-1)) & (-align) ); +} + +void Chunk::assignFileOffset(uint64_t &curOffset, uint64_t &curAddress) { + if ( this->occupiesNoDiskSpace() ) { + // FileOffset does not change, but address space does change. + uint64_t alignedAddress = alignTo(curAddress, _align2); + _address = alignedAddress; + curAddress = alignedAddress + _size; + } + else { + // FileOffset and address both move by _size amount after alignment. + uint64_t alignPadding = alignTo(curAddress, _align2) - curAddress; + _fileOffset = curOffset + alignPadding; + _address = curAddress + alignPadding; + curOffset = _fileOffset + _size; + curAddress = _address + _size; + } + + DEBUG_WITH_TYPE("WriterMachO-layout", llvm::dbgs() + << " fileOffset=" + << llvm::format("0x%08X", _fileOffset) + << " address=" + << llvm::format("0x%016X", _address) + << " info=" << this->info() << "\n"); +} + + + +//===----------------------------------------------------------------------===// +// SectionChunk +//===----------------------------------------------------------------------===// + +SectionChunk::SectionChunk(StringRef seg, StringRef sect, + uint32_t flags, const WriterOptionsMachO &options, + MachOWriter &writer) + : _segmentName(seg), _sectionName(sect), _options(options), + _writer(writer), _flags(flags), _permissions(0) { + +} + +SectionChunk* SectionChunk::make(DefinedAtom::ContentType type, + const WriterOptionsMachO &options, + MachOWriter &writer) { + switch ( type ) { + case DefinedAtom::typeCode: + return new SectionChunk("__TEXT", "__text", + S_REGULAR | S_ATTR_PURE_INSTRUCTIONS, + options, writer); + break; + case DefinedAtom::typeCString: + return new SectionChunk("__TEXT", "__cstring", + S_CSTRING_LITERALS, + options, writer); + break; + case DefinedAtom::typeStub: + return new SectionChunk("__TEXT", "__stubs", + S_SYMBOL_STUBS | S_ATTR_PURE_INSTRUCTIONS, + options, writer); + break; + case DefinedAtom::typeStubHelper: + return new SectionChunk("__TEXT", "__stub_helper", + S_REGULAR | S_ATTR_PURE_INSTRUCTIONS, + options, writer); + break; + case DefinedAtom::typeLazyPointer: + return new SectionChunk("__DATA", "__la_symbol_ptr", + S_LAZY_SYMBOL_POINTERS, + options, writer); + break; + case DefinedAtom::typeGOT: + return new SectionChunk("__DATA", "__got", + S_NON_LAZY_SYMBOL_POINTERS, + options, writer); + break; + default: + assert(0 && "TO DO: add support for more sections"); + break; + } + return nullptr; +} + +bool SectionChunk::occupiesNoDiskSpace() { + return ( (_flags & SECTION_TYPE) == S_ZEROFILL ); +} + +StringRef SectionChunk::segmentName() const { + return _segmentName; +} + +StringRef SectionChunk::sectionName() { + return _sectionName; +} + +uint32_t SectionChunk::flags() const { + return _flags; +} + +uint32_t SectionChunk::permissions() { + return _permissions; +} + +const char* SectionChunk::info() { + return _sectionName.data(); +} + +const std::vector<SectionChunk::AtomInfo>& SectionChunk::atoms() const { + return _atoms; +} + +void SectionChunk::appendAtom(const DefinedAtom *atom) { + // Figure out offset for atom in this section given alignment constraints. + uint64_t offset = _size; + DefinedAtom::Alignment atomAlign = atom->alignment(); + uint64_t align2 = 1 << atomAlign.powerOf2; + uint64_t requiredModulus = atomAlign.modulus; + uint64_t currentModulus = (offset % align2); + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + offset += requiredModulus-currentModulus; + else + offset += align2+requiredModulus-currentModulus; + } + // Record max alignment of any atom in this section. + if ( align2 > _align2 ) + _align2 = align2; + // Assign atom to this section with this offset. + SectionChunk::AtomInfo ai = {atom, offset}; + _atoms.push_back(ai); + // Update section size to include this atom. + _size = offset + atom->size(); + // Update permissions + DefinedAtom::ContentPermissions perms = atom->permissions(); + if ( (perms & DefinedAtom::permR__) == DefinedAtom::permR__ ) + _permissions |= VM_PROT_READ; + if ( (perms & DefinedAtom::permRW_) == DefinedAtom::permRW_ ) + _permissions |= VM_PROT_WRITE; + if ( (perms & DefinedAtom::permR_X) == DefinedAtom::permR_X ) + _permissions |= VM_PROT_EXECUTE; +} + + +void SectionChunk::write(uint8_t *chunkBuffer) { + // Each section's content is just its atoms' content. + for (const AtomInfo &atomInfo : _atoms ) { + // Copy raw content of atom to file buffer. + ArrayRef<uint8_t> content = atomInfo.atom->rawContent(); + uint64_t contentSize = content.size(); + if ( contentSize == 0 ) + continue; + uint8_t* atomContent = chunkBuffer + atomInfo.offsetInSection; + ::memcpy(atomContent, content.data(), contentSize); + // Apply fixups to file buffer + for (const Reference *ref : *atomInfo.atom) { + uint32_t offset = ref->offsetInAtom(); + uint64_t targetAddress = 0; + if ( ref->target() != nullptr ) + targetAddress = _writer.addressOfAtom(ref->target()); + uint64_t fixupAddress = _writer.addressOfAtom(atomInfo.atom) + offset; + this->applyFixup(ref->kind(), ref->addend(), &atomContent[offset], + fixupAddress, targetAddress); + } + } +} + + + +void SectionChunk::applyFixup(Reference::Kind kind, uint64_t addend, + uint8_t* location, uint64_t fixupAddress, + uint64_t targetAddress) { + //fprintf(stderr, "applyFixup(kind=%s, addend=0x%0llX, " + // "fixupAddress=0x%0llX, targetAddress=0x%0llX\n", + // kindToString(kind).data(), addend, + // fixupAddress, targetAddress); + if ( ReferenceKind::isRipRel32(kind) ) { + // compute rip relative value and update. + int32_t* loc32 = reinterpret_cast<int32_t*>(location); + *loc32 = (targetAddress - (fixupAddress+4)) + addend; + } + else if ( kind == ReferenceKind::x86_64_pointer64 ) { + uint64_t* loc64 = reinterpret_cast<uint64_t*>(location); + *loc64 = targetAddress + addend; + } +} + + + +//===----------------------------------------------------------------------===// +// MachHeaderChunk +//===----------------------------------------------------------------------===// + +MachHeaderChunk::MachHeaderChunk(const WriterOptionsMachO &options, + const File &file) { + // Set up mach_header based on options + _mh.magic = MAGIC_64; + _mh.cputype = options.cpuType(); + _mh.cpusubtype = options.cpuSubtype(); + _mh.filetype = this->filetype(options.outputKind()); + _mh.ncmds = 0; + _mh.sizeofcmds = 0; + _mh.flags = 0; + _mh.reserved = 0; + + _size = _mh.size(); +} + + +StringRef MachHeaderChunk::segmentName() const { + return StringRef("__TEXT"); +} + +void MachHeaderChunk::write(uint8_t *chunkBuffer) { + _mh.copyTo(chunkBuffer); +} + +const char* MachHeaderChunk::info() { + return "mach_header"; +} + +void MachHeaderChunk::recordLoadCommand(load_command* lc) { + _mh.recordLoadCommand(lc); +} + +uint64_t MachHeaderChunk::loadCommandsSize() { + return _mh.sizeofcmds; +} + +uint32_t MachHeaderChunk::filetype(WriterOptionsMachO::OutputKind kind) { + switch ( kind ) { + case WriterOptionsMachO::outputDynamicExecutable: + return MH_EXECUTE; + case WriterOptionsMachO::outputDylib: + return MH_DYLIB; + case WriterOptionsMachO::outputBundle: + return MH_BUNDLE; + case WriterOptionsMachO::outputObjectFile: + return MH_OBJECT; + } + assert(0 && "file outputkind not supported"); +} + + + +//===----------------------------------------------------------------------===// +// LoadCommandsChunk +//===----------------------------------------------------------------------===// + +LoadCommandsChunk::LoadCommandsChunk(MachHeaderChunk &mh, + const WriterOptionsMachO &options, + MachOWriter& writer) + : _mh(mh), _options(options), _writer(writer), + _linkEditSegment(nullptr), _symbolTableLoadCommand(nullptr), + _entryPointLoadCommand(nullptr), _dyldInfoLoadCommand(nullptr) { +} + + +StringRef LoadCommandsChunk::segmentName() const { + return StringRef("__TEXT"); +} + +void LoadCommandsChunk::write(uint8_t *chunkBuffer) { + uint8_t* p = chunkBuffer; + for ( load_command* lc : _loadCmds ) { + assert( ((uintptr_t)p & 0x3) == 0); + lc->copyTo(p); + p += lc->cmdsize; + } +} + +const char* LoadCommandsChunk::info() { + return "load commands"; +} + +void LoadCommandsChunk::setMachOSection(SectionChunk *chunk, + segment_command_64 *seg, uint32_t index) { + for (ChunkSegInfo &entry : _sectionInfo) { + if ( entry.chunk == chunk ) { + entry.section = &(seg->sections[index]); + entry.segment = seg; + return; + } + } + assert(0 && "setMachOSection() chunk not found"); +} + +uint32_t LoadCommandsChunk::permissionsFromSections( + const SmallVector<SectionChunk*,16> §ions) { + uint32_t result = 0; + for (SectionChunk *chunk : sections) { + result |= chunk->permissions(); + } + return result; +} + +void LoadCommandsChunk::computeSize(const lld::File &file) { + // Main executables have a __PAGEZERO segment. + uint64_t pageZeroSize = _options.pageZeroSize(); + if ( pageZeroSize != 0 ) { + segment_command_64* pzSegCmd = segment_command_64::make(0); + strcpy(pzSegCmd->segname, "__PAGEZERO"); + pzSegCmd->vmaddr = 0; + pzSegCmd->vmsize = pageZeroSize; + pzSegCmd->fileoff = 0; + pzSegCmd->filesize = 0; + pzSegCmd->maxprot = 0; + pzSegCmd->initprot = 0; + pzSegCmd->nsects = 0; + pzSegCmd->flags = 0; + this->addLoadCommand(pzSegCmd); + } + // Add other segment load commands + StringRef lastSegName = StringRef("__TEXT"); + SmallVector<SectionChunk*,16> sections; + for (ChunkSegInfo &entry : _sectionInfo) { + StringRef entryName = entry.chunk->segmentName(); + if ( !lastSegName.equals(entryName) ) { + // Start of new segment, so create load command for all previous sections. + segment_command_64* segCmd = segment_command_64::make(sections.size()); + strncpy(segCmd->segname, lastSegName.data(), 16); + segCmd->initprot = this->permissionsFromSections(sections); + segCmd->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE; + this->addLoadCommand(segCmd); + unsigned int index = 0; + for (SectionChunk *chunk : sections) { + this->setMachOSection(chunk, segCmd, index); + ++index; + } + // Reset to begin new segment. + sections.clear(); + lastSegName = entryName; + } + sections.push_back(entry.chunk); + } + // Add last segment load command. + segment_command_64* segCmd = segment_command_64::make(sections.size()); + strncpy(segCmd->segname, lastSegName.data(), 16); + segCmd->initprot = this->permissionsFromSections(sections);; + segCmd->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE; + this->addLoadCommand(segCmd); + unsigned int index = 0; + for (SectionChunk *chunk : sections) { + this->setMachOSection(chunk, segCmd, index); + ++index; + } + + // Add LINKEDIT segment load command + _linkEditSegment = segment_command_64::make(0); + strcpy(_linkEditSegment->segname, "__LINKEDIT"); + _linkEditSegment->initprot = VM_PROT_READ; + _linkEditSegment->maxprot = VM_PROT_READ; + this->addLoadCommand(_linkEditSegment); + + // Add dyld load command. + this->addLoadCommand(dylinker_command::make("/usr/lib/dyld")); + + // Add dylib load commands. + llvm::StringMap<uint32_t> dylibNamesToOrdinal; + for (const SharedLibraryAtom* shlibAtom : file.sharedLibrary() ) { + StringRef installName = shlibAtom->loadName(); + if ( dylibNamesToOrdinal.count(installName) == 0 ) { + uint32_t ord = dylibNamesToOrdinal.size(); + dylibNamesToOrdinal[installName] = ord; + } + } + for (llvm::StringMap<uint32_t>::iterator it=dylibNamesToOrdinal.begin(), + end=dylibNamesToOrdinal.end(); it != end; ++it) { + this->addLoadCommand(dylib_command::make(it->first().data())); + } + + // Add symbol table load command + _symbolTableLoadCommand = symtab_command::make(); + this->addLoadCommand(_symbolTableLoadCommand); + + // Add dyld info load command + _dyldInfoLoadCommand = dyld_info_command::make(); + this->addLoadCommand(_dyldInfoLoadCommand); + + // Add entry point load command to main executables + if (_options.outputKind() == WriterOptionsMachO::outputDynamicExecutable) { + _entryPointLoadCommand = entry_point_command::make(); + this->addLoadCommand(_entryPointLoadCommand); + } + + // Compute total size. + _size = _mh.loadCommandsSize(); +} + + +void LoadCommandsChunk::updateLoadCommandContent(const lld::File &file) { + // Update segment/section information in segment load commands + segment_command_64 *lastSegment = nullptr; + for (ChunkSegInfo &entry : _sectionInfo) { + // Set section info. + ::strncpy(entry.section->sectname, entry.chunk->sectionName().data(), 16); + ::strncpy(entry.section->segname, entry.chunk->segmentName().data(), 16); + entry.section->addr = entry.chunk->address(); + entry.section->size = entry.chunk->size(); + entry.section->offset = entry.chunk->fileOffset(); + entry.section->align = entry.chunk->align2(); + entry.section->reloff = 0; + entry.section->nreloc = 0; + entry.section->flags = entry.chunk->flags(); + // Adjust segment info if needed. + if ( entry.segment != lastSegment ) { + // This is first section in segment. + if ( strcmp(entry.segment->segname, "__TEXT") == 0 ) { + // __TEXT segment is special need mach_header section. + entry.segment->vmaddr = _writer._chunks.front()->address(); + entry.segment->fileoff = _writer._chunks.front()->fileOffset(); + } + else { + entry.segment->vmaddr = entry.chunk->address(); + entry.segment->fileoff = entry.chunk->fileOffset(); + } + + lastSegment = entry.segment; + } + uint64_t sectionEndAddr = entry.section->addr + entry.section->size; + if ( entry.segment->vmaddr + entry.segment->vmsize < sectionEndAddr) { + uint64_t sizeToEndOfSection = sectionEndAddr - entry.segment->vmaddr; + entry.segment->vmsize = alignTo(sizeToEndOfSection, 12); + // zero-fill sections do not increase the segment's filesize + if ( ! entry.chunk->occupiesNoDiskSpace() ) { + entry.segment->filesize = alignTo(sizeToEndOfSection, 12); + } + } + } + uint64_t linkEditSize = _writer._stringsChunk->fileOffset() + + _writer._stringsChunk->size() + - _writer._linkEditStartOffset; + _linkEditSegment->vmaddr = _writer._linkEditStartAddress; + _linkEditSegment->vmsize = alignTo(linkEditSize,12); + _linkEditSegment->fileoff = _writer._linkEditStartOffset; + _linkEditSegment->filesize = linkEditSize; + + // Update dyld_info load command. + _dyldInfoLoadCommand->bind_off = _writer._bindingInfo->fileOffset(); + _dyldInfoLoadCommand->bind_size = _writer._bindingInfo->size(); + _dyldInfoLoadCommand->lazy_bind_off = _writer._lazyBindingInfo->fileOffset(); + _dyldInfoLoadCommand->lazy_bind_size = _writer._lazyBindingInfo->size(); + + + // Update symbol table load command. + _symbolTableLoadCommand->symoff = _writer._symbolTableChunk->fileOffset(); + _symbolTableLoadCommand->nsyms = _writer._symbolTableChunk->count(); + _symbolTableLoadCommand->stroff = _writer._stringsChunk->fileOffset(); + _symbolTableLoadCommand->strsize = _writer._stringsChunk->size(); + + // Update entry point + if ( _entryPointLoadCommand != nullptr ) { + const Atom *mainAtom = _writer._mainAtom; + assert(mainAtom != nullptr); + uint32_t entryOffset = _writer.addressOfAtom(mainAtom) - _mh.address(); + _entryPointLoadCommand->entryoff = entryOffset; + } +} + + +void LoadCommandsChunk::addSection(SectionChunk* chunk) { + LoadCommandsChunk::ChunkSegInfo csi = {chunk, nullptr, nullptr}; + _sectionInfo.push_back(csi); +} + +void LoadCommandsChunk::addLoadCommand(load_command* lc) { + _mh.recordLoadCommand(lc); + _loadCmds.push_back(lc); +} + + + +//===----------------------------------------------------------------------===// +// LoadCommandPaddingChunk +//===----------------------------------------------------------------------===// + +LoadCommandPaddingChunk::LoadCommandPaddingChunk(LoadCommandsChunk& lcc) + : _loadCommandsChunk(lcc) { +} + +StringRef LoadCommandPaddingChunk::segmentName() const { + return StringRef("__TEXT"); +} + +void LoadCommandPaddingChunk::write(uint8_t *chunkBuffer) { + // Zero fill padding. +} + +const char* LoadCommandPaddingChunk::info() { + return "padding"; +} + +// Segments are page sized. Normally, any extra space not used by atoms +// is put at the end of the last page. But the __TEXT segment is special. +// Any extra space is put between the load commands and the first section. +// The padding is put there to allow the load commands to be +// post-processed which might potentially grow them. +void LoadCommandPaddingChunk::computeSize() { + // Layout __TEXT sections backwards from end of page to get padding up front. + uint64_t addr = 0; + std::vector<LoadCommandsChunk::ChunkSegInfo>& sects + = _loadCommandsChunk._sectionInfo; + for (auto it=sects.rbegin(), end=sects.rend(); it != end; ++it) { + LoadCommandsChunk::ChunkSegInfo &entry = *it; + if ( !entry.chunk->segmentName().equals("__TEXT") ) + continue; + addr -= entry.chunk->size(); + addr = addr & (0 - (1 << entry.chunk->align2())); + } + // Subtract out size of mach_header and all load commands. + addr -= _loadCommandsChunk._mh.size(); + addr -= _loadCommandsChunk.size(); + // Modulo page size to get padding needed between load commands + // and first section. + _size = (addr % 4096); +} + +//===----------------------------------------------------------------------===// +// LinkEditChunk +//===----------------------------------------------------------------------===// + +LinkEditChunk::LinkEditChunk() { + _align2 = 3; +} + +StringRef LinkEditChunk::segmentName() const { + return StringRef("__LINKEDIT"); +} + + +//===----------------------------------------------------------------------===// +// DyldInfoChunk +//===----------------------------------------------------------------------===// +DyldInfoChunk::DyldInfoChunk(MachOWriter &writer) + : _writer(writer) { +} + +void DyldInfoChunk::write(uint8_t *chunkBuffer) { + ::memcpy(chunkBuffer, &_bytes[0], _bytes.size()); +} + +void DyldInfoChunk::append_byte(uint8_t b) { + _bytes.push_back(b); +} + +void DyldInfoChunk::append_string(StringRef str) { + _bytes.insert(_bytes.end(), str.begin(), str.end()); + _bytes.push_back('\0'); +} + +void DyldInfoChunk::append_uleb128(uint64_t value) { + uint8_t byte; + do { + byte = value & 0x7F; + value &= ~0x7F; + if ( value != 0 ) + byte |= 0x80; + _bytes.push_back(byte); + value = value >> 7; + } while( byte >= 0x80 ); +} + + + +//===----------------------------------------------------------------------===// +// BindingInfoChunk +//===----------------------------------------------------------------------===// + +BindingInfoChunk::BindingInfoChunk(MachOWriter &writer) + : DyldInfoChunk(writer) { +} + +const char* BindingInfoChunk::info() { + return "binding info"; +} + +void BindingInfoChunk::computeSize(const lld::File &file, + const std::vector<SectionChunk*> &chunks) { + for (const SectionChunk *chunk : chunks ) { + // skip lazy pointer section + if ( chunk->flags() == S_LAZY_SYMBOL_POINTERS ) + continue; + // skip code sections + if ( chunk->flags() == (S_REGULAR | S_ATTR_PURE_INSTRUCTIONS) ) + continue; + uint64_t segStartAddr = 0; + uint64_t segEndAddr = 0; + uint32_t segIndex = 0; + _writer.findSegment(chunk->segmentName(), + &segIndex, &segStartAddr, &segEndAddr); + for (const SectionChunk::AtomInfo &info : chunk->atoms() ) { + const DefinedAtom* atom = info.atom; + StringRef targetName; + int ordinal; + + // look for fixups pointing to shlib atoms + for (const Reference *ref : *atom ) { + const Atom *target = ref->target(); + if ( target != nullptr ) { + const SharedLibraryAtom *shlTarget + = dyn_cast<SharedLibraryAtom>(target); + if ( shlTarget != nullptr ) { + assert(ref->kind() == ReferenceKind::x86_64_pointer64); + targetName = shlTarget->name(); + ordinal = 1; + } + } + } + + if ( targetName.empty() ) + continue; + + // write location of fixup + this->append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segIndex); + uint64_t address = _writer.addressOfAtom(atom); + this->append_uleb128(address - segStartAddr); + + // write ordinal + if ( ordinal <= 0 ) { + // special lookups are encoded as negative numbers in BindingInfo + this->append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM + | (ordinal & BIND_IMMEDIATE_MASK) ); + } + else if ( ordinal <= 15 ) { + // small ordinals are encoded in opcode + this->append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | ordinal); + } + else { + this->append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + this->append_uleb128(ordinal); + } + + // write binding type + this->append_byte(BIND_OPCODE_SET_TYPE_IMM | BIND_TYPE_POINTER); + + // write symbol name and flags + int flags = 0; + this->append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | flags); + this->append_string(targetName); + + // write do bind + this->append_byte(BIND_OPCODE_DO_BIND); + this->append_byte(BIND_OPCODE_DONE); + } + } + _size = _bytes.size(); +} + + +//===----------------------------------------------------------------------===// +// LazyBindingInfoChunk +//===----------------------------------------------------------------------===// + +LazyBindingInfoChunk::LazyBindingInfoChunk(MachOWriter &writer) + : DyldInfoChunk(writer) { +} + +const char* LazyBindingInfoChunk::info() { + return "lazy binding info"; +} + +void LazyBindingInfoChunk::updateHelper(const DefinedAtom *lazyPointerAtom, + uint32_t offset) { + for (const Reference *ref : *lazyPointerAtom ) { + if ( ref->kind() != ReferenceKind::x86_64_pointer64 ) + continue; + const DefinedAtom *helperAtom = dyn_cast<DefinedAtom>(ref->target()); + assert(helperAtom != nullptr); + for (const Reference *href : *helperAtom ) { + if ( href->kind() == ReferenceKind::x86_64_lazyImm ) { + (const_cast<Reference*>(href))->setAddend(offset); + return; + } + } + } + assert(0 && "could not update helper lazy immediate value"); +} + +void LazyBindingInfoChunk::computeSize(const lld::File &file, + const std::vector<SectionChunk*> &chunks) { + for (const SectionChunk *chunk : chunks ) { + if ( chunk->flags() != S_LAZY_SYMBOL_POINTERS ) + continue; + uint64_t segStartAddr = 0; + uint64_t segEndAddr = 0; + uint32_t segIndex = 0; + _writer.findSegment(chunk->segmentName(), + &segIndex, &segStartAddr, &segEndAddr); + for (const SectionChunk::AtomInfo &info : chunk->atoms() ) { + const DefinedAtom *lazyPointerAtom = info.atom; + assert(lazyPointerAtom->contentType() == DefinedAtom::typeLazyPointer); + // Update help to have offset of the lazy binding info. + this->updateHelper(lazyPointerAtom, _bytes.size()); + + // Write location of fixup. + this->append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segIndex); + uint64_t address = _writer.addressOfAtom(lazyPointerAtom); + this->append_uleb128(address - segStartAddr); + + // write ordinal + int ordinal = 1; + if ( ordinal <= 0 ) { + // special lookups are encoded as negative numbers in BindingInfo + this->append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM + | (ordinal & BIND_IMMEDIATE_MASK) ); + } + else if ( ordinal <= 15 ) { + // small ordinals are encoded in opcode + this->append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | ordinal); + } + else { + this->append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + this->append_uleb128(ordinal); + } + + // write symbol name and flags + int flags = 0; + StringRef name; + for (const Reference *ref : *lazyPointerAtom ) { + if ( ref->kind() == ReferenceKind::x86_64_lazyTarget ) { + const Atom *shlib = ref->target(); + assert(shlib != nullptr); + name = shlib->name(); + } + } + assert(!name.empty()); + this->append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | flags); + this->append_string(name); + + // write do bind + this->append_byte(BIND_OPCODE_DO_BIND); + this->append_byte(BIND_OPCODE_DONE); + } + } + _size = _bytes.size(); +} + + +//===----------------------------------------------------------------------===// +// SymbolTableChunk +//===----------------------------------------------------------------------===// + +SymbolTableChunk::SymbolTableChunk(SymbolStringsChunk& str) + : _stringsChunk(str) { +} + +void SymbolTableChunk::write(uint8_t *chunkBuffer) { + uint8_t *p = chunkBuffer; + for ( nlist_64 &sym : _globalDefinedsymbols ) { + sym.copyTo(p); + p += sizeof(nlist_64); + } + for ( nlist_64 &sym : _localDefinedsymbols ) { + sym.copyTo(p); + p += sizeof(nlist_64); + } + for ( nlist_64 &sym : _undefinedsymbols ) { + sym.copyTo(p); + p += sizeof(nlist_64); + } +} + +const char* SymbolTableChunk::info() { + return "symbol tables "; +} + +uint32_t SymbolTableChunk::count() { + return _globalDefinedsymbols.size() + + _localDefinedsymbols.size() + + _undefinedsymbols.size(); +} + +uint8_t SymbolTableChunk::nType(const DefinedAtom *atom) { + uint8_t result = N_SECT; + switch ( atom->scope() ) { + case DefinedAtom::scopeTranslationUnit: + break; + case DefinedAtom::scopeLinkageUnit: + result |= N_EXT | N_PEXT; + break; + case DefinedAtom::scopeGlobal: + result |= N_EXT; + break; + } + return result; +} + +void SymbolTableChunk::computeSize(const lld::File &file, + const std::vector<SectionChunk*> &chunks) { + // Add symbols for definitions + unsigned int sectionIndex = 1; + for (const SectionChunk *chunk : chunks ) { + for (const SectionChunk::AtomInfo &info : chunk->atoms() ) { + if ( info.atom->name().empty() ) + continue; + uint64_t atomAddress = chunk->address() + info.offsetInSection; + nlist_64 sym; + sym.n_strx = _stringsChunk.stringIndex(info.atom->name()); + sym.n_type = this->nType(info.atom); + sym.n_sect = sectionIndex; + sym.n_value = atomAddress; + if ( info.atom->scope() == DefinedAtom::scopeGlobal ) + _globalDefinedsymbols.push_back(sym); + else + _localDefinedsymbols.push_back(sym); + } + ++sectionIndex; + } + + // Add symbols for undefined/sharedLibrary symbols + for (const SharedLibraryAtom* atom : file.sharedLibrary() ) { + nlist_64 sym; + sym.n_strx = _stringsChunk.stringIndex(atom->name()); + sym.n_type = N_UNDF; + sym.n_sect = 0; + sym.n_value = 0; + _undefinedsymbols.push_back(sym); + } + + _size = sizeof(nlist_64) * this->count(); +} + + +//===----------------------------------------------------------------------===// +// SymbolStringsChunk +//===----------------------------------------------------------------------===// + +SymbolStringsChunk::SymbolStringsChunk() { + // mach-o reserves the first byte in the string pool so that + // zero is never a valid string index. + _strings.push_back('\0'); +} + + +void SymbolStringsChunk::write(uint8_t *chunkBuffer) { + ::memcpy(chunkBuffer, &_strings[0], _strings.size()); +} + +const char* SymbolStringsChunk::info() { + return "symbol strings "; +} + +void SymbolStringsChunk::computeSize(const lld::File &file, + const std::vector<SectionChunk*>&) { + _size = _strings.size(); +} + + +uint32_t SymbolStringsChunk::stringIndex(StringRef str) { + uint32_t result = _strings.size(); + _strings.insert(_strings.end(), str.begin(), str.end()); + _strings.push_back('\0'); + return result; +} + + +//===----------------------------------------------------------------------===// +// MachOWriter +//===----------------------------------------------------------------------===// + +MachOWriter::MachOWriter(const WriterOptionsMachO &options) + : _options(options), _stubsPass(options), _cRuntimeFile(options), + _bindingInfo(nullptr), _lazyBindingInfo(nullptr), + _symbolTableChunk(nullptr), _stringsChunk(nullptr), _mainAtom(nullptr), + _linkEditStartOffset(0), _linkEditStartAddress(0) { +} + +void MachOWriter::build(const lld::File &file) { + // Create objects for each chunk. + this->createChunks(file); + + // Now that SectionChunks have sizes, load commands can be laid out + _loadCommandsChunk->computeSize(file); + + // Now that load commands are sized, padding can be computed + _paddingChunk->computeSize(); + + // Now that all chunks (except linkedit) have sizes, assign file offsets + this->assignFileOffsets(); + + // Now chunks have file offsets each atom can be assigned an address + this->buildAtomToAddressMap(); + + // Now that atoms have address, symbol table can be build + this->buildLinkEdit(file); + + // Assign file offsets to linkedit chunks + this->assignLinkEditFileOffsets(); + + // Finally, update load commands to reflect linkEdit layout + _loadCommandsChunk->updateLoadCommandContent(file); +} + + +void MachOWriter::createChunks(const lld::File &file) { + // Assign atoms to chunks, creating new chunks as needed + std::map<DefinedAtom::ContentType, SectionChunk*> map; + for (const DefinedAtom* atom : file.defined() ) { + assert( atom->sectionChoice() == DefinedAtom::sectionBasedOnContent ); + DefinedAtom::ContentType type = atom->contentType(); + auto pos = map.find(type); + if ( pos == map.end() ) { + SectionChunk *chunk = SectionChunk::make(type, _options, *this); + map[type] = chunk; + chunk->appendAtom(atom); + } + else { + pos->second->appendAtom(atom); + } + } + + // Sort Chunks so ones in same segment are contiguous. + + + // Make chunks in __TEXT for mach_header and load commands at start. + MachHeaderChunk *mhc = new MachHeaderChunk(_options, file); + _chunks.push_back(mhc); + + _loadCommandsChunk = new LoadCommandsChunk(*mhc, _options, *this); + _chunks.push_back(_loadCommandsChunk); + + _paddingChunk = new LoadCommandPaddingChunk(*_loadCommandsChunk); + _chunks.push_back(_paddingChunk); + + for (auto it=map.begin(); it != map.end(); ++it) { + _chunks.push_back(it->second); + _sectionChunks.push_back(it->second); + _loadCommandsChunk->addSection(it->second); + } + + // Make LINKEDIT chunks. + _bindingInfo = new BindingInfoChunk(*this); + _lazyBindingInfo = new LazyBindingInfoChunk(*this); + _stringsChunk = new SymbolStringsChunk(); + _symbolTableChunk = new SymbolTableChunk(*_stringsChunk); + this->addLinkEditChunk(_bindingInfo); + this->addLinkEditChunk(_lazyBindingInfo); + this->addLinkEditChunk(_symbolTableChunk); + this->addLinkEditChunk(_stringsChunk); +} + + +void MachOWriter::addLinkEditChunk(LinkEditChunk *chunk) { + _linkEditChunks.push_back(chunk); + _chunks.push_back(chunk); +} + + +void MachOWriter::buildAtomToAddressMap() { + DEBUG_WITH_TYPE("WriterMachO-layout", llvm::dbgs() + << "assign atom addresses:\n"); + const bool lookForMain = + (_options.outputKind() == WriterOptionsMachO::outputDynamicExecutable); + for (SectionChunk *chunk : _sectionChunks ) { + for (const SectionChunk::AtomInfo &info : chunk->atoms() ) { + _atomToAddress[info.atom] = chunk->address() + info.offsetInSection; + if ( lookForMain + && (info.atom->contentType() == DefinedAtom::typeCode) + && (info.atom->size() != 0) + && info.atom->name().equals("_main") ) { + _mainAtom = info.atom; + } + DEBUG_WITH_TYPE("WriterMachO-layout", llvm::dbgs() + << " address=" + << llvm::format("0x%016X", _atomToAddress[info.atom]) + << " atom=" << info.atom + << " name=" << info.atom->name() << "\n"); + } + } +} + +//void MachOWriter::dump() { +// for ( Chunk *chunk : _chunks ) { +// fprintf(stderr, "size=0x%08llX, fileOffset=0x%08llX, address=0x%08llX %s\n", +// chunk->size(), chunk->fileOffset(),chunk->address(), chunk->info()); +// } +//} + +void MachOWriter::assignFileOffsets() { + DEBUG_WITH_TYPE("WriterMachO-layout", llvm::dbgs() + << "assign file offsets:\n"); + uint64_t offset = 0; + uint64_t address = _options.pageZeroSize(); + for ( Chunk *chunk : _chunks ) { + if ( chunk->segmentName().equals("__LINKEDIT") ) { + _linkEditStartOffset = Chunk::alignTo(offset, 12); + _linkEditStartAddress = Chunk::alignTo(address, 12); + break; + } + chunk->assignFileOffset(offset, address); + } +} + +void MachOWriter::assignLinkEditFileOffsets() { + DEBUG_WITH_TYPE("WriterMachO-layout", llvm::dbgs() + << "assign LINKEDIT file offsets:\n"); + uint64_t offset = _linkEditStartOffset; + uint64_t address = _linkEditStartAddress; + for ( Chunk *chunk : _linkEditChunks ) { + chunk->assignFileOffset(offset, address); + } +} + +void MachOWriter::buildLinkEdit(const lld::File &file) { + for (LinkEditChunk *chunk : _linkEditChunks) { + chunk->computeSize(file, _sectionChunks); + } +} + + +uint64_t MachOWriter::addressOfAtom(const Atom *atom) { + return _atomToAddress[atom]; +} + + +void MachOWriter::findSegment(StringRef segmentName, uint32_t *segIndex, + uint64_t *segStartAddr, uint64_t *segEndAddr) { + const uint64_t kInvalidAddress = (uint64_t)(-1); + StringRef lastSegName("__TEXT"); + *segIndex = 0; + if ( _options.pageZeroSize() != 0 ) { + *segIndex = 1; + } + *segStartAddr = kInvalidAddress; + *segEndAddr = kInvalidAddress; + for (SectionChunk *chunk : _sectionChunks ) { + if ( ! lastSegName.equals(chunk->segmentName()) ) { + *segIndex += 1; + lastSegName = chunk->segmentName(); + } + if ( chunk->segmentName().equals(segmentName) ) { + uint64_t chunkEndAddr = chunk->address() + chunk->size(); + if ( *segStartAddr == kInvalidAddress ) { + *segStartAddr = chunk->address(); + *segEndAddr = chunkEndAddr; + } + else if ( *segEndAddr < chunkEndAddr ) { + *segEndAddr = chunkEndAddr; + } + } + } +} + + +// +// Creates a mach-o final linked image from the given atom graph and writes +// it to the supplied output stream. +// +error_code MachOWriter::writeFile(const lld::File &file, StringRef path) { + this->build(file); + +// FIXME: re-enable when FileOutputBuffer is in LLVMSupport.a +#if 0 + uint64_t totalSize = _chunks.back()->fileOffset() + _chunks.back()->size(); + + OwningPtr<llvm::FileOutputBuffer> buffer; + error_code ec = llvm::FileOutputBuffer::create(path, + totalSize, buffer, + llvm::FileOutputBuffer::F_executable); + if ( ec ) + return ec; + + DEBUG_WITH_TYPE("WriterMachO-layout", llvm::dbgs() << "writeFile:\n"); + for ( Chunk *chunk : _chunks ) { + DEBUG_WITH_TYPE("WriterMachO-layout", llvm::dbgs() + << " fileOffset=" + << llvm::format("0x%08X", chunk->fileOffset()) + << " chunk=" + << chunk->info() + << "\n"); + chunk->write(buffer->getBufferStart()+chunk->fileOffset()); + } + + return buffer->commit(); +#else + return error_code::success(); +#endif +} + + +StubsPass *MachOWriter::stubPass() { + return &_stubsPass; +} + +GOTPass *MachOWriter::gotPass() { + return &_gotPass; +} + +void MachOWriter::addFiles(InputFiles &inputFiles) { + inputFiles.prependFile(_cRuntimeFile); +} + + +} // namespace mach_o + +Writer* createWriterMachO(const WriterOptionsMachO &options) { + return new lld::mach_o::MachOWriter(options); +} + +WriterOptionsMachO::WriterOptionsMachO() + : _outputkind(outputDynamicExecutable), + _archName("x86_64"), + _architecture(arch_x86_64), + _pageZeroSize(0x10000000), + _cpuType(mach_o::CPU_TYPE_X86_64), + _cpuSubtype(mach_o::CPU_SUBTYPE_X86_64_ALL), + _noTextRelocations(true) { +} + +WriterOptionsMachO::~WriterOptionsMachO() { +} + + +} // namespace lld + |