summaryrefslogtreecommitdiff
path: root/lib/ReaderWriter/Native/WriterNative.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ReaderWriter/Native/WriterNative.cpp')
-rw-r--r--lib/ReaderWriter/Native/WriterNative.cpp566
1 files changed, 566 insertions, 0 deletions
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