llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWr...

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1561 lines
56 KiB
C++
Raw Normal View History

//===- 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.
lld: Fix initial Mach-O load commands size calculation omitting LC_FUNCTION_STARTS Patch by Nicholas Allegra. The Mach-O writer calculates the size of load commands multiple times. First, Util::assignAddressesToSections() (in MachONormalizedFileFromAtoms.cpp) calculates the size using headerAndLoadCommandsSize() (in MachONormalizedFileBinaryWriter.cpp), which creates a temporary MachOFileLayout for the NormalizedFile, only to retrieve its headerAndLoadCommandsSize. Later, writeBinary() (in MachONormalizedFileBinaryWriter.cpp) creates a new layout and uses the offsets from that layout to actually write out everything in the NormalizedFile. But the NormalizedFile changes between the first computation and the second. When Util::assignAddressesToSections is called, file.functionStarts is always empty because Util::addFunctionStarts has not yet been called. Yet MachOFileLayout decides whether to include a LC_FUNCTION_STARTS command based on whether file.functionStarts is nonempty. Therefore, the initial computation always omits it. Because padding for the __TEXT segment (to make its size a multiple of the page size) is added between the load commands and the first section, LLD still generates a valid binary as long as the amount of padding happens to be large enough to fit LC_FUNCTION_STARTS command, which it usually is. However, it's easy to reproduce the issue by adding a section of a precise size. Given foo.c: __attribute__((section("__TEXT,__foo"))) char foo[0xd78] = {0}; Run: clang -dynamiclib -o foo.dylib foo.c -fuse-ld=lld -install_name /usr/lib/foo.dylib otool -lvv foo.dylib This should produce: truncated or malformed object (offset field of section 1 in LC_SEGMENT_64 command 0 not past the headers of the file) This commit: - Changes MachOFileLayout to always assume LC_FUNCTION_STARTS is present for the initial computation, as long as generating LC_FUNCTION_STARTS is enabled. It would be slightly better to check whether there are actually any functions, since no LC_FUNCTION_STARTS will be generated if not, but it doesn't cause a problem if the initial computation is too high. - Adds a test. - Adds an assert in MachOFileLayout::writeSectionContent() that we are not writing section content into the load commands region (which would happen if the offset was calculated too low due to the initial load commands size calculation being too low). Adds an assert in MachOFileLayout::writeLoadCommands to validate a similar situation where two size-of-load-commands computations are expected to be equivalent. llvm-svn: 358545
2019-04-17 09:47:16 +08:00
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:
lld: Fix initial Mach-O load commands size calculation omitting LC_FUNCTION_STARTS Patch by Nicholas Allegra. The Mach-O writer calculates the size of load commands multiple times. First, Util::assignAddressesToSections() (in MachONormalizedFileFromAtoms.cpp) calculates the size using headerAndLoadCommandsSize() (in MachONormalizedFileBinaryWriter.cpp), which creates a temporary MachOFileLayout for the NormalizedFile, only to retrieve its headerAndLoadCommandsSize. Later, writeBinary() (in MachONormalizedFileBinaryWriter.cpp) creates a new layout and uses the offsets from that layout to actually write out everything in the NormalizedFile. But the NormalizedFile changes between the first computation and the second. When Util::assignAddressesToSections is called, file.functionStarts is always empty because Util::addFunctionStarts has not yet been called. Yet MachOFileLayout decides whether to include a LC_FUNCTION_STARTS command based on whether file.functionStarts is nonempty. Therefore, the initial computation always omits it. Because padding for the __TEXT segment (to make its size a multiple of the page size) is added between the load commands and the first section, LLD still generates a valid binary as long as the amount of padding happens to be large enough to fit LC_FUNCTION_STARTS command, which it usually is. However, it's easy to reproduce the issue by adding a section of a precise size. Given foo.c: __attribute__((section("__TEXT,__foo"))) char foo[0xd78] = {0}; Run: clang -dynamiclib -o foo.dylib foo.c -fuse-ld=lld -install_name /usr/lib/foo.dylib otool -lvv foo.dylib This should produce: truncated or malformed object (offset field of section 1 in LC_SEGMENT_64 command 0 not past the headers of the file) This commit: - Changes MachOFileLayout to always assume LC_FUNCTION_STARTS is present for the initial computation, as long as generating LC_FUNCTION_STARTS is enabled. It would be slightly better to check whether there are actually any functions, since no LC_FUNCTION_STARTS will be generated if not, but it doesn't cause a problem if the initial computation is too high. - Adds a test. - Adds an assert in MachOFileLayout::writeSectionContent() that we are not writing section content into the load commands region (which would happen if the offset was calculated too low due to the initial load commands size calculation being too low). Adds an assert in MachOFileLayout::writeLoadCommands to validate a similar situation where two size-of-load-commands computations are expected to be equivalent. llvm-svn: 358545
2019-04-17 09:47:16 +08:00
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;
};
lld: Fix initial Mach-O load commands size calculation omitting LC_FUNCTION_STARTS Patch by Nicholas Allegra. The Mach-O writer calculates the size of load commands multiple times. First, Util::assignAddressesToSections() (in MachONormalizedFileFromAtoms.cpp) calculates the size using headerAndLoadCommandsSize() (in MachONormalizedFileBinaryWriter.cpp), which creates a temporary MachOFileLayout for the NormalizedFile, only to retrieve its headerAndLoadCommandsSize. Later, writeBinary() (in MachONormalizedFileBinaryWriter.cpp) creates a new layout and uses the offsets from that layout to actually write out everything in the NormalizedFile. But the NormalizedFile changes between the first computation and the second. When Util::assignAddressesToSections is called, file.functionStarts is always empty because Util::addFunctionStarts has not yet been called. Yet MachOFileLayout decides whether to include a LC_FUNCTION_STARTS command based on whether file.functionStarts is nonempty. Therefore, the initial computation always omits it. Because padding for the __TEXT segment (to make its size a multiple of the page size) is added between the load commands and the first section, LLD still generates a valid binary as long as the amount of padding happens to be large enough to fit LC_FUNCTION_STARTS command, which it usually is. However, it's easy to reproduce the issue by adding a section of a precise size. Given foo.c: __attribute__((section("__TEXT,__foo"))) char foo[0xd78] = {0}; Run: clang -dynamiclib -o foo.dylib foo.c -fuse-ld=lld -install_name /usr/lib/foo.dylib otool -lvv foo.dylib This should produce: truncated or malformed object (offset field of section 1 in LC_SEGMENT_64 command 0 not past the headers of the file) This commit: - Changes MachOFileLayout to always assume LC_FUNCTION_STARTS is present for the initial computation, as long as generating LC_FUNCTION_STARTS is enabled. It would be slightly better to check whether there are actually any functions, since no LC_FUNCTION_STARTS will be generated if not, but it doesn't cause a problem if the initial computation is too high. - Adds a test. - Adds an assert in MachOFileLayout::writeSectionContent() that we are not writing section content into the load commands region (which would happen if the offset was calculated too low due to the initial load commands size calculation being too low). Adds an assert in MachOFileLayout::writeLoadCommands to validate a similar situation where two size-of-load-commands computations are expected to be equivalent. llvm-svn: 358545
2019-04-17 09:47:16 +08:00
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;
}
lld: Fix initial Mach-O load commands size calculation omitting LC_FUNCTION_STARTS Patch by Nicholas Allegra. The Mach-O writer calculates the size of load commands multiple times. First, Util::assignAddressesToSections() (in MachONormalizedFileFromAtoms.cpp) calculates the size using headerAndLoadCommandsSize() (in MachONormalizedFileBinaryWriter.cpp), which creates a temporary MachOFileLayout for the NormalizedFile, only to retrieve its headerAndLoadCommandsSize. Later, writeBinary() (in MachONormalizedFileBinaryWriter.cpp) creates a new layout and uses the offsets from that layout to actually write out everything in the NormalizedFile. But the NormalizedFile changes between the first computation and the second. When Util::assignAddressesToSections is called, file.functionStarts is always empty because Util::addFunctionStarts has not yet been called. Yet MachOFileLayout decides whether to include a LC_FUNCTION_STARTS command based on whether file.functionStarts is nonempty. Therefore, the initial computation always omits it. Because padding for the __TEXT segment (to make its size a multiple of the page size) is added between the load commands and the first section, LLD still generates a valid binary as long as the amount of padding happens to be large enough to fit LC_FUNCTION_STARTS command, which it usually is. However, it's easy to reproduce the issue by adding a section of a precise size. Given foo.c: __attribute__((section("__TEXT,__foo"))) char foo[0xd78] = {0}; Run: clang -dynamiclib -o foo.dylib foo.c -fuse-ld=lld -install_name /usr/lib/foo.dylib otool -lvv foo.dylib This should produce: truncated or malformed object (offset field of section 1 in LC_SEGMENT_64 command 0 not past the headers of the file) This commit: - Changes MachOFileLayout to always assume LC_FUNCTION_STARTS is present for the initial computation, as long as generating LC_FUNCTION_STARTS is enabled. It would be slightly better to check whether there are actually any functions, since no LC_FUNCTION_STARTS will be generated if not, but it doesn't cause a problem if the initial computation is too high. - Adds a test. - Adds an assert in MachOFileLayout::writeSectionContent() that we are not writing section content into the load commands region (which would happen if the offset was calculated too low due to the initial load commands size calculation being too low). Adds an assert in MachOFileLayout::writeLoadCommands to validate a similar situation where two size-of-load-commands computations are expected to be equivalent. llvm-svn: 358545
2019-04-17 09:47:16 +08:00
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++;
}
lld: Fix initial Mach-O load commands size calculation omitting LC_FUNCTION_STARTS Patch by Nicholas Allegra. The Mach-O writer calculates the size of load commands multiple times. First, Util::assignAddressesToSections() (in MachONormalizedFileFromAtoms.cpp) calculates the size using headerAndLoadCommandsSize() (in MachONormalizedFileBinaryWriter.cpp), which creates a temporary MachOFileLayout for the NormalizedFile, only to retrieve its headerAndLoadCommandsSize. Later, writeBinary() (in MachONormalizedFileBinaryWriter.cpp) creates a new layout and uses the offsets from that layout to actually write out everything in the NormalizedFile. But the NormalizedFile changes between the first computation and the second. When Util::assignAddressesToSections is called, file.functionStarts is always empty because Util::addFunctionStarts has not yet been called. Yet MachOFileLayout decides whether to include a LC_FUNCTION_STARTS command based on whether file.functionStarts is nonempty. Therefore, the initial computation always omits it. Because padding for the __TEXT segment (to make its size a multiple of the page size) is added between the load commands and the first section, LLD still generates a valid binary as long as the amount of padding happens to be large enough to fit LC_FUNCTION_STARTS command, which it usually is. However, it's easy to reproduce the issue by adding a section of a precise size. Given foo.c: __attribute__((section("__TEXT,__foo"))) char foo[0xd78] = {0}; Run: clang -dynamiclib -o foo.dylib foo.c -fuse-ld=lld -install_name /usr/lib/foo.dylib otool -lvv foo.dylib This should produce: truncated or malformed object (offset field of section 1 in LC_SEGMENT_64 command 0 not past the headers of the file) This commit: - Changes MachOFileLayout to always assume LC_FUNCTION_STARTS is present for the initial computation, as long as generating LC_FUNCTION_STARTS is enabled. It would be slightly better to check whether there are actually any functions, since no LC_FUNCTION_STARTS will be generated if not, but it doesn't cause a problem if the initial computation is too high. - Adds a test. - Adds an assert in MachOFileLayout::writeSectionContent() that we are not writing section content into the load commands region (which would happen if the offset was calculated too low due to the initial load commands size calculation being too low). Adds an assert in MachOFileLayout::writeLoadCommands to validate a similar situation where two size-of-load-commands computations are expected to be equivalent. llvm-svn: 358545
2019-04-17 09:47:16 +08:00
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
lld: Fix initial Mach-O load commands size calculation omitting LC_FUNCTION_STARTS Patch by Nicholas Allegra. The Mach-O writer calculates the size of load commands multiple times. First, Util::assignAddressesToSections() (in MachONormalizedFileFromAtoms.cpp) calculates the size using headerAndLoadCommandsSize() (in MachONormalizedFileBinaryWriter.cpp), which creates a temporary MachOFileLayout for the NormalizedFile, only to retrieve its headerAndLoadCommandsSize. Later, writeBinary() (in MachONormalizedFileBinaryWriter.cpp) creates a new layout and uses the offsets from that layout to actually write out everything in the NormalizedFile. But the NormalizedFile changes between the first computation and the second. When Util::assignAddressesToSections is called, file.functionStarts is always empty because Util::addFunctionStarts has not yet been called. Yet MachOFileLayout decides whether to include a LC_FUNCTION_STARTS command based on whether file.functionStarts is nonempty. Therefore, the initial computation always omits it. Because padding for the __TEXT segment (to make its size a multiple of the page size) is added between the load commands and the first section, LLD still generates a valid binary as long as the amount of padding happens to be large enough to fit LC_FUNCTION_STARTS command, which it usually is. However, it's easy to reproduce the issue by adding a section of a precise size. Given foo.c: __attribute__((section("__TEXT,__foo"))) char foo[0xd78] = {0}; Run: clang -dynamiclib -o foo.dylib foo.c -fuse-ld=lld -install_name /usr/lib/foo.dylib otool -lvv foo.dylib This should produce: truncated or malformed object (offset field of section 1 in LC_SEGMENT_64 command 0 not past the headers of the file) This commit: - Changes MachOFileLayout to always assume LC_FUNCTION_STARTS is present for the initial computation, as long as generating LC_FUNCTION_STARTS is enabled. It would be slightly better to check whether there are actually any functions, since no LC_FUNCTION_STARTS will be generated if not, but it doesn't cause a problem if the initial computation is too high. - Adds a test. - Adds an assert in MachOFileLayout::writeSectionContent() that we are not writing section content into the load commands region (which would happen if the offset was calculated too low due to the initial load commands size calculation being too low). Adds an assert in MachOFileLayout::writeLoadCommands to validate a similar situation where two size-of-load-commands computations are expected to be equivalent. llvm-svn: 358545
2019-04-17 09:47:16 +08:00
+ 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");
}
}
lld: Fix initial Mach-O load commands size calculation omitting LC_FUNCTION_STARTS Patch by Nicholas Allegra. The Mach-O writer calculates the size of load commands multiple times. First, Util::assignAddressesToSections() (in MachONormalizedFileFromAtoms.cpp) calculates the size using headerAndLoadCommandsSize() (in MachONormalizedFileBinaryWriter.cpp), which creates a temporary MachOFileLayout for the NormalizedFile, only to retrieve its headerAndLoadCommandsSize. Later, writeBinary() (in MachONormalizedFileBinaryWriter.cpp) creates a new layout and uses the offsets from that layout to actually write out everything in the NormalizedFile. But the NormalizedFile changes between the first computation and the second. When Util::assignAddressesToSections is called, file.functionStarts is always empty because Util::addFunctionStarts has not yet been called. Yet MachOFileLayout decides whether to include a LC_FUNCTION_STARTS command based on whether file.functionStarts is nonempty. Therefore, the initial computation always omits it. Because padding for the __TEXT segment (to make its size a multiple of the page size) is added between the load commands and the first section, LLD still generates a valid binary as long as the amount of padding happens to be large enough to fit LC_FUNCTION_STARTS command, which it usually is. However, it's easy to reproduce the issue by adding a section of a precise size. Given foo.c: __attribute__((section("__TEXT,__foo"))) char foo[0xd78] = {0}; Run: clang -dynamiclib -o foo.dylib foo.c -fuse-ld=lld -install_name /usr/lib/foo.dylib otool -lvv foo.dylib This should produce: truncated or malformed object (offset field of section 1 in LC_SEGMENT_64 command 0 not past the headers of the file) This commit: - Changes MachOFileLayout to always assume LC_FUNCTION_STARTS is present for the initial computation, as long as generating LC_FUNCTION_STARTS is enabled. It would be slightly better to check whether there are actually any functions, since no LC_FUNCTION_STARTS will be generated if not, but it doesn't cause a problem if the initial computation is too high. - Adds a test. - Adds an assert in MachOFileLayout::writeSectionContent() that we are not writing section content into the load commands region (which would happen if the offset was calculated too low due to the initial load commands size calculation being too low). Adds an assert in MachOFileLayout::writeLoadCommands to validate a similar situation where two size-of-load-commands computations are expected to be equivalent. llvm-svn: 358545
2019-04-17 09:47:16 +08:00
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
lld: Fix initial Mach-O load commands size calculation omitting LC_FUNCTION_STARTS Patch by Nicholas Allegra. The Mach-O writer calculates the size of load commands multiple times. First, Util::assignAddressesToSections() (in MachONormalizedFileFromAtoms.cpp) calculates the size using headerAndLoadCommandsSize() (in MachONormalizedFileBinaryWriter.cpp), which creates a temporary MachOFileLayout for the NormalizedFile, only to retrieve its headerAndLoadCommandsSize. Later, writeBinary() (in MachONormalizedFileBinaryWriter.cpp) creates a new layout and uses the offsets from that layout to actually write out everything in the NormalizedFile. But the NormalizedFile changes between the first computation and the second. When Util::assignAddressesToSections is called, file.functionStarts is always empty because Util::addFunctionStarts has not yet been called. Yet MachOFileLayout decides whether to include a LC_FUNCTION_STARTS command based on whether file.functionStarts is nonempty. Therefore, the initial computation always omits it. Because padding for the __TEXT segment (to make its size a multiple of the page size) is added between the load commands and the first section, LLD still generates a valid binary as long as the amount of padding happens to be large enough to fit LC_FUNCTION_STARTS command, which it usually is. However, it's easy to reproduce the issue by adding a section of a precise size. Given foo.c: __attribute__((section("__TEXT,__foo"))) char foo[0xd78] = {0}; Run: clang -dynamiclib -o foo.dylib foo.c -fuse-ld=lld -install_name /usr/lib/foo.dylib otool -lvv foo.dylib This should produce: truncated or malformed object (offset field of section 1 in LC_SEGMENT_64 command 0 not past the headers of the file) This commit: - Changes MachOFileLayout to always assume LC_FUNCTION_STARTS is present for the initial computation, as long as generating LC_FUNCTION_STARTS is enabled. It would be slightly better to check whether there are actually any functions, since no LC_FUNCTION_STARTS will be generated if not, but it doesn't cause a problem if the initial computation is too high. - Adds a test. - Adds an assert in MachOFileLayout::writeSectionContent() that we are not writing section content into the load commands region (which would happen if the offset was calculated too low due to the initial load commands size calculation being too low). Adds an assert in MachOFileLayout::writeLoadCommands to validate a similar situation where two size-of-load-commands computations are expected to be equivalent. llvm-svn: 358545
2019-04-17 09:47:16 +08:00
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);
}
}
lld: Fix initial Mach-O load commands size calculation omitting LC_FUNCTION_STARTS Patch by Nicholas Allegra. The Mach-O writer calculates the size of load commands multiple times. First, Util::assignAddressesToSections() (in MachONormalizedFileFromAtoms.cpp) calculates the size using headerAndLoadCommandsSize() (in MachONormalizedFileBinaryWriter.cpp), which creates a temporary MachOFileLayout for the NormalizedFile, only to retrieve its headerAndLoadCommandsSize. Later, writeBinary() (in MachONormalizedFileBinaryWriter.cpp) creates a new layout and uses the offsets from that layout to actually write out everything in the NormalizedFile. But the NormalizedFile changes between the first computation and the second. When Util::assignAddressesToSections is called, file.functionStarts is always empty because Util::addFunctionStarts has not yet been called. Yet MachOFileLayout decides whether to include a LC_FUNCTION_STARTS command based on whether file.functionStarts is nonempty. Therefore, the initial computation always omits it. Because padding for the __TEXT segment (to make its size a multiple of the page size) is added between the load commands and the first section, LLD still generates a valid binary as long as the amount of padding happens to be large enough to fit LC_FUNCTION_STARTS command, which it usually is. However, it's easy to reproduce the issue by adding a section of a precise size. Given foo.c: __attribute__((section("__TEXT,__foo"))) char foo[0xd78] = {0}; Run: clang -dynamiclib -o foo.dylib foo.c -fuse-ld=lld -install_name /usr/lib/foo.dylib otool -lvv foo.dylib This should produce: truncated or malformed object (offset field of section 1 in LC_SEGMENT_64 command 0 not past the headers of the file) This commit: - Changes MachOFileLayout to always assume LC_FUNCTION_STARTS is present for the initial computation, as long as generating LC_FUNCTION_STARTS is enabled. It would be slightly better to check whether there are actually any functions, since no LC_FUNCTION_STARTS will be generated if not, but it doesn't cause a problem if the initial computation is too high. - Adds a test. - Adds an assert in MachOFileLayout::writeSectionContent() that we are not writing section content into the load commands region (which would happen if the offset was calculated too low due to the initial load commands size calculation being too low). Adds an assert in MachOFileLayout::writeLoadCommands to validate a similar situation where two size-of-load-commands computations are expected to be equivalent. llvm-svn: 358545
2019-04-17 09:47:16 +08:00
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;
lld: Fix initial Mach-O load commands size calculation omitting LC_FUNCTION_STARTS Patch by Nicholas Allegra. The Mach-O writer calculates the size of load commands multiple times. First, Util::assignAddressesToSections() (in MachONormalizedFileFromAtoms.cpp) calculates the size using headerAndLoadCommandsSize() (in MachONormalizedFileBinaryWriter.cpp), which creates a temporary MachOFileLayout for the NormalizedFile, only to retrieve its headerAndLoadCommandsSize. Later, writeBinary() (in MachONormalizedFileBinaryWriter.cpp) creates a new layout and uses the offsets from that layout to actually write out everything in the NormalizedFile. But the NormalizedFile changes between the first computation and the second. When Util::assignAddressesToSections is called, file.functionStarts is always empty because Util::addFunctionStarts has not yet been called. Yet MachOFileLayout decides whether to include a LC_FUNCTION_STARTS command based on whether file.functionStarts is nonempty. Therefore, the initial computation always omits it. Because padding for the __TEXT segment (to make its size a multiple of the page size) is added between the load commands and the first section, LLD still generates a valid binary as long as the amount of padding happens to be large enough to fit LC_FUNCTION_STARTS command, which it usually is. However, it's easy to reproduce the issue by adding a section of a precise size. Given foo.c: __attribute__((section("__TEXT,__foo"))) char foo[0xd78] = {0}; Run: clang -dynamiclib -o foo.dylib foo.c -fuse-ld=lld -install_name /usr/lib/foo.dylib otool -lvv foo.dylib This should produce: truncated or malformed object (offset field of section 1 in LC_SEGMENT_64 command 0 not past the headers of the file) This commit: - Changes MachOFileLayout to always assume LC_FUNCTION_STARTS is present for the initial computation, as long as generating LC_FUNCTION_STARTS is enabled. It would be slightly better to check whether there are actually any functions, since no LC_FUNCTION_STARTS will be generated if not, but it doesn't cause a problem if the initial computation is too high. - Adds a test. - Adds an assert in MachOFileLayout::writeSectionContent() that we are not writing section content into the load commands region (which would happen if the offset was calculated too low due to the initial load commands size calculation being too low). Adds an assert in MachOFileLayout::writeLoadCommands to validate a similar situation where two size-of-load-commands computations are expected to be equivalent. llvm-svn: 358545
2019-04-17 09:47:16 +08:00
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) {
lld: Fix initial Mach-O load commands size calculation omitting LC_FUNCTION_STARTS Patch by Nicholas Allegra. The Mach-O writer calculates the size of load commands multiple times. First, Util::assignAddressesToSections() (in MachONormalizedFileFromAtoms.cpp) calculates the size using headerAndLoadCommandsSize() (in MachONormalizedFileBinaryWriter.cpp), which creates a temporary MachOFileLayout for the NormalizedFile, only to retrieve its headerAndLoadCommandsSize. Later, writeBinary() (in MachONormalizedFileBinaryWriter.cpp) creates a new layout and uses the offsets from that layout to actually write out everything in the NormalizedFile. But the NormalizedFile changes between the first computation and the second. When Util::assignAddressesToSections is called, file.functionStarts is always empty because Util::addFunctionStarts has not yet been called. Yet MachOFileLayout decides whether to include a LC_FUNCTION_STARTS command based on whether file.functionStarts is nonempty. Therefore, the initial computation always omits it. Because padding for the __TEXT segment (to make its size a multiple of the page size) is added between the load commands and the first section, LLD still generates a valid binary as long as the amount of padding happens to be large enough to fit LC_FUNCTION_STARTS command, which it usually is. However, it's easy to reproduce the issue by adding a section of a precise size. Given foo.c: __attribute__((section("__TEXT,__foo"))) char foo[0xd78] = {0}; Run: clang -dynamiclib -o foo.dylib foo.c -fuse-ld=lld -install_name /usr/lib/foo.dylib otool -lvv foo.dylib This should produce: truncated or malformed object (offset field of section 1 in LC_SEGMENT_64 command 0 not past the headers of the file) This commit: - Changes MachOFileLayout to always assume LC_FUNCTION_STARTS is present for the initial computation, as long as generating LC_FUNCTION_STARTS is enabled. It would be slightly better to check whether there are actually any functions, since no LC_FUNCTION_STARTS will be generated if not, but it doesn't cause a problem if the initial computation is too high. - Adds a test. - Adds an assert in MachOFileLayout::writeSectionContent() that we are not writing section content into the load commands region (which would happen if the offset was calculated too low due to the initial load commands size calculation being too low). Adds an assert in MachOFileLayout::writeLoadCommands to validate a similar situation where two size-of-load-commands computations are expected to be equivalent. llvm-svn: 358545
2019-04-17 09:47:16 +08:00
MachOFileLayout layout(file, false);
return layout.writeBinary(path);
}
} // namespace normalized
} // namespace mach_o
} // namespace lld