diff options
Diffstat (limited to 'lib/ReaderWriter/Native')
-rw-r--r-- | lib/ReaderWriter/Native/CMakeLists.txt | 7 | ||||
-rw-r--r-- | lib/ReaderWriter/Native/Makefile | 14 | ||||
-rw-r--r-- | lib/ReaderWriter/Native/NativeFileFormat.h | 258 | ||||
-rw-r--r-- | lib/ReaderWriter/Native/ReaderNative.cpp | 1013 | ||||
-rw-r--r-- | lib/ReaderWriter/Native/WriterNative.cpp | 566 |
5 files changed, 1858 insertions, 0 deletions
diff --git a/lib/ReaderWriter/Native/CMakeLists.txt b/lib/ReaderWriter/Native/CMakeLists.txt new file mode 100644 index 000000000000..e15f3d60e89c --- /dev/null +++ b/lib/ReaderWriter/Native/CMakeLists.txt @@ -0,0 +1,7 @@ +add_llvm_library(lldNative + ReaderNative.cpp + WriterNative.cpp + LINK_LIBS + lldCore + LLVMSupport + ) diff --git a/lib/ReaderWriter/Native/Makefile b/lib/ReaderWriter/Native/Makefile new file mode 100644 index 000000000000..6aba37868900 --- /dev/null +++ b/lib/ReaderWriter/Native/Makefile @@ -0,0 +1,14 @@ +##===- lld/lib/ReaderWriter/Native/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLD_LEVEL := ../../.. +LIBRARYNAME := lldNative +USEDLIBS = lldCore.a + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/Native/NativeFileFormat.h b/lib/ReaderWriter/Native/NativeFileFormat.h new file mode 100644 index 000000000000..535072fe2314 --- /dev/null +++ b/lib/ReaderWriter/Native/NativeFileFormat.h @@ -0,0 +1,258 @@ +//===- lib/ReaderWriter/Native/NativeFileFormat.h -------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_NATIVE_NATIVE_FILE_FORMAT_H +#define LLD_READER_WRITER_NATIVE_NATIVE_FILE_FORMAT_H + +#include "llvm/Support/DataTypes.h" +#include <cstdint> + +namespace lld { + +// +// Overview: +// +// The number one design goal of this file format is enable the linker to +// read object files into in-memory Atom objects extremely quickly. +// The second design goal is to enable future modifications to the +// Atom attribute model. +// +// The llvm native object file format is not like traditional object file +// formats (e.g. ELF, COFF, mach-o). There is no symbol table and no +// sections. Instead the file is essentially an array of archived Atoms. +// It is *not* serialized Atoms which would require deserialization into +// in memory objects. Instead it is an array of read-only info about each +// Atom. The NativeReader bulk creates in-memory Atoms which just have +// an ivar which points to the read-only info for that Atom. No additional +// processing is done to construct the in-memory Atoms. All Atom attribute +// getter methods are virtual calls which dig up the info they need from the +// ivar data. +// +// To support the gradual evolution of Atom attributes, the Atom read-only +// data is versioned. The NativeReader chooses which in-memory Atom class +// to use based on the version. What this means is that if new attributes +// are added (or changed) in the Atom model, a new native atom class and +// read-only atom info struct needs to be defined. Then, all the existing +// native reader atom classes need to be modified to do their best effort +// to map their old style read-only data to the new Atom model. At some point +// some classes to support old versions may be dropped. +// +// +// Details: +// +// The native object file format consists of a header that specifies the +// endianness of the file and the architecture along with a list of "chunks" +// in the file. A Chunk is simply a tagged range of the file. There is +// one chunk for the array of atom infos. There is another chunk for the +// string pool, and another for the content pool. +// +// It turns out there most atoms have very similar sets of attributes, only +// the name and content attribute vary. To exploit this fact to reduce the file +// size, the atom read-only info contains just the name and content info plus +// a reference to which attribute set it uses. The attribute sets are stored +// in another chunk. +// + + +// +// An entry in the NativeFileHeader that describes one chunk of the file. +// +struct NativeChunk { + uint32_t signature; + uint32_t fileOffset; + uint32_t fileSize; + uint32_t elementCount; +}; + + +// +// The header in a native object file +// +struct NativeFileHeader { + uint8_t magic[16]; + uint32_t endian; + uint32_t architecture; + uint32_t fileSize; + uint32_t chunkCount; + // NativeChunk chunks[] +}; + +// +// Possible values for NativeChunk.signature field +// +enum NativeChunkSignatures { + NCS_DefinedAtomsV1 = 1, + NCS_AttributesArrayV1 = 2, + NCS_AbsoluteAttributesV1 = 12, + NCS_UndefinedAtomsV1 = 3, + NCS_SharedLibraryAtomsV1 = 4, + NCS_AbsoluteAtomsV1 = 5, + NCS_Strings = 6, + NCS_ReferencesArrayV1 = 7, + NCS_ReferencesArrayV2 = 8, + NCS_TargetsTable = 9, + NCS_AddendsTable = 10, + NCS_Content = 11, +}; + +// +// The 16-bytes at the start of a native object file +// +#define NATIVE_FILE_HEADER_MAGIC "llvm nat obj v1 " + +// +// Possible values for the NativeFileHeader.endian field +// +enum { + NFH_BigEndian = 0x42696745, + NFH_LittleEndian = 0x4574696c +}; + + +// +// Possible values for the NativeFileHeader.architecture field +// +enum { + NFA_x86 = 1, + NFA_x86_64 = 2, + NFA_armv6 = 3, + NFA_armv7 = 4, +}; + + +// +// The NCS_DefinedAtomsV1 chunk contains an array of these structs +// +struct NativeDefinedAtomIvarsV1 { + uint32_t nameOffset; + uint32_t attributesOffset; + uint32_t referencesStartIndex; + uint32_t referencesCount; + uint32_t contentOffset; + uint32_t contentSize; + uint64_t sectionSize; +}; + + +// +// The NCS_AttributesArrayV1 chunk contains an array of these structs +// +struct NativeAtomAttributesV1 { + uint32_t sectionNameOffset; + uint16_t align2; + uint16_t alignModulus; + uint8_t scope; + uint8_t interposable; + uint8_t merge; + uint8_t contentType; + uint8_t sectionChoice; + uint8_t deadStrip; + uint8_t dynamicExport; + uint8_t permissions; + uint8_t alias; + uint8_t codeModel; +}; + + + +// +// The NCS_UndefinedAtomsV1 chunk contains an array of these structs +// +struct NativeUndefinedAtomIvarsV1 { + uint32_t nameOffset; + uint32_t flags; + uint32_t fallbackNameOffset; +}; + + +// +// The NCS_SharedLibraryAtomsV1 chunk contains an array of these structs +// +struct NativeSharedLibraryAtomIvarsV1 { + uint64_t size; + uint32_t nameOffset; + uint32_t loadNameOffset; + uint32_t type; + uint32_t flags; +}; + + + +// +// The NCS_AbsoluteAtomsV1 chunk contains an array of these structs +// +struct NativeAbsoluteAtomIvarsV1 { + uint32_t nameOffset; + uint32_t attributesOffset; + uint32_t reserved; + uint64_t value; +}; + + + +// +// The NCS_ReferencesArrayV1 chunk contains an array of these structs +// +struct NativeReferenceIvarsV1 { + enum { + noTarget = UINT16_MAX + }; + uint32_t offsetInAtom; + uint16_t kindValue; + uint8_t kindNamespace; + uint8_t kindArch; + uint16_t targetIndex; + uint16_t addendIndex; +}; + + +// +// The NCS_ReferencesArrayV2 chunk contains an array of these structs +// +struct NativeReferenceIvarsV2 { + enum : unsigned { + noTarget = UINT32_MAX + }; + uint64_t offsetInAtom; + int64_t addend; + uint16_t kindValue; + uint8_t kindNamespace; + uint8_t kindArch; + uint32_t targetIndex; + uint32_t tag; +}; + + +// +// The NCS_TargetsTable chunk contains an array of uint32_t entries. +// The C++ class Reference has a target() method that returns a +// pointer to another Atom. We can't have pointers in object files, +// so instead NativeReferenceIvarsV1 contains an index to the target. +// The index is into this NCS_TargetsTable of uint32_t entries. +// The values in this table are the index of the (target) atom in this file. +// For DefinedAtoms the value is from 0 to NCS_DefinedAtomsV1.elementCount. +// For UndefinedAtoms the value is from NCS_DefinedAtomsV1.elementCount to +// NCS_DefinedAtomsV1.elementCount+NCS_UndefinedAtomsV1.elementCount. +// + + +// +// The NCS_AddendsTable chunk contains an array of int64_t entries. +// If we allocated space for addends directly in NativeReferenceIvarsV1 +// it would double the size of that struct. But since addends are rare, +// we instead just keep a pool of addends and have NativeReferenceIvarsV1 +// (if it needs an addend) just store the index (into the pool) of the +// addend it needs. +// + + + +} // namespace lld + +#endif // LLD_READER_WRITER_NATIVE_NATIVE_FILE_FORMAT_H diff --git a/lib/ReaderWriter/Native/ReaderNative.cpp b/lib/ReaderWriter/Native/ReaderNative.cpp new file mode 100644 index 000000000000..84cdb4b997e8 --- /dev/null +++ b/lib/ReaderWriter/Native/ReaderNative.cpp @@ -0,0 +1,1013 @@ +//===- lib/ReaderWriter/Native/ReaderNative.cpp ---------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NativeFileFormat.h" +#include "lld/Core/Atom.h" +#include "lld/Core/Error.h" +#include "lld/Core/File.h" +#include "lld/Core/Reader.h" +#include "lld/Core/Simple.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> +#include <vector> + +namespace lld { +namespace native { + +// forward reference +class File; + +// +// An object of this class is instantied for each NativeDefinedAtomIvarsV1 +// struct in the NCS_DefinedAtomsV1 chunk. +// +class NativeDefinedAtomV1 : public DefinedAtom { +public: + NativeDefinedAtomV1(const File& f, + const NativeDefinedAtomIvarsV1* ivarData) + : _file(&f), _ivarData(ivarData) { } + + const lld::File& file() const override; + + uint64_t ordinal() const override; + + StringRef name() const override; + + uint64_t size() const override { return _ivarData->contentSize; } + + uint64_t sectionSize() const override { return _ivarData->sectionSize; } + + DefinedAtom::Scope scope() const override { + return (DefinedAtom::Scope)(attributes().scope); + } + + DefinedAtom::Interposable interposable() const override { + return (DefinedAtom::Interposable)(attributes().interposable); + } + + DefinedAtom::Merge merge() const override { + return (DefinedAtom::Merge)(attributes().merge); + } + + DefinedAtom::ContentType contentType() const override { + const NativeAtomAttributesV1& attr = attributes(); + return (DefinedAtom::ContentType)(attr.contentType); + } + + DefinedAtom::Alignment alignment() const override { + return DefinedAtom::Alignment(attributes().align2, attributes().alignModulus); + } + + DefinedAtom::SectionChoice sectionChoice() const override { + return (DefinedAtom::SectionChoice)(attributes().sectionChoice); + } + + StringRef customSectionName() const override; + + DefinedAtom::DeadStripKind deadStrip() const override { + return (DefinedAtom::DeadStripKind)(attributes().deadStrip); + } + + DynamicExport dynamicExport() const override { + return (DynamicExport)attributes().dynamicExport; + } + + DefinedAtom::CodeModel codeModel() const override { + return DefinedAtom::CodeModel(attributes().codeModel); + } + + DefinedAtom::ContentPermissions permissions() const override { + return (DefinedAtom::ContentPermissions)(attributes().permissions); + } + + ArrayRef<uint8_t> rawContent() const override; + + reference_iterator begin() const override; + + reference_iterator end() const override; + + const Reference* derefIterator(const void*) const override; + + void incrementIterator(const void*& it) const override; + +private: + const NativeAtomAttributesV1& attributes() const; + + const File *_file; + const NativeDefinedAtomIvarsV1 *_ivarData; +}; + + + +// +// An object of this class is instantied for each NativeUndefinedAtomIvarsV1 +// struct in the NCS_UndefinedAtomsV1 chunk. +// +class NativeUndefinedAtomV1 : public UndefinedAtom { +public: + NativeUndefinedAtomV1(const File& f, + const NativeUndefinedAtomIvarsV1* ivarData) + : _file(&f), _ivarData(ivarData) { } + + const lld::File& file() const override; + StringRef name() const override; + + CanBeNull canBeNull() const override { + return (CanBeNull)(_ivarData->flags & 0x3); + } + + const UndefinedAtom *fallback() const override; + +private: + const File *_file; + const NativeUndefinedAtomIvarsV1 *_ivarData; + mutable std::unique_ptr<const SimpleUndefinedAtom> _fallback; +}; + + +// +// An object of this class is instantied for each NativeUndefinedAtomIvarsV1 +// struct in the NCS_SharedLibraryAtomsV1 chunk. +// +class NativeSharedLibraryAtomV1 : public SharedLibraryAtom { +public: + NativeSharedLibraryAtomV1(const File& f, + const NativeSharedLibraryAtomIvarsV1* ivarData) + : _file(&f), _ivarData(ivarData) { } + + const lld::File& file() const override; + StringRef name() const override; + StringRef loadName() const override; + + bool canBeNullAtRuntime() const override { + return (_ivarData->flags & 0x1); + } + + Type type() const override { + return (Type)_ivarData->type; + } + + uint64_t size() const override { + return _ivarData->size; + } + +private: + const File *_file; + const NativeSharedLibraryAtomIvarsV1 *_ivarData; +}; + + +// +// An object of this class is instantied for each NativeAbsoluteAtomIvarsV1 +// struct in the NCS_AbsoluteAtomsV1 chunk. +// +class NativeAbsoluteAtomV1 : public AbsoluteAtom { +public: + NativeAbsoluteAtomV1(const File& f, + const NativeAbsoluteAtomIvarsV1* ivarData) + : _file(&f), _ivarData(ivarData) { } + + const lld::File& file() const override; + StringRef name() const override; + Scope scope() const override { + const NativeAtomAttributesV1& attr = absAttributes(); + return (Scope)(attr.scope); + } + uint64_t value() const override { + return _ivarData->value; + } + +private: + const NativeAtomAttributesV1& absAttributes() const; + const File *_file; + const NativeAbsoluteAtomIvarsV1 *_ivarData; +}; + + +// +// An object of this class is instantied for each NativeReferenceIvarsV1 +// struct in the NCS_ReferencesArrayV1 chunk. +// +class NativeReferenceV1 : public Reference { +public: + NativeReferenceV1(const File &f, const NativeReferenceIvarsV1 *ivarData) + : Reference((KindNamespace)ivarData->kindNamespace, + (KindArch)ivarData->kindArch, ivarData->kindValue), + _file(&f), _ivarData(ivarData) {} + + uint64_t offsetInAtom() const override { + return _ivarData->offsetInAtom; + } + + const Atom* target() const override; + Addend addend() const override; + void setTarget(const Atom* newAtom) override; + void setAddend(Addend a) override; + +private: + const File *_file; + const NativeReferenceIvarsV1 *_ivarData; +}; + + +// +// An object of this class is instantied for each NativeReferenceIvarsV1 +// struct in the NCS_ReferencesArrayV1 chunk. +// +class NativeReferenceV2 : public Reference { +public: + NativeReferenceV2(const File &f, const NativeReferenceIvarsV2 *ivarData) + : Reference((KindNamespace)ivarData->kindNamespace, + (KindArch)ivarData->kindArch, ivarData->kindValue), + _file(&f), _ivarData(ivarData) {} + + uint64_t offsetInAtom() const override { + return _ivarData->offsetInAtom; + } + + const Atom* target() const override; + Addend addend() const override; + void setTarget(const Atom* newAtom) override; + void setAddend(Addend a) override; + uint32_t tag() const override; + +private: + const File *_file; + const NativeReferenceIvarsV2 *_ivarData; +}; + + +// +// lld::File object for native llvm object file +// +class File : public lld::File { +public: + File(std::unique_ptr<MemoryBuffer> mb) + : lld::File(mb->getBufferIdentifier(), kindObject), + _mb(std::move(mb)), // Reader now takes ownership of buffer + _header(nullptr), _targetsTable(nullptr), _targetsTableCount(0), + _strings(nullptr), _stringsMaxOffset(0), _addends(nullptr), + _addendsMaxIndex(0), _contentStart(nullptr), _contentEnd(nullptr) { + _header = + reinterpret_cast<const NativeFileHeader *>(_mb->getBufferStart()); + } + + /// Parses a File object from a native object file. + std::error_code doParse() override { + const uint8_t *const base = + reinterpret_cast<const uint8_t *>(_mb->getBufferStart()); + StringRef path(_mb->getBufferIdentifier()); + const NativeFileHeader *const header = + reinterpret_cast<const NativeFileHeader *>(base); + const NativeChunk *const chunks = + reinterpret_cast<const NativeChunk *>(base + sizeof(NativeFileHeader)); + // make sure magic matches + if (memcmp(header->magic, NATIVE_FILE_HEADER_MAGIC, + sizeof(header->magic)) != 0) + return make_error_code(NativeReaderError::unknown_file_format); + + // make sure mapped file contains all needed data + const size_t fileSize = _mb->getBufferSize(); + if (header->fileSize > fileSize) + return make_error_code(NativeReaderError::file_too_short); + + DEBUG_WITH_TYPE("ReaderNative", + llvm::dbgs() << " Native File Header:" << " fileSize=" + << header->fileSize << " chunkCount=" + << header->chunkCount << "\n"); + + // process each chunk + for (uint32_t i = 0; i < header->chunkCount; ++i) { + std::error_code ec; + const NativeChunk* chunk = &chunks[i]; + // sanity check chunk is within file + if ( chunk->fileOffset > fileSize ) + return make_error_code(NativeReaderError::file_malformed); + if ( (chunk->fileOffset + chunk->fileSize) > fileSize) + return make_error_code(NativeReaderError::file_malformed); + // process chunk, based on signature + switch ( chunk->signature ) { + case NCS_DefinedAtomsV1: + ec = processDefinedAtomsV1(base, chunk); + break; + case NCS_AttributesArrayV1: + ec = processAttributesV1(base, chunk); + break; + case NCS_UndefinedAtomsV1: + ec = processUndefinedAtomsV1(base, chunk); + break; + case NCS_SharedLibraryAtomsV1: + ec = processSharedLibraryAtomsV1(base, chunk); + break; + case NCS_AbsoluteAtomsV1: + ec = processAbsoluteAtomsV1(base, chunk); + break; + case NCS_AbsoluteAttributesV1: + ec = processAbsoluteAttributesV1(base, chunk); + break; + case NCS_ReferencesArrayV1: + ec = processReferencesV1(base, chunk); + break; + case NCS_ReferencesArrayV2: + ec = processReferencesV2(base, chunk); + break; + case NCS_TargetsTable: + ec = processTargetsTable(base, chunk); + break; + case NCS_AddendsTable: + ec = processAddendsTable(base, chunk); + break; + case NCS_Content: + ec = processContent(base, chunk); + break; + case NCS_Strings: + ec = processStrings(base, chunk); + break; + default: + return make_error_code(NativeReaderError::unknown_chunk_type); + } + if ( ec ) { + return ec; + } + } + // TO DO: validate enough chunks were used + + DEBUG_WITH_TYPE("ReaderNative", { + llvm::dbgs() << " ReaderNative DefinedAtoms:\n"; + for (const DefinedAtom *a : defined()) { + llvm::dbgs() << llvm::format(" 0x%09lX", a) + << ", name=" << a->name() + << ", size=" << a->size() << "\n"; + for (const Reference *r : *a) { + llvm::dbgs() << " offset=" + << llvm::format("0x%03X", r->offsetInAtom()) + << ", kind=" << r->kindValue() + << ", target=" << r->target() << "\n"; + } + } + }); + return make_error_code(NativeReaderError::success); + } + + virtual ~File() { + // _mb is automatically deleted because of std::unique_ptr<> + + // All other ivar pointers are pointers into the MemoryBuffer, except + // the _definedAtoms array which was allocated to contain an array + // of Atom objects. The atoms have empty destructors, so it is ok + // to just delete the memory. + delete _definedAtoms._arrayStart; + delete _undefinedAtoms._arrayStart; + delete _sharedLibraryAtoms._arrayStart; + delete _absoluteAtoms._arrayStart; + delete _referencesV1.arrayStart; + delete _referencesV2.arrayStart; + delete [] _targetsTable; + } + + const atom_collection<DefinedAtom>& defined() const override { + return _definedAtoms; + } + const atom_collection<UndefinedAtom>& undefined() const override { + return _undefinedAtoms; + } + const atom_collection<SharedLibraryAtom>& sharedLibrary() const override { + return _sharedLibraryAtoms; + } + const atom_collection<AbsoluteAtom> &absolute() const override { + return _absoluteAtoms; + } + +private: + friend NativeDefinedAtomV1; + friend NativeUndefinedAtomV1; + friend NativeSharedLibraryAtomV1; + friend NativeAbsoluteAtomV1; + friend NativeReferenceV1; + friend NativeReferenceV2; + + // instantiate array of DefinedAtoms from v1 ivar data in file + std::error_code processDefinedAtomsV1(const uint8_t *base, + const NativeChunk *chunk) { + const size_t atomSize = sizeof(NativeDefinedAtomV1); + size_t atomsArraySize = chunk->elementCount * atomSize; + uint8_t* atomsStart = reinterpret_cast<uint8_t*> + (operator new(atomsArraySize, std::nothrow)); + if (atomsStart == nullptr) + return make_error_code(NativeReaderError::memory_error); + const size_t ivarElementSize = chunk->fileSize + / chunk->elementCount; + if ( ivarElementSize != sizeof(NativeDefinedAtomIvarsV1) ) + return make_error_code(NativeReaderError::file_malformed); + uint8_t* atomsEnd = atomsStart + atomsArraySize; + const NativeDefinedAtomIvarsV1* ivarData = + reinterpret_cast<const NativeDefinedAtomIvarsV1*> + (base + chunk->fileOffset); + for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { + NativeDefinedAtomV1* atomAllocSpace = + reinterpret_cast<NativeDefinedAtomV1*>(s); + new (atomAllocSpace) NativeDefinedAtomV1(*this, ivarData); + ++ivarData; + } + this->_definedAtoms._arrayStart = atomsStart; + this->_definedAtoms._arrayEnd = atomsEnd; + this->_definedAtoms._elementSize = atomSize; + this->_definedAtoms._elementCount = chunk->elementCount; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk DefinedAtomsV1: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + + + // set up pointers to attributes array + std::error_code processAttributesV1(const uint8_t *base, + const NativeChunk *chunk) { + this->_attributes = base + chunk->fileOffset; + this->_attributesMaxOffset = chunk->fileSize; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk AttributesV1: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + // set up pointers to attributes array + std::error_code processAbsoluteAttributesV1(const uint8_t *base, + const NativeChunk *chunk) { + this->_absAttributes = base + chunk->fileOffset; + this->_absAbsoluteMaxOffset = chunk->fileSize; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk AbsoluteAttributesV1: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + // instantiate array of UndefinedAtoms from v1 ivar data in file + std::error_code processUndefinedAtomsV1(const uint8_t *base, + const NativeChunk *chunk) { + const size_t atomSize = sizeof(NativeUndefinedAtomV1); + size_t atomsArraySize = chunk->elementCount * atomSize; + uint8_t* atomsStart = reinterpret_cast<uint8_t*> + (operator new(atomsArraySize, std::nothrow)); + if (atomsStart == nullptr) + return make_error_code(NativeReaderError::memory_error); + const size_t ivarElementSize = chunk->fileSize + / chunk->elementCount; + if ( ivarElementSize != sizeof(NativeUndefinedAtomIvarsV1) ) + return make_error_code(NativeReaderError::file_malformed); + uint8_t* atomsEnd = atomsStart + atomsArraySize; + const NativeUndefinedAtomIvarsV1* ivarData = + reinterpret_cast<const NativeUndefinedAtomIvarsV1*> + (base + chunk->fileOffset); + for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { + NativeUndefinedAtomV1* atomAllocSpace = + reinterpret_cast<NativeUndefinedAtomV1*>(s); + new (atomAllocSpace) NativeUndefinedAtomV1(*this, ivarData); + ++ivarData; + } + this->_undefinedAtoms._arrayStart = atomsStart; + this->_undefinedAtoms._arrayEnd = atomsEnd; + this->_undefinedAtoms._elementSize = atomSize; + this->_undefinedAtoms._elementCount = chunk->elementCount; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk UndefinedAtomsV1:" + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + + // instantiate array of ShareLibraryAtoms from v1 ivar data in file + std::error_code processSharedLibraryAtomsV1(const uint8_t *base, + const NativeChunk *chunk) { + const size_t atomSize = sizeof(NativeSharedLibraryAtomV1); + size_t atomsArraySize = chunk->elementCount * atomSize; + uint8_t* atomsStart = reinterpret_cast<uint8_t*> + (operator new(atomsArraySize, std::nothrow)); + if (atomsStart == nullptr) + return make_error_code(NativeReaderError::memory_error); + const size_t ivarElementSize = chunk->fileSize + / chunk->elementCount; + if ( ivarElementSize != sizeof(NativeSharedLibraryAtomIvarsV1) ) + return make_error_code(NativeReaderError::file_malformed); + uint8_t* atomsEnd = atomsStart + atomsArraySize; + const NativeSharedLibraryAtomIvarsV1* ivarData = + reinterpret_cast<const NativeSharedLibraryAtomIvarsV1*> + (base + chunk->fileOffset); + for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { + NativeSharedLibraryAtomV1* atomAllocSpace = + reinterpret_cast<NativeSharedLibraryAtomV1*>(s); + new (atomAllocSpace) NativeSharedLibraryAtomV1(*this, ivarData); + ++ivarData; + } + this->_sharedLibraryAtoms._arrayStart = atomsStart; + this->_sharedLibraryAtoms._arrayEnd = atomsEnd; + this->_sharedLibraryAtoms._elementSize = atomSize; + this->_sharedLibraryAtoms._elementCount = chunk->elementCount; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk SharedLibraryAtomsV1:" + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + + // instantiate array of AbsoluteAtoms from v1 ivar data in file + std::error_code processAbsoluteAtomsV1(const uint8_t *base, + const NativeChunk *chunk) { + const size_t atomSize = sizeof(NativeAbsoluteAtomV1); + size_t atomsArraySize = chunk->elementCount * atomSize; + uint8_t* atomsStart = reinterpret_cast<uint8_t*> + (operator new(atomsArraySize, std::nothrow)); + if (atomsStart == nullptr) + return make_error_code(NativeReaderError::memory_error); + const size_t ivarElementSize = chunk->fileSize + / chunk->elementCount; + if ( ivarElementSize != sizeof(NativeAbsoluteAtomIvarsV1) ) + return make_error_code(NativeReaderError::file_malformed); + uint8_t* atomsEnd = atomsStart + atomsArraySize; + const NativeAbsoluteAtomIvarsV1* ivarData = + reinterpret_cast<const NativeAbsoluteAtomIvarsV1*> + (base + chunk->fileOffset); + for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { + NativeAbsoluteAtomV1* atomAllocSpace = + reinterpret_cast<NativeAbsoluteAtomV1*>(s); + new (atomAllocSpace) NativeAbsoluteAtomV1(*this, ivarData); + ++ivarData; + } + this->_absoluteAtoms._arrayStart = atomsStart; + this->_absoluteAtoms._arrayEnd = atomsEnd; + this->_absoluteAtoms._elementSize = atomSize; + this->_absoluteAtoms._elementCount = chunk->elementCount; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk AbsoluteAtomsV1: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + template <class T, class U> + std::error_code + processReferences(const uint8_t *base, const NativeChunk *chunk, + uint8_t *&refsStart, uint8_t *&refsEnd) const { + if (chunk->elementCount == 0) + return make_error_code(NativeReaderError::success); + size_t refsArraySize = chunk->elementCount * sizeof(T); + refsStart = reinterpret_cast<uint8_t *>( + operator new(refsArraySize, std::nothrow)); + if (refsStart == nullptr) + return make_error_code(NativeReaderError::memory_error); + const size_t ivarElementSize = chunk->fileSize / chunk->elementCount; + if (ivarElementSize != sizeof(U)) + return make_error_code(NativeReaderError::file_malformed); + refsEnd = refsStart + refsArraySize; + const U* ivarData = reinterpret_cast<const U *>(base + chunk->fileOffset); + for (uint8_t *s = refsStart; s != refsEnd; s += sizeof(T), ++ivarData) { + T *atomAllocSpace = reinterpret_cast<T *>(s); + new (atomAllocSpace) T(*this, ivarData); + } + return make_error_code(NativeReaderError::success); + } + + // instantiate array of References from v1 ivar data in file + std::error_code processReferencesV1(const uint8_t *base, + const NativeChunk *chunk) { + uint8_t *refsStart, *refsEnd; + if (std::error_code ec = + processReferences<NativeReferenceV1, NativeReferenceIvarsV1>( + base, chunk, refsStart, refsEnd)) + return ec; + this->_referencesV1.arrayStart = refsStart; + this->_referencesV1.arrayEnd = refsEnd; + this->_referencesV1.elementSize = sizeof(NativeReferenceV1); + this->_referencesV1.elementCount = chunk->elementCount; + DEBUG_WITH_TYPE("ReaderNative", { + llvm::dbgs() << " chunk ReferencesV1: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize << "\n"; + }); + return make_error_code(NativeReaderError::success); + } + + // instantiate array of References from v2 ivar data in file + std::error_code processReferencesV2(const uint8_t *base, + const NativeChunk *chunk) { + uint8_t *refsStart, *refsEnd; + if (std::error_code ec = + processReferences<NativeReferenceV2, NativeReferenceIvarsV2>( + base, chunk, refsStart, refsEnd)) + return ec; + this->_referencesV2.arrayStart = refsStart; + this->_referencesV2.arrayEnd = refsEnd; + this->_referencesV2.elementSize = sizeof(NativeReferenceV2); + this->_referencesV2.elementCount = chunk->elementCount; + DEBUG_WITH_TYPE("ReaderNative", { + llvm::dbgs() << " chunk ReferencesV2: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize << "\n"; + }); + return make_error_code(NativeReaderError::success); + } + + // set up pointers to target table + std::error_code processTargetsTable(const uint8_t *base, + const NativeChunk *chunk) { + const uint32_t* targetIndexes = reinterpret_cast<const uint32_t*> + (base + chunk->fileOffset); + this->_targetsTableCount = chunk->elementCount; + this->_targetsTable = new const Atom*[chunk->elementCount]; + for (uint32_t i=0; i < chunk->elementCount; ++i) { + const uint32_t index = targetIndexes[i]; + if ( index < _definedAtoms._elementCount ) { + const uint8_t* p = _definedAtoms._arrayStart + + index * _definedAtoms._elementSize; + this->_targetsTable[i] = reinterpret_cast<const DefinedAtom*>(p); + continue; + } + const uint32_t undefIndex = index - _definedAtoms._elementCount; + if ( undefIndex < _undefinedAtoms._elementCount ) { + const uint8_t* p = _undefinedAtoms._arrayStart + + undefIndex * _undefinedAtoms._elementSize; + this->_targetsTable[i] = reinterpret_cast<const UndefinedAtom*>(p); + continue; + } + const uint32_t slIndex = index - _definedAtoms._elementCount + - _undefinedAtoms._elementCount; + if ( slIndex < _sharedLibraryAtoms._elementCount ) { + const uint8_t* p = _sharedLibraryAtoms._arrayStart + + slIndex * _sharedLibraryAtoms._elementSize; + this->_targetsTable[i] = reinterpret_cast<const SharedLibraryAtom*>(p); + continue; + } + const uint32_t abIndex = index - _definedAtoms._elementCount + - _undefinedAtoms._elementCount + - _sharedLibraryAtoms._elementCount; + if ( abIndex < _absoluteAtoms._elementCount ) { + const uint8_t* p = _absoluteAtoms._arrayStart + + abIndex * _absoluteAtoms._elementSize; + this->_targetsTable[i] = reinterpret_cast<const AbsoluteAtom*>(p); + continue; + } + return make_error_code(NativeReaderError::file_malformed); + } + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk Targets Table: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + + // set up pointers to addend pool in file + std::error_code processAddendsTable(const uint8_t *base, + const NativeChunk *chunk) { + this->_addends = reinterpret_cast<const Reference::Addend*> + (base + chunk->fileOffset); + this->_addendsMaxIndex = chunk->elementCount; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk Addends: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + // set up pointers to string pool in file + std::error_code processStrings(const uint8_t *base, + const NativeChunk *chunk) { + this->_strings = reinterpret_cast<const char*>(base + chunk->fileOffset); + this->_stringsMaxOffset = chunk->fileSize; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk Strings: " + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + // set up pointers to content area in file + std::error_code processContent(const uint8_t *base, + const NativeChunk *chunk) { + this->_contentStart = base + chunk->fileOffset; + this->_contentEnd = base + chunk->fileOffset + chunk->fileSize; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk content: " + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + StringRef string(uint32_t offset) const { + assert(offset < _stringsMaxOffset); + return StringRef(&_strings[offset]); + } + + Reference::Addend addend(uint32_t index) const { + if ( index == 0 ) + return 0; // addend index zero is used to mean "no addend" + assert(index <= _addendsMaxIndex); + return _addends[index-1]; // one-based indexing + } + + const NativeAtomAttributesV1& attribute(uint32_t off) const { + assert(off < _attributesMaxOffset); + return *reinterpret_cast<const NativeAtomAttributesV1*>(_attributes + off); + } + + const NativeAtomAttributesV1& absAttribute(uint32_t off) const { + assert(off < _absAbsoluteMaxOffset); + return *reinterpret_cast<const NativeAtomAttributesV1*>(_absAttributes + off); + } + + const uint8_t* content(uint32_t offset, uint32_t size) const { + const uint8_t* result = _contentStart + offset; + assert((result+size) <= _contentEnd); + return result; + } + + const Reference* referenceByIndex(uintptr_t index) const { + if (index < _referencesV1.elementCount) { + return reinterpret_cast<const NativeReferenceV1*>( + _referencesV1.arrayStart + index * _referencesV1.elementSize); + } + assert(index < _referencesV2.elementCount); + return reinterpret_cast<const NativeReferenceV2*>( + _referencesV2.arrayStart + index * _referencesV2.elementSize); + } + + const Atom* targetV1(uint16_t index) const { + if ( index == NativeReferenceIvarsV1::noTarget ) + return nullptr; + assert(index < _targetsTableCount); + return _targetsTable[index]; + } + + void setTargetV1(uint16_t index, const Atom* newAtom) const { + assert(index != NativeReferenceIvarsV1::noTarget); + assert(index > _targetsTableCount); + _targetsTable[index] = newAtom; + } + + const Atom* targetV2(uint32_t index) const { + if (index == NativeReferenceIvarsV2::noTarget) + return nullptr; + assert(index < _targetsTableCount); + return _targetsTable[index]; + } + + void setTargetV2(uint32_t index, const Atom* newAtom) const { + assert(index != NativeReferenceIvarsV2::noTarget); + assert(index > _targetsTableCount); + _targetsTable[index] = newAtom; + } + + template <typename T> + class AtomArray : public File::atom_collection<T> { + public: + AtomArray() : _arrayStart(nullptr), _arrayEnd(nullptr), + _elementSize(0), _elementCount(0) { } + + virtual atom_iterator<T> begin() const { + return atom_iterator<T>(*this, reinterpret_cast<const void*>(_arrayStart)); + } + virtual atom_iterator<T> end() const{ + return atom_iterator<T>(*this, reinterpret_cast<const void*>(_arrayEnd)); + } + virtual const T* deref(const void* it) const { + return reinterpret_cast<const T*>(it); + } + virtual void next(const void*& it) const { + const uint8_t* p = reinterpret_cast<const uint8_t*>(it); + p += _elementSize; + it = reinterpret_cast<const void*>(p); + } + virtual uint64_t size() const { return _elementCount; } + const uint8_t *_arrayStart; + const uint8_t *_arrayEnd; + uint32_t _elementSize; + uint32_t _elementCount; + }; + + struct IvarArray { + IvarArray() : + arrayStart(nullptr), + arrayEnd(nullptr), + elementSize(0), + elementCount(0) { } + + const uint8_t* arrayStart; + const uint8_t* arrayEnd; + uint32_t elementSize; + uint32_t elementCount; + }; + + std::unique_ptr<MemoryBuffer> _mb; + const NativeFileHeader* _header; + AtomArray<DefinedAtom> _definedAtoms; + AtomArray<UndefinedAtom> _undefinedAtoms; + AtomArray<SharedLibraryAtom> _sharedLibraryAtoms; + AtomArray<AbsoluteAtom> _absoluteAtoms; + const uint8_t* _absAttributes; + uint32_t _absAbsoluteMaxOffset; + const uint8_t* _attributes; + uint32_t _attributesMaxOffset; + IvarArray _referencesV1; + IvarArray _referencesV2; + const Atom** _targetsTable; + uint32_t _targetsTableCount; + const char* _strings; + uint32_t _stringsMaxOffset; + const Reference::Addend* _addends; + uint32_t _addendsMaxIndex; + const uint8_t *_contentStart; + const uint8_t *_contentEnd; +}; + +inline const lld::File &NativeDefinedAtomV1::file() const { + return *_file; +} + +inline uint64_t NativeDefinedAtomV1:: ordinal() const { + const uint8_t* p = reinterpret_cast<const uint8_t*>(_ivarData); + return p - _file->_definedAtoms._arrayStart; +} + +inline StringRef NativeDefinedAtomV1::name() const { + return _file->string(_ivarData->nameOffset); +} + +inline const NativeAtomAttributesV1& NativeDefinedAtomV1::attributes() const { + return _file->attribute(_ivarData->attributesOffset); +} + +inline ArrayRef<uint8_t> NativeDefinedAtomV1::rawContent() const { + if (!occupiesDiskSpace()) + return ArrayRef<uint8_t>(); + const uint8_t* p = _file->content(_ivarData->contentOffset, + _ivarData->contentSize); + return ArrayRef<uint8_t>(p, _ivarData->contentSize); +} + +inline StringRef NativeDefinedAtomV1::customSectionName() const { + uint32_t offset = attributes().sectionNameOffset; + return _file->string(offset); +} + +DefinedAtom::reference_iterator NativeDefinedAtomV1::begin() const { + uintptr_t index = _ivarData->referencesStartIndex; + const void* it = reinterpret_cast<const void*>(index); + return reference_iterator(*this, it); +} + +DefinedAtom::reference_iterator NativeDefinedAtomV1::end() const { + uintptr_t index = _ivarData->referencesStartIndex+_ivarData->referencesCount; + const void* it = reinterpret_cast<const void*>(index); + return reference_iterator(*this, it); +} + +const Reference* NativeDefinedAtomV1::derefIterator(const void* it) const { + uintptr_t index = reinterpret_cast<uintptr_t>(it); + return _file->referenceByIndex(index); +} + +void NativeDefinedAtomV1::incrementIterator(const void*& it) const { + uintptr_t index = reinterpret_cast<uintptr_t>(it); + ++index; + it = reinterpret_cast<const void*>(index); +} + +inline const lld::File& NativeUndefinedAtomV1::file() const { + return *_file; +} + +inline StringRef NativeUndefinedAtomV1::name() const { + return _file->string(_ivarData->nameOffset); +} + +inline const UndefinedAtom *NativeUndefinedAtomV1::fallback() const { + if (!_ivarData->fallbackNameOffset) + return nullptr; + if (!_fallback) + _fallback.reset(new SimpleUndefinedAtom( + *_file, _file->string(_ivarData->fallbackNameOffset))); + return _fallback.get(); +} + +inline const lld::File& NativeSharedLibraryAtomV1::file() const { + return *_file; +} + +inline StringRef NativeSharedLibraryAtomV1::name() const { + return _file->string(_ivarData->nameOffset); +} + +inline StringRef NativeSharedLibraryAtomV1::loadName() const { + return _file->string(_ivarData->loadNameOffset); +} + + + +inline const lld::File& NativeAbsoluteAtomV1::file() const { + return *_file; +} + +inline StringRef NativeAbsoluteAtomV1::name() const { + return _file->string(_ivarData->nameOffset); +} + +inline const NativeAtomAttributesV1& NativeAbsoluteAtomV1::absAttributes() const { + return _file->absAttribute(_ivarData->attributesOffset); +} + +inline const Atom* NativeReferenceV1::target() const { + return _file->targetV1(_ivarData->targetIndex); +} + +inline Reference::Addend NativeReferenceV1::addend() const { + return _file->addend(_ivarData->addendIndex); +} + +inline void NativeReferenceV1::setTarget(const Atom* newAtom) { + return _file->setTargetV1(_ivarData->targetIndex, newAtom); +} + +inline void NativeReferenceV1::setAddend(Addend a) { + // Do nothing if addend value is not being changed. + if (addend() == a) + return; + llvm_unreachable("setAddend() not supported"); +} + +inline const Atom* NativeReferenceV2::target() const { + return _file->targetV2(_ivarData->targetIndex); +} + +inline Reference::Addend NativeReferenceV2::addend() const { + return _ivarData->addend; +} + +inline void NativeReferenceV2::setTarget(const Atom* newAtom) { + return _file->setTargetV2(_ivarData->targetIndex, newAtom); +} + +inline void NativeReferenceV2::setAddend(Addend a) { + // Do nothing if addend value is not being changed. + if (addend() == a) + return; + llvm_unreachable("setAddend() not supported"); +} + +uint32_t NativeReferenceV2::tag() const { return _ivarData->tag; } + +} // end namespace native + +namespace { + +class NativeReader : public Reader { +public: + virtual bool canParse(file_magic magic, StringRef, + const MemoryBuffer &mb) const override { + const NativeFileHeader *const header = + reinterpret_cast<const NativeFileHeader *>(mb.getBufferStart()); + return (memcmp(header->magic, NATIVE_FILE_HEADER_MAGIC, + sizeof(header->magic)) == 0); + } + + virtual std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &, + std::vector<std::unique_ptr<File>> &result) const override { + auto *file = new lld::native::File(std::move(mb)); + result.push_back(std::unique_ptr<File>(file)); + return std::error_code(); + } +}; + +} + +void Registry::addSupportNativeObjects() { + add(std::unique_ptr<Reader>(new NativeReader())); +} + +} // end namespace lld diff --git a/lib/ReaderWriter/Native/WriterNative.cpp b/lib/ReaderWriter/Native/WriterNative.cpp new file mode 100644 index 000000000000..5e01a6ce1c7c --- /dev/null +++ b/lib/ReaderWriter/Native/WriterNative.cpp @@ -0,0 +1,566 @@ +//===- lib/ReaderWriter/Native/WriterNative.cpp ---------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NativeFileFormat.h" +#include "lld/Core/File.h" +#include "lld/Core/LinkingContext.h" +#include "lld/Core/Writer.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdint> +#include <set> +#include <system_error> +#include <vector> + +namespace lld { +namespace native { + +/// +/// Class for writing native object files. +/// +class Writer : public lld::Writer { +public: + std::error_code writeFile(const lld::File &file, StringRef outPath) override { + // reserve first byte for unnamed atoms + _stringPool.push_back('\0'); + // visit all atoms + for ( const DefinedAtom *defAtom : file.defined() ) { + this->addIVarsForDefinedAtom(*defAtom); + // We are trying to process all atoms, but the defined() iterator does not + // return group children. So, when a group parent is found, we need to + // handle each child atom. + if (defAtom->isGroupParent()) { + for (const Reference *r : *defAtom) { + if (r->kindNamespace() != lld::Reference::KindNamespace::all) + continue; + if (r->kindValue() == lld::Reference::kindGroupChild) { + const DefinedAtom *target = dyn_cast<DefinedAtom>(r->target()); + assert(target && "Internal Error: kindGroupChild references need " + "to be associated with Defined Atoms only"); + this->addIVarsForDefinedAtom(*target); + } + } + } + } + for ( const UndefinedAtom *undefAtom : file.undefined() ) { + this->addIVarsForUndefinedAtom(*undefAtom); + } + for ( const SharedLibraryAtom *shlibAtom : file.sharedLibrary() ) { + this->addIVarsForSharedLibraryAtom(*shlibAtom); + } + for ( const AbsoluteAtom *absAtom : file.absolute() ) { + this->addIVarsForAbsoluteAtom(*absAtom); + } + + maybeConvertReferencesToV1(); + + // construct file header based on atom information accumulated + this->makeHeader(); + + std::error_code ec; + llvm::raw_fd_ostream out(outPath, ec, llvm::sys::fs::F_None); + if (ec) + return ec; + + this->write(out); + + return std::error_code(); + } + + virtual ~Writer() { + } + +private: + + // write the lld::File in native format to the specified stream + void write(raw_ostream &out) { + assert(out.tell() == 0); + out.write((char*)_headerBuffer, _headerBufferSize); + + writeChunk(out, _definedAtomIvars, NCS_DefinedAtomsV1); + writeChunk(out, _attributes, NCS_AttributesArrayV1); + writeChunk(out, _undefinedAtomIvars, NCS_UndefinedAtomsV1); + writeChunk(out, _sharedLibraryAtomIvars, NCS_SharedLibraryAtomsV1); + writeChunk(out, _absoluteAtomIvars, NCS_AbsoluteAtomsV1); + writeChunk(out, _absAttributes, NCS_AbsoluteAttributesV1); + writeChunk(out, _stringPool, NCS_Strings); + writeChunk(out, _referencesV1, NCS_ReferencesArrayV1); + writeChunk(out, _referencesV2, NCS_ReferencesArrayV2); + + if (!_targetsTableIndex.empty()) { + assert(out.tell() == findChunk(NCS_TargetsTable).fileOffset); + writeTargetTable(out); + } + + if (!_addendsTableIndex.empty()) { + assert(out.tell() == findChunk(NCS_AddendsTable).fileOffset); + writeAddendTable(out); + } + + writeChunk(out, _contentPool, NCS_Content); + } + + template<class T> + void writeChunk(raw_ostream &out, std::vector<T> &vector, uint32_t signature) { + if (vector.empty()) + return; + assert(out.tell() == findChunk(signature).fileOffset); + out.write((char*)&vector[0], vector.size() * sizeof(T)); + } + + void addIVarsForDefinedAtom(const DefinedAtom& atom) { + _definedAtomIndex[&atom] = _definedAtomIvars.size(); + NativeDefinedAtomIvarsV1 ivar; + unsigned refsCount; + ivar.nameOffset = getNameOffset(atom); + ivar.attributesOffset = getAttributeOffset(atom); + ivar.referencesStartIndex = getReferencesIndex(atom, refsCount); + ivar.referencesCount = refsCount; + ivar.contentOffset = getContentOffset(atom); + ivar.contentSize = atom.size(); + ivar.sectionSize = atom.sectionSize(); + _definedAtomIvars.push_back(ivar); + } + + void addIVarsForUndefinedAtom(const UndefinedAtom& atom) { + _undefinedAtomIndex[&atom] = _undefinedAtomIvars.size(); + NativeUndefinedAtomIvarsV1 ivar; + ivar.nameOffset = getNameOffset(atom); + ivar.flags = (atom.canBeNull() & 0x03); + ivar.fallbackNameOffset = 0; + if (atom.fallback()) + ivar.fallbackNameOffset = getNameOffset(*atom.fallback()); + _undefinedAtomIvars.push_back(ivar); + } + + void addIVarsForSharedLibraryAtom(const SharedLibraryAtom& atom) { + _sharedLibraryAtomIndex[&atom] = _sharedLibraryAtomIvars.size(); + NativeSharedLibraryAtomIvarsV1 ivar; + ivar.size = atom.size(); + ivar.nameOffset = getNameOffset(atom); + ivar.loadNameOffset = getSharedLibraryNameOffset(atom.loadName()); + ivar.type = (uint32_t)atom.type(); + ivar.flags = atom.canBeNullAtRuntime(); + _sharedLibraryAtomIvars.push_back(ivar); + } + + void addIVarsForAbsoluteAtom(const AbsoluteAtom& atom) { + _absoluteAtomIndex[&atom] = _absoluteAtomIvars.size(); + NativeAbsoluteAtomIvarsV1 ivar; + ivar.nameOffset = getNameOffset(atom); + ivar.attributesOffset = getAttributeOffset(atom); + ivar.reserved = 0; + ivar.value = atom.value(); + _absoluteAtomIvars.push_back(ivar); + } + + void convertReferencesToV1() { + for (const NativeReferenceIvarsV2 &v2 : _referencesV2) { + NativeReferenceIvarsV1 v1; + v1.offsetInAtom = v2.offsetInAtom; + v1.kindNamespace = v2.kindNamespace; + v1.kindArch = v2.kindArch; + v1.kindValue = v2.kindValue; + v1.targetIndex = (v2.targetIndex == NativeReferenceIvarsV2::noTarget) ? + (uint16_t)NativeReferenceIvarsV1::noTarget : v2.targetIndex; + v1.addendIndex = this->getAddendIndex(v2.addend); + _referencesV1.push_back(v1); + } + _referencesV2.clear(); + } + + bool canConvertReferenceToV1(const NativeReferenceIvarsV2 &ref) { + bool validOffset = (ref.offsetInAtom == NativeReferenceIvarsV2::noTarget) || + ref.offsetInAtom < NativeReferenceIvarsV1::noTarget; + return validOffset && ref.targetIndex < UINT16_MAX; + } + + // Convert vector of NativeReferenceIvarsV2 to NativeReferenceIvarsV1 if + // possible. + void maybeConvertReferencesToV1() { + std::set<int64_t> addends; + for (const NativeReferenceIvarsV2 &ref : _referencesV2) { + if (!canConvertReferenceToV1(ref)) + return; + addends.insert(ref.addend); + if (addends.size() >= UINT16_MAX) + return; + } + convertReferencesToV1(); + } + + // fill out native file header and chunk directory + void makeHeader() { + const bool hasDefines = !_definedAtomIvars.empty(); + const bool hasUndefines = !_undefinedAtomIvars.empty(); + const bool hasSharedLibraries = !_sharedLibraryAtomIvars.empty(); + const bool hasAbsolutes = !_absoluteAtomIvars.empty(); + const bool hasReferencesV1 = !_referencesV1.empty(); + const bool hasReferencesV2 = !_referencesV2.empty(); + const bool hasTargetsTable = !_targetsTableIndex.empty(); + const bool hasAddendTable = !_addendsTableIndex.empty(); + const bool hasContent = !_contentPool.empty(); + + int chunkCount = 1; // always have string pool chunk + if ( hasDefines ) chunkCount += 2; + if ( hasUndefines ) ++chunkCount; + if ( hasSharedLibraries ) ++chunkCount; + if ( hasAbsolutes ) chunkCount += 2; + if ( hasReferencesV1 ) ++chunkCount; + if ( hasReferencesV2 ) ++chunkCount; + if ( hasTargetsTable ) ++chunkCount; + if ( hasAddendTable ) ++chunkCount; + if ( hasContent ) ++chunkCount; + + _headerBufferSize = sizeof(NativeFileHeader) + + chunkCount*sizeof(NativeChunk); + _headerBuffer = reinterpret_cast<NativeFileHeader*> + (operator new(_headerBufferSize, std::nothrow)); + NativeChunk *chunks = + reinterpret_cast<NativeChunk*>(reinterpret_cast<char*>(_headerBuffer) + + sizeof(NativeFileHeader)); + memcpy(_headerBuffer->magic, NATIVE_FILE_HEADER_MAGIC, + sizeof(_headerBuffer->magic)); + _headerBuffer->endian = NFH_LittleEndian; + _headerBuffer->architecture = 0; + _headerBuffer->fileSize = 0; + _headerBuffer->chunkCount = chunkCount; + + // create chunk for defined atom ivar array + int nextIndex = 0; + uint32_t nextFileOffset = _headerBufferSize; + if (hasDefines) { + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _definedAtomIvars, + NCS_DefinedAtomsV1); + + // create chunk for attributes + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _attributes, + NCS_AttributesArrayV1); + } + + // create chunk for undefined atom array + if (hasUndefines) + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _undefinedAtomIvars, + NCS_UndefinedAtomsV1); + + // create chunk for shared library atom array + if (hasSharedLibraries) + fillChunkHeader(chunks[nextIndex++], nextFileOffset, + _sharedLibraryAtomIvars, NCS_SharedLibraryAtomsV1); + + // create chunk for shared library atom array + if (hasAbsolutes) { + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _absoluteAtomIvars, + NCS_AbsoluteAtomsV1); + + // create chunk for attributes + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _absAttributes, + NCS_AbsoluteAttributesV1); + } + + // create chunk for symbol strings + // pad end of string pool to 4-bytes + while ((_stringPool.size() % 4) != 0) + _stringPool.push_back('\0'); + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _stringPool, + NCS_Strings); + + // create chunk for referencesV2 + if (hasReferencesV1) + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _referencesV1, + NCS_ReferencesArrayV1); + + // create chunk for referencesV2 + if (hasReferencesV2) + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _referencesV2, + NCS_ReferencesArrayV2); + + // create chunk for target table + if (hasTargetsTable) { + NativeChunk& cht = chunks[nextIndex++]; + cht.signature = NCS_TargetsTable; + cht.fileOffset = nextFileOffset; + cht.fileSize = _targetsTableIndex.size() * sizeof(uint32_t); + cht.elementCount = _targetsTableIndex.size(); + nextFileOffset = cht.fileOffset + cht.fileSize; + } + + // create chunk for addend table + if (hasAddendTable) { + NativeChunk& chad = chunks[nextIndex++]; + chad.signature = NCS_AddendsTable; + chad.fileOffset = nextFileOffset; + chad.fileSize = _addendsTableIndex.size() * sizeof(Reference::Addend); + chad.elementCount = _addendsTableIndex.size(); + nextFileOffset = chad.fileOffset + chad.fileSize; + } + + // create chunk for content + if (hasContent) + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _contentPool, + NCS_Content); + + _headerBuffer->fileSize = nextFileOffset; + } + + template<class T> + void fillChunkHeader(NativeChunk &chunk, uint32_t &nextFileOffset, + const std::vector<T> &data, uint32_t signature) { + chunk.signature = signature; + chunk.fileOffset = nextFileOffset; + chunk.fileSize = data.size() * sizeof(T); + chunk.elementCount = data.size(); + nextFileOffset = chunk.fileOffset + chunk.fileSize; + } + + // scan header to find particular chunk + NativeChunk& findChunk(uint32_t signature) { + const uint32_t chunkCount = _headerBuffer->chunkCount; + NativeChunk* chunks = + reinterpret_cast<NativeChunk*>(reinterpret_cast<char*>(_headerBuffer) + + sizeof(NativeFileHeader)); + for (uint32_t i=0; i < chunkCount; ++i) { + if ( chunks[i].signature == signature ) + return chunks[i]; + } + llvm_unreachable("findChunk() signature not found"); + } + + // append atom name to string pool and return offset + uint32_t getNameOffset(const Atom& atom) { + return this->getNameOffset(atom.name()); + } + + // check if name is already in pool or append and return offset + uint32_t getSharedLibraryNameOffset(StringRef name) { + assert(!name.empty()); + // look to see if this library name was used by another atom + for (auto &it : _sharedLibraryNames) + if (name.equals(it.first)) + return it.second; + // first use of this library name + uint32_t result = this->getNameOffset(name); + _sharedLibraryNames.push_back(std::make_pair(name, result)); + return result; + } + + // append atom name to string pool and return offset + uint32_t getNameOffset(StringRef name) { + if ( name.empty() ) + return 0; + uint32_t result = _stringPool.size(); + _stringPool.insert(_stringPool.end(), name.begin(), name.end()); + _stringPool.push_back(0); + return result; + } + + // append atom cotent to content pool and return offset + uint32_t getContentOffset(const DefinedAtom& atom) { + if (!atom.occupiesDiskSpace()) + return 0; + uint32_t result = _contentPool.size(); + ArrayRef<uint8_t> cont = atom.rawContent(); + _contentPool.insert(_contentPool.end(), cont.begin(), cont.end()); + return result; + } + + // reuse existing attributes entry or create a new one and return offet + uint32_t getAttributeOffset(const DefinedAtom& atom) { + NativeAtomAttributesV1 attrs = computeAttributesV1(atom); + return getOrPushAttribute(_attributes, attrs); + } + + uint32_t getAttributeOffset(const AbsoluteAtom& atom) { + NativeAtomAttributesV1 attrs = computeAbsoluteAttributes(atom); + return getOrPushAttribute(_absAttributes, attrs); + } + + uint32_t getOrPushAttribute(std::vector<NativeAtomAttributesV1> &dest, + const NativeAtomAttributesV1 &attrs) { + for (size_t i = 0, e = dest.size(); i < e; ++i) { + if (!memcmp(&dest[i], &attrs, sizeof(attrs))) { + // found that this set of attributes already used, so re-use + return i * sizeof(attrs); + } + } + // append new attribute set to end + uint32_t result = dest.size() * sizeof(attrs); + dest.push_back(attrs); + return result; + } + + uint32_t sectionNameOffset(const DefinedAtom& atom) { + // if section based on content, then no custom section name available + if (atom.sectionChoice() == DefinedAtom::sectionBasedOnContent) + return 0; + StringRef name = atom.customSectionName(); + assert(!name.empty()); + // look to see if this section name was used by another atom + for (auto &it : _sectionNames) + if (name.equals(it.first)) + return it.second; + // first use of this section name + uint32_t result = this->getNameOffset(name); + _sectionNames.push_back(std::make_pair(name, result)); + return result; + } + + NativeAtomAttributesV1 computeAttributesV1(const DefinedAtom& atom) { + NativeAtomAttributesV1 attrs; + attrs.sectionNameOffset = sectionNameOffset(atom); + attrs.align2 = atom.alignment().powerOf2; + attrs.alignModulus = atom.alignment().modulus; + attrs.scope = atom.scope(); + attrs.interposable = atom.interposable(); + attrs.merge = atom.merge(); + attrs.contentType = atom.contentType(); + attrs.sectionChoice = atom.sectionChoice(); + attrs.deadStrip = atom.deadStrip(); + attrs.dynamicExport = atom.dynamicExport(); + attrs.codeModel = atom.codeModel(); + attrs.permissions = atom.permissions(); + return attrs; + } + + NativeAtomAttributesV1 computeAbsoluteAttributes(const AbsoluteAtom& atom) { + NativeAtomAttributesV1 attrs; + attrs.scope = atom.scope(); + return attrs; + } + + // add references for this atom in a contiguous block in NCS_ReferencesArrayV2 + uint32_t getReferencesIndex(const DefinedAtom& atom, unsigned& refsCount) { + size_t startRefSize = _referencesV2.size(); + uint32_t result = startRefSize; + for (const Reference *ref : atom) { + NativeReferenceIvarsV2 nref; + nref.offsetInAtom = ref->offsetInAtom(); + nref.kindNamespace = (uint8_t)ref->kindNamespace(); + nref.kindArch = (uint8_t)ref->kindArch(); + nref.kindValue = ref->kindValue(); + nref.targetIndex = this->getTargetIndex(ref->target()); + nref.addend = ref->addend(); + nref.tag = ref->tag(); + _referencesV2.push_back(nref); + } + refsCount = _referencesV2.size() - startRefSize; + return (refsCount == 0) ? 0 : result; + } + + uint32_t getTargetIndex(const Atom* target) { + if ( target == nullptr ) + return NativeReferenceIvarsV2::noTarget; + TargetToIndex::const_iterator pos = _targetsTableIndex.find(target); + if ( pos != _targetsTableIndex.end() ) { + return pos->second; + } + uint32_t result = _targetsTableIndex.size(); + _targetsTableIndex[target] = result; + return result; + } + + void writeTargetTable(raw_ostream &out) { + // Build table of target indexes + uint32_t maxTargetIndex = _targetsTableIndex.size(); + assert(maxTargetIndex > 0); + std::vector<uint32_t> targetIndexes(maxTargetIndex); + for (auto &it : _targetsTableIndex) { + const Atom* atom = it.first; + uint32_t targetIndex = it.second; + assert(targetIndex < maxTargetIndex); + + TargetToIndex::iterator pos = _definedAtomIndex.find(atom); + if (pos != _definedAtomIndex.end()) { + targetIndexes[targetIndex] = pos->second; + continue; + } + uint32_t base = _definedAtomIvars.size(); + + pos = _undefinedAtomIndex.find(atom); + if (pos != _undefinedAtomIndex.end()) { + targetIndexes[targetIndex] = pos->second + base; + continue; + } + base += _undefinedAtomIndex.size(); + + pos = _sharedLibraryAtomIndex.find(atom); + if (pos != _sharedLibraryAtomIndex.end()) { + targetIndexes[targetIndex] = pos->second + base; + continue; + } + base += _sharedLibraryAtomIndex.size(); + + pos = _absoluteAtomIndex.find(atom); + assert(pos != _absoluteAtomIndex.end()); + targetIndexes[targetIndex] = pos->second + base; + } + // write table + out.write((char*)&targetIndexes[0], maxTargetIndex * sizeof(uint32_t)); + } + + uint32_t getAddendIndex(Reference::Addend addend) { + if ( addend == 0 ) + return 0; // addend index zero is used to mean "no addend" + AddendToIndex::const_iterator pos = _addendsTableIndex.find(addend); + if ( pos != _addendsTableIndex.end() ) { + return pos->second; + } + uint32_t result = _addendsTableIndex.size() + 1; // one-based index + _addendsTableIndex[addend] = result; + return result; + } + + void writeAddendTable(raw_ostream &out) { + // Build table of addends + uint32_t maxAddendIndex = _addendsTableIndex.size(); + std::vector<Reference::Addend> addends(maxAddendIndex); + for (auto &it : _addendsTableIndex) { + Reference::Addend addend = it.first; + uint32_t index = it.second; + assert(index <= maxAddendIndex); + addends[index-1] = addend; + } + // write table + out.write((char*)&addends[0], maxAddendIndex*sizeof(Reference::Addend)); + } + + typedef std::vector<std::pair<StringRef, uint32_t>> NameToOffsetVector; + + typedef llvm::DenseMap<const Atom*, uint32_t> TargetToIndex; + typedef llvm::DenseMap<Reference::Addend, uint32_t> AddendToIndex; + + NativeFileHeader* _headerBuffer; + size_t _headerBufferSize; + std::vector<char> _stringPool; + std::vector<uint8_t> _contentPool; + std::vector<NativeDefinedAtomIvarsV1> _definedAtomIvars; + std::vector<NativeAtomAttributesV1> _attributes; + std::vector<NativeAtomAttributesV1> _absAttributes; + std::vector<NativeUndefinedAtomIvarsV1> _undefinedAtomIvars; + std::vector<NativeSharedLibraryAtomIvarsV1> _sharedLibraryAtomIvars; + std::vector<NativeAbsoluteAtomIvarsV1> _absoluteAtomIvars; + std::vector<NativeReferenceIvarsV1> _referencesV1; + std::vector<NativeReferenceIvarsV2> _referencesV2; + TargetToIndex _targetsTableIndex; + TargetToIndex _definedAtomIndex; + TargetToIndex _undefinedAtomIndex; + TargetToIndex _sharedLibraryAtomIndex; + TargetToIndex _absoluteAtomIndex; + AddendToIndex _addendsTableIndex; + NameToOffsetVector _sectionNames; + NameToOffsetVector _sharedLibraryNames; +}; +} // end namespace native + +std::unique_ptr<Writer> createWriterNative() { + return std::unique_ptr<Writer>(new native::Writer()); +} +} // end namespace lld |