//===- 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/Simple.h" #include "lld/ReaderWriter/Reader.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 #include 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; } 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().sectionChoiceAndPosition >> 4); } StringRef customSectionName() const override; SectionPosition sectionPosition() const override { return (DefinedAtom::SectionPosition)( attributes().sectionChoiceAndPosition & 0xF); } 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 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 _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; 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 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(_mb->getBufferStart()); } /// Parses a File object from a native object file. std::error_code doParse() override { const uint8_t *const base = reinterpret_cast(_mb->getBufferStart()); StringRef path(_mb->getBufferIdentifier()); const NativeFileHeader *const header = reinterpret_cast(base); const NativeChunk *const chunks = reinterpret_cast(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& defined() const override { return _definedAtoms; } const atom_collection& undefined() const override { return _undefinedAtoms; } const atom_collection& sharedLibrary() const override { return _sharedLibraryAtoms; } const atom_collection &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 (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 (base + chunk->fileOffset); for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { NativeDefinedAtomV1* atomAllocSpace = reinterpret_cast(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 (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 (base + chunk->fileOffset); for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { NativeUndefinedAtomV1* atomAllocSpace = reinterpret_cast(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 (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 (base + chunk->fileOffset); for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { NativeSharedLibraryAtomV1* atomAllocSpace = reinterpret_cast(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 (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 (base + chunk->fileOffset); for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { NativeAbsoluteAtomV1* atomAllocSpace = reinterpret_cast(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 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( 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(base + chunk->fileOffset); for (uint8_t *s = refsStart; s != refsEnd; s += sizeof(T), ++ivarData) { T *atomAllocSpace = reinterpret_cast(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( 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( 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 (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(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(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(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(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 (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(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(_attributes + off); } const NativeAtomAttributesV1& absAttribute(uint32_t off) const { assert(off < _absAbsoluteMaxOffset); return *reinterpret_cast(_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( _referencesV1.arrayStart + index * _referencesV1.elementSize); } assert(index < _referencesV2.elementCount); return reinterpret_cast( _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 class AtomArray : public File::atom_collection { public: AtomArray() : _arrayStart(nullptr), _arrayEnd(nullptr), _elementSize(0), _elementCount(0) { } virtual atom_iterator begin() const { return atom_iterator(*this, reinterpret_cast(_arrayStart)); } virtual atom_iterator end() const{ return atom_iterator(*this, reinterpret_cast(_arrayEnd)); } virtual const T* deref(const void* it) const { return reinterpret_cast(it); } virtual void next(const void*& it) const { const uint8_t* p = reinterpret_cast(it); p += _elementSize; it = reinterpret_cast(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 _mb; const NativeFileHeader* _header; AtomArray _definedAtoms; AtomArray _undefinedAtoms; AtomArray _sharedLibraryAtoms; AtomArray _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(_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 NativeDefinedAtomV1::rawContent() const { if (!occupiesDiskSpace()) return ArrayRef(); const uint8_t* p = _file->content(_ivarData->contentOffset, _ivarData->contentSize); return ArrayRef(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(index); return reference_iterator(*this, it); } DefinedAtom::reference_iterator NativeDefinedAtomV1::end() const { uintptr_t index = _ivarData->referencesStartIndex+_ivarData->referencesCount; const void* it = reinterpret_cast(index); return reference_iterator(*this, it); } const Reference* NativeDefinedAtomV1::derefIterator(const void* it) const { uintptr_t index = reinterpret_cast(it); return _file->referenceByIndex(index); } void NativeDefinedAtomV1::incrementIterator(const void*& it) const { uintptr_t index = reinterpret_cast(it); ++index; it = reinterpret_cast(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"); } } // 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(mb.getBufferStart()); return (memcmp(header->magic, NATIVE_FILE_HEADER_MAGIC, sizeof(header->magic)) == 0); } virtual std::error_code loadFile(std::unique_ptr mb, const class Registry &, std::vector> &result) const override { auto *file = new lld::native::File(std::move(mb)); result.push_back(std::unique_ptr(file)); return std::error_code(); } }; } void Registry::addSupportNativeObjects() { add(std::unique_ptr(new NativeReader())); } } // end namespace lld