| //===- 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 |
| , StringRef name |
| , UndefinedAtom::CanBeNull cbn) |
| : _file(f) |
| , _name(name) |
| , _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; |
| UndefinedAtom::CanBeNull _canBeNull; |
| }; |
| |
| |
| |
| /// |
| /// Concrete instance of lld::SharedLibraryAtom created parsing YAML files. |
| /// |
| class YAMLSharedLibraryAtom : public SharedLibraryAtom { |
| public: |
| YAMLSharedLibraryAtom( YAMLFile &f |
| , int32_t |
| , StringRef name |
| , StringRef loadName |
| , bool cbn) |
| : _file(f) |
| , _name(name) |
| , _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; |
| StringRef _loadName; |
| bool _canBeNull; |
| }; |
| |
| |
| |
| /// |
| /// Concrete instance of lld::AbsoluteAtom created parsing YAML object files. |
| /// |
| class YAMLAbsoluteAtom : public AbsoluteAtom { |
| public: |
| YAMLAbsoluteAtom(YAMLFile &f, int32_t, StringRef name, uint64_t v) |
| : _file(f) |
| , _name(name) |
| , _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; |
| 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); |
| } |
| |
| |
| /// parseFile - Parse the specified YAML formatted MemoryBuffer |
| /// into lld::File object(s) and append each to the specified vector<File*>. |
| static 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 |