diff options
Diffstat (limited to 'lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp')
| -rw-r--r-- | lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp | 1357 |
1 files changed, 1357 insertions, 0 deletions
diff --git a/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp b/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp new file mode 100644 index 00000000000..cadd2637e08 --- /dev/null +++ b/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp @@ -0,0 +1,1357 @@ +//===- lib/ReaderWriter/YAML/ReaderWriterYAML.cpp -------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lld/Core/ArchiveLibraryFile.h" +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/Error.h" +#include "lld/Core/File.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/Reference.h" +#include "lld/ReaderWriter/ReaderYAML.h" +#include "lld/ReaderWriter/WriterYAML.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/system_error.h" +#include "llvm/Support/YAMLTraits.h" + +#include <string> + +using llvm::yaml::MappingTraits; +using llvm::yaml::ScalarEnumerationTraits; +using llvm::yaml::ScalarTraits; +using llvm::yaml::IO; +using llvm::yaml::SequenceTraits; +using llvm::yaml::DocumentListTraits; + + +/// The conversion of Atoms to and from YAML uses LLVM's YAML I/O. This +/// file just defines template specializations on the lld types which control +/// how the mapping is done to and from YAML. + + +namespace { +/// Most of the traits are context-free and always do the same transformation. +/// But, there are some traits that need some contextual information to properly +/// do their transform. This struct is available via io.getContext() and +/// supplies contextual information. +class ContextInfo { +public: + ContextInfo(const lld::ReaderOptionsYAML &ro) + : _currentFile(nullptr), _readerOptions(&ro), _writerOptions(nullptr) { } + ContextInfo(const lld::WriterOptionsYAML &wo) + : _currentFile(nullptr), _readerOptions(nullptr), _writerOptions(&wo) { } + + lld::File *_currentFile; + const lld::ReaderOptionsYAML *_readerOptions; + const lld::WriterOptionsYAML *_writerOptions; +}; + + +/// Used when writing yaml files. +/// In most cases, atoms names are unambiguous, so references can just +/// use the atom name as the target (e.g. target: foo). But in a few +/// cases that does not work, so ref-names are added. These are labels +/// used only in yaml. The labels do not exist in the Atom model. +/// +/// One need for ref-names are when atoms have no user supplied name +/// (e.g. c-string literal). Another case is when two object files with +/// identically named static functions are merged (ld -r) into one object file. +/// In that case referencing the function by name is ambiguous, so a unique +/// ref-name is added. +class RefNameBuilder { +public: + RefNameBuilder(const lld::File &file) + : _collisionCount(0), _unnamedCounter(0) { + if (&file == nullptr) + return; + // visit all atoms + for (const lld::DefinedAtom *atom : file.defined()) { + // Build map of atoms names to detect duplicates + if (!atom->name().empty()) + buildDuplicateNameMap(*atom); + + // Find references to unnamed atoms and create ref-names for them. + for (const lld::Reference *ref : *atom) { + // create refname for any unnamed reference target + const lld::Atom *target = ref->target(); + if ((target != nullptr) && target->name().empty()) { + std::string storage; + llvm::raw_string_ostream buffer(storage); + buffer << llvm::format("L%03d", _unnamedCounter++); + llvm::StringRef newName = copyString(buffer.str()); + _refNames[target] = newName; + DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs() + << "unnamed atom: creating ref-name: '" << newName + << "' (" << (void*)newName.data() << ", " + << newName.size() << ")\n"); + } + } + } + for (const lld::UndefinedAtom *undefAtom : file.undefined()) { + buildDuplicateNameMap(*undefAtom); + } + for (const lld::SharedLibraryAtom *shlibAtom : file.sharedLibrary()) { + buildDuplicateNameMap(*shlibAtom); + } + for (const lld::AbsoluteAtom *absAtom : file.absolute()) { + buildDuplicateNameMap(*absAtom); + } + } + + void buildDuplicateNameMap(const lld::Atom &atom) { + assert(!atom.name().empty()); + NameToAtom::iterator pos = _nameMap.find(atom.name()); + if ( pos != _nameMap.end() ) { + // Found name collision, give each a unique ref-name. + std::string Storage; + llvm::raw_string_ostream buffer(Storage); + buffer << atom.name() << llvm::format(".%03d", ++_collisionCount); + llvm::StringRef newName = copyString(buffer.str()); + _refNames[&atom] = newName; + DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs() + << "name collsion: creating ref-name: '" << newName + << "' (" << (void*)newName.data() << ", " + << newName.size() << ")\n"); + const lld::Atom *prevAtom = pos->second; + AtomToRefName::iterator pos2 = _refNames.find(prevAtom); + if ( pos2 == _refNames.end() ) { + // Only create ref-name for previous if none already created. + std::string Storage2; + llvm::raw_string_ostream buffer2(Storage2); + buffer2 << prevAtom->name() << llvm::format(".%03d", ++_collisionCount); + llvm::StringRef newName2 = copyString(buffer2.str()); + _refNames[prevAtom] = newName2; + DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs() + << "name collsion: creating ref-name: '" << newName2 + << "' (" << (void*)newName2.data() << ", " + << newName2.size() << ")\n"); + } + } + else { + // First time we've seen this name, just add it to map. + _nameMap[atom.name()] = &atom; + DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs() + << "atom name seen for first time: '" << atom.name() + << "' (" << (void*)atom.name().data() << ", " + << atom.name().size() << ")\n"); + } + } + + bool hasRefName(const lld::Atom *atom) { + return _refNames.count(atom); + } + + llvm::StringRef refName(const lld::Atom *atom) { + return _refNames.find(atom)->second; + } + +private: + typedef llvm::StringMap<const lld::Atom*> NameToAtom; + typedef llvm::DenseMap<const lld::Atom*, std::string> AtomToRefName; + + // Allocate a new copy of this string and keep track of allocations + // in _stringCopies, so they can be freed when RefNameBuilder is destroyed. + llvm::StringRef copyString(llvm::StringRef str) { + // We want _stringCopies to own the string memory so it is deallocated + // when the File object is destroyed. But we need a StringRef that + // points into that memory. + std::unique_ptr<char> s = std::unique_ptr<char>(new char[str.size()]); + memcpy(s.get(), str.data(), str.size()); + llvm::StringRef r = llvm::StringRef(s.get(), str.size()); + _stringCopies.push_back(std::move(s)); + return r; + } + + unsigned int _collisionCount; + unsigned int _unnamedCounter; + NameToAtom _nameMap; + AtomToRefName _refNames; + std::vector<std::unique_ptr<char>> _stringCopies; +}; + + +/// Used when reading yaml files to find the target of a reference +/// that could be a name or ref-name. +class RefNameResolver { +public: + RefNameResolver(const lld::File *file, IO &io); + + const lld::Atom *lookup(llvm::StringRef name) const { + NameToAtom::const_iterator pos = _nameMap.find(name); + if (pos != _nameMap.end()) { + return pos->second; + } + else { + _io.setError(llvm::Twine("no such atom name: ") + name); + return nullptr; + } + } + +private: + typedef llvm::StringMap<const lld::Atom*> NameToAtom; + + void add(llvm::StringRef name, const lld::Atom *atom) { + if (_nameMap.count(name)) { + _io.setError(llvm::Twine("duplicate atom name: ") + name); + } + else { + _nameMap[name] = atom; + } + } + + IO &_io; + NameToAtom _nameMap; +}; + + +// Used in NormalizedFile to hold the atoms lists. +template <typename T> +class AtomList : public lld::File::atom_collection<T> { +public: + virtual lld::File::atom_iterator<T> begin() const { + return lld::File::atom_iterator<T>(*this, reinterpret_cast<const void*> + (_atoms.data())); + } + virtual lld::File::atom_iterator<T> end() const{ + return lld::File::atom_iterator<T>(*this, reinterpret_cast<const void*> + (_atoms.data() + _atoms.size())); + } + virtual const T *deref(const void *it) const { + return *reinterpret_cast<const T *const*>(it); + } + virtual void next(const void *&it) const { + const T *const *p = reinterpret_cast<const T *const *>(it); + ++p; + it = reinterpret_cast<const void*>(p); + } + virtual void push_back(const T *element) { + _atoms.push_back(element); + } + std::vector<const T*> _atoms; +}; + +/// Mapping of kind: field in yaml files. +enum FileKinds { + fileKindObjectAtoms, // atom based object file encoded in yaml + fileKindArchive, // static archive library encoded in yaml + fileKindObjectELF, // ELF object files encoded in yaml + fileKindObjectMachO // mach-o object files encoded in yaml +}; + +struct ArchMember { + FileKinds _kind; + llvm::StringRef _name; + const lld::File *_content; +}; + +// The content bytes in a DefinedAtom are just uint8_t but we want +// special formatting, so define a strong type. +LLVM_YAML_STRONG_TYPEDEF(uint8_t, ImplicitHex8); + +// SharedLibraryAtoms have a bool canBeNull() method which we'd like to be +// more readable than just true/false. +LLVM_YAML_STRONG_TYPEDEF(bool, ShlibCanBeNull); + +// lld::Reference::Kind is a typedef of int32. We need a stronger +// type to make template matching work, so invent RefKind. +LLVM_YAML_STRONG_TYPEDEF(lld::Reference::Kind, RefKind); + + +} // namespace anon + + +// This is a custom formatter for RefKind +template<> +struct ScalarTraits<RefKind> { + static void output(const RefKind &value, void *ctxt, + llvm::raw_ostream &out) { + assert(ctxt != nullptr); + ContextInfo *info = reinterpret_cast<ContextInfo*>(ctxt); + out << info->_writerOptions->kindToString(value); + } + + static StringRef input(StringRef scalar, void *ctxt, RefKind &value) { + assert(ctxt != nullptr); + ContextInfo *info = reinterpret_cast<ContextInfo*>(ctxt); + value = info->_readerOptions->kindFromString(scalar); + return StringRef(); + } +}; + + +template <> +struct ScalarEnumerationTraits<lld::File::Kind> { + static void enumeration(IO &io, lld::File::Kind &value) { + io.enumCase(value, "object", lld::File::kindObject); + io.enumCase(value, "shared-library", lld::File::kindSharedLibrary); + io.enumCase(value, "static-library", lld::File::kindArchiveLibrary); + } +}; + +template <> +struct ScalarEnumerationTraits<lld::Atom::Scope> { + static void enumeration(IO &io, lld::Atom::Scope &value) { + io.enumCase(value, "global", lld::Atom::scopeGlobal); + io.enumCase(value, "hidden", lld::Atom::scopeLinkageUnit); + io.enumCase(value, "static", lld::Atom::scopeTranslationUnit); + } +}; + +template <> +struct ScalarEnumerationTraits<lld::DefinedAtom::SectionChoice> { + static void enumeration(IO &io, lld::DefinedAtom::SectionChoice &value) { + io.enumCase(value, "content", lld::DefinedAtom::sectionBasedOnContent); + io.enumCase(value, "custom", lld::DefinedAtom::sectionCustomPreferred); + io.enumCase(value, "custom-required", + lld::DefinedAtom::sectionCustomRequired); + } +}; + +template <> +struct ScalarEnumerationTraits<lld::DefinedAtom::Interposable> { + static void enumeration(IO &io, lld::DefinedAtom::Interposable &value) { + io.enumCase(value, "no", lld::DefinedAtom::interposeNo); + io.enumCase(value, "yes", lld::DefinedAtom::interposeYes); + io.enumCase(value, "yes-and-weak", + lld::DefinedAtom::interposeYesAndRuntimeWeak); + } +}; + +template <> +struct ScalarEnumerationTraits<lld::DefinedAtom::Merge> { + static void enumeration(IO &io, lld::DefinedAtom::Merge &value) { + io.enumCase(value, "no", lld::DefinedAtom::mergeNo); + io.enumCase(value, "as-tentative", lld::DefinedAtom::mergeAsTentative); + io.enumCase(value, "as-weak", lld::DefinedAtom::mergeAsWeak); + io.enumCase(value, "as-addressed-weak", + lld::DefinedAtom::mergeAsWeakAndAddressUsed); + } +}; + +template <> +struct ScalarEnumerationTraits<lld::DefinedAtom::DeadStripKind> { + static void enumeration(IO &io, lld::DefinedAtom::DeadStripKind &value) { + io.enumCase(value, "normal", lld::DefinedAtom::deadStripNormal); + io.enumCase(value, "never", lld::DefinedAtom::deadStripNever); + io.enumCase(value, "always", lld::DefinedAtom::deadStripAlways); + } +}; + +template <> +struct ScalarEnumerationTraits<lld::DefinedAtom::ContentPermissions> { + static void enumeration(IO &io, lld::DefinedAtom::ContentPermissions &value) { + io.enumCase(value, "---", lld::DefinedAtom::perm___); + io.enumCase(value, "r--", lld::DefinedAtom::permR__); + io.enumCase(value, "r-x", lld::DefinedAtom::permR_X); + io.enumCase(value, "rw-", lld::DefinedAtom::permRW_); + io.enumCase(value, "rwx", lld::DefinedAtom::permRWX); + io.enumCase(value, "rw-l", lld::DefinedAtom::permRW_L); + } +}; + +template <> +struct ScalarEnumerationTraits<lld::DefinedAtom::ContentType> { + static void enumeration(IO &io, lld::DefinedAtom::ContentType &value) { + io.enumCase(value, "unknown", + lld::DefinedAtom::typeUnknown); + io.enumCase(value, "code", + lld::DefinedAtom::typeCode); + io.enumCase(value, "stub", + lld::DefinedAtom::typeStub); + io.enumCase(value, "constant", + lld::DefinedAtom::typeConstant); + io.enumCase(value, "data", + lld::DefinedAtom::typeData); + io.enumCase(value, "zero-fill", + lld::DefinedAtom::typeZeroFill); + io.enumCase(value, "got", + lld::DefinedAtom::typeGOT); + io.enumCase(value, "resolver", + lld::DefinedAtom::typeResolver); + io.enumCase(value, "branch-island", + lld::DefinedAtom::typeBranchIsland); + io.enumCase(value, "branch-shim", + lld::DefinedAtom::typeBranchShim); + io.enumCase(value, "stub-helper", + lld::DefinedAtom::typeStubHelper); + io.enumCase(value, "c-string", + lld::DefinedAtom::typeCString); + io.enumCase(value, "utf16-string", + lld::DefinedAtom::typeUTF16String); + io.enumCase(value, "unwind-cfi", + lld::DefinedAtom::typeCFI); + io.enumCase(value, "unwind-lsda", + lld::DefinedAtom::typeLSDA); + io.enumCase(value, "const-4-byte", + lld::DefinedAtom::typeLiteral4); + io.enumCase(value, "const-8-byte", + lld::DefinedAtom::typeLiteral8); + io.enumCase(value, "const-16-byte", + lld::DefinedAtom::typeLiteral16); + io.enumCase(value, "lazy-pointer", + lld::DefinedAtom::typeLazyPointer); + io.enumCase(value, "lazy-dylib-pointer", + lld::DefinedAtom::typeLazyDylibPointer); + io.enumCase(value, "cfstring", + lld::DefinedAtom::typeCFString); + io.enumCase(value, "initializer-pointer", + lld::DefinedAtom::typeInitializerPtr); + io.enumCase(value, "terminator-pointer", + lld::DefinedAtom::typeTerminatorPtr); + io.enumCase(value, "c-string-pointer", + lld::DefinedAtom::typeCStringPtr); + io.enumCase(value, "objc-class-pointer", + lld::DefinedAtom::typeObjCClassPtr); + io.enumCase(value, "objc-category-list", + lld::DefinedAtom::typeObjC2CategoryList); + io.enumCase(value, "objc-class1", + lld::DefinedAtom::typeObjC1Class); + io.enumCase(value, "dtraceDOF", + lld::DefinedAtom::typeDTraceDOF); + io.enumCase(value, "lto-temp", + lld::DefinedAtom::typeTempLTO); + io.enumCase(value, "compact-unwind", + lld::DefinedAtom::typeCompactUnwindInfo); + io.enumCase(value, "tlv-thunk", + lld::DefinedAtom::typeThunkTLV); + io.enumCase(value, "tlv-data", + lld::DefinedAtom::typeTLVInitialData); + io.enumCase(value, "tlv-zero-fill", + lld::DefinedAtom::typeTLVInitialZeroFill); + io.enumCase(value, "tlv-initializer-ptr", + lld::DefinedAtom::typeTLVInitializerPtr); + io.enumCase(value, "first-in-section", + lld::DefinedAtom::typeFirstInSection); + io.enumCase(value, "last-in-section", + lld::DefinedAtom::typeLastInSection); + } +}; + +template <> +struct ScalarEnumerationTraits<lld::UndefinedAtom::CanBeNull> { + static void enumeration(IO &io, lld::UndefinedAtom::CanBeNull &value) { + io.enumCase(value, "never", lld::UndefinedAtom::canBeNullNever); + io.enumCase(value, "at-runtime", lld::UndefinedAtom::canBeNullAtRuntime); + io.enumCase(value, "at-buildtime", lld::UndefinedAtom::canBeNullAtBuildtime); + } +}; + + +template <> +struct ScalarEnumerationTraits<ShlibCanBeNull> { + static void enumeration(IO &io, ShlibCanBeNull &value) { + io.enumCase(value, "never", false); + io.enumCase(value, "at-runtime", true); + } +}; + + + +/// This is a custom formatter for lld::DefinedAtom::Alignment. Values look +/// like: +/// 2^3 # 8-byte aligned +/// 7 mod 2^4 # 16-byte aligned plus 7 bytes +template<> +struct ScalarTraits<lld::DefinedAtom::Alignment> { + static void output(const lld::DefinedAtom::Alignment &value, void *ctxt, + llvm::raw_ostream &out) { + if (value.modulus == 0) { + out << llvm::format("2^%d", value.powerOf2); + } + else { + out << llvm::format("%d mod 2^%d", value.modulus, value.powerOf2); + } + } + + static StringRef input(StringRef scalar, void *ctxt, + lld::DefinedAtom::Alignment &value) { + value.modulus = 0; + size_t modStart = scalar.find("mod"); + if (modStart != StringRef::npos) { + StringRef modStr = scalar.slice(0, modStart); + modStr = modStr.rtrim(); + unsigned int modulus; + if (modStr.getAsInteger(0, modulus)) { + return "malformed alignment modulus"; + } + value.modulus = modulus; + scalar = scalar.drop_front(modStart+3); + scalar = scalar.ltrim(); + } + if (!scalar.startswith("2^")) { + return "malformed alignment"; + } + StringRef powerStr = scalar.drop_front(2); + unsigned int power; + if (powerStr.getAsInteger(0, power)) { + return "malformed alignment power"; + } + value.powerOf2 = power; + if (value.modulus > (1<<value.powerOf2)) { + return "malformed alignment, modulus too large for power"; + } + return StringRef(); // returning empty string means success + } +}; + + + + +template <> +struct ScalarEnumerationTraits<FileKinds> { + static void enumeration(IO &io, FileKinds &value) { + io.enumCase(value, "object", fileKindObjectAtoms); + io.enumCase(value, "archive", fileKindArchive); + io.enumCase(value, "object-elf", fileKindObjectELF); + io.enumCase(value, "object-mach-o", fileKindObjectMachO); + } +}; + +template <> +struct MappingTraits<ArchMember> { + static void mapping(IO &io, ArchMember &member) { + io.mapOptional("kind", member._kind, fileKindObjectAtoms); + io.mapOptional("name", member._name); + io.mapRequired("content", member._content); + } +}; + +LLVM_YAML_IS_SEQUENCE_VECTOR(ArchMember); + + +// Declare that an AtomList is a yaml sequence. +template<typename T> +struct SequenceTraits<AtomList<T>> { + static size_t size(IO &io, AtomList<T> &seq) { + return seq._atoms.size(); + } + static const T *&element(IO &io, AtomList<T> &seq, size_t index) { + if (index >= seq._atoms.size()) + seq._atoms.resize(index+1); + return seq._atoms[index]; + } +}; + +// Used to allow DefinedAtom content bytes to be a flow sequence of +// two-digit hex numbers without the leading 0x (e.g. FF, 04, 0A) +template<> +struct ScalarTraits<ImplicitHex8> { + static void output(const ImplicitHex8 &val, void*, llvm::raw_ostream &out) { + uint8_t num = val; + out << llvm::format("%02X", num); + } + + static llvm::StringRef input(llvm::StringRef str, void*, ImplicitHex8 &val) { + unsigned long long n; + if (getAsUnsignedInteger(str, 16, n)) + return "invalid two-digit-hex number"; + if (n > 0xFF) + return "out of range two-digit-hex number"; + val = n; + return StringRef(); // returning empty string means success + } +}; + +// Always write DefinedAtoms content bytes as a flow sequence. +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(ImplicitHex8); + + +// YAML conversion for std::vector<const lld::File*> +template<> +struct DocumentListTraits< std::vector<const lld::File*> > { + static size_t size(IO &io, std::vector<const lld::File*> &seq) { + return seq.size(); + } + static const lld::File *&element(IO &io, std::vector<const lld::File*> &seq, + size_t index) { + if (index >= seq.size()) + seq.resize(index+1); + return seq[index]; + } +}; + + +// YAML conversion for const lld::File* +template <> +struct MappingTraits<const lld::File*> { + + class NormArchiveFile : public lld::ArchiveLibraryFile { + public: + NormArchiveFile(IO &io) : ArchiveLibraryFile(""), _path() { + } + NormArchiveFile(IO &io, const lld::File *file) + : ArchiveLibraryFile(file->path()), + _path(file->path()) { + // If we want to support writing archives, this constructor would + // need to populate _members. + } + + const lld::File *denormalize(IO &io) { + return this; + } + + virtual void addAtom(const lld::Atom&) { + llvm_unreachable("cannot add atoms to yaml .o files"); + } + virtual const atom_collection<lld::DefinedAtom> &defined() const { + return _noDefinedAtoms; + } + virtual const atom_collection<lld::UndefinedAtom> &undefined() const { + return _noUndefinedAtoms; + } + virtual const atom_collection<lld::SharedLibraryAtom> &sharedLibrary()const{ + return _noSharedLibaryAtoms; + } + virtual const atom_collection<lld::AbsoluteAtom> &absolute() const { + return _noAbsoluteAtoms; + } + virtual const File *find(StringRef name, bool dataSymbolOnly) const { + for (const ArchMember &member : _members) { + for (const lld::DefinedAtom *atom : member._content->defined() ) { + if (name == atom->name()) { + if (!dataSymbolOnly) + return member._content; + switch (atom->contentType()) { + case lld::DefinedAtom::typeData: + case lld::DefinedAtom::typeZeroFill: + return member._content; + default: + break; + } + } + } + } + return nullptr; + } + + StringRef _path; + std::vector<ArchMember> _members; + }; + + + class NormalizedFile : public lld::File { + public: + NormalizedFile(IO &io) : File(""), _rnb(nullptr) { + } + NormalizedFile(IO &io, const lld::File *file) + : File(file->path()), + _rnb(new RefNameBuilder(*file)), + _path(file->path()) { + for (const lld::DefinedAtom *a : file->defined()) + _definedAtoms.push_back(a); + for (const lld::UndefinedAtom *a : file->undefined()) + _undefinedAtoms.push_back(a); + for (const lld::SharedLibraryAtom *a : file->sharedLibrary()) + _sharedLibraryAtoms.push_back(a); + for (const lld::AbsoluteAtom *a : file->absolute()) + _absoluteAtoms.push_back(a); + } + const lld::File *denormalize(IO &io); + + + virtual void addAtom(const lld::Atom&) { + llvm_unreachable("cannot add atoms to yaml .o files"); + } + virtual const atom_collection<lld::DefinedAtom> &defined() const { + return _definedAtoms; + } + virtual const atom_collection<lld::UndefinedAtom> &undefined() const { + return _undefinedAtoms; + } + virtual const atom_collection<lld::SharedLibraryAtom> &sharedLibrary()const{ + return _sharedLibraryAtoms; + } + virtual const atom_collection<lld::AbsoluteAtom> &absolute() const { + return _absoluteAtoms; + } + + // Allocate a new copy of this string and keep track of allocations + // in _stringCopies, so they can be freed when File is destroyed. + StringRef copyString(StringRef str) { + // We want _stringCopies to own the string memory so it is deallocated + // when the File object is destroyed. But we need a StringRef that + // points into that memory. + std::unique_ptr<char> s = std::unique_ptr<char>(new char[str.size()]); + memcpy(s.get(), str.data(), str.size()); + llvm::StringRef r = llvm::StringRef(s.get(), str.size()); + _stringCopies.push_back(std::move(s)); + return r; + } + + RefNameBuilder *_rnb; + StringRef _path; + AtomList<lld::DefinedAtom> _definedAtoms; + AtomList<lld::UndefinedAtom> _undefinedAtoms; + AtomList<lld::SharedLibraryAtom> _sharedLibraryAtoms; + AtomList<lld::AbsoluteAtom> _absoluteAtoms; + std::vector<std::unique_ptr<char>> _stringCopies; + }; + + + static void mapping(IO &io, const lld::File *&file) { + // We only support writing atom based YAML + FileKinds kind = fileKindObjectAtoms; + // If reading, peek ahead to see what kind of file this is. + io.mapOptional("kind", kind, fileKindObjectAtoms); + // + switch (kind) { + case fileKindObjectAtoms: + mappingAtoms(io, file); + break; + case fileKindArchive: + mappingArchive(io, file); + break; + case fileKindObjectELF: + case fileKindObjectMachO: + // Eventually we will have an external function to call, similar + // to mappingAtoms() and mappingArchive() but implememented + // with coresponding file format code. + default: + llvm_unreachable("section based YAML not supported yet"); + } + } + + static void mappingAtoms(IO &io, const lld::File *&file) { + MappingNormalizationHeap<NormalizedFile, const lld::File*> keys(io, file); + ContextInfo *info = reinterpret_cast<ContextInfo*>(io.getContext()); + assert(info != nullptr); + info->_currentFile = keys.operator->(); + + io.mapOptional("path", keys->_path); + io.mapOptional("defined-atoms", keys->_definedAtoms); + io.mapOptional("undefined-atoms", keys->_undefinedAtoms); + io.mapOptional("shared-library-atoms", keys->_sharedLibraryAtoms); + io.mapOptional("absolute-atoms", keys->_absoluteAtoms); + } + + static void mappingArchive(IO &io, const lld::File *&file) { + MappingNormalizationHeap<NormArchiveFile, const lld::File*> keys(io, file); + + io.mapOptional("path", keys->_path); + io.mapOptional("members", keys->_members); + } + +}; + + + +// YAML conversion for const lld::Reference* +template <> +struct MappingTraits<const lld::Reference*> { + + class NormalizedReference : public lld::Reference { + public: + NormalizedReference(IO &io) + : _target(nullptr), _targetName(), _offset(0), _addend(0) , _kind(0) { + } + NormalizedReference(IO &io, const lld::Reference *ref) + : _target(nullptr), + _targetName(targetName(io, ref)), + _offset(ref->offsetInAtom()), + _addend(ref->addend()), + _kind(ref->kind()) { + } + const lld::Reference *denormalize(IO &io) { + ContextInfo *info = reinterpret_cast<ContextInfo*>(io.getContext()); + assert(info != nullptr); + typedef MappingTraits<const lld::File*>::NormalizedFile NormalizedFile; + NormalizedFile *f = reinterpret_cast<NormalizedFile*>(info->_currentFile); + if (!_targetName.empty()) + _targetName = f->copyString(_targetName); + DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs() + << "created Reference to name: '" << _targetName + << "' (" << (void*)_targetName.data() << ", " + << _targetName.size() << ")\n"); + return this; + } + void bind(const RefNameResolver&); + static StringRef targetName(IO &io, const lld::Reference *ref); + + virtual uint64_t offsetInAtom() const { return _offset; } + virtual Kind kind() const { return _kind; } + virtual const lld::Atom *target() const { return _target; } + virtual Addend addend() const { return _addend; } + virtual void setKind(Kind k) { _kind = k; } + virtual void setAddend(Addend a) { _addend = a; } + virtual void setTarget(const lld::Atom *a) { _target = a; } + + const lld::Atom *_target; + StringRef _targetName; + uint32_t _offset; + Addend _addend; + RefKind _kind; + }; + + + static void mapping(IO &io, const lld::Reference *&ref) { + MappingNormalizationHeap<NormalizedReference, + const lld::Reference*> keys(io, ref); + + io.mapRequired("kind", keys->_kind); + io.mapOptional("offset", keys->_offset); + io.mapOptional("target", keys->_targetName); + io.mapOptional("addend", keys->_addend, (lld::Reference::Addend)0); + } +}; + +LLVM_YAML_IS_SEQUENCE_VECTOR(const lld::Reference*) + + +// YAML conversion for const lld::DefinedAtom* +template <> +struct MappingTraits<const lld::DefinedAtom*> { + + class NormalizedAtom : public lld::DefinedAtom { + public: + NormalizedAtom(IO &io) + : _file(fileFromContext(io)), _name(), _refName(), + _alignment(0), _content(), _references() { + } + NormalizedAtom(IO &io, const lld::DefinedAtom *atom) + : _file(fileFromContext(io)), + _name(atom->name()), + _refName(), + _scope(atom->scope()), + _interpose(atom->interposable()), + _merge(atom->merge()), + _contentType(atom->contentType()), + _alignment(atom->alignment()), + _sectionChoice(atom->sectionChoice()), + _deadStrip(atom->deadStrip()), + _permissions(atom->permissions()), + _size(atom->size()), + _sectionName(atom->customSectionName()) { + for ( const lld::Reference *r : *atom ) + _references.push_back(r); + ArrayRef<uint8_t> cont = atom->rawContent(); + _content.reserve(cont.size()); + for (uint8_t x : cont) + _content.push_back(x); + } + const lld::DefinedAtom *denormalize(IO &io) { + ContextInfo *info = reinterpret_cast<ContextInfo*>(io.getContext()); + assert(info != nullptr); + typedef MappingTraits<const lld::File*>::NormalizedFile NormalizedFile; + NormalizedFile *f = reinterpret_cast<NormalizedFile*>(info->_currentFile); + if ( !_name.empty() ) + _name = f->copyString(_name); + if ( !_refName.empty() ) + _refName = f->copyString(_refName); + if ( !_sectionName.empty() ) + _sectionName = f->copyString(_sectionName); + DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs() + << "created DefinedAtom named: '" << _name + << "' (" << (void*)_name.data() << ", " + << _name.size() << ")\n"); + return this; + } + void bind(const RefNameResolver&); + // Extract current File object from YAML I/O parsing context + const lld::File &fileFromContext(IO &io) { + ContextInfo *info = reinterpret_cast<ContextInfo*>(io.getContext()); + assert(info != nullptr); + assert(info->_currentFile != nullptr); + return *info->_currentFile; + } + + virtual const lld::File &file() const { return _file; } + virtual StringRef name() const { return _name; } + virtual uint64_t size() const { return _size; } + virtual Scope scope() const { return _scope; } + virtual Interposable interposable() const { return _interpose; } + virtual Merge merge() const { return _merge; } + virtual ContentType contentType() const { return _contentType; } + virtual Alignment alignment() const { return _alignment; } + virtual SectionChoice sectionChoice() const { return _sectionChoice; } + virtual StringRef customSectionName() const { return _sectionName;} + virtual DeadStripKind deadStrip() const { return _deadStrip; } + virtual ContentPermissions permissions() const { return _permissions; } + virtual bool isThumb() const { return false; } + virtual bool isAlias() const { return false; } + ArrayRef<uint8_t> rawContent() const { + return ArrayRef<uint8_t>((uint8_t*)&_content.operator[](0), + _content.size()); } + virtual uint64_t ordinal() const { return 0; } + + reference_iterator begin() const { + uintptr_t index = 0; + const void *it = reinterpret_cast<const void*>(index); + return reference_iterator(*this, it); + } + reference_iterator end() const { + uintptr_t index = _references.size(); + const void *it = reinterpret_cast<const void*>(index); + return reference_iterator(*this, it); + } + const lld::Reference *derefIterator(const void *it) const { + uintptr_t index = reinterpret_cast<uintptr_t>(it); + assert(index < _references.size()); + return _references[index]; + } + void incrementIterator(const void *&it) const { + uintptr_t index = reinterpret_cast<uintptr_t>(it); + ++index; + it = reinterpret_cast<const void*>(index); + } + + const lld::File &_file; + StringRef _name; + StringRef _refName; + Scope _scope; + Interposable _interpose; + Merge _merge; + ContentType _contentType; + Alignment _alignment; + SectionChoice _sectionChoice; + DeadStripKind _deadStrip; + ContentPermissions _permissions; + std::vector<ImplicitHex8> _content; + uint64_t _size; + StringRef _sectionName; + std::vector<const lld::Reference*> _references; + }; + + static void mapping(IO &io, const lld::DefinedAtom *&atom) { + MappingNormalizationHeap<NormalizedAtom, + const lld::DefinedAtom*> keys(io, atom); + if ( io.outputting() ) { + // If writing YAML, check if atom needs a ref-name. + typedef MappingTraits<const lld::File*>::NormalizedFile NormalizedFile; + ContextInfo *info = reinterpret_cast<ContextInfo*>(io.getContext()); + assert(info != nullptr); + NormalizedFile *f = reinterpret_cast<NormalizedFile*>(info->_currentFile); + assert(f); + assert(f->_rnb); + if ( f->_rnb->hasRefName(atom) ) { + keys->_refName = f->_rnb->refName(atom); + } + } + + io.mapOptional("name", keys->_name, + StringRef()); + io.mapOptional("ref-name", keys->_refName, + StringRef()); + io.mapOptional("scope", keys->_scope, + lld::DefinedAtom::scopeTranslationUnit); + io.mapOptional("type", keys->_contentType, + lld::DefinedAtom::typeCode); + io.mapOptional("content", keys->_content); + io.mapOptional("size", keys->_size, + (uint64_t)keys->_content.size()); + io.mapOptional("interposable", keys->_interpose, + lld::DefinedAtom::interposeNo); + io.mapOptional("merge", keys->_merge, + lld::DefinedAtom::mergeNo); + io.mapOptional("alignment", keys->_alignment, + lld::DefinedAtom::Alignment(0)); + io.mapOptional("section-choice", keys->_sectionChoice, + lld::DefinedAtom::sectionBasedOnContent); + io.mapOptional("section-name", keys->_sectionName, + StringRef()); + io.mapOptional("dead-strip", keys->_deadStrip, + lld::DefinedAtom::deadStripNormal); + io.mapOptional("permissions", keys->_permissions, + lld::DefinedAtom::permR_X); + io.mapOptional("fixups", keys->_references); + } +}; + + + + +inline +const lld::File* +MappingTraits<const lld::File*>::NormalizedFile::denormalize(IO &io) { + typedef MappingTraits<const lld::DefinedAtom*>::NormalizedAtom NormalizedAtom; + + RefNameResolver nameResolver(this, io); + // Now that all atoms are parsed, references can be bound. + for (const lld::DefinedAtom *a : this->defined() ) { + NormalizedAtom *normAtom = (NormalizedAtom*)a; + normAtom->bind(nameResolver); + } + return this; +} + +inline +void MappingTraits<const lld::DefinedAtom*>:: + NormalizedAtom::bind(const RefNameResolver &resolver) { + typedef MappingTraits<const lld::Reference*>::NormalizedReference + NormalizedReference; + for (const lld::Reference *ref : _references) { + NormalizedReference *normRef = (NormalizedReference*)ref; + normRef->bind(resolver); + } +} + +inline +void MappingTraits<const lld::Reference*>:: + NormalizedReference::bind(const RefNameResolver &resolver) { + _target = resolver.lookup(_targetName); +} + + + +// YAML conversion for const lld::UndefinedAtom* +template <> +struct MappingTraits<const lld::UndefinedAtom*> { + + class NormalizedAtom : public lld::UndefinedAtom { + public: + NormalizedAtom(IO &io) + : _file(fileFromContext(io)), _name(), _canBeNull(canBeNullNever) { + } + NormalizedAtom(IO &io, const lld::UndefinedAtom *atom) + : _file(fileFromContext(io)), + _name(atom->name()), + _canBeNull(atom->canBeNull()) { + } + const lld::UndefinedAtom *denormalize(IO &io) { + ContextInfo *info = reinterpret_cast<ContextInfo*>(io.getContext()); + assert(info != nullptr); + typedef MappingTraits<const lld::File*>::NormalizedFile NormalizedFile; + NormalizedFile *f = reinterpret_cast<NormalizedFile*>(info->_currentFile); + if ( !_name.empty() ) + _name = f->copyString(_name); + + DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs() + << "created UndefinedAtom named: '" << _name + << "' (" << (void*)_name.data() << ", " + << _name.size() << ")\n"); + return this; + } + // Extract current File object from YAML I/O parsing context + const lld::File &fileFromContext(IO &io) { + ContextInfo *info = reinterpret_cast<ContextInfo*>(io.getContext()); + assert(info != nullptr); + assert(info->_currentFile != nullptr); + return *info->_currentFile; + } + + virtual const lld::File &file() const { return _file; } + virtual StringRef name() const { return _name; } + virtual CanBeNull canBeNull() const { return _canBeNull; } + + const lld::File &_file; + StringRef _name; + CanBeNull _canBeNull; + }; + + + static void mapping(IO &io, const lld::UndefinedAtom* &atom) { + MappingNormalizationHeap<NormalizedAtom, + const lld::UndefinedAtom*> keys(io, atom); + + io.mapRequired("name", keys->_name); + io.mapOptional("can-be-null", keys->_canBeNull, + lld::UndefinedAtom::canBeNullNever); + } +}; + + +// YAML conversion for const lld::SharedLibraryAtom* +template <> +struct MappingTraits<const lld::SharedLibraryAtom*> { + + class NormalizedAtom : public lld::SharedLibraryAtom { + public: + NormalizedAtom(IO &io) + : _file(fileFromContext(io)), _name(), _loadName(), _canBeNull(false) { + } + NormalizedAtom(IO &io, const lld::SharedLibraryAtom *atom) + : _file(fileFromContext(io)), + _name(atom->name()), + _loadName(atom->loadName()), + _canBeNull(atom->canBeNullAtRuntime()) { + } + const lld::SharedLibraryAtom *denormalize(IO &io) { + ContextInfo *info = reinterpret_cast<ContextInfo*>(io.getContext()); + assert(info != nullptr); + typedef MappingTraits<const lld::File*>::NormalizedFile NormalizedFile; + NormalizedFile *f = reinterpret_cast<NormalizedFile*>(info->_currentFile); + if ( !_name.empty() ) + _name = f->copyString(_name); + if ( !_loadName.empty() ) + _loadName = f->copyString(_loadName); + + DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs() + << "created SharedLibraryAtom named: '" << _name + << "' (" << (void*)_name.data() << ", " + << _name.size() << ")\n"); + return this; + } + // Extract current File object from YAML I/O parsing context + const lld::File &fileFromContext(IO &io) { + ContextInfo *info = reinterpret_cast<ContextInfo*>(io.getContext()); + assert(info != nullptr); + assert(info->_currentFile != nullptr); + return *info->_currentFile; + } + + virtual const lld::File &file() const { return _file; } + virtual StringRef name() const { return _name; } + virtual StringRef loadName() const { return _loadName;} + virtual bool canBeNullAtRuntime() const { return _canBeNull; } + + const lld::File &_file; + StringRef _name; + StringRef _loadName; + ShlibCanBeNull _canBeNull; + }; + + + static void mapping(IO &io, const lld::SharedLibraryAtom *&atom) { + + MappingNormalizationHeap<NormalizedAtom, + const lld::SharedLibraryAtom*> keys(io, atom); + + io.mapRequired("name", keys->_name); + io.mapOptional("load-name", keys->_loadName); + io.mapOptional("can-be-null", keys->_canBeNull, + (ShlibCanBeNull)false); + } +}; + + +// YAML conversion for const lld::AbsoluteAtom* +template <> +struct MappingTraits<const lld::AbsoluteAtom*> { + + class NormalizedAtom : public lld::AbsoluteAtom { + public: + NormalizedAtom(IO &io) + : _file(fileFromContext(io)), _name(), _scope(), _value(0) { + } + NormalizedAtom(IO &io, const lld::AbsoluteAtom *atom) + : _file(fileFromContext(io)), + _name(atom->name()), + _scope(atom->scope()), + _value(atom->value()) { + } + const lld::AbsoluteAtom *denormalize(IO &io) { + ContextInfo *info = reinterpret_cast<ContextInfo*>(io.getContext()); + assert(info != nullptr); + typedef MappingTraits<const lld::File*>::NormalizedFile NormalizedFile; + NormalizedFile *f = reinterpret_cast<NormalizedFile*>(info->_currentFile); + if ( !_name.empty() ) + _name = f->copyString(_name); + + DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs() + << "created AbsoluteAtom named: '" << _name + << "' (" << (void*)_name.data() << ", " + << _name.size() << ")\n"); + return this; + } + // Extract current File object from YAML I/O parsing context + const lld::File &fileFromContext(IO &io) { + ContextInfo *info = reinterpret_cast<ContextInfo*>(io.getContext()); + assert(info != nullptr); + assert(info->_currentFile != nullptr); + return *info->_currentFile; + } + + virtual const lld::File &file() const { return _file; } + virtual StringRef name() const { return _name; } + virtual uint64_t value() const { return _value; } + virtual Scope scope() const { return _scope; } + + const lld::File &_file; + StringRef _name; + StringRef _refName; + Scope _scope; + Hex64 _value; + }; + + + static void mapping(IO &io, const lld::AbsoluteAtom *&atom) { + MappingNormalizationHeap<NormalizedAtom, + const lld::AbsoluteAtom*> keys(io, atom); + + if ( io.outputting() ) { + typedef MappingTraits<const lld::File*>::NormalizedFile NormalizedFile; + ContextInfo *info = reinterpret_cast<ContextInfo*>(io.getContext()); + assert(info != nullptr); + NormalizedFile *f = reinterpret_cast<NormalizedFile*>(info->_currentFile); + assert(f); + assert(f->_rnb); + if ( f->_rnb->hasRefName(atom) ) { + keys->_refName = f->_rnb->refName(atom); + } + } + + io.mapRequired("name", keys->_name); + io.mapOptional("ref-name", keys->_refName, StringRef()); + io.mapOptional("scope", keys->_scope); + io.mapRequired("value", keys->_value); + } +}; + + +RefNameResolver::RefNameResolver(const lld::File *file, IO &io) : _io(io) { + typedef MappingTraits<const lld::DefinedAtom*>::NormalizedAtom NormalizedAtom; + for (const lld::DefinedAtom *a : file->defined() ) { + NormalizedAtom *na = (NormalizedAtom*)a; + if ( na->_refName.empty() ) + add(na->_name, a); + else + add(na->_refName, a); + } + + for (const lld::UndefinedAtom *a : file->undefined() ) + add(a->name(), a); + + for (const lld::SharedLibraryAtom *a : file->sharedLibrary() ) + add(a->name(), a); + + typedef MappingTraits<const lld::AbsoluteAtom*>::NormalizedAtom NormAbsAtom; + for (const lld::AbsoluteAtom *a : file->absolute() ) { + NormAbsAtom *na = (NormAbsAtom*)a; + if ( na->_refName.empty() ) + add(na->_name, a); + else + add(na->_refName, a); + } +} + + +inline +llvm::StringRef MappingTraits<const lld::Reference*>::NormalizedReference:: + targetName(IO &io, const lld::Reference *ref) { + if ( ref->target() == nullptr ) + return llvm::StringRef(); + ContextInfo *info = reinterpret_cast<ContextInfo*>(io.getContext()); + assert(info != nullptr); + typedef MappingTraits<const lld::File*>::NormalizedFile NormalizedFile; + NormalizedFile *f = reinterpret_cast<NormalizedFile*>(info->_currentFile); + RefNameBuilder *rnb = f->_rnb; + if ( rnb->hasRefName(ref->target()) ) + return rnb->refName(ref->target()); + return ref->target()->name(); +} + + + +namespace lld { +namespace yaml { + +class Writer : public lld::Writer { +public: + Writer(const WriterOptionsYAML &options) : _options(options) { + } + + virtual error_code writeFile(const lld::File &file, StringRef outPath) { + // Create stream to path. + std::string errorInfo; + llvm::raw_fd_ostream out(outPath.data(), errorInfo); + if (!errorInfo.empty()) + return llvm::make_error_code(llvm::errc::no_such_file_or_directory); + + // Create yaml Output writer, using yaml options for context. + ContextInfo context(_options); + llvm::yaml::Output yout(out, &context); + + // Write yaml output. + const lld::File *fileRef = &file; + yout << fileRef; + + return error_code::success(); + } + + virtual StubsPass *stubPass() { + return _options.stubPass(); + } + + virtual GOTPass *gotPass() { + return _options.gotPass(); + } + + +private: + const WriterOptionsYAML &_options; +}; + + + +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) { + // Note: we do not take ownership of the MemoryBuffer. That is + // because yaml may produce multiple File objects, so there is no + // *one* File to take ownership. Therefore, the yaml File objects + // produced must make copies of all strings that come from YAML I/O. + // Otherwise the strings will become invalid when this MemoryBuffer + // is deallocated. + + // Create YAML Input parser. + ContextInfo context(_options); + llvm::yaml::Input yin(mb->getBuffer(), &context); + + // Fill vector with File objects created by parsing yaml. + std::vector<const lld::File*> createdFiles; + yin >> createdFiles; + + // Quit now if there were parsing errors. + if ( yin.error() ) + return make_error_code(lld::yaml_reader_error::illegal_value); + + for (const File *file : createdFiles) { + // Note: parseFile() should return vector of *const* File + File *f = const_cast<File*>(file); + result.emplace_back(f); + } + return make_error_code(lld::yaml_reader_error::success); + } + +private: + const ReaderOptionsYAML &_options; +}; + + + +} // namespace yaml + + +Writer *createWriterYAML(const WriterOptionsYAML &options) { + return new lld::yaml::Writer(options); +} + +WriterOptionsYAML::WriterOptionsYAML() { +} + +WriterOptionsYAML::~WriterOptionsYAML() { +} + + + +Reader *createReaderYAML(const ReaderOptionsYAML &options) { + return new lld::yaml::ReaderYAML(options); +} + +ReaderOptionsYAML::ReaderOptionsYAML() { +} + +ReaderOptionsYAML::~ReaderOptionsYAML() { +} + + +} // namespace lld + |

