diff options
Diffstat (limited to 'lib/ReaderWriter/Native/WriterNative.cpp')
-rw-r--r-- | lib/ReaderWriter/Native/WriterNative.cpp | 566 |
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 |