forked from OSchip/llvm-project
1017 lines
37 KiB
C++
1017 lines
37 KiB
C++
//===- 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;
|
|
}
|
|
|
|
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<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;
|
|
|
|
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");
|
|
}
|
|
|
|
} // 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
|