diff options
Diffstat (limited to 'lld/lib/ReaderWriter/YAML/ReaderYAML.cpp')
| -rw-r--r-- | lld/lib/ReaderWriter/YAML/ReaderYAML.cpp | 1264 |
1 files changed, 1264 insertions, 0 deletions
diff --git a/lld/lib/ReaderWriter/YAML/ReaderYAML.cpp b/lld/lib/ReaderWriter/YAML/ReaderYAML.cpp new file mode 100644 index 00000000000..8da77713698 --- /dev/null +++ b/lld/lib/ReaderWriter/YAML/ReaderYAML.cpp @@ -0,0 +1,1264 @@ +//===- lib/ReaderWriter/YAML/ReaderYAML.cpp - Reads YAML object files -----===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/ReaderWriter/ReaderYAML.h" + +#include "lld/Core/AbsoluteAtom.h" +#include "lld/Core/ArchiveLibraryFile.h" +#include "lld/Core/Atom.h" +#include "lld/Core/Error.h" +#include "lld/Core/File.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/Reference.h" +#include "lld/Core/SharedLibraryAtom.h" +#include "lld/Core/UndefinedAtom.h" + +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/system_error.h" +#include "llvm/Support/YAMLParser.h" + +#include <cstring> +#include <vector> + +#include "YamlKeyValues.h" + + +namespace lld { +namespace yaml { + + +/// +/// Concrete instance of lld::Reference created parsing YAML object files +/// +class YAMLReference : public Reference { +public: + YAMLReference() + : _target(nullptr) + , _targetNameNode(nullptr) + , _offsetInAtom(0) + , _addend(0) + , _kind(0) + {} + + virtual uint64_t offsetInAtom() const { + return _offsetInAtom; + } + + virtual Kind kind() const { + return _kind; + } + + virtual void setKind(Kind k) { + _kind = k; + } + + virtual const Atom *target() const { + return _target; + } + + virtual Addend addend() const { + return _addend; + } + + virtual void setAddend(Addend a) { + _addend = a; + } + + virtual void setTarget(const Atom *newAtom) { + _target = newAtom; + } + + typedef llvm::yaml::ScalarNode ScalarNode; + + const Atom *_target; + ScalarNode * _targetNameNode; + uint64_t _offsetInAtom; + Addend _addend; + Kind _kind; +}; + + +/// +/// Concrete instance of lld::File created parsing YAML object files. +/// +class YAMLFile : public ArchiveLibraryFile { +public: + YAMLFile() + : ArchiveLibraryFile("<anonymous>") + , _lastRefIndex(0) + , _kind(File::kindObject) { + } + + ~YAMLFile(); + + // Depending on the YAML description, this file can be either an + // lld::ArchiveLibraryFile or lld::File. + virtual File::Kind kind() const { + return _kind; + } + + virtual const atom_collection<DefinedAtom> &defined() const { + return _definedAtoms; + } + virtual const atom_collection<UndefinedAtom> &undefined() const { + return _undefinedAtoms; + } + virtual const atom_collection<SharedLibraryAtom> &sharedLibrary() const { + return _sharedLibraryAtoms; + } + virtual const atom_collection<AbsoluteAtom> &absolute() const { + return _absoluteAtoms; + } + + virtual void addAtom(const Atom&) { + assert(0 && "cannot add atoms to YAML files"); + } + + // Standard way that archives are searched. + virtual const File *find(StringRef name, bool dataSymbolOnly) const; + + error_code bindTargetReferences(llvm::yaml::Stream &stream); + + void addDefinedAtom(class YAMLDefinedAtom *atom, StringRef refName); + void addUndefinedAtom(UndefinedAtom *atom); + void addSharedLibraryAtom(SharedLibraryAtom *atom); + void addAbsoluteAtom(AbsoluteAtom *atom); + Atom *findAtom(StringRef name); + void addMember(StringRef); + void setName(StringRef); + + StringRef copyString(StringRef); + + struct NameAtomPair { + NameAtomPair(StringRef n, Atom *a) : name(n), atom(a) {} + StringRef name; + Atom *atom; + }; + + atom_collection_vector<DefinedAtom> _definedAtoms; + atom_collection_vector<UndefinedAtom> _undefinedAtoms; + atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms; + atom_collection_vector<AbsoluteAtom> _absoluteAtoms; + std::vector<YAMLReference> _references; + std::vector<NameAtomPair> _nameToAtomMapping; + std::vector<std::unique_ptr<YAMLFile>> _memberFiles; + std::vector<char*> _stringCopies; + unsigned int _lastRefIndex; + File::Kind _kind; +}; + + + +/// +/// Concrete instance of lld::DefinedAtom created parsing YAML object files. +/// +class YAMLDefinedAtom : public DefinedAtom { +public: + YAMLDefinedAtom( uint32_t ord + , YAMLFile &file + , DefinedAtom::Scope scope + , DefinedAtom::ContentType type + , DefinedAtom::SectionChoice sectionChoice + , DefinedAtom::Interposable interpose + , DefinedAtom::Merge merge + , DefinedAtom::DeadStripKind deadStrip + , DefinedAtom::ContentPermissions perms + , bool isThumb + , bool isAlias + , DefinedAtom::Alignment alignment + , StringRef name + , StringRef sectionName + , uint64_t size + , std::vector<uint8_t>& content) + : _file(file) + , _name(name) + , _sectionName(sectionName) + , _size(size) + , _ord(ord) + , _content(content) + , _alignment(alignment) + , _scope(scope) + , _type(type) + , _sectionChoice(sectionChoice) + , _interpose(interpose) + , _merge(merge) + , _deadStrip(deadStrip) + , _permissions(perms) + , _isThumb(isThumb) + , _isAlias(isAlias) + , _refStartIndex(file._lastRefIndex) + , _refEndIndex(file._references.size()) { + file._lastRefIndex = _refEndIndex; + } + + virtual const class File &file() const { + return _file; + } + + virtual StringRef name() const { + return _name; + } + + virtual uint64_t size() const { + return _content.empty() ? _size : _content.size(); + } + + virtual DefinedAtom::Scope scope() const { + return _scope; + } + + virtual DefinedAtom::Interposable interposable() const { + return _interpose; + } + + virtual DefinedAtom::Merge merge() const { + return _merge; + } + + virtual DefinedAtom::ContentType contentType() const { + return _type; + } + + virtual DefinedAtom::Alignment alignment() const { + return _alignment; + } + + virtual DefinedAtom::SectionChoice sectionChoice() const { + return _sectionChoice; + } + + virtual StringRef customSectionName() const { + return _sectionName; + } + + virtual DefinedAtom::DeadStripKind deadStrip() const { + return _deadStrip; + } + + virtual DefinedAtom::ContentPermissions permissions() const { + return _permissions; + } + + virtual bool isThumb() const { + return _isThumb; + } + + virtual bool isAlias() const { + return _isAlias; + } + + ArrayRef<uint8_t> rawContent() const { + return ArrayRef<uint8_t>(_content); + } + + virtual uint64_t ordinal() const { + return _ord; + } + + DefinedAtom::reference_iterator begin() const { + uintptr_t index = _refStartIndex; + const void* it = reinterpret_cast<const void*>(index); + return reference_iterator(*this, it); + } + + DefinedAtom::reference_iterator end() const { + uintptr_t index = _refEndIndex; + const void* it = reinterpret_cast<const void*>(index); + return reference_iterator(*this, it); + } + + const Reference* derefIterator(const void* it) const { + uintptr_t index = reinterpret_cast<uintptr_t>(it); + assert(index >= _refStartIndex); + assert(index < _refEndIndex); + assert(index < _file._references.size()); + return &_file._references[index]; + } + + void incrementIterator(const void*& it) const { + uintptr_t index = reinterpret_cast<uintptr_t>(it); + ++index; + it = reinterpret_cast<const void*>(index); + } + + // Convert each target name to a pointer to an atom object + error_code bindTargetReferences(llvm::yaml::Stream &stream) const { + for (unsigned int i=_refStartIndex; i < _refEndIndex; ++i) { + llvm::SmallString<32> storage; + llvm::yaml::ScalarNode *node = _file._references[i]._targetNameNode; + StringRef name = node->getValue(storage); + Atom *targetAtom = _file.findAtom(name); + if ( targetAtom ) { + _file._references[i]._target = targetAtom; + } + else { + stream.printError(node, "Fixup has target '" + name + + "' which does not exist"); + return make_error_code(yaml_reader_error::illegal_value); + } + } + return make_error_code(yaml_reader_error::success); + } + +private: + YAMLFile &_file; + StringRef _name; + StringRef _sectionName; + unsigned long _size; + uint32_t _ord; + std::vector<uint8_t> _content; + DefinedAtom::Alignment _alignment; + DefinedAtom::Scope _scope; + DefinedAtom::ContentType _type; + DefinedAtom::SectionChoice _sectionChoice; + DefinedAtom::Interposable _interpose; + DefinedAtom::Merge _merge; + DefinedAtom::DeadStripKind _deadStrip; + DefinedAtom::ContentPermissions _permissions; + bool _isThumb; + bool _isAlias; + unsigned int _refStartIndex; + unsigned int _refEndIndex; +}; + + + +/// +/// Concrete instance of lld::UndefinedAtom created parsing YAML object files. +/// +class YAMLUndefinedAtom : public UndefinedAtom { +public: + YAMLUndefinedAtom( YAMLFile &f + , int32_t ord + , StringRef name + , UndefinedAtom::CanBeNull cbn) + : _file(f) + , _name(name) + , _ordinal(ord) + , _canBeNull(cbn) { + } + + virtual const class File &file() const { + return _file; + } + + virtual StringRef name() const { + return _name; + } + + virtual CanBeNull canBeNull() const { + return _canBeNull; + } + +private: + YAMLFile &_file; + StringRef _name; + uint32_t _ordinal; + UndefinedAtom::CanBeNull _canBeNull; +}; + + + +/// +/// Concrete instance of lld::SharedLibraryAtom created parsing YAML files. +/// +class YAMLSharedLibraryAtom : public SharedLibraryAtom { +public: + YAMLSharedLibraryAtom( YAMLFile &f + , int32_t ord + , StringRef name + , StringRef loadName + , bool cbn) + : _file(f) + , _name(name) + , _ordinal(ord) + , _loadName(loadName) + , _canBeNull(cbn) { + } + + virtual const class File &file() const { + return _file; + } + + virtual StringRef name() const { + return _name; + } + + virtual StringRef loadName() const { + return _loadName; + } + + virtual bool canBeNullAtRuntime() const { + return _canBeNull; + } + +private: + YAMLFile &_file; + StringRef _name; + uint32_t _ordinal; + StringRef _loadName; + bool _canBeNull; +}; + + + +/// +/// Concrete instance of lld::AbsoluteAtom created parsing YAML object files. +/// +class YAMLAbsoluteAtom : public AbsoluteAtom { +public: + YAMLAbsoluteAtom(YAMLFile &f, int32_t ord, StringRef name, uint64_t v) + : _file(f) + , _name(name) + , _ordinal(ord) + , _value(v) { + } + + virtual const class File &file() const { + return _file; + } + + virtual StringRef name() const { + return _name; + } + + virtual uint64_t value() const { + return _value; + } + +private: + YAMLFile &_file; + StringRef _name; + uint32_t _ordinal; + uint64_t _value; +}; + + + + +//===----------------------------------------------------------------------===// +// YAMLFile methods +//===----------------------------------------------------------------------===// + +YAMLFile::~YAMLFile() { + for (char *s : _stringCopies) { + delete [] s; + } +} + + +error_code YAMLFile::bindTargetReferences(llvm::yaml::Stream &stream) { + error_code ec; + for (const DefinedAtom *defAtom : _definedAtoms) { + const YAMLDefinedAtom *atom = + reinterpret_cast<const YAMLDefinedAtom*>(defAtom); + ec = atom->bindTargetReferences(stream); + if ( ec ) + return ec; + } + return ec; +} + +Atom *YAMLFile::findAtom(StringRef name) { + for (auto &ci : _nameToAtomMapping) { + if (ci.name == name) + return ci.atom; + } + return nullptr; +} + +void YAMLFile::addDefinedAtom(YAMLDefinedAtom *atom, StringRef refName) { + _definedAtoms._atoms.push_back(atom); + _nameToAtomMapping.push_back(NameAtomPair(refName, atom)); +} + +void YAMLFile::addUndefinedAtom(UndefinedAtom *atom) { + _undefinedAtoms._atoms.push_back(atom); + _nameToAtomMapping.push_back(NameAtomPair(atom->name(), atom)); +} + +void YAMLFile::addSharedLibraryAtom(SharedLibraryAtom *atom) { + _sharedLibraryAtoms._atoms.push_back(atom); + _nameToAtomMapping.push_back(NameAtomPair(atom->name(), atom)); +} + +void YAMLFile::addAbsoluteAtom(AbsoluteAtom *atom) { + _absoluteAtoms._atoms.push_back(atom); + _nameToAtomMapping.push_back(NameAtomPair(atom->name(), atom)); +} + +void YAMLFile::setName(StringRef name) { + _path = StringRef(name); +} + + +// Allocate a new copy of this string and keep track of allocations +// in _stringCopies, so they can be freed when YAMLFile is destroyed. +StringRef YAMLFile::copyString(StringRef str) { + char* s = new char[str.size()]; + memcpy(s, str.data(), str.size()); + _stringCopies.push_back(s); + return StringRef(s, str.size()); +} + +const File *YAMLFile::find(StringRef name, bool dataSymbolOnly) const { + for (auto &file : _memberFiles) { + for (const DefinedAtom *atom : file->defined() ) { + if (name == atom->name()) + return file.get(); + } + } + return nullptr; +} + + + +/// +/// The state machine that drives the YAMLParser stream and instantiates +/// Files and Atoms. This class also buffers all the attribures for the +/// current atom and current fixup. Once all attributes are accumulated, +/// a new atom or fixup instance is instantiated. +/// +class YAMLState { +public: + YAMLState(const ReaderOptionsYAML &opts, llvm::yaml::Stream *s, YAMLFile *f); + + void parse(llvm::yaml::Node *node, StringRef keyword, + llvm::yaml::Node *keywordNode=nullptr); + error_code error() { return _error; } + +private: + typedef llvm::yaml::Node Node; + typedef llvm::yaml::ScalarNode ScalarNode; + typedef llvm::yaml::SequenceNode SequenceNode; + typedef llvm::yaml::MappingNode MappingNode; + typedef llvm::yaml::Stream Stream; + + void resetState(); + void setAlign2(StringRef n); + + void makeReference(); + void makeAtom(Node *node); + void makeDefinedAtom(Node *node); + void makeUndefinedAtom(Node *node); + void makeSharedLibraryAtom(Node *node); + void makeAbsoluteAtom(Node *node); + + void parseMemberName(ScalarNode *node); + void parseAtomName(ScalarNode *node); + void parseAtomRefName(ScalarNode *node); + void parseAtomType(ScalarNode *node); + void parseAtomScope(ScalarNode *node); + void parseAtomDefinition(ScalarNode *node); + void parseAtomDeadStrip(ScalarNode *node); + void parseAtomSectionChoice(ScalarNode *node); + void parseAtomInterposable(ScalarNode *node); + void parseAtomMerge(ScalarNode *node); + void parseAtomIsThumb(ScalarNode *node); + void parseAtomIsAlias(ScalarNode *node); + void parseAtomSectionName(ScalarNode *node); + void parseAtomSize(ScalarNode *node); + void parseAtomPermissions(ScalarNode *node); + void parseAtomCanBeNull(ScalarNode *node); + void parseFixUpOffset(ScalarNode *node); + void parseFixUpKind(ScalarNode *node); + void parseFixUpTarget(ScalarNode *node); + void parseFixUpAddend(ScalarNode *node); + void parseAtomContentByte(ScalarNode *node); + void parseAtomLoadName(ScalarNode *node); + void parseAtomValue(ScalarNode *node); + + StringRef extractString(ScalarNode *node); + + typedef void (YAMLState:: *ParseScalar)(ScalarNode *node); + typedef void (YAMLState:: *ParseSeq)(SequenceNode *node); + typedef void (YAMLState:: *ParseMap)(MappingNode *node); + + enum State { inError, inTop, inDoc, inArch, inMemb, + inAtoms, inAtom, inFixUps, inFixUp, inBytes }; + struct Transistion { + State state; + const char* keyword; + State newState; + ParseScalar customAction; + }; + + static const char* stateName(State); + + void moveToState(State s); + void returnToState(State s, Node *node); + + static const Transistion _s_transistions[]; + + const ReaderOptionsYAML &_options; + error_code _error; + llvm::yaml::Stream *_stream; + YAMLFile *_file; + YAMLFile *_archiveFile; + State _state; + StringRef _name; + StringRef _refName; + StringRef _sectionName; + StringRef _loadName; + StringRef _memberName; + unsigned long long _size; + uint64_t _value; + uint32_t _ordinal; + std::vector<uint8_t> _content; + DefinedAtom::Alignment _alignment; + Atom::Definition _definition; + DefinedAtom::Scope _scope; + DefinedAtom::ContentType _type; + DefinedAtom::SectionChoice _sectionChoice; + DefinedAtom::Interposable _interpose; + DefinedAtom::Merge _merge; + DefinedAtom::DeadStripKind _deadStrip; + DefinedAtom::ContentPermissions _permissions; + bool _isThumb; + bool _isAlias; + UndefinedAtom::CanBeNull _canBeNull; + YAMLReference _ref; + bool _hasDefinedAtomAttributes; + bool _hasUndefinedAtomAttributes; + bool _hasSharedLibraryAtomAttributes; + bool _hasAbsoluteAtomAttributes; +}; + + +// +// This transition table is the heart of the state machine. +// The table is read left-to-right columns A,B,C,D as: +// If the state is A and key B is seen switch to state C then +// if D is not nullptr call that method with the key's value, +// if D is nullptr, recursively parse in the new state. +// +const YAMLState::Transistion YAMLState::_s_transistions[] = { + { inTop, "<root>", inDoc, nullptr }, + { inDoc, "archive", inArch, nullptr }, + { inArch, "<any-seq-item>", inMemb, nullptr }, + { inMemb, "atoms", inAtoms, nullptr }, + { inMemb, "name", inMemb, &YAMLState::parseMemberName }, + { inDoc, "atoms", inAtoms, nullptr }, + { inAtoms, "<any-seq-item>", inAtom, nullptr }, + { inAtom, "name", inAtom, &YAMLState::parseAtomName }, + { inAtom, "ref-name", inAtom, &YAMLState::parseAtomRefName }, + { inAtom, "type", inAtom, &YAMLState::parseAtomType }, + { inAtom, "scope", inAtom, &YAMLState::parseAtomScope }, + { inAtom, "definition", inAtom, &YAMLState::parseAtomDefinition }, + { inAtom, "dead-strip", inAtom, &YAMLState::parseAtomDeadStrip }, + { inAtom, "section-choice", inAtom, &YAMLState::parseAtomSectionChoice }, + { inAtom, "interposable", inAtom, &YAMLState::parseAtomInterposable }, + { inAtom, "merge", inAtom, &YAMLState::parseAtomMerge }, + { inAtom, "is-thumb", inAtom, &YAMLState::parseAtomIsThumb }, + { inAtom, "is-alias", inAtom, &YAMLState::parseAtomIsAlias }, + { inAtom, "section-name", inAtom, &YAMLState::parseAtomSectionName }, + { inAtom, "size", inAtom, &YAMLState::parseAtomSize }, + { inAtom, "permissions", inAtom, &YAMLState::parseAtomPermissions }, + { inAtom, "can-be-null", inAtom, &YAMLState::parseAtomCanBeNull }, + { inAtom, "content", inBytes, nullptr }, + { inAtom, "fixups", inFixUps,nullptr }, + { inBytes, "<any-seq-item>", inBytes, &YAMLState::parseAtomContentByte }, + { inFixUps,"<any-seq-item>", inFixUp, nullptr }, + { inFixUp, "offset", inFixUp, &YAMLState::parseFixUpOffset }, + { inFixUp, "kind", inFixUp, &YAMLState::parseFixUpKind }, + { inFixUp, "target", inFixUp, &YAMLState::parseFixUpTarget }, + { inFixUp, "addend", inFixUp, &YAMLState::parseFixUpAddend }, + { inAtom, "load-name", inAtom, &YAMLState::parseAtomLoadName }, + { inAtom, "value", inAtom, &YAMLState::parseAtomValue }, + { inError, nullptr, inAtom, nullptr }, +}; + + + +YAMLState::YAMLState(const ReaderOptionsYAML &opts, Stream *stream, + YAMLFile *file) + : _options(opts) + , _error(make_error_code(yaml_reader_error::success)) + , _stream(stream) + , _file(file) + , _archiveFile(nullptr) + , _state(inTop) + , _alignment(0, 0) { + this->resetState(); +} + +void YAMLState::makeAtom(Node *node) { + switch (_definition ) { + case Atom::definitionRegular: + this->makeDefinedAtom(node); + break; + case Atom::definitionUndefined: + this->makeUndefinedAtom(node); + break; + case Atom::definitionSharedLibrary: + this->makeSharedLibraryAtom(node); + break; + case Atom::definitionAbsolute: + this->makeAbsoluteAtom(node); + break; + } + ++_ordinal; + + // reset state for next atom + this->resetState(); +} + +void YAMLState::makeDefinedAtom(Node *node) { + if ( _hasAbsoluteAtomAttributes ) { + _stream->printError(node, "Defined atom '" + _name + + "' has attributes only allowed on absolute atoms"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + if ( _hasSharedLibraryAtomAttributes ) { + _stream->printError(node, "Defined atom '" + _name + + "' has attributes only allowed on shared library atoms"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + + YAMLDefinedAtom *a = new YAMLDefinedAtom(_ordinal, *_file, _scope, _type + , _sectionChoice, _interpose, _merge, _deadStrip + , _permissions, _isThumb, _isAlias, _alignment + , _name, _sectionName, _size, _content); + _file->addDefinedAtom(a, !_refName.empty() ? _refName : _name); +} + +void YAMLState::makeUndefinedAtom(Node *node) { + if ( _hasDefinedAtomAttributes ) { + _stream->printError(node, "Undefined atom '" + _name + + "' has attributes only allowed on defined atoms"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + if ( _hasAbsoluteAtomAttributes ) { + _stream->printError(node, "Defined atom '" + _name + + "' has attributes only allowed on absolute atoms"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + UndefinedAtom *a = new YAMLUndefinedAtom(*_file, _ordinal, _name, _canBeNull); + _file->addUndefinedAtom(a); +} + +void YAMLState::makeSharedLibraryAtom(Node *node) { + if ( _hasDefinedAtomAttributes ) { + _stream->printError(node, "SharedLibrary atom '" + _name + + "' has attributes only allowed on defined atoms"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + if ( _hasAbsoluteAtomAttributes ) { + _stream->printError(node, "Defined atom '" + _name + + "' has attributes only allowed on absolute atoms"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + bool nullable = (_canBeNull == UndefinedAtom::canBeNullAtRuntime); + SharedLibraryAtom *a = new YAMLSharedLibraryAtom(*_file, _ordinal, _name, + _loadName, nullable); + _file->addSharedLibraryAtom(a); +} + +void YAMLState::makeAbsoluteAtom(Node *node) { + if ( _hasDefinedAtomAttributes ) { + _stream->printError(node, "Absolute atom '" + _name + + "' has attributes only allowed on defined atoms"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + if ( _hasSharedLibraryAtomAttributes ) { + _stream->printError(node, "Absolute atom '" + _name + + "' has attributes only allowed on shared library atoms"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + AbsoluteAtom *a = new YAMLAbsoluteAtom(*_file, _ordinal, _name, _value); + _file->addAbsoluteAtom(a); +} + + + +void YAMLState::resetState() { + _name = StringRef(); + _refName = StringRef(); + _sectionName = StringRef(); + _loadName = StringRef(); + _memberName = StringRef(); + _size = 0; + _value = 0; + _ordinal = 0; + _content.clear(); + _alignment.powerOf2 = 0; + _alignment.modulus = 0; + _definition = KeyValues::definitionDefault; + _scope = KeyValues::scopeDefault; + _type = KeyValues::contentTypeDefault; + _sectionChoice = KeyValues::sectionChoiceDefault; + _interpose = KeyValues::interposableDefault; + _merge = KeyValues::mergeDefault; + _deadStrip = KeyValues::deadStripKindDefault; + _permissions = KeyValues::permissionsDefault; + _isThumb = KeyValues::isThumbDefault; + _isAlias = KeyValues::isAliasDefault; + _canBeNull = KeyValues::canBeNullDefault; + _ref._target = nullptr; + _ref._targetNameNode= nullptr; + _ref._addend = 0; + _ref._offsetInAtom = 0; + _ref._kind = 0; + + _hasDefinedAtomAttributes = false; + _hasUndefinedAtomAttributes = false; + _hasSharedLibraryAtomAttributes = false; + _hasAbsoluteAtomAttributes = false; +} + + +void YAMLState::makeReference() { + _file->_references.push_back(_ref); + // clear for next ref + _ref._target = nullptr; + _ref._targetNameNode= nullptr; + _ref._addend = 0; + _ref._offsetInAtom = 0; + _ref._kind = 0; +} + + + +void YAMLState::setAlign2(StringRef s) { + if (StringRef(s).getAsInteger(10, _alignment.powerOf2)) + _alignment.powerOf2 = 1; +} + + +// For debug logging +const char* YAMLState::stateName(State s) { + switch ( s ) { + case inError: + return "inError"; + case inTop: + return "inTop"; + case inDoc: + return "inDoc"; + case inArch: + return "inArch"; + case inMemb: + return "inMemb"; + case inAtoms: + return "inAtoms"; + case inAtom: + return "inAtom"; + case inFixUps: + return "inFixUps"; + case inFixUp: + return "inFixUp"; + case inBytes: + return "inBytes"; + } + return "unknown case"; +} + +// Called by parse() when recursing and switching to a new state. +void YAMLState::moveToState(State newState) { + if ( newState == _state ) + return; + DEBUG_WITH_TYPE("objtxt", llvm::dbgs() << "moveToState(" << stateName(newState) + << "), _state=" << stateName(_state) << "\n"); + + if ( newState == inArch ) { + // Seen "archive:", repurpose existing YAMLFile to be archive file + _file->_kind = File::kindArchiveLibrary; + _archiveFile = _file; + _file = nullptr; + } + + if ( newState == inMemb ) { + assert(_state == inArch); + // Make new YAMLFile for this member + std::unique_ptr<YAMLFile> memberFile(new YAMLFile); + _file = memberFile.get(); + assert(_archiveFile != nullptr); + _archiveFile->_memberFiles.emplace_back(memberFile.release()); + } + + _state = newState; +} + +// Called by parse() when returning from recursion and restoring the old state. +void YAMLState::returnToState(State prevState, Node *node) { + if ( prevState == _state ) + return; + DEBUG_WITH_TYPE("objtxt", llvm::dbgs() + << "returnToState(" << stateName(prevState) + << "), _state=" << stateName(_state) << "\n"); + // If done with an atom, instantiate an object for it. + if ( (_state == inAtom) && (prevState == inAtoms) ) + this->makeAtom(node); + // If done wit a fixup, instantiate an object for it. + if ( (_state == inFixUp) && (prevState == inFixUps) ) + this->makeReference(); + _state = prevState; +} + +// If a string in the yaml document is quoted in a way that there is no +// contiguous range of bytes that a StringRef can point to, then we make +// a copy of the string and have the StringRef point to that. +StringRef YAMLState::extractString(ScalarNode *node) { + llvm::SmallString<32> storage; + StringRef str = node->getValue(storage); + //if ( str.data() == storage.begin() ) { + str = _file->copyString(str); + //} + return str; +} + + +void YAMLState::parseMemberName(ScalarNode *node) { + _memberName = extractString(node); +} + +void YAMLState::parseAtomName(ScalarNode *node) { + _name = extractString(node); +} + +void YAMLState::parseAtomRefName(ScalarNode *node) { + _refName = extractString(node); +} + +void YAMLState::parseAtomScope(ScalarNode *node) { + llvm::SmallString<32> storage; + if ( KeyValues::scope(node->getValue(storage), _scope) ) { + _stream->printError(node, "Invalid value for 'scope:'"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseAtomDefinition(ScalarNode *node) { + llvm::SmallString<32> storage; + if ( KeyValues::definition(node->getValue(storage), _definition) ) { + _stream->printError(node, "Invalid value for 'definition:'"); + _error = make_error_code(yaml_reader_error::illegal_value); + } +} + +void YAMLState::parseAtomType(ScalarNode *node) { + llvm::SmallString<32> storage; + if ( KeyValues::contentType(node->getValue(storage), _type) ) { + _stream->printError(node, "Invalid value for 'type:'"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseAtomDeadStrip(ScalarNode *node) { + llvm::SmallString<32> storage; + if ( KeyValues::deadStripKind(node->getValue(storage), _deadStrip) ) { + _stream->printError(node, "Invalid value for 'dead-strip:'"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseAtomSectionChoice(ScalarNode *node) { + llvm::SmallString<32> storage; + if ( KeyValues::sectionChoice(node->getValue(storage), _sectionChoice) ) { + _stream->printError(node, "Invalid value for 'section-choice:'"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseAtomInterposable(ScalarNode *node) { + llvm::SmallString<32> storage; + if ( KeyValues::interposable(node->getValue(storage), _interpose) ) { + _stream->printError(node, "Invalid value for 'interposable:'"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseAtomMerge(ScalarNode *node) { + llvm::SmallString<32> storage; + if ( KeyValues::merge(node->getValue(storage), _merge) ) { + _stream->printError(node, "Invalid value for 'merge:'"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseAtomIsThumb(ScalarNode *node) { + llvm::SmallString<32> storage; + if ( KeyValues::isThumb(node->getValue(storage), _isThumb) ) { + _stream->printError(node, "Invalid value for 'thumb:'"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseAtomIsAlias(ScalarNode *node) { + llvm::SmallString<32> storage; + if ( KeyValues::isAlias(node->getValue(storage), _isAlias) ) { + _stream->printError(node, "Invalid value for 'is-alias:'"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseAtomSectionName(ScalarNode *node) { + _sectionName = extractString(node); + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseAtomSize(ScalarNode *node) { + llvm::SmallString<32> storage; + StringRef offsetStr = node->getValue(storage); + if ( offsetStr.getAsInteger(0, _size) ) { + _stream->printError(node, "Invalid value for atom 'size:'"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseAtomPermissions(ScalarNode *node) { + llvm::SmallString<32> storage; + if ( KeyValues::permissions(node->getValue(storage), _permissions) ) { + _stream->printError(node, "Invalid value for 'permissions:'"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseAtomCanBeNull(ScalarNode *node) { + llvm::SmallString<32> storage; + if ( KeyValues::canBeNull(node->getValue(storage), _canBeNull) ) { + _stream->printError(node, "Invalid value for 'can-be-null:'"); + _error = make_error_code(yaml_reader_error::illegal_value); + } +} + +void YAMLState::parseFixUpOffset(ScalarNode *node) { + llvm::SmallString<32> storage; + StringRef offsetStr = node->getValue(storage); + if ( offsetStr.getAsInteger(0, _ref._offsetInAtom) ) { + _stream->printError(node, "Invalid value for fixup 'offset:'"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseFixUpKind(ScalarNode *node) { + llvm::SmallString<32> storage; + _ref._kind = _options.kindFromString(node->getValue(storage)); + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseFixUpTarget(ScalarNode *node) { + _ref._targetNameNode = node; + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseFixUpAddend(ScalarNode *node) { + llvm::SmallString<32> storage; + StringRef offsetStr = node->getValue(storage); + if ( offsetStr.getAsInteger(0, _ref._addend) ) { + _stream->printError(node, "Invalid value for fixup 'addend:'"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseAtomContentByte(ScalarNode *node) { + llvm::SmallString<32> storage; + StringRef str = node->getValue(storage); + unsigned int contentByte; + if ( str.getAsInteger(16, contentByte) ) { + _stream->printError(node, "Invalid content hex byte '0x" + str + "'"); + _error = make_error_code(yaml_reader_error::illegal_value); + return; + } + if (contentByte > 0xFF) { + _stream->printError(node, "Content hex byte out of range (0x" + + str + " > 0xFF)"); + _error = make_error_code(yaml_reader_error::illegal_value); + return; + } + _content.push_back(contentByte & 0xFF); + _hasDefinedAtomAttributes = true; +} + +void YAMLState::parseAtomLoadName(ScalarNode *node) { + _loadName = extractString(node); + _hasSharedLibraryAtomAttributes = true; +} + + +void YAMLState::parseAtomValue(ScalarNode *node) { + llvm::SmallString<32> storage; + StringRef offsetStr = node->getValue(storage); + if ( offsetStr.getAsInteger(0, _value) ) { + _stream->printError(node, "Invalid value for fixup 'addend:'"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + _hasAbsoluteAtomAttributes = true; +} + +// +// This is the parsing engine that walks the nodes in the yaml document +// stream. It is table driven. See _s_transistions. +// +void YAMLState::parse(Node *node, StringRef keyword, Node *keywordNode) { + using namespace llvm::yaml; + DEBUG_WITH_TYPE("objtxt", llvm::dbgs() << "parse(" << keyword << "), _state=" + << stateName(_state) << "\n"); + if ( _error ) + return; + State savedState = _state; + for(const Transistion* t=_s_transistions; t->state != inError; ++t) { + if ( t->state != _state ) + continue; + if ( ! keyword.equals(t->keyword) ) + continue; + ParseScalar action = t->customAction; + this->moveToState(t->newState); + if ( ScalarNode *sc = llvm::dyn_cast<ScalarNode>(node) ) { + if ( action ) { + (*this.*action)(sc); + } + else { + _stream->printError(node, "unexpected scalar"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + } + else if ( SequenceNode *seq = llvm::dyn_cast<SequenceNode>(node) ) { + if ( action ) { + _stream->printError(node, "unexpected sequence"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + else { + for (Node &seqEntry : *seq ) { + this->parse(&seqEntry, StringRef("<any-seq-item>")); + if ( _error ) + break; + } + } + } + else if ( MappingNode *map = llvm::dyn_cast<MappingNode>(node) ) { + if ( action ) { + _stream->printError(node, "unexpected map"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + else { + llvm::SmallString<32> storage; + for (auto &keyVal : *map) { + ScalarNode *keyScalar = llvm::dyn_cast<ScalarNode>(keyVal.getKey()); + llvm::StringRef keyStr = keyScalar->getValue(storage); + this->parse(keyVal.getValue(), keyStr, keyScalar); + if ( _error ) + break; + } + } + } + else { + _stream->printError(node, "unexpected node type"); + _error = make_error_code(yaml_reader_error::illegal_value); + } + this->returnToState(savedState, node); + return; + } + switch (_state) { + case inAtom: + _stream->printError(keywordNode, "Unknown atom attribute '" + + keyword + ":'"); + break; + case inFixUp: + _stream->printError(keywordNode, "Unknown fixup attribute '" + + keyword + ":'"); + break; + case inDoc: + _stream->printError(keywordNode, "Unknown file attribute '" + + keyword + ":'"); + break; + default: + _stream->printError(keywordNode, "Unknown keyword '" + + keyword + ":'"); + } + _error = make_error_code(yaml_reader_error::illegal_value); +} + + +/// parseObjectText - Parse the specified YAML formatted MemoryBuffer +/// into lld::File object(s) and append each to the specified vector<File*>. +error_code parseFile(std::unique_ptr<MemoryBuffer> &mb, + const ReaderOptionsYAML &options, + std::vector<std::unique_ptr<File>> &result) { + llvm::SourceMgr srcMgr; + llvm::yaml::Stream stream(mb->getBuffer(), srcMgr); + + for (llvm::yaml::Document &d : stream) { + std::unique_ptr<yaml::YAMLFile> curFile(new yaml::YAMLFile); + if (llvm::isa<llvm::yaml::NullNode>(d.getRoot())) + continue; // Empty files are allowed. + yaml::YAMLState yamlState(options, &stream, curFile.get()); + yamlState.parse(d.getRoot(), StringRef("<root>")); + + if ( stream.failed() ) + return make_error_code(yaml_reader_error::illegal_value); + if ( yamlState.error() ) + return yamlState.error(); + + error_code ec = curFile->bindTargetReferences(stream); + if ( ec ) + return ec; + result.emplace_back(curFile.release()); + } + + return make_error_code(yaml_reader_error::success); +} + + + +} // namespace yaml + + + +class ReaderYAML: public Reader { +public: + ReaderYAML(const ReaderOptionsYAML &options) : _options(options) { + } + + error_code parseFile(std::unique_ptr<MemoryBuffer> mb, + std::vector<std::unique_ptr<File>> &result) { + return lld::yaml::parseFile(mb, _options, result); + } + +private: + const ReaderOptionsYAML &_options; +}; + + + +Reader* createReaderYAML(const ReaderOptionsYAML &options) { + return new ReaderYAML(options); +} + +ReaderOptionsYAML::ReaderOptionsYAML() { +} + +ReaderOptionsYAML::~ReaderOptionsYAML() { +} + + + + +} // namespace lld |

