diff options
Diffstat (limited to 'lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp')
| -rw-r--r-- | lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp | 461 |
1 files changed, 307 insertions, 154 deletions
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp index 4ecfece0629e..f3e159684e15 100644 --- a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp +++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp @@ -25,6 +25,8 @@ #include "MachONormalizedFileBinaryUtils.h" #include "lld/Core/Error.h" #include "lld/Core/LLVM.h" +#include "llvm/ADT/ilist.h" +#include "llvm/ADT/ilist_node.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -35,7 +37,6 @@ #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/Format.h" #include "llvm/Support/Host.h" -#include "llvm/Support/LEB128.h" #include "llvm/Support/MachO.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" @@ -50,6 +51,72 @@ namespace lld { namespace mach_o { namespace normalized { +struct TrieNode; // Forward declaration. + +struct TrieEdge : public llvm::ilist_node<TrieEdge> { + TrieEdge(StringRef s, TrieNode *node) : _subString(s), _child(node) {} + + StringRef _subString; + struct TrieNode *_child; +}; + +} // namespace normalized +} // namespace mach_o +} // namespace lld + + +namespace llvm { + using lld::mach_o::normalized::TrieEdge; + template <> + struct ilist_traits<TrieEdge> + : public ilist_default_traits<TrieEdge> { + private: + mutable ilist_half_node<TrieEdge> Sentinel; + public: + TrieEdge *createSentinel() const { + return static_cast<TrieEdge*>(&Sentinel); + } + void destroySentinel(TrieEdge *) const {} + + TrieEdge *provideInitialHead() const { return createSentinel(); } + TrieEdge *ensureHead(TrieEdge*) const { return createSentinel(); } + static void noteHead(TrieEdge*, TrieEdge*) {} + void deleteNode(TrieEdge *N) {} + + private: + void createNode(const TrieEdge &); + }; +} // namespace llvm + + +namespace lld { +namespace mach_o { +namespace normalized { + +struct TrieNode { + typedef llvm::ilist<TrieEdge> TrieEdgeList; + + TrieNode(StringRef s) + : _cummulativeString(s), _address(0), _flags(0), _other(0), + _trieOffset(0), _hasExportInfo(false) {} + ~TrieNode() = default; + + void addSymbol(const Export &entry, BumpPtrAllocator &allocator, + std::vector<TrieNode *> &allNodes); + bool updateOffset(uint32_t &offset); + void appendToByteBuffer(ByteBuffer &out); + +private: + StringRef _cummulativeString; + TrieEdgeList _children; + uint64_t _address; + uint64_t _flags; + uint64_t _other; + StringRef _importedName; + uint32_t _trieOffset; + bool _hasExportInfo; +}; + /// Utility class for writing a mach-o binary file given an in-memory /// normalized file. class MachOFileLayout { @@ -66,13 +133,13 @@ public: /// Writes the normalized file as a binary mach-o file to the specified /// path. This does not have a stream interface because the generated /// file may need the 'x' bit set. - std::error_code writeBinary(StringRef path); + llvm::Error writeBinary(StringRef path); private: uint32_t loadCommandsSize(uint32_t &count); void buildFileOffsets(); void writeMachHeader(); - std::error_code writeLoadCommands(); + llvm::Error writeLoadCommands(); void writeSectionContent(); void writeRelocations(); void writeSymbolTable(); @@ -80,6 +147,7 @@ private: void writeBindingInfo(); void writeLazyBindingInfo(); void writeExportInfo(); + void writeFunctionStartsInfo(); void writeDataInCodeInfo(); void writeLinkEditContent(); void buildLinkEditInfo(); @@ -87,6 +155,7 @@ private: void buildBindInfo(); void buildLazyBindInfo(); void buildExportTrie(); + void computeFunctionStartsSize(); void computeDataInCodeSize(); void computeSymbolTableSizes(); void buildSectionRelocations(); @@ -110,83 +179,12 @@ private: }; template <typename T> - std::error_code writeSingleSegmentLoadCommand(uint8_t *&lc); - template <typename T> std::error_code writeSegmentLoadCommands(uint8_t *&lc); + llvm::Error writeSingleSegmentLoadCommand(uint8_t *&lc); + template <typename T> llvm::Error writeSegmentLoadCommands(uint8_t *&lc); uint32_t pointerAlign(uint32_t value); static StringRef dyldPath(); - class ByteBuffer { - public: - ByteBuffer() : _ostream(_bytes) { } - - void append_byte(uint8_t b) { - _ostream << b; - } - void append_uleb128(uint64_t value) { - llvm::encodeULEB128(value, _ostream); - } - void append_uleb128Fixed(uint64_t value, unsigned byteCount) { - unsigned min = llvm::getULEB128Size(value); - assert(min <= byteCount); - unsigned pad = byteCount - min; - llvm::encodeULEB128(value, _ostream, pad); - } - void append_sleb128(int64_t value) { - llvm::encodeSLEB128(value, _ostream); - } - void append_string(StringRef str) { - _ostream << str; - append_byte(0); - } - void align(unsigned alignment) { - while ( (_ostream.tell() % alignment) != 0 ) - append_byte(0); - } - size_t size() { - return _ostream.tell(); - } - const uint8_t *bytes() { - return reinterpret_cast<const uint8_t*>(_ostream.str().data()); - } - - private: - SmallVector<char, 128> _bytes; - // Stream ivar must be after SmallVector ivar to construct properly. - llvm::raw_svector_ostream _ostream; - }; - - struct TrieNode; // Forward declaration. - - struct TrieEdge { - TrieEdge(StringRef s, TrieNode *node) : _subString(s), _child(node) {} - - StringRef _subString; - struct TrieNode *_child; - }; - - struct TrieNode { - TrieNode(StringRef s) - : _cummulativeString(s), _address(0), _flags(0), _other(0), - _trieOffset(0), _hasExportInfo(false) {} - ~TrieNode() = default; - - void addSymbol(const Export &entry, BumpPtrAllocator &allocator, - std::vector<TrieNode *> &allNodes); - bool updateOffset(uint32_t &offset); - void appendToByteBuffer(ByteBuffer &out); - - private: - StringRef _cummulativeString; - std::list<TrieEdge> _children; - uint64_t _address; - uint64_t _flags; - uint64_t _other; - StringRef _importedName; - uint32_t _trieOffset; - bool _hasExportInfo; - }; - struct SegExtraInfo { uint32_t fileOffset; uint32_t fileSize; @@ -209,6 +207,7 @@ private: uint32_t _countOfLoadCommands; uint32_t _endOfLoadCommands; uint32_t _startOfRelocations; + uint32_t _startOfFunctionStarts; uint32_t _startOfDataInCode; uint32_t _startOfSymbols; uint32_t _startOfIndirectSymbols; @@ -219,6 +218,7 @@ private: uint32_t _symbolTableUndefinesStartIndex; uint32_t _symbolStringPoolSize; uint32_t _symbolTableSize; + uint32_t _functionStartsSize; uint32_t _dataInCodeSize; uint32_t _indirectSymbolTableCount; // Used in object file creation only @@ -255,7 +255,7 @@ StringRef MachOFileLayout::dyldPath() { } uint32_t MachOFileLayout::pointerAlign(uint32_t value) { - return llvm::RoundUpToAlignment(value, _is64 ? 8 : 4); + return llvm::alignTo(value, _is64 ? 8 : 4); } @@ -280,7 +280,15 @@ MachOFileLayout::MachOFileLayout(const NormalizedFile &file) + file.sections.size() * sectsSize + sizeof(symtab_command); _countOfLoadCommands = 2; - if (!_file.dataInCode.empty()) { + if (file.hasMinVersionLoadCommand) { + _endOfLoadCommands += sizeof(version_min_command); + _countOfLoadCommands++; + } + if (!_file.functionStarts.empty()) { + _endOfLoadCommands += sizeof(linkedit_data_command); + _countOfLoadCommands++; + } + if (_file.generateDataInCodeLoadCommand) { _endOfLoadCommands += sizeof(linkedit_data_command); _countOfLoadCommands++; } @@ -292,7 +300,7 @@ MachOFileLayout::MachOFileLayout(const NormalizedFile &file) if (isZeroFillSection(sect.type)) _sectInfo[§].fileOffset = 0; else { - offset = llvm::RoundUpToAlignment(offset, sect.alignment); + offset = llvm::alignTo(offset, sect.alignment); _sectInfo[§].fileOffset = offset; offset += sect.content.size(); } @@ -301,11 +309,13 @@ MachOFileLayout::MachOFileLayout(const NormalizedFile &file) _endOfSectionsContent = offset; computeSymbolTableSizes(); + computeFunctionStartsSize(); computeDataInCodeSize(); // Align start of relocations. _startOfRelocations = pointerAlign(_endOfSectionsContent); - _startOfDataInCode = _startOfRelocations + relocCount * 8; + _startOfFunctionStarts = _startOfRelocations + relocCount * 8; + _startOfDataInCode = _startOfFunctionStarts + _functionStartsSize; _startOfSymbols = _startOfDataInCode + _dataInCodeSize; // Add Indirect symbol table. _startOfIndirectSymbols = _startOfSymbols + _symbolTableSize; @@ -346,7 +356,8 @@ MachOFileLayout::MachOFileLayout(const NormalizedFile &file) _endOfLazyBindingInfo = _startOfLazyBindingInfo + _lazyBindingInfo.size(); _startOfExportTrie = _endOfLazyBindingInfo; _endOfExportTrie = _startOfExportTrie + _exportTrie.size(); - _startOfDataInCode = _endOfExportTrie; + _startOfFunctionStarts = _endOfExportTrie; + _startOfDataInCode = _startOfFunctionStarts + _functionStartsSize; _startOfSymbols = _startOfDataInCode + _dataInCodeSize; _startOfIndirectSymbols = _startOfSymbols + _symbolTableSize; _startOfSymbolStrings = _startOfIndirectSymbols @@ -368,6 +379,7 @@ MachOFileLayout::MachOFileLayout(const NormalizedFile &file) << " endOfLazyBindingInfo=" << _endOfLazyBindingInfo << "\n" << " startOfExportTrie=" << _startOfExportTrie << "\n" << " endOfExportTrie=" << _endOfExportTrie << "\n" + << " startOfFunctionStarts=" << _startOfFunctionStarts << "\n" << " startOfDataInCode=" << _startOfDataInCode << "\n" << " startOfSymbols=" << _startOfSymbols << "\n" << " startOfSymbolStrings=" << _startOfSymbolStrings << "\n" @@ -389,9 +401,6 @@ uint32_t MachOFileLayout::loadCommandsSize(uint32_t &count) { count += _file.segments.size(); // Add section record for each section. size += _file.sections.size() * sectionSize; - // Add one LC_SEGMENT for implicit __LINKEDIT segment - size += segCommandSize; - ++count; // If creating a dylib, add LC_ID_DYLIB. if (_file.fileType == llvm::MachO::MH_DYLIB) { @@ -413,10 +422,25 @@ uint32_t MachOFileLayout::loadCommandsSize(uint32_t &count) { ++count; } - // If main executable add LC_LOAD_DYLINKER and LC_MAIN + // If main executable add LC_LOAD_DYLINKER if (_file.fileType == llvm::MachO::MH_EXECUTE) { size += pointerAlign(sizeof(dylinker_command) + dyldPath().size()+1); ++count; + } + + // Add LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_VERSION_MIN_WATCHOS, + // LC_VERSION_MIN_TVOS + if (_file.hasMinVersionLoadCommand) { + size += sizeof(version_min_command); + ++count; + } + + // Add LC_SOURCE_VERSION + size += sizeof(source_version_command); + ++count; + + // If main executable add LC_MAIN + if (_file.fileType == llvm::MachO::MH_EXECUTE) { size += sizeof(entry_point_command); ++count; } @@ -433,8 +457,15 @@ uint32_t MachOFileLayout::loadCommandsSize(uint32_t &count) { ++count; } - // Add LC_DATA_IN_CODE if needed - if (!_file.dataInCode.empty()) { + // Add LC_FUNCTION_STARTS if needed + if (!_file.functionStarts.empty()) { + size += sizeof(linkedit_data_command); + ++count; + } + + // Add LC_DATA_IN_CODE if requested. Note, we do encode zero length entries. + // FIXME: Zero length entries is only to match ld64. Should we change this? + if (_file.generateDataInCodeLoadCommand) { size += sizeof(linkedit_data_command); ++count; } @@ -517,7 +548,7 @@ void MachOFileLayout::buildFileOffsets() { llvm::dbgs() << "buildFileOffsets()\n"); for (const Segment &sg : _file.segments) { _segInfo[&sg].fileOffset = fileOffset; - if ((_seg1addr == INT64_MAX) && sg.access) + if ((_seg1addr == INT64_MAX) && sg.init_access) _seg1addr = sg.address; DEBUG_WITH_TYPE("MachOFileLayout", llvm::dbgs() << " segment=" << sg.name @@ -525,7 +556,7 @@ void MachOFileLayout::buildFileOffsets() { uint32_t segFileSize = 0; // A segment that is not zero-fill must use a least one page of disk space. - if (sg.access) + if (sg.init_access) segFileSize = _file.pageSize; for (const Section *s : _segInfo[&sg].sections) { uint32_t sectOffset = s->address - sg.address; @@ -539,10 +570,11 @@ void MachOFileLayout::buildFileOffsets() { << ", fileOffset=" << fileOffset << "\n"); } - _segInfo[&sg].fileSize = llvm::RoundUpToAlignment(segFileSize, - _file.pageSize); - fileOffset = llvm::RoundUpToAlignment(fileOffset + segFileSize, - _file.pageSize); + // round up all segments to page aligned, except __LINKEDIT + if (!sg.name.equals("__LINKEDIT")) { + _segInfo[&sg].fileSize = llvm::alignTo(segFileSize, _file.pageSize); + fileOffset = llvm::alignTo(fileOffset + segFileSize, _file.pageSize); + } _addressOfLinkEdit = sg.address + sg.size; } _startOfLinkEdit = fileOffset; @@ -553,10 +585,23 @@ size_t MachOFileLayout::size() const { } void MachOFileLayout::writeMachHeader() { + auto cpusubtype = MachOLinkingContext::cpuSubtypeFromArch(_file.arch); + // dynamic x86 executables on newer OS version should also set the + // CPU_SUBTYPE_LIB64 mask in the CPU subtype. + // FIXME: Check that this is a dynamic executable, not a static one. + if (_file.fileType == llvm::MachO::MH_EXECUTE && + cpusubtype == CPU_SUBTYPE_X86_64_ALL && + _file.os == MachOLinkingContext::OS::macOSX) { + uint32_t version; + bool failed = MachOLinkingContext::parsePackedVersion("10.5", version); + if (!failed && _file.minOSverson >= version) + cpusubtype |= CPU_SUBTYPE_LIB64; + } + mach_header *mh = reinterpret_cast<mach_header*>(_buffer); mh->magic = _is64 ? llvm::MachO::MH_MAGIC_64 : llvm::MachO::MH_MAGIC; mh->cputype = MachOLinkingContext::cpuTypeFromArch(_file.arch); - mh->cpusubtype = MachOLinkingContext::cpuSubtypeFromArch(_file.arch); + mh->cpusubtype = cpusubtype; mh->filetype = _file.fileType; mh->ncmds = _countOfLoadCommands; mh->sizeofcmds = _endOfLoadCommands - _startOfLoadCommands; @@ -583,7 +628,7 @@ uint32_t MachOFileLayout::indirectSymbolElementSize(const Section §) { } template <typename T> -std::error_code MachOFileLayout::writeSingleSegmentLoadCommand(uint8_t *&lc) { +llvm::Error MachOFileLayout::writeSingleSegmentLoadCommand(uint8_t *&lc) { typename T::command* seg = reinterpret_cast<typename T::command*>(lc); seg->cmd = T::LC; seg->cmdsize = sizeof(typename T::command) @@ -623,15 +668,37 @@ std::error_code MachOFileLayout::writeSingleSegmentLoadCommand(uint8_t *&lc) { ++sout; } lc = next; - return std::error_code(); + return llvm::Error(); } template <typename T> -std::error_code MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) { +llvm::Error MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) { uint32_t indirectSymRunningIndex = 0; for (const Segment &seg : _file.segments) { - // Write segment command with trailing sections. + // Link edit has no sections and a custom range of address, so handle it + // specially. SegExtraInfo &segInfo = _segInfo[&seg]; + if (seg.name.equals("__LINKEDIT")) { + size_t linkeditSize = _endOfLinkEdit - _startOfLinkEdit; + typename T::command* cmd = reinterpret_cast<typename T::command*>(lc); + cmd->cmd = T::LC; + cmd->cmdsize = sizeof(typename T::command); + uint8_t *next = lc + cmd->cmdsize; + setString16("__LINKEDIT", cmd->segname); + cmd->vmaddr = _addressOfLinkEdit; + cmd->vmsize = llvm::alignTo(linkeditSize, _file.pageSize); + cmd->fileoff = _startOfLinkEdit; + cmd->filesize = linkeditSize; + cmd->initprot = seg.init_access; + cmd->maxprot = seg.max_access; + cmd->nsects = 0; + cmd->flags = 0; + if (_swap) + swapStruct(*cmd); + lc = next; + continue; + } + // Write segment command with trailing sections. typename T::command* cmd = reinterpret_cast<typename T::command*>(lc); cmd->cmd = T::LC; cmd->cmdsize = sizeof(typename T::command) @@ -642,8 +709,8 @@ std::error_code MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) { cmd->vmsize = seg.size; cmd->fileoff = segInfo.fileOffset; cmd->filesize = segInfo.fileSize; - cmd->maxprot = seg.access; - cmd->initprot = seg.access; + cmd->initprot = seg.init_access; + cmd->maxprot = seg.max_access; cmd->nsects = segInfo.sections.size(); cmd->flags = 0; if (_swap) @@ -671,36 +738,52 @@ std::error_code MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) { } lc = reinterpret_cast<uint8_t*>(next); } - // Add implicit __LINKEDIT segment - size_t linkeditSize = _endOfLinkEdit - _startOfLinkEdit; - typename T::command* cmd = reinterpret_cast<typename T::command*>(lc); - cmd->cmd = T::LC; - cmd->cmdsize = sizeof(typename T::command); - uint8_t *next = lc + cmd->cmdsize; - setString16("__LINKEDIT", cmd->segname); - cmd->vmaddr = _addressOfLinkEdit; - cmd->vmsize = llvm::RoundUpToAlignment(linkeditSize, _file.pageSize); - cmd->fileoff = _startOfLinkEdit; - cmd->filesize = linkeditSize; - cmd->maxprot = VM_PROT_READ; - cmd->initprot = VM_PROT_READ; - cmd->nsects = 0; - cmd->flags = 0; + return llvm::Error(); +} + +static void writeVersionMinLoadCommand(const NormalizedFile &_file, + bool _swap, + uint8_t *&lc) { + if (!_file.hasMinVersionLoadCommand) + return; + version_min_command *vm = reinterpret_cast<version_min_command*>(lc); + switch (_file.os) { + case MachOLinkingContext::OS::unknown: + vm->cmd = _file.minOSVersionKind; + vm->cmdsize = sizeof(version_min_command); + vm->version = _file.minOSverson; + vm->sdk = 0; + break; + case MachOLinkingContext::OS::macOSX: + vm->cmd = LC_VERSION_MIN_MACOSX; + vm->cmdsize = sizeof(version_min_command); + vm->version = _file.minOSverson; + vm->sdk = _file.sdkVersion; + break; + case MachOLinkingContext::OS::iOS: + case MachOLinkingContext::OS::iOS_simulator: + vm->cmd = LC_VERSION_MIN_IPHONEOS; + vm->cmdsize = sizeof(version_min_command); + vm->version = _file.minOSverson; + vm->sdk = _file.sdkVersion; + break; + } if (_swap) - swapStruct(*cmd); - lc = next; - return std::error_code(); + swapStruct(*vm); + lc += sizeof(version_min_command); } -std::error_code MachOFileLayout::writeLoadCommands() { - std::error_code ec; +llvm::Error MachOFileLayout::writeLoadCommands() { uint8_t *lc = &_buffer[_startOfLoadCommands]; if (_file.fileType == llvm::MachO::MH_OBJECT) { // Object files have one unnamed segment which holds all sections. - if (_is64) - ec = writeSingleSegmentLoadCommand<MachO64Trait>(lc); - else - ec = writeSingleSegmentLoadCommand<MachO32Trait>(lc); + if (_is64) { + if (auto ec = writeSingleSegmentLoadCommand<MachO64Trait>(lc)) + return ec; + } else { + if (auto ec = writeSingleSegmentLoadCommand<MachO32Trait>(lc)) + return ec; + } // Add LC_SYMTAB with symbol table info symtab_command* st = reinterpret_cast<symtab_command*>(lc); st->cmd = LC_SYMTAB; @@ -713,8 +796,25 @@ std::error_code MachOFileLayout::writeLoadCommands() { if (_swap) swapStruct(*st); lc += sizeof(symtab_command); - // Add LC_DATA_IN_CODE if needed. - if (_dataInCodeSize != 0) { + + // Add LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, + // LC_VERSION_MIN_WATCHOS, LC_VERSION_MIN_TVOS + writeVersionMinLoadCommand(_file, _swap, lc); + + // Add LC_FUNCTION_STARTS if needed. + if (_functionStartsSize != 0) { + linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc); + dl->cmd = LC_FUNCTION_STARTS; + dl->cmdsize = sizeof(linkedit_data_command); + dl->dataoff = _startOfFunctionStarts; + dl->datasize = _functionStartsSize; + if (_swap) + swapStruct(*dl); + lc += sizeof(linkedit_data_command); + } + + // Add LC_DATA_IN_CODE if requested. + if (_file.generateDataInCodeLoadCommand) { linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc); dl->cmd = LC_DATA_IN_CODE; dl->cmdsize = sizeof(linkedit_data_command); @@ -726,10 +826,13 @@ std::error_code MachOFileLayout::writeLoadCommands() { } } else { // Final linked images have sections under segments. - if (_is64) - ec = writeSegmentLoadCommands<MachO64Trait>(lc); - else - ec = writeSegmentLoadCommands<MachO32Trait>(lc); + if (_is64) { + if (auto ec = writeSegmentLoadCommands<MachO64Trait>(lc)) + return ec; + } else { + if (auto ec = writeSegmentLoadCommands<MachO32Trait>(lc)) + return ec; + } // Add LC_ID_DYLIB command for dynamic libraries. if (_file.fileType == llvm::MachO::MH_DYLIB) { @@ -809,7 +912,7 @@ std::error_code MachOFileLayout::writeLoadCommands() { lc += sizeof(dysymtab_command); } - // If main executable, add LC_LOAD_DYLINKER and LC_MAIN. + // If main executable, add LC_LOAD_DYLINKER if (_file.fileType == llvm::MachO::MH_EXECUTE) { // Build LC_LOAD_DYLINKER load command. uint32_t size=pointerAlign(sizeof(dylinker_command)+dyldPath().size()+1); @@ -822,14 +925,39 @@ std::error_code MachOFileLayout::writeLoadCommands() { memcpy(lc+sizeof(dylinker_command), dyldPath().data(), dyldPath().size()); lc[sizeof(dylinker_command)+dyldPath().size()] = '\0'; lc += size; + } + + // Add LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_VERSION_MIN_WATCHOS, + // LC_VERSION_MIN_TVOS + writeVersionMinLoadCommand(_file, _swap, lc); + + // Add LC_SOURCE_VERSION + { + // Note, using a temporary here to appease UB as we may not be aligned + // enough for a struct containing a uint64_t when emitting a 32-bit binary + source_version_command sv; + sv.cmd = LC_SOURCE_VERSION; + sv.cmdsize = sizeof(source_version_command); + sv.version = _file.sourceVersion; + if (_swap) + swapStruct(sv); + memcpy(lc, &sv, sizeof(source_version_command)); + lc += sizeof(source_version_command); + } + + // If main executable, add LC_MAIN. + if (_file.fileType == llvm::MachO::MH_EXECUTE) { // Build LC_MAIN load command. - entry_point_command* ep = reinterpret_cast<entry_point_command*>(lc); - ep->cmd = LC_MAIN; - ep->cmdsize = sizeof(entry_point_command); - ep->entryoff = _file.entryAddress - _seg1addr; - ep->stacksize = _file.stackSize; + // Note, using a temporary here to appease UB as we may not be aligned + // enough for a struct containing a uint64_t when emitting a 32-bit binary + entry_point_command ep; + ep.cmd = LC_MAIN; + ep.cmdsize = sizeof(entry_point_command); + ep.entryoff = _file.entryAddress - _seg1addr; + ep.stacksize = _file.stackSize; if (_swap) - swapStruct(*ep); + swapStruct(ep); + memcpy(lc, &ep, sizeof(entry_point_command)); lc += sizeof(entry_point_command); } @@ -865,8 +993,20 @@ std::error_code MachOFileLayout::writeLoadCommands() { lc += size; } - // Add LC_DATA_IN_CODE if needed. - if (_dataInCodeSize != 0) { + // Add LC_FUNCTION_STARTS if needed. + if (_functionStartsSize != 0) { + linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc); + dl->cmd = LC_FUNCTION_STARTS; + dl->cmdsize = sizeof(linkedit_data_command); + dl->dataoff = _startOfFunctionStarts; + dl->datasize = _functionStartsSize; + if (_swap) + swapStruct(*dl); + lc += sizeof(linkedit_data_command); + } + + // Add LC_DATA_IN_CODE if requested. + if (_file.generateDataInCodeLoadCommand) { linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc); dl->cmd = LC_DATA_IN_CODE; dl->cmdsize = sizeof(linkedit_data_command); @@ -877,7 +1017,7 @@ std::error_code MachOFileLayout::writeLoadCommands() { lc += sizeof(linkedit_data_command); } } - return ec; + return llvm::Error(); } void MachOFileLayout::writeSectionContent() { @@ -936,6 +1076,13 @@ void MachOFileLayout::appendSymbols(const std::vector<Symbol> &symbols, } } +void MachOFileLayout::writeFunctionStartsInfo() { + if (!_functionStartsSize) + return; + memcpy(&_buffer[_startOfFunctionStarts], _file.functionStarts.data(), + _functionStartsSize); +} + void MachOFileLayout::writeDataInCodeInfo() { uint32_t offset = _startOfDataInCode; for (const DataInCode &entry : _file.dataInCode) { @@ -1011,6 +1158,7 @@ void MachOFileLayout::buildLinkEditInfo() { buildLazyBindInfo(); buildExportTrie(); computeSymbolTableSizes(); + computeFunctionStartsSize(); computeDataInCodeSize(); } @@ -1079,9 +1227,9 @@ void MachOFileLayout::buildLazyBindInfo() { _lazyBindingInfo.align(_is64 ? 8 : 4); } -void MachOFileLayout::TrieNode::addSymbol(const Export& entry, - BumpPtrAllocator &allocator, - std::vector<TrieNode*> &allNodes) { +void TrieNode::addSymbol(const Export& entry, + BumpPtrAllocator &allocator, + std::vector<TrieNode*> &allNodes) { StringRef partialStr = entry.name.drop_front(_cummulativeString.size()); for (TrieEdge &edge : _children) { StringRef edgeStr = edge._subString; @@ -1110,7 +1258,7 @@ void MachOFileLayout::TrieNode::addSymbol(const Export& entry, abEdge._subString = abEdgeStr; abEdge._child = bNode; auto *bcEdge = new (allocator) TrieEdge(bcEdgeStr, cNode); - bNode->_children.push_back(std::move(*bcEdge)); + bNode->_children.insert(bNode->_children.end(), bcEdge); bNode->addSymbol(entry, allocator, allNodes); return; } @@ -1125,7 +1273,7 @@ void MachOFileLayout::TrieNode::addSymbol(const Export& entry, // No commonality with any existing child, make a new edge. auto *newNode = new (allocator) TrieNode(entry.name.copy(allocator)); auto *newEdge = new (allocator) TrieEdge(partialStr, newNode); - _children.push_back(std::move(*newEdge)); + _children.insert(_children.end(), newEdge); DEBUG_WITH_TYPE("trie-builder", llvm::dbgs() << "new TrieNode('" << entry.name << "') with edge '" << partialStr << "' from node='" @@ -1139,7 +1287,7 @@ void MachOFileLayout::TrieNode::addSymbol(const Export& entry, allNodes.push_back(newNode); } -bool MachOFileLayout::TrieNode::updateOffset(uint32_t& offset) { +bool TrieNode::updateOffset(uint32_t& offset) { uint32_t nodeSize = 1; // Length when no export info if (_hasExportInfo) { if (_flags & EXPORT_SYMBOL_FLAGS_REEXPORT) { @@ -1171,7 +1319,7 @@ bool MachOFileLayout::TrieNode::updateOffset(uint32_t& offset) { return result; } -void MachOFileLayout::TrieNode::appendToByteBuffer(ByteBuffer &out) { +void TrieNode::appendToByteBuffer(ByteBuffer &out) { if (_hasExportInfo) { if (_flags & EXPORT_SYMBOL_FLAGS_REEXPORT) { if (!_importedName.empty()) { @@ -1290,6 +1438,10 @@ void MachOFileLayout::computeSymbolTableSizes() { } } +void MachOFileLayout::computeFunctionStartsSize() { + _functionStartsSize = _file.functionStarts.size(); +} + void MachOFileLayout::computeDataInCodeSize() { _dataInCodeSize = _file.dataInCode.size() * sizeof(data_in_code_entry); } @@ -1297,6 +1449,7 @@ void MachOFileLayout::computeDataInCodeSize() { void MachOFileLayout::writeLinkEditContent() { if (_file.fileType == llvm::MachO::MH_OBJECT) { writeRelocations(); + writeFunctionStartsInfo(); writeDataInCodeInfo(); writeSymbolTable(); } else { @@ -1305,15 +1458,16 @@ void MachOFileLayout::writeLinkEditContent() { writeLazyBindingInfo(); // TODO: add weak binding info writeExportInfo(); + writeFunctionStartsInfo(); writeDataInCodeInfo(); writeSymbolTable(); } } -std::error_code MachOFileLayout::writeBinary(StringRef path) { +llvm::Error MachOFileLayout::writeBinary(StringRef path) { // Check for pending error from constructor. if (_ec) - return _ec; + return llvm::errorCodeToError(_ec); // Create FileOutputBuffer with calculated size. unsigned flags = 0; if (_file.fileType != llvm::MachO::MH_OBJECT) @@ -1321,23 +1475,22 @@ std::error_code MachOFileLayout::writeBinary(StringRef path) { ErrorOr<std::unique_ptr<llvm::FileOutputBuffer>> fobOrErr = llvm::FileOutputBuffer::create(path, size(), flags); if (std::error_code ec = fobOrErr.getError()) - return ec; + return llvm::errorCodeToError(ec); std::unique_ptr<llvm::FileOutputBuffer> &fob = *fobOrErr; // Write content. _buffer = fob->getBufferStart(); writeMachHeader(); - std::error_code ec = writeLoadCommands(); - if (ec) + if (auto ec = writeLoadCommands()) return ec; writeSectionContent(); writeLinkEditContent(); fob->commit(); - return std::error_code(); + return llvm::Error(); } /// Takes in-memory normalized view and writes a mach-o object file. -std::error_code writeBinary(const NormalizedFile &file, StringRef path) { +llvm::Error writeBinary(const NormalizedFile &file, StringRef path) { MachOFileLayout layout(file); return layout.writeBinary(path); } |
