forked from OSchip/llvm-project
1561 lines
56 KiB
C++
1561 lines
56 KiB
C++
//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp ---------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
///
|
|
/// \file For mach-o object files, this implementation converts normalized
|
|
/// mach-o in memory to mach-o binary on disk.
|
|
///
|
|
/// +---------------+
|
|
/// | binary mach-o |
|
|
/// +---------------+
|
|
/// ^
|
|
/// |
|
|
/// |
|
|
/// +------------+
|
|
/// | normalized |
|
|
/// +------------+
|
|
|
|
#include "MachONormalizedFile.h"
|
|
#include "MachONormalizedFileBinaryUtils.h"
|
|
#include "lld/Common/LLVM.h"
|
|
#include "lld/Core/Error.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/ilist.h"
|
|
#include "llvm/ADT/ilist_node.h"
|
|
#include "llvm/BinaryFormat/MachO.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/Errc.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/FileOutputBuffer.h"
|
|
#include "llvm/Support/Format.h"
|
|
#include "llvm/Support/Host.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <functional>
|
|
#include <list>
|
|
#include <map>
|
|
#include <system_error>
|
|
|
|
using namespace llvm::MachO;
|
|
|
|
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_alloc_traits<TrieEdge> : ilist_noalloc_traits<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);
|
|
|
|
void addOrderedNodes(const Export &entry,
|
|
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;
|
|
bool _ordered = false;
|
|
};
|
|
|
|
/// Utility class for writing a mach-o binary file given an in-memory
|
|
/// normalized file.
|
|
class MachOFileLayout {
|
|
public:
|
|
/// All layout computation is done in the constructor.
|
|
MachOFileLayout(const NormalizedFile &file, bool alwaysIncludeFunctionStarts);
|
|
|
|
/// Returns the final file size as computed in the constructor.
|
|
size_t size() const;
|
|
|
|
// Returns size of the mach_header and load commands.
|
|
size_t headerAndLoadCommandsSize() const;
|
|
|
|
/// 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.
|
|
llvm::Error writeBinary(StringRef path);
|
|
|
|
private:
|
|
uint32_t loadCommandsSize(uint32_t &count,
|
|
bool alwaysIncludeFunctionStarts);
|
|
void buildFileOffsets();
|
|
void writeMachHeader();
|
|
llvm::Error writeLoadCommands();
|
|
void writeSectionContent();
|
|
void writeRelocations();
|
|
void writeSymbolTable();
|
|
void writeRebaseInfo();
|
|
void writeBindingInfo();
|
|
void writeLazyBindingInfo();
|
|
void writeExportInfo();
|
|
void writeFunctionStartsInfo();
|
|
void writeDataInCodeInfo();
|
|
void writeLinkEditContent();
|
|
void buildLinkEditInfo();
|
|
void buildRebaseInfo();
|
|
void buildBindInfo();
|
|
void buildLazyBindInfo();
|
|
void buildExportTrie();
|
|
void computeFunctionStartsSize();
|
|
void computeDataInCodeSize();
|
|
void computeSymbolTableSizes();
|
|
void buildSectionRelocations();
|
|
void appendSymbols(const std::vector<Symbol> &symbols,
|
|
uint32_t &symOffset, uint32_t &strOffset);
|
|
uint32_t indirectSymbolIndex(const Section §, uint32_t &index);
|
|
uint32_t indirectSymbolElementSize(const Section §);
|
|
|
|
// For use as template parameter to load command methods.
|
|
struct MachO64Trait {
|
|
typedef llvm::MachO::segment_command_64 command;
|
|
typedef llvm::MachO::section_64 section;
|
|
enum { LC = llvm::MachO::LC_SEGMENT_64 };
|
|
};
|
|
|
|
// For use as template parameter to load command methods.
|
|
struct MachO32Trait {
|
|
typedef llvm::MachO::segment_command command;
|
|
typedef llvm::MachO::section section;
|
|
enum { LC = llvm::MachO::LC_SEGMENT };
|
|
};
|
|
|
|
template <typename T>
|
|
llvm::Error writeSingleSegmentLoadCommand(uint8_t *&lc);
|
|
template <typename T> llvm::Error writeSegmentLoadCommands(uint8_t *&lc);
|
|
|
|
uint32_t pointerAlign(uint32_t value);
|
|
static StringRef dyldPath();
|
|
|
|
struct SegExtraInfo {
|
|
uint32_t fileOffset;
|
|
uint32_t fileSize;
|
|
std::vector<const Section*> sections;
|
|
};
|
|
typedef std::map<const Segment*, SegExtraInfo> SegMap;
|
|
struct SectionExtraInfo {
|
|
uint32_t fileOffset;
|
|
};
|
|
typedef std::map<const Section*, SectionExtraInfo> SectionMap;
|
|
|
|
const NormalizedFile &_file;
|
|
std::error_code _ec;
|
|
uint8_t *_buffer;
|
|
const bool _is64;
|
|
const bool _swap;
|
|
const bool _bigEndianArch;
|
|
uint64_t _seg1addr;
|
|
uint32_t _startOfLoadCommands;
|
|
uint32_t _countOfLoadCommands;
|
|
uint32_t _endOfLoadCommands;
|
|
uint32_t _startOfRelocations;
|
|
uint32_t _startOfFunctionStarts;
|
|
uint32_t _startOfDataInCode;
|
|
uint32_t _startOfSymbols;
|
|
uint32_t _startOfIndirectSymbols;
|
|
uint32_t _startOfSymbolStrings;
|
|
uint32_t _endOfSymbolStrings;
|
|
uint32_t _symbolTableLocalsStartIndex;
|
|
uint32_t _symbolTableGlobalsStartIndex;
|
|
uint32_t _symbolTableUndefinesStartIndex;
|
|
uint32_t _symbolStringPoolSize;
|
|
uint32_t _symbolTableSize;
|
|
uint32_t _functionStartsSize;
|
|
uint32_t _dataInCodeSize;
|
|
uint32_t _indirectSymbolTableCount;
|
|
// Used in object file creation only
|
|
uint32_t _startOfSectionsContent;
|
|
uint32_t _endOfSectionsContent;
|
|
// Used in final linked image only
|
|
uint32_t _startOfLinkEdit;
|
|
uint32_t _startOfRebaseInfo;
|
|
uint32_t _endOfRebaseInfo;
|
|
uint32_t _startOfBindingInfo;
|
|
uint32_t _endOfBindingInfo;
|
|
uint32_t _startOfLazyBindingInfo;
|
|
uint32_t _endOfLazyBindingInfo;
|
|
uint32_t _startOfExportTrie;
|
|
uint32_t _endOfExportTrie;
|
|
uint32_t _endOfLinkEdit;
|
|
uint64_t _addressOfLinkEdit;
|
|
SegMap _segInfo;
|
|
SectionMap _sectInfo;
|
|
ByteBuffer _rebaseInfo;
|
|
ByteBuffer _bindingInfo;
|
|
ByteBuffer _lazyBindingInfo;
|
|
ByteBuffer _weakBindingInfo;
|
|
ByteBuffer _exportTrie;
|
|
};
|
|
|
|
size_t headerAndLoadCommandsSize(const NormalizedFile &file,
|
|
bool includeFunctionStarts) {
|
|
MachOFileLayout layout(file, includeFunctionStarts);
|
|
return layout.headerAndLoadCommandsSize();
|
|
}
|
|
|
|
StringRef MachOFileLayout::dyldPath() {
|
|
return "/usr/lib/dyld";
|
|
}
|
|
|
|
uint32_t MachOFileLayout::pointerAlign(uint32_t value) {
|
|
return llvm::alignTo(value, _is64 ? 8 : 4);
|
|
}
|
|
|
|
|
|
size_t MachOFileLayout::headerAndLoadCommandsSize() const {
|
|
return _endOfLoadCommands;
|
|
}
|
|
|
|
MachOFileLayout::MachOFileLayout(const NormalizedFile &file,
|
|
bool alwaysIncludeFunctionStarts)
|
|
: _file(file),
|
|
_is64(MachOLinkingContext::is64Bit(file.arch)),
|
|
_swap(!MachOLinkingContext::isHostEndian(file.arch)),
|
|
_bigEndianArch(MachOLinkingContext::isBigEndian(file.arch)),
|
|
_seg1addr(INT64_MAX) {
|
|
_startOfLoadCommands = _is64 ? sizeof(mach_header_64) : sizeof(mach_header);
|
|
const size_t segCommandBaseSize =
|
|
(_is64 ? sizeof(segment_command_64) : sizeof(segment_command));
|
|
const size_t sectsSize = (_is64 ? sizeof(section_64) : sizeof(section));
|
|
if (file.fileType == llvm::MachO::MH_OBJECT) {
|
|
// object files have just one segment load command containing all sections
|
|
_endOfLoadCommands = _startOfLoadCommands
|
|
+ segCommandBaseSize
|
|
+ file.sections.size() * sectsSize
|
|
+ sizeof(symtab_command);
|
|
_countOfLoadCommands = 2;
|
|
if (file.hasMinVersionLoadCommand) {
|
|
_endOfLoadCommands += sizeof(version_min_command);
|
|
_countOfLoadCommands++;
|
|
}
|
|
if (!_file.functionStarts.empty() || alwaysIncludeFunctionStarts) {
|
|
_endOfLoadCommands += sizeof(linkedit_data_command);
|
|
_countOfLoadCommands++;
|
|
}
|
|
if (_file.generateDataInCodeLoadCommand) {
|
|
_endOfLoadCommands += sizeof(linkedit_data_command);
|
|
_countOfLoadCommands++;
|
|
}
|
|
// Assign file offsets to each section.
|
|
_startOfSectionsContent = _endOfLoadCommands;
|
|
unsigned relocCount = 0;
|
|
uint64_t offset = _startOfSectionsContent;
|
|
for (const Section § : file.sections) {
|
|
if (isZeroFillSection(sect.type))
|
|
_sectInfo[§].fileOffset = 0;
|
|
else {
|
|
offset = llvm::alignTo(offset, sect.alignment);
|
|
_sectInfo[§].fileOffset = offset;
|
|
offset += sect.content.size();
|
|
}
|
|
relocCount += sect.relocations.size();
|
|
}
|
|
_endOfSectionsContent = offset;
|
|
|
|
computeSymbolTableSizes();
|
|
computeFunctionStartsSize();
|
|
computeDataInCodeSize();
|
|
|
|
// Align start of relocations.
|
|
_startOfRelocations = pointerAlign(_endOfSectionsContent);
|
|
_startOfFunctionStarts = _startOfRelocations + relocCount * 8;
|
|
_startOfDataInCode = _startOfFunctionStarts + _functionStartsSize;
|
|
_startOfSymbols = _startOfDataInCode + _dataInCodeSize;
|
|
// Add Indirect symbol table.
|
|
_startOfIndirectSymbols = _startOfSymbols + _symbolTableSize;
|
|
// Align start of symbol table and symbol strings.
|
|
_startOfSymbolStrings = _startOfIndirectSymbols
|
|
+ pointerAlign(_indirectSymbolTableCount * sizeof(uint32_t));
|
|
_endOfSymbolStrings = _startOfSymbolStrings
|
|
+ pointerAlign(_symbolStringPoolSize);
|
|
_endOfLinkEdit = _endOfSymbolStrings;
|
|
DEBUG_WITH_TYPE("MachOFileLayout",
|
|
llvm::dbgs() << "MachOFileLayout()\n"
|
|
<< " startOfLoadCommands=" << _startOfLoadCommands << "\n"
|
|
<< " countOfLoadCommands=" << _countOfLoadCommands << "\n"
|
|
<< " endOfLoadCommands=" << _endOfLoadCommands << "\n"
|
|
<< " startOfRelocations=" << _startOfRelocations << "\n"
|
|
<< " startOfSymbols=" << _startOfSymbols << "\n"
|
|
<< " startOfSymbolStrings=" << _startOfSymbolStrings << "\n"
|
|
<< " endOfSymbolStrings=" << _endOfSymbolStrings << "\n"
|
|
<< " startOfSectionsContent=" << _startOfSectionsContent << "\n"
|
|
<< " endOfSectionsContent=" << _endOfSectionsContent << "\n");
|
|
} else {
|
|
// Final linked images have one load command per segment.
|
|
_endOfLoadCommands = _startOfLoadCommands
|
|
+ loadCommandsSize(_countOfLoadCommands,
|
|
alwaysIncludeFunctionStarts);
|
|
|
|
// Assign section file offsets.
|
|
buildFileOffsets();
|
|
buildLinkEditInfo();
|
|
|
|
// LINKEDIT of final linked images has in order:
|
|
// rebase info, binding info, lazy binding info, weak binding info,
|
|
// data-in-code, symbol table, indirect symbol table, symbol table strings.
|
|
_startOfRebaseInfo = _startOfLinkEdit;
|
|
_endOfRebaseInfo = _startOfRebaseInfo + _rebaseInfo.size();
|
|
_startOfBindingInfo = _endOfRebaseInfo;
|
|
_endOfBindingInfo = _startOfBindingInfo + _bindingInfo.size();
|
|
_startOfLazyBindingInfo = _endOfBindingInfo;
|
|
_endOfLazyBindingInfo = _startOfLazyBindingInfo + _lazyBindingInfo.size();
|
|
_startOfExportTrie = _endOfLazyBindingInfo;
|
|
_endOfExportTrie = _startOfExportTrie + _exportTrie.size();
|
|
_startOfFunctionStarts = _endOfExportTrie;
|
|
_startOfDataInCode = _startOfFunctionStarts + _functionStartsSize;
|
|
_startOfSymbols = _startOfDataInCode + _dataInCodeSize;
|
|
_startOfIndirectSymbols = _startOfSymbols + _symbolTableSize;
|
|
_startOfSymbolStrings = _startOfIndirectSymbols
|
|
+ pointerAlign(_indirectSymbolTableCount * sizeof(uint32_t));
|
|
_endOfSymbolStrings = _startOfSymbolStrings
|
|
+ pointerAlign(_symbolStringPoolSize);
|
|
_endOfLinkEdit = _endOfSymbolStrings;
|
|
DEBUG_WITH_TYPE("MachOFileLayout",
|
|
llvm::dbgs() << "MachOFileLayout()\n"
|
|
<< " startOfLoadCommands=" << _startOfLoadCommands << "\n"
|
|
<< " countOfLoadCommands=" << _countOfLoadCommands << "\n"
|
|
<< " endOfLoadCommands=" << _endOfLoadCommands << "\n"
|
|
<< " startOfLinkEdit=" << _startOfLinkEdit << "\n"
|
|
<< " startOfRebaseInfo=" << _startOfRebaseInfo << "\n"
|
|
<< " endOfRebaseInfo=" << _endOfRebaseInfo << "\n"
|
|
<< " startOfBindingInfo=" << _startOfBindingInfo << "\n"
|
|
<< " endOfBindingInfo=" << _endOfBindingInfo << "\n"
|
|
<< " startOfLazyBindingInfo=" << _startOfLazyBindingInfo << "\n"
|
|
<< " endOfLazyBindingInfo=" << _endOfLazyBindingInfo << "\n"
|
|
<< " startOfExportTrie=" << _startOfExportTrie << "\n"
|
|
<< " endOfExportTrie=" << _endOfExportTrie << "\n"
|
|
<< " startOfFunctionStarts=" << _startOfFunctionStarts << "\n"
|
|
<< " startOfDataInCode=" << _startOfDataInCode << "\n"
|
|
<< " startOfSymbols=" << _startOfSymbols << "\n"
|
|
<< " startOfSymbolStrings=" << _startOfSymbolStrings << "\n"
|
|
<< " endOfSymbolStrings=" << _endOfSymbolStrings << "\n"
|
|
<< " addressOfLinkEdit=" << _addressOfLinkEdit << "\n");
|
|
}
|
|
}
|
|
|
|
uint32_t MachOFileLayout::loadCommandsSize(uint32_t &count,
|
|
bool alwaysIncludeFunctionStarts) {
|
|
uint32_t size = 0;
|
|
count = 0;
|
|
|
|
const size_t segCommandSize =
|
|
(_is64 ? sizeof(segment_command_64) : sizeof(segment_command));
|
|
const size_t sectionSize = (_is64 ? sizeof(section_64) : sizeof(section));
|
|
|
|
// Add LC_SEGMENT for each segment.
|
|
size += _file.segments.size() * segCommandSize;
|
|
count += _file.segments.size();
|
|
// Add section record for each section.
|
|
size += _file.sections.size() * sectionSize;
|
|
|
|
// If creating a dylib, add LC_ID_DYLIB.
|
|
if (_file.fileType == llvm::MachO::MH_DYLIB) {
|
|
size += sizeof(dylib_command) + pointerAlign(_file.installName.size() + 1);
|
|
++count;
|
|
}
|
|
|
|
// Add LC_DYLD_INFO
|
|
size += sizeof(dyld_info_command);
|
|
++count;
|
|
|
|
// Add LC_SYMTAB
|
|
size += sizeof(symtab_command);
|
|
++count;
|
|
|
|
// Add LC_DYSYMTAB
|
|
if (_file.fileType != llvm::MachO::MH_PRELOAD) {
|
|
size += sizeof(dysymtab_command);
|
|
++count;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Add LC_LOAD_DYLIB for each dependent dylib.
|
|
for (const DependentDylib &dep : _file.dependentDylibs) {
|
|
size += sizeof(dylib_command) + pointerAlign(dep.path.size()+1);
|
|
++count;
|
|
}
|
|
|
|
// Add LC_RPATH
|
|
for (const StringRef &path : _file.rpaths) {
|
|
size += pointerAlign(sizeof(rpath_command) + path.size() + 1);
|
|
++count;
|
|
}
|
|
|
|
// Add LC_FUNCTION_STARTS if needed
|
|
if (!_file.functionStarts.empty() || alwaysIncludeFunctionStarts) {
|
|
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;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static bool overlaps(const Segment &s1, const Segment &s2) {
|
|
if (s2.address >= s1.address+s1.size)
|
|
return false;
|
|
if (s1.address >= s2.address+s2.size)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool overlaps(const Section &s1, const Section &s2) {
|
|
if (s2.address >= s1.address+s1.content.size())
|
|
return false;
|
|
if (s1.address >= s2.address+s2.content.size())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void MachOFileLayout::buildFileOffsets() {
|
|
// Verify no segments overlap
|
|
for (const Segment &sg1 : _file.segments) {
|
|
for (const Segment &sg2 : _file.segments) {
|
|
if (&sg1 == &sg2)
|
|
continue;
|
|
if (overlaps(sg1,sg2)) {
|
|
_ec = make_error_code(llvm::errc::executable_format_error);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify no sections overlap
|
|
for (const Section &s1 : _file.sections) {
|
|
for (const Section &s2 : _file.sections) {
|
|
if (&s1 == &s2)
|
|
continue;
|
|
if (overlaps(s1,s2)) {
|
|
_ec = make_error_code(llvm::errc::executable_format_error);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build side table of extra info about segments and sections.
|
|
SegExtraInfo t;
|
|
t.fileOffset = 0;
|
|
for (const Segment &sg : _file.segments) {
|
|
_segInfo[&sg] = t;
|
|
}
|
|
SectionExtraInfo t2;
|
|
t2.fileOffset = 0;
|
|
// Assign sections to segments.
|
|
for (const Section &s : _file.sections) {
|
|
_sectInfo[&s] = t2;
|
|
bool foundSegment = false;
|
|
for (const Segment &sg : _file.segments) {
|
|
if (sg.name.equals(s.segmentName)) {
|
|
if ((s.address >= sg.address)
|
|
&& (s.address+s.content.size() <= sg.address+sg.size)) {
|
|
_segInfo[&sg].sections.push_back(&s);
|
|
foundSegment = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!foundSegment) {
|
|
_ec = make_error_code(llvm::errc::executable_format_error);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Assign file offsets.
|
|
uint32_t fileOffset = 0;
|
|
DEBUG_WITH_TYPE("MachOFileLayout",
|
|
llvm::dbgs() << "buildFileOffsets()\n");
|
|
for (const Segment &sg : _file.segments) {
|
|
_segInfo[&sg].fileOffset = fileOffset;
|
|
if ((_seg1addr == INT64_MAX) && sg.init_access)
|
|
_seg1addr = sg.address;
|
|
DEBUG_WITH_TYPE("MachOFileLayout",
|
|
llvm::dbgs() << " segment=" << sg.name
|
|
<< ", fileOffset=" << _segInfo[&sg].fileOffset << "\n");
|
|
|
|
uint32_t segFileSize = 0;
|
|
// A segment that is not zero-fill must use a least one page of disk space.
|
|
if (sg.init_access)
|
|
segFileSize = _file.pageSize;
|
|
for (const Section *s : _segInfo[&sg].sections) {
|
|
uint32_t sectOffset = s->address - sg.address;
|
|
uint32_t sectFileSize =
|
|
isZeroFillSection(s->type) ? 0 : s->content.size();
|
|
segFileSize = std::max(segFileSize, sectOffset + sectFileSize);
|
|
|
|
_sectInfo[s].fileOffset = _segInfo[&sg].fileOffset + sectOffset;
|
|
DEBUG_WITH_TYPE("MachOFileLayout",
|
|
llvm::dbgs() << " section=" << s->sectionName
|
|
<< ", fileOffset=" << fileOffset << "\n");
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
size_t MachOFileLayout::size() const {
|
|
return _endOfSymbolStrings;
|
|
}
|
|
|
|
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 = cpusubtype;
|
|
mh->filetype = _file.fileType;
|
|
mh->ncmds = _countOfLoadCommands;
|
|
mh->sizeofcmds = _endOfLoadCommands - _startOfLoadCommands;
|
|
mh->flags = _file.flags;
|
|
if (_swap)
|
|
swapStruct(*mh);
|
|
}
|
|
|
|
uint32_t MachOFileLayout::indirectSymbolIndex(const Section §,
|
|
uint32_t &index) {
|
|
if (sect.indirectSymbols.empty())
|
|
return 0;
|
|
uint32_t result = index;
|
|
index += sect.indirectSymbols.size();
|
|
return result;
|
|
}
|
|
|
|
uint32_t MachOFileLayout::indirectSymbolElementSize(const Section §) {
|
|
if (sect.indirectSymbols.empty())
|
|
return 0;
|
|
if (sect.type != S_SYMBOL_STUBS)
|
|
return 0;
|
|
return sect.content.size() / sect.indirectSymbols.size();
|
|
}
|
|
|
|
template <typename T>
|
|
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)
|
|
+ _file.sections.size() * sizeof(typename T::section);
|
|
uint8_t *next = lc + seg->cmdsize;
|
|
memset(seg->segname, 0, 16);
|
|
seg->flags = 0;
|
|
seg->vmaddr = 0;
|
|
seg->fileoff = _endOfLoadCommands;
|
|
seg->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
|
|
seg->initprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
|
|
seg->nsects = _file.sections.size();
|
|
if (seg->nsects) {
|
|
seg->vmsize = _file.sections.back().address
|
|
+ _file.sections.back().content.size();
|
|
seg->filesize = _sectInfo[&_file.sections.back()].fileOffset +
|
|
_file.sections.back().content.size() -
|
|
_sectInfo[&_file.sections.front()].fileOffset;
|
|
}
|
|
if (_swap)
|
|
swapStruct(*seg);
|
|
typename T::section *sout = reinterpret_cast<typename T::section*>
|
|
(lc+sizeof(typename T::command));
|
|
uint32_t relOffset = _startOfRelocations;
|
|
uint32_t indirectSymRunningIndex = 0;
|
|
for (const Section &sin : _file.sections) {
|
|
setString16(sin.sectionName, sout->sectname);
|
|
setString16(sin.segmentName, sout->segname);
|
|
sout->addr = sin.address;
|
|
sout->size = sin.content.size();
|
|
sout->offset = _sectInfo[&sin].fileOffset;
|
|
sout->align = llvm::Log2_32(sin.alignment);
|
|
sout->reloff = sin.relocations.empty() ? 0 : relOffset;
|
|
sout->nreloc = sin.relocations.size();
|
|
sout->flags = sin.type | sin.attributes;
|
|
sout->reserved1 = indirectSymbolIndex(sin, indirectSymRunningIndex);
|
|
sout->reserved2 = indirectSymbolElementSize(sin);
|
|
relOffset += sin.relocations.size() * sizeof(any_relocation_info);
|
|
if (_swap)
|
|
swapStruct(*sout);
|
|
++sout;
|
|
}
|
|
lc = next;
|
|
return llvm::Error::success();
|
|
}
|
|
|
|
template <typename T>
|
|
llvm::Error MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) {
|
|
uint32_t indirectSymRunningIndex = 0;
|
|
for (const Segment &seg : _file.segments) {
|
|
// 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)
|
|
+ segInfo.sections.size() * sizeof(typename T::section);
|
|
uint8_t *next = lc + cmd->cmdsize;
|
|
setString16(seg.name, cmd->segname);
|
|
cmd->vmaddr = seg.address;
|
|
cmd->vmsize = seg.size;
|
|
cmd->fileoff = segInfo.fileOffset;
|
|
cmd->filesize = segInfo.fileSize;
|
|
cmd->initprot = seg.init_access;
|
|
cmd->maxprot = seg.max_access;
|
|
cmd->nsects = segInfo.sections.size();
|
|
cmd->flags = 0;
|
|
if (_swap)
|
|
swapStruct(*cmd);
|
|
typename T::section *sect = reinterpret_cast<typename T::section*>
|
|
(lc+sizeof(typename T::command));
|
|
for (const Section *section : segInfo.sections) {
|
|
setString16(section->sectionName, sect->sectname);
|
|
setString16(section->segmentName, sect->segname);
|
|
sect->addr = section->address;
|
|
sect->size = section->content.size();
|
|
if (isZeroFillSection(section->type))
|
|
sect->offset = 0;
|
|
else
|
|
sect->offset = section->address - seg.address + segInfo.fileOffset;
|
|
sect->align = llvm::Log2_32(section->alignment);
|
|
sect->reloff = 0;
|
|
sect->nreloc = 0;
|
|
sect->flags = section->type | section->attributes;
|
|
sect->reserved1 = indirectSymbolIndex(*section, indirectSymRunningIndex);
|
|
sect->reserved2 = indirectSymbolElementSize(*section);
|
|
if (_swap)
|
|
swapStruct(*sect);
|
|
++sect;
|
|
}
|
|
lc = reinterpret_cast<uint8_t*>(next);
|
|
}
|
|
return llvm::Error::success();
|
|
}
|
|
|
|
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(*vm);
|
|
lc += sizeof(version_min_command);
|
|
}
|
|
|
|
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) {
|
|
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;
|
|
st->cmdsize = sizeof(symtab_command);
|
|
st->symoff = _startOfSymbols;
|
|
st->nsyms = _file.stabsSymbols.size() + _file.localSymbols.size() +
|
|
_file.globalSymbols.size() + _file.undefinedSymbols.size();
|
|
st->stroff = _startOfSymbolStrings;
|
|
st->strsize = _endOfSymbolStrings - _startOfSymbolStrings;
|
|
if (_swap)
|
|
swapStruct(*st);
|
|
lc += sizeof(symtab_command);
|
|
|
|
// 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);
|
|
dl->dataoff = _startOfDataInCode;
|
|
dl->datasize = _dataInCodeSize;
|
|
if (_swap)
|
|
swapStruct(*dl);
|
|
lc += sizeof(linkedit_data_command);
|
|
}
|
|
} else {
|
|
// Final linked images have sections under segments.
|
|
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) {
|
|
dylib_command *dc = reinterpret_cast<dylib_command*>(lc);
|
|
StringRef path = _file.installName;
|
|
uint32_t size = sizeof(dylib_command) + pointerAlign(path.size() + 1);
|
|
dc->cmd = LC_ID_DYLIB;
|
|
dc->cmdsize = size;
|
|
dc->dylib.name = sizeof(dylib_command); // offset
|
|
// needs to be some constant value different than the one in LC_LOAD_DYLIB
|
|
dc->dylib.timestamp = 1;
|
|
dc->dylib.current_version = _file.currentVersion;
|
|
dc->dylib.compatibility_version = _file.compatVersion;
|
|
if (_swap)
|
|
swapStruct(*dc);
|
|
memcpy(lc + sizeof(dylib_command), path.begin(), path.size());
|
|
lc[sizeof(dylib_command) + path.size()] = '\0';
|
|
lc += size;
|
|
}
|
|
|
|
// Add LC_DYLD_INFO_ONLY.
|
|
dyld_info_command* di = reinterpret_cast<dyld_info_command*>(lc);
|
|
di->cmd = LC_DYLD_INFO_ONLY;
|
|
di->cmdsize = sizeof(dyld_info_command);
|
|
di->rebase_off = _rebaseInfo.size() ? _startOfRebaseInfo : 0;
|
|
di->rebase_size = _rebaseInfo.size();
|
|
di->bind_off = _bindingInfo.size() ? _startOfBindingInfo : 0;
|
|
di->bind_size = _bindingInfo.size();
|
|
di->weak_bind_off = 0;
|
|
di->weak_bind_size = 0;
|
|
di->lazy_bind_off = _lazyBindingInfo.size() ? _startOfLazyBindingInfo : 0;
|
|
di->lazy_bind_size = _lazyBindingInfo.size();
|
|
di->export_off = _exportTrie.size() ? _startOfExportTrie : 0;
|
|
di->export_size = _exportTrie.size();
|
|
if (_swap)
|
|
swapStruct(*di);
|
|
lc += sizeof(dyld_info_command);
|
|
|
|
// Add LC_SYMTAB with symbol table info.
|
|
symtab_command* st = reinterpret_cast<symtab_command*>(lc);
|
|
st->cmd = LC_SYMTAB;
|
|
st->cmdsize = sizeof(symtab_command);
|
|
st->symoff = _startOfSymbols;
|
|
st->nsyms = _file.stabsSymbols.size() + _file.localSymbols.size() +
|
|
_file.globalSymbols.size() + _file.undefinedSymbols.size();
|
|
st->stroff = _startOfSymbolStrings;
|
|
st->strsize = _endOfSymbolStrings - _startOfSymbolStrings;
|
|
if (_swap)
|
|
swapStruct(*st);
|
|
lc += sizeof(symtab_command);
|
|
|
|
// Add LC_DYSYMTAB
|
|
if (_file.fileType != llvm::MachO::MH_PRELOAD) {
|
|
dysymtab_command* dst = reinterpret_cast<dysymtab_command*>(lc);
|
|
dst->cmd = LC_DYSYMTAB;
|
|
dst->cmdsize = sizeof(dysymtab_command);
|
|
dst->ilocalsym = _symbolTableLocalsStartIndex;
|
|
dst->nlocalsym = _file.stabsSymbols.size() +
|
|
_file.localSymbols.size();
|
|
dst->iextdefsym = _symbolTableGlobalsStartIndex;
|
|
dst->nextdefsym = _file.globalSymbols.size();
|
|
dst->iundefsym = _symbolTableUndefinesStartIndex;
|
|
dst->nundefsym = _file.undefinedSymbols.size();
|
|
dst->tocoff = 0;
|
|
dst->ntoc = 0;
|
|
dst->modtaboff = 0;
|
|
dst->nmodtab = 0;
|
|
dst->extrefsymoff = 0;
|
|
dst->nextrefsyms = 0;
|
|
dst->indirectsymoff = _startOfIndirectSymbols;
|
|
dst->nindirectsyms = _indirectSymbolTableCount;
|
|
dst->extreloff = 0;
|
|
dst->nextrel = 0;
|
|
dst->locreloff = 0;
|
|
dst->nlocrel = 0;
|
|
if (_swap)
|
|
swapStruct(*dst);
|
|
lc += sizeof(dysymtab_command);
|
|
}
|
|
|
|
// 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);
|
|
dylinker_command* dl = reinterpret_cast<dylinker_command*>(lc);
|
|
dl->cmd = LC_LOAD_DYLINKER;
|
|
dl->cmdsize = size;
|
|
dl->name = sizeof(dylinker_command); // offset
|
|
if (_swap)
|
|
swapStruct(*dl);
|
|
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.
|
|
// 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);
|
|
memcpy(lc, &ep, sizeof(entry_point_command));
|
|
lc += sizeof(entry_point_command);
|
|
}
|
|
|
|
// Add LC_LOAD_DYLIB commands
|
|
for (const DependentDylib &dep : _file.dependentDylibs) {
|
|
dylib_command* dc = reinterpret_cast<dylib_command*>(lc);
|
|
uint32_t size = sizeof(dylib_command) + pointerAlign(dep.path.size()+1);
|
|
dc->cmd = dep.kind;
|
|
dc->cmdsize = size;
|
|
dc->dylib.name = sizeof(dylib_command); // offset
|
|
// needs to be some constant value different than the one in LC_ID_DYLIB
|
|
dc->dylib.timestamp = 2;
|
|
dc->dylib.current_version = dep.currentVersion;
|
|
dc->dylib.compatibility_version = dep.compatVersion;
|
|
if (_swap)
|
|
swapStruct(*dc);
|
|
memcpy(lc+sizeof(dylib_command), dep.path.begin(), dep.path.size());
|
|
lc[sizeof(dylib_command)+dep.path.size()] = '\0';
|
|
lc += size;
|
|
}
|
|
|
|
// Add LC_RPATH
|
|
for (const StringRef &path : _file.rpaths) {
|
|
rpath_command *rpc = reinterpret_cast<rpath_command *>(lc);
|
|
uint32_t size = pointerAlign(sizeof(rpath_command) + path.size() + 1);
|
|
rpc->cmd = LC_RPATH;
|
|
rpc->cmdsize = size;
|
|
rpc->path = sizeof(rpath_command); // offset
|
|
if (_swap)
|
|
swapStruct(*rpc);
|
|
memcpy(lc+sizeof(rpath_command), path.begin(), path.size());
|
|
lc[sizeof(rpath_command)+path.size()] = '\0';
|
|
lc += size;
|
|
}
|
|
|
|
// 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);
|
|
dl->dataoff = _startOfDataInCode;
|
|
dl->datasize = _dataInCodeSize;
|
|
if (_swap)
|
|
swapStruct(*dl);
|
|
lc += sizeof(linkedit_data_command);
|
|
}
|
|
}
|
|
assert(lc == &_buffer[_endOfLoadCommands]);
|
|
return llvm::Error::success();
|
|
}
|
|
|
|
void MachOFileLayout::writeSectionContent() {
|
|
for (const Section &s : _file.sections) {
|
|
// Copy all section content to output buffer.
|
|
if (isZeroFillSection(s.type))
|
|
continue;
|
|
if (s.content.empty())
|
|
continue;
|
|
uint32_t offset = _sectInfo[&s].fileOffset;
|
|
assert(offset >= _endOfLoadCommands);
|
|
uint8_t *p = &_buffer[offset];
|
|
memcpy(p, &s.content[0], s.content.size());
|
|
p += s.content.size();
|
|
}
|
|
}
|
|
|
|
void MachOFileLayout::writeRelocations() {
|
|
uint32_t relOffset = _startOfRelocations;
|
|
for (Section sect : _file.sections) {
|
|
for (Relocation r : sect.relocations) {
|
|
any_relocation_info* rb = reinterpret_cast<any_relocation_info*>(
|
|
&_buffer[relOffset]);
|
|
*rb = packRelocation(r, _swap, _bigEndianArch);
|
|
relOffset += sizeof(any_relocation_info);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MachOFileLayout::appendSymbols(const std::vector<Symbol> &symbols,
|
|
uint32_t &symOffset, uint32_t &strOffset) {
|
|
for (const Symbol &sym : symbols) {
|
|
if (_is64) {
|
|
nlist_64* nb = reinterpret_cast<nlist_64*>(&_buffer[symOffset]);
|
|
nb->n_strx = strOffset - _startOfSymbolStrings;
|
|
nb->n_type = sym.type | sym.scope;
|
|
nb->n_sect = sym.sect;
|
|
nb->n_desc = sym.desc;
|
|
nb->n_value = sym.value;
|
|
if (_swap)
|
|
swapStruct(*nb);
|
|
symOffset += sizeof(nlist_64);
|
|
} else {
|
|
nlist* nb = reinterpret_cast<nlist*>(&_buffer[symOffset]);
|
|
nb->n_strx = strOffset - _startOfSymbolStrings;
|
|
nb->n_type = sym.type | sym.scope;
|
|
nb->n_sect = sym.sect;
|
|
nb->n_desc = sym.desc;
|
|
nb->n_value = sym.value;
|
|
if (_swap)
|
|
swapStruct(*nb);
|
|
symOffset += sizeof(nlist);
|
|
}
|
|
memcpy(&_buffer[strOffset], sym.name.begin(), sym.name.size());
|
|
strOffset += sym.name.size();
|
|
_buffer[strOffset++] ='\0'; // Strings in table have nul terminator.
|
|
}
|
|
}
|
|
|
|
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) {
|
|
data_in_code_entry *dst = reinterpret_cast<data_in_code_entry*>(
|
|
&_buffer[offset]);
|
|
dst->offset = entry.offset;
|
|
dst->length = entry.length;
|
|
dst->kind = entry.kind;
|
|
if (_swap)
|
|
swapStruct(*dst);
|
|
offset += sizeof(data_in_code_entry);
|
|
}
|
|
}
|
|
|
|
void MachOFileLayout::writeSymbolTable() {
|
|
// Write symbol table and symbol strings in parallel.
|
|
uint32_t symOffset = _startOfSymbols;
|
|
uint32_t strOffset = _startOfSymbolStrings;
|
|
// Reserve n_strx offset of zero to mean no name.
|
|
_buffer[strOffset++] = ' ';
|
|
_buffer[strOffset++] = '\0';
|
|
appendSymbols(_file.stabsSymbols, symOffset, strOffset);
|
|
appendSymbols(_file.localSymbols, symOffset, strOffset);
|
|
appendSymbols(_file.globalSymbols, symOffset, strOffset);
|
|
appendSymbols(_file.undefinedSymbols, symOffset, strOffset);
|
|
// Write indirect symbol table array.
|
|
uint32_t *indirects = reinterpret_cast<uint32_t*>
|
|
(&_buffer[_startOfIndirectSymbols]);
|
|
if (_file.fileType == llvm::MachO::MH_OBJECT) {
|
|
// Object files have sections in same order as input normalized file.
|
|
for (const Section §ion : _file.sections) {
|
|
for (uint32_t index : section.indirectSymbols) {
|
|
if (_swap)
|
|
*indirects++ = llvm::sys::getSwappedBytes(index);
|
|
else
|
|
*indirects++ = index;
|
|
}
|
|
}
|
|
} else {
|
|
// Final linked images must sort sections from normalized file.
|
|
for (const Segment &seg : _file.segments) {
|
|
SegExtraInfo &segInfo = _segInfo[&seg];
|
|
for (const Section *section : segInfo.sections) {
|
|
for (uint32_t index : section->indirectSymbols) {
|
|
if (_swap)
|
|
*indirects++ = llvm::sys::getSwappedBytes(index);
|
|
else
|
|
*indirects++ = index;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MachOFileLayout::writeRebaseInfo() {
|
|
memcpy(&_buffer[_startOfRebaseInfo], _rebaseInfo.bytes(), _rebaseInfo.size());
|
|
}
|
|
|
|
void MachOFileLayout::writeBindingInfo() {
|
|
memcpy(&_buffer[_startOfBindingInfo],
|
|
_bindingInfo.bytes(), _bindingInfo.size());
|
|
}
|
|
|
|
void MachOFileLayout::writeLazyBindingInfo() {
|
|
memcpy(&_buffer[_startOfLazyBindingInfo],
|
|
_lazyBindingInfo.bytes(), _lazyBindingInfo.size());
|
|
}
|
|
|
|
void MachOFileLayout::writeExportInfo() {
|
|
memcpy(&_buffer[_startOfExportTrie], _exportTrie.bytes(), _exportTrie.size());
|
|
}
|
|
|
|
void MachOFileLayout::buildLinkEditInfo() {
|
|
buildRebaseInfo();
|
|
buildBindInfo();
|
|
buildLazyBindInfo();
|
|
buildExportTrie();
|
|
computeSymbolTableSizes();
|
|
computeFunctionStartsSize();
|
|
computeDataInCodeSize();
|
|
}
|
|
|
|
void MachOFileLayout::buildSectionRelocations() {
|
|
|
|
}
|
|
|
|
void MachOFileLayout::buildRebaseInfo() {
|
|
// TODO: compress rebasing info.
|
|
for (const RebaseLocation& entry : _file.rebasingInfo) {
|
|
_rebaseInfo.append_byte(REBASE_OPCODE_SET_TYPE_IMM | entry.kind);
|
|
_rebaseInfo.append_byte(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
|
|
| entry.segIndex);
|
|
_rebaseInfo.append_uleb128(entry.segOffset);
|
|
_rebaseInfo.append_uleb128(REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1);
|
|
}
|
|
_rebaseInfo.append_byte(REBASE_OPCODE_DONE);
|
|
_rebaseInfo.align(_is64 ? 8 : 4);
|
|
}
|
|
|
|
void MachOFileLayout::buildBindInfo() {
|
|
// TODO: compress bind info.
|
|
uint64_t lastAddend = 0;
|
|
int lastOrdinal = 0x80000000;
|
|
StringRef lastSymbolName;
|
|
BindType lastType = (BindType)0;
|
|
Hex32 lastSegOffset = ~0U;
|
|
uint8_t lastSegIndex = (uint8_t)~0U;
|
|
for (const BindLocation& entry : _file.bindingInfo) {
|
|
if (entry.ordinal != lastOrdinal) {
|
|
if (entry.ordinal <= 0)
|
|
_bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM |
|
|
(entry.ordinal & BIND_IMMEDIATE_MASK));
|
|
else if (entry.ordinal <= BIND_IMMEDIATE_MASK)
|
|
_bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
|
|
entry.ordinal);
|
|
else {
|
|
_bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
|
|
_bindingInfo.append_uleb128(entry.ordinal);
|
|
}
|
|
lastOrdinal = entry.ordinal;
|
|
}
|
|
|
|
if (lastSymbolName != entry.symbolName) {
|
|
_bindingInfo.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM);
|
|
_bindingInfo.append_string(entry.symbolName);
|
|
lastSymbolName = entry.symbolName;
|
|
}
|
|
|
|
if (lastType != entry.kind) {
|
|
_bindingInfo.append_byte(BIND_OPCODE_SET_TYPE_IMM | entry.kind);
|
|
lastType = entry.kind;
|
|
}
|
|
|
|
if (lastSegIndex != entry.segIndex || lastSegOffset != entry.segOffset) {
|
|
_bindingInfo.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
|
|
| entry.segIndex);
|
|
_bindingInfo.append_uleb128(entry.segOffset);
|
|
lastSegIndex = entry.segIndex;
|
|
lastSegOffset = entry.segOffset;
|
|
}
|
|
if (entry.addend != lastAddend) {
|
|
_bindingInfo.append_byte(BIND_OPCODE_SET_ADDEND_SLEB);
|
|
_bindingInfo.append_sleb128(entry.addend);
|
|
lastAddend = entry.addend;
|
|
}
|
|
_bindingInfo.append_byte(BIND_OPCODE_DO_BIND);
|
|
}
|
|
_bindingInfo.append_byte(BIND_OPCODE_DONE);
|
|
_bindingInfo.align(_is64 ? 8 : 4);
|
|
}
|
|
|
|
void MachOFileLayout::buildLazyBindInfo() {
|
|
for (const BindLocation& entry : _file.lazyBindingInfo) {
|
|
_lazyBindingInfo.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
|
|
| entry.segIndex);
|
|
_lazyBindingInfo.append_uleb128(entry.segOffset);
|
|
if (entry.ordinal <= 0)
|
|
_lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM |
|
|
(entry.ordinal & BIND_IMMEDIATE_MASK));
|
|
else if (entry.ordinal <= BIND_IMMEDIATE_MASK)
|
|
_lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
|
|
entry.ordinal);
|
|
else {
|
|
_lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
|
|
_lazyBindingInfo.append_uleb128(entry.ordinal);
|
|
}
|
|
// FIXME: We need to | the opcode here with flags.
|
|
_lazyBindingInfo.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM);
|
|
_lazyBindingInfo.append_string(entry.symbolName);
|
|
_lazyBindingInfo.append_byte(BIND_OPCODE_DO_BIND);
|
|
_lazyBindingInfo.append_byte(BIND_OPCODE_DONE);
|
|
}
|
|
_lazyBindingInfo.align(_is64 ? 8 : 4);
|
|
}
|
|
|
|
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;
|
|
if (partialStr.startswith(edgeStr)) {
|
|
// Already have matching edge, go down that path.
|
|
edge._child->addSymbol(entry, allocator, allNodes);
|
|
return;
|
|
}
|
|
// See if string has common prefix with existing edge.
|
|
for (int n=edgeStr.size()-1; n > 0; --n) {
|
|
if (partialStr.substr(0, n).equals(edgeStr.substr(0, n))) {
|
|
// Splice in new node: was A -> C, now A -> B -> C
|
|
StringRef bNodeStr = edge._child->_cummulativeString;
|
|
bNodeStr = bNodeStr.drop_back(edgeStr.size()-n).copy(allocator);
|
|
auto *bNode = new (allocator) TrieNode(bNodeStr);
|
|
allNodes.push_back(bNode);
|
|
TrieNode* cNode = edge._child;
|
|
StringRef abEdgeStr = edgeStr.substr(0,n).copy(allocator);
|
|
StringRef bcEdgeStr = edgeStr.substr(n).copy(allocator);
|
|
DEBUG_WITH_TYPE("trie-builder", llvm::dbgs()
|
|
<< "splice in TrieNode('" << bNodeStr
|
|
<< "') between edge '"
|
|
<< abEdgeStr << "' and edge='"
|
|
<< bcEdgeStr<< "'\n");
|
|
TrieEdge& abEdge = edge;
|
|
abEdge._subString = abEdgeStr;
|
|
abEdge._child = bNode;
|
|
auto *bcEdge = new (allocator) TrieEdge(bcEdgeStr, cNode);
|
|
bNode->_children.insert(bNode->_children.end(), bcEdge);
|
|
bNode->addSymbol(entry, allocator, allNodes);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) {
|
|
assert(entry.otherOffset != 0);
|
|
}
|
|
if (entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) {
|
|
assert(entry.otherOffset != 0);
|
|
}
|
|
// 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.insert(_children.end(), newEdge);
|
|
DEBUG_WITH_TYPE("trie-builder", llvm::dbgs()
|
|
<< "new TrieNode('" << entry.name << "') with edge '"
|
|
<< partialStr << "' from node='"
|
|
<< _cummulativeString << "'\n");
|
|
newNode->_address = entry.offset;
|
|
newNode->_flags = entry.flags | entry.kind;
|
|
newNode->_other = entry.otherOffset;
|
|
if ((entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) && !entry.otherName.empty())
|
|
newNode->_importedName = entry.otherName.copy(allocator);
|
|
newNode->_hasExportInfo = true;
|
|
allNodes.push_back(newNode);
|
|
}
|
|
|
|
void TrieNode::addOrderedNodes(const Export& entry,
|
|
std::vector<TrieNode*> &orderedNodes) {
|
|
if (!_ordered) {
|
|
orderedNodes.push_back(this);
|
|
_ordered = true;
|
|
}
|
|
|
|
StringRef partialStr = entry.name.drop_front(_cummulativeString.size());
|
|
for (TrieEdge &edge : _children) {
|
|
StringRef edgeStr = edge._subString;
|
|
if (partialStr.startswith(edgeStr)) {
|
|
// Already have matching edge, go down that path.
|
|
edge._child->addOrderedNodes(entry, orderedNodes);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool TrieNode::updateOffset(uint32_t& offset) {
|
|
uint32_t nodeSize = 1; // Length when no export info
|
|
if (_hasExportInfo) {
|
|
if (_flags & EXPORT_SYMBOL_FLAGS_REEXPORT) {
|
|
nodeSize = llvm::getULEB128Size(_flags);
|
|
nodeSize += llvm::getULEB128Size(_other); // Other contains ordinal.
|
|
nodeSize += _importedName.size();
|
|
++nodeSize; // Trailing zero in imported name.
|
|
} else {
|
|
nodeSize = llvm::getULEB128Size(_flags) + llvm::getULEB128Size(_address);
|
|
if (_flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER)
|
|
nodeSize += llvm::getULEB128Size(_other);
|
|
}
|
|
// Overall node size so far is uleb128 of export info + actual export info.
|
|
nodeSize += llvm::getULEB128Size(nodeSize);
|
|
}
|
|
// Compute size of all child edges.
|
|
++nodeSize; // Byte for number of children.
|
|
for (TrieEdge &edge : _children) {
|
|
nodeSize += edge._subString.size() + 1 // String length.
|
|
+ llvm::getULEB128Size(edge._child->_trieOffset); // Offset len.
|
|
}
|
|
// On input, 'offset' is new prefered location for this node.
|
|
bool result = (_trieOffset != offset);
|
|
// Store new location in node object for use by parents.
|
|
_trieOffset = offset;
|
|
// Update offset for next iteration.
|
|
offset += nodeSize;
|
|
// Return true if _trieOffset was changed.
|
|
return result;
|
|
}
|
|
|
|
void TrieNode::appendToByteBuffer(ByteBuffer &out) {
|
|
if (_hasExportInfo) {
|
|
if (_flags & EXPORT_SYMBOL_FLAGS_REEXPORT) {
|
|
if (!_importedName.empty()) {
|
|
// nodes with re-export info: size, flags, ordinal, import-name
|
|
uint32_t nodeSize = llvm::getULEB128Size(_flags)
|
|
+ llvm::getULEB128Size(_other)
|
|
+ _importedName.size() + 1;
|
|
assert(nodeSize < 256);
|
|
out.append_byte(nodeSize);
|
|
out.append_uleb128(_flags);
|
|
out.append_uleb128(_other);
|
|
out.append_string(_importedName);
|
|
} else {
|
|
// nodes without re-export info: size, flags, ordinal, empty-string
|
|
uint32_t nodeSize = llvm::getULEB128Size(_flags)
|
|
+ llvm::getULEB128Size(_other) + 1;
|
|
assert(nodeSize < 256);
|
|
out.append_byte(nodeSize);
|
|
out.append_uleb128(_flags);
|
|
out.append_uleb128(_other);
|
|
out.append_byte(0);
|
|
}
|
|
} else if ( _flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) {
|
|
// Nodes with export info: size, flags, address, other
|
|
uint32_t nodeSize = llvm::getULEB128Size(_flags)
|
|
+ llvm::getULEB128Size(_address)
|
|
+ llvm::getULEB128Size(_other);
|
|
assert(nodeSize < 256);
|
|
out.append_byte(nodeSize);
|
|
out.append_uleb128(_flags);
|
|
out.append_uleb128(_address);
|
|
out.append_uleb128(_other);
|
|
} else {
|
|
// Nodes with export info: size, flags, address
|
|
uint32_t nodeSize = llvm::getULEB128Size(_flags)
|
|
+ llvm::getULEB128Size(_address);
|
|
assert(nodeSize < 256);
|
|
out.append_byte(nodeSize);
|
|
out.append_uleb128(_flags);
|
|
out.append_uleb128(_address);
|
|
}
|
|
} else {
|
|
// Node with no export info.
|
|
uint32_t nodeSize = 0;
|
|
out.append_byte(nodeSize);
|
|
}
|
|
// Add number of children.
|
|
assert(_children.size() < 256);
|
|
out.append_byte(_children.size());
|
|
// Append each child edge substring and node offset.
|
|
for (TrieEdge &edge : _children) {
|
|
out.append_string(edge._subString);
|
|
out.append_uleb128(edge._child->_trieOffset);
|
|
}
|
|
}
|
|
|
|
void MachOFileLayout::buildExportTrie() {
|
|
if (_file.exportInfo.empty())
|
|
return;
|
|
|
|
// For all temporary strings and objects used building trie.
|
|
BumpPtrAllocator allocator;
|
|
|
|
// Build trie of all exported symbols.
|
|
auto *rootNode = new (allocator) TrieNode(StringRef());
|
|
std::vector<TrieNode*> allNodes;
|
|
allNodes.reserve(_file.exportInfo.size()*2);
|
|
allNodes.push_back(rootNode);
|
|
for (const Export& entry : _file.exportInfo) {
|
|
rootNode->addSymbol(entry, allocator, allNodes);
|
|
}
|
|
|
|
std::vector<TrieNode*> orderedNodes;
|
|
orderedNodes.reserve(allNodes.size());
|
|
|
|
for (const Export& entry : _file.exportInfo)
|
|
rootNode->addOrderedNodes(entry, orderedNodes);
|
|
|
|
// Assign each node in the vector an offset in the trie stream, iterating
|
|
// until all uleb128 sizes have stabilized.
|
|
bool more;
|
|
do {
|
|
uint32_t offset = 0;
|
|
more = false;
|
|
for (TrieNode* node : orderedNodes) {
|
|
if (node->updateOffset(offset))
|
|
more = true;
|
|
}
|
|
} while (more);
|
|
|
|
// Serialize trie to ByteBuffer.
|
|
for (TrieNode* node : orderedNodes) {
|
|
node->appendToByteBuffer(_exportTrie);
|
|
}
|
|
_exportTrie.align(_is64 ? 8 : 4);
|
|
}
|
|
|
|
void MachOFileLayout::computeSymbolTableSizes() {
|
|
// MachO symbol tables have three ranges: locals, globals, and undefines
|
|
const size_t nlistSize = (_is64 ? sizeof(nlist_64) : sizeof(nlist));
|
|
_symbolTableSize = nlistSize * (_file.stabsSymbols.size()
|
|
+ _file.localSymbols.size()
|
|
+ _file.globalSymbols.size()
|
|
+ _file.undefinedSymbols.size());
|
|
// Always reserve 1-byte for the empty string and 1-byte for its terminator.
|
|
_symbolStringPoolSize = 2;
|
|
for (const Symbol &sym : _file.stabsSymbols) {
|
|
_symbolStringPoolSize += (sym.name.size()+1);
|
|
}
|
|
for (const Symbol &sym : _file.localSymbols) {
|
|
_symbolStringPoolSize += (sym.name.size()+1);
|
|
}
|
|
for (const Symbol &sym : _file.globalSymbols) {
|
|
_symbolStringPoolSize += (sym.name.size()+1);
|
|
}
|
|
for (const Symbol &sym : _file.undefinedSymbols) {
|
|
_symbolStringPoolSize += (sym.name.size()+1);
|
|
}
|
|
_symbolTableLocalsStartIndex = 0;
|
|
_symbolTableGlobalsStartIndex = _file.stabsSymbols.size() +
|
|
_file.localSymbols.size();
|
|
_symbolTableUndefinesStartIndex = _symbolTableGlobalsStartIndex
|
|
+ _file.globalSymbols.size();
|
|
|
|
_indirectSymbolTableCount = 0;
|
|
for (const Section § : _file.sections) {
|
|
_indirectSymbolTableCount += sect.indirectSymbols.size();
|
|
}
|
|
}
|
|
|
|
void MachOFileLayout::computeFunctionStartsSize() {
|
|
_functionStartsSize = _file.functionStarts.size();
|
|
}
|
|
|
|
void MachOFileLayout::computeDataInCodeSize() {
|
|
_dataInCodeSize = _file.dataInCode.size() * sizeof(data_in_code_entry);
|
|
}
|
|
|
|
void MachOFileLayout::writeLinkEditContent() {
|
|
if (_file.fileType == llvm::MachO::MH_OBJECT) {
|
|
writeRelocations();
|
|
writeFunctionStartsInfo();
|
|
writeDataInCodeInfo();
|
|
writeSymbolTable();
|
|
} else {
|
|
writeRebaseInfo();
|
|
writeBindingInfo();
|
|
writeLazyBindingInfo();
|
|
// TODO: add weak binding info
|
|
writeExportInfo();
|
|
writeFunctionStartsInfo();
|
|
writeDataInCodeInfo();
|
|
writeSymbolTable();
|
|
}
|
|
}
|
|
|
|
llvm::Error MachOFileLayout::writeBinary(StringRef path) {
|
|
// Check for pending error from constructor.
|
|
if (_ec)
|
|
return llvm::errorCodeToError(_ec);
|
|
// Create FileOutputBuffer with calculated size.
|
|
unsigned flags = 0;
|
|
if (_file.fileType != llvm::MachO::MH_OBJECT)
|
|
flags = llvm::FileOutputBuffer::F_executable;
|
|
Expected<std::unique_ptr<llvm::FileOutputBuffer>> fobOrErr =
|
|
llvm::FileOutputBuffer::create(path, size(), flags);
|
|
if (Error E = fobOrErr.takeError())
|
|
return E;
|
|
std::unique_ptr<llvm::FileOutputBuffer> &fob = *fobOrErr;
|
|
// Write content.
|
|
_buffer = fob->getBufferStart();
|
|
writeMachHeader();
|
|
if (auto ec = writeLoadCommands())
|
|
return ec;
|
|
writeSectionContent();
|
|
writeLinkEditContent();
|
|
if (Error E = fob->commit())
|
|
return E;
|
|
|
|
return llvm::Error::success();
|
|
}
|
|
|
|
/// Takes in-memory normalized view and writes a mach-o object file.
|
|
llvm::Error writeBinary(const NormalizedFile &file, StringRef path) {
|
|
MachOFileLayout layout(file, false);
|
|
return layout.writeBinary(path);
|
|
}
|
|
|
|
} // namespace normalized
|
|
} // namespace mach_o
|
|
} // namespace lld
|