//===- 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 #include #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("") , _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 &defined() const { return _definedAtoms; } virtual const atom_collection &undefined() const { return _undefinedAtoms; } virtual const atom_collection &sharedLibrary() const { return _sharedLibraryAtoms; } virtual const atom_collection &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 _definedAtoms; atom_collection_vector _undefinedAtoms; atom_collection_vector _sharedLibraryAtoms; atom_collection_vector _absoluteAtoms; std::vector _references; std::vector _nameToAtomMapping; std::vector> _memberFiles; std::vector _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& 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 rawContent() const { return ArrayRef(_content); } virtual uint64_t ordinal() const { return _ord; } DefinedAtom::reference_iterator begin() const { uintptr_t index = _refStartIndex; const void* it = reinterpret_cast(index); return reference_iterator(*this, it); } DefinedAtom::reference_iterator end() const { uintptr_t index = _refEndIndex; const void* it = reinterpret_cast(index); return reference_iterator(*this, it); } const Reference* derefIterator(const void* it) const { uintptr_t index = reinterpret_cast(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(it); ++index; it = reinterpret_cast(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 _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(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 _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, "", inDoc, nullptr }, { inDoc, "archive", inArch, nullptr }, { inArch, "", inMemb, nullptr }, { inMemb, "atoms", inAtoms, nullptr }, { inMemb, "name", inMemb, &YAMLState::parseMemberName }, { inDoc, "atoms", inAtoms, nullptr }, { inAtoms, "", 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, "", inBytes, &YAMLState::parseAtomContentByte }, { inFixUps,"", 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 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(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(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("")); if ( _error ) break; } } } else if ( MappingNode *map = llvm::dyn_cast(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(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. error_code parseFile(std::unique_ptr &mb, const ReaderOptionsYAML &options, std::vector> &result) { llvm::SourceMgr srcMgr; llvm::yaml::Stream stream(mb->getBuffer(), srcMgr); for (llvm::yaml::Document &d : stream) { std::unique_ptr curFile(new yaml::YAMLFile); if (llvm::isa(d.getRoot())) continue; // Empty files are allowed. yaml::YAMLState yamlState(options, &stream, curFile.get()); yamlState.parse(d.getRoot(), StringRef("")); 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 mb, std::vector> &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