blob: 17b45b9ca827828dabe3641869c53cdc46297371 [file] [log] [blame]
//===- 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 &sect, uint32_t &index);
uint32_t indirectSymbolElementSize(const Section &sect);
// 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 &sect : file.sections) {
if (isZeroFillSection(sect.type))
_sectInfo[&sect].fileOffset = 0;
else {
offset = llvm::alignTo(offset, sect.alignment);
_sectInfo[&sect].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 &sect,
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 &sect) {
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 &section : _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 &sect : _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