| //===- Core/YamlWriter.cpp - Writes YAML ----------------------------------===// |
| // |
| // The LLVM Linker |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "lld/Core/YamlWriter.h" |
| #include "YamlKeyValues.h" |
| #include "lld/Core/Atom.h" |
| #include "lld/Core/File.h" |
| #include "lld/Core/Platform.h" |
| #include "lld/Core/Reference.h" |
| |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/OwningPtr.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/ADT/StringMap.h" |
| #include "llvm/Support/DataTypes.h" |
| #include "llvm/Support/Format.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include "llvm/Support/system_error.h" |
| |
| #include <vector> |
| |
| namespace lld { |
| namespace yaml { |
| |
| namespace { |
| /// |
| /// 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 File& file) |
| : _collisionCount(0), _unnamedCounter(0) { |
| // visit all atoms |
| for( const 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 Reference *ref : *atom) { |
| // create refname for any unnamed reference target |
| if ( ref->target()->name().empty() ) { |
| std::string Storage; |
| llvm::raw_string_ostream Buffer(Storage); |
| Buffer << llvm::format("L%03d", _unnamedCounter++); |
| _refNames[ref->target()] = Buffer.str(); |
| } |
| } |
| } |
| for( const UndefinedAtom *undefAtom : file.undefined() ) { |
| buildDuplicateNameMap(*undefAtom); |
| } |
| for( const SharedLibraryAtom *shlibAtom : file.sharedLibrary() ) { |
| buildDuplicateNameMap(*shlibAtom); |
| } |
| for( const AbsoluteAtom *absAtom : file.absolute() ) { |
| buildDuplicateNameMap(*absAtom); |
| } |
| |
| |
| } |
| |
| void buildDuplicateNameMap(const 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); |
| _refNames[&atom] = Buffer.str(); |
| const Atom* prevAtom = pos->second; |
| AtomToRefName::iterator pos2 = _refNames.find(prevAtom); |
| if ( pos2 == _refNames.end() ) { |
| // only create ref-name for previous if none already created |
| Buffer << prevAtom->name() << llvm::format(".%03d", ++_collisionCount); |
| _refNames[prevAtom] = Buffer.str(); |
| } |
| } |
| else { |
| // First time we've seen this name, just add it to map. |
| _nameMap[atom.name()] = &atom; |
| } |
| } |
| |
| bool hasRefName(const Atom* atom) { |
| return _refNames.count(atom); |
| } |
| |
| StringRef refName(const Atom *atom) { |
| return _refNames.find(atom)->second; |
| } |
| |
| private: |
| typedef llvm::StringMap<const Atom*> NameToAtom; |
| typedef llvm::DenseMap<const Atom*, std::string> AtomToRefName; |
| |
| unsigned int _collisionCount; |
| unsigned int _unnamedCounter; |
| NameToAtom _nameMap; |
| AtomToRefName _refNames; |
| }; |
| |
| |
| /// |
| /// Helper class for writeObjectText() to write out atoms in yaml format. |
| /// |
| class AtomWriter { |
| public: |
| AtomWriter(const File& file, Platform& platform, RefNameBuilder& rnb) |
| : _file(file), _platform(platform), _rnb(rnb), _firstAtom(true) { } |
| |
| |
| void write(raw_ostream &out) { |
| // write header |
| out << "---\n"; |
| |
| // visit all atoms |
| for( const DefinedAtom *atom : _file.defined() ) { |
| writeDefinedAtom(*atom, out); |
| } |
| for( const UndefinedAtom *undefAtom : _file.undefined() ) { |
| writeUndefinedAtom(*undefAtom, out); |
| } |
| for( const SharedLibraryAtom *shlibAtom : _file.sharedLibrary() ) { |
| writeSharedLibraryAtom(*shlibAtom, out); |
| } |
| for( const AbsoluteAtom *absAtom : _file.absolute() ) { |
| writeAbsoluteAtom(*absAtom, out); |
| } |
| |
| out << "...\n"; |
| } |
| |
| |
| void writeDefinedAtom(const DefinedAtom &atom, raw_ostream &out) { |
| if ( _firstAtom ) { |
| out << "atoms:\n"; |
| _firstAtom = false; |
| } |
| else { |
| // add blank line between atoms for readability |
| out << "\n"; |
| } |
| |
| bool hasDash = false; |
| if ( !atom.name().empty() ) { |
| out << " - " |
| << KeyValues::nameKeyword |
| << ":" |
| << spacePadding(KeyValues::nameKeyword) |
| << atom.name() |
| << "\n"; |
| hasDash = true; |
| } |
| |
| if ( _rnb.hasRefName(&atom) ) { |
| out << (hasDash ? " " : " - ") |
| << KeyValues::refNameKeyword |
| << ":" |
| << spacePadding(KeyValues::refNameKeyword) |
| << _rnb.refName(&atom) |
| << "\n"; |
| hasDash = true; |
| } |
| |
| if ( atom.definition() != KeyValues::definitionDefault ) { |
| out << (hasDash ? " " : " - ") |
| << KeyValues::definitionKeyword |
| << ":" |
| << spacePadding(KeyValues::definitionKeyword) |
| << KeyValues::definition(atom.definition()) |
| << "\n"; |
| hasDash = true; |
| } |
| |
| if ( atom.scope() != KeyValues::scopeDefault ) { |
| out << (hasDash ? " " : " - ") |
| << KeyValues::scopeKeyword |
| << ":" |
| << spacePadding(KeyValues::scopeKeyword) |
| << KeyValues::scope(atom.scope()) |
| << "\n"; |
| hasDash = true; |
| } |
| |
| if ( atom.interposable() != KeyValues::interposableDefault ) { |
| out << " " |
| << KeyValues::interposableKeyword |
| << ":" |
| << spacePadding(KeyValues::interposableKeyword) |
| << KeyValues::interposable(atom.interposable()) |
| << "\n"; |
| } |
| |
| if ( atom.merge() != KeyValues::mergeDefault ) { |
| out << " " |
| << KeyValues::mergeKeyword |
| << ":" |
| << spacePadding(KeyValues::mergeKeyword) |
| << KeyValues::merge(atom.merge()) |
| << "\n"; |
| } |
| |
| if ( atom.contentType() != KeyValues::contentTypeDefault ) { |
| out << " " |
| << KeyValues::contentTypeKeyword |
| << ":" |
| << spacePadding(KeyValues::contentTypeKeyword) |
| << KeyValues::contentType(atom.contentType()) |
| << "\n"; |
| } |
| |
| if ( atom.deadStrip() != KeyValues::deadStripKindDefault ) { |
| out << " " |
| << KeyValues::deadStripKindKeyword |
| << ":" |
| << spacePadding(KeyValues::deadStripKindKeyword) |
| << KeyValues::deadStripKind(atom.deadStrip()) |
| << "\n"; |
| } |
| |
| if ( atom.sectionChoice() != KeyValues::sectionChoiceDefault ) { |
| out << " " |
| << KeyValues::sectionChoiceKeyword |
| << ":" |
| << spacePadding(KeyValues::sectionChoiceKeyword) |
| << KeyValues::sectionChoice(atom.sectionChoice()) |
| << "\n"; |
| assert( ! atom.customSectionName().empty() ); |
| out << " " |
| << KeyValues::sectionNameKeyword |
| << ":" |
| << spacePadding(KeyValues::sectionNameKeyword) |
| << atom.customSectionName() |
| << "\n"; |
| } |
| |
| if ( atom.isThumb() != KeyValues::isThumbDefault ) { |
| out << " " |
| << KeyValues::isThumbKeyword |
| << ":" |
| << spacePadding(KeyValues::isThumbKeyword) |
| << KeyValues::isThumb(atom.isThumb()) |
| << "\n"; |
| } |
| |
| if ( atom.isAlias() != KeyValues::isAliasDefault ) { |
| out << " " |
| << KeyValues::isAliasKeyword |
| << ":" |
| << spacePadding(KeyValues::isAliasKeyword) |
| << KeyValues::isAlias(atom.isAlias()) |
| << "\n"; |
| } |
| |
| if ( (atom.contentType() != DefinedAtom::typeZeroFill) |
| && (atom.size() != 0) ) { |
| out << " " |
| << KeyValues::contentKeyword |
| << ":" |
| << spacePadding(KeyValues::contentKeyword) |
| << "[ "; |
| ArrayRef<uint8_t> arr = atom.rawContent(); |
| bool needComma = false; |
| for (unsigned int i=0; i < arr.size(); ++i) { |
| if ( needComma ) |
| out << ", "; |
| if ( ((i % 12) == 0) && (i != 0) ) { |
| out << "\n "; |
| } |
| out << hexdigit(arr[i] >> 4); |
| out << hexdigit(arr[i] & 0x0F); |
| needComma = true; |
| } |
| out << " ]\n"; |
| } |
| |
| bool wroteFirstFixup = false; |
| for (const Reference *ref : atom) { |
| if ( !wroteFirstFixup ) { |
| out << " fixups:\n"; |
| wroteFirstFixup = true; |
| } |
| out << " - " |
| << KeyValues::fixupsOffsetKeyword |
| << ":" |
| << spacePadding(KeyValues::fixupsOffsetKeyword) |
| << ref->offsetInAtom() |
| << "\n"; |
| out << " " |
| << KeyValues::fixupsKindKeyword |
| << ":" |
| << spacePadding(KeyValues::fixupsKindKeyword) |
| << _platform.kindToString(ref->kind()) |
| << "\n"; |
| const Atom* target = ref->target(); |
| if (target != nullptr) { |
| StringRef refName = target->name(); |
| if ( _rnb.hasRefName(target) ) |
| refName = _rnb.refName(target); |
| assert(!refName.empty()); |
| out << " " |
| << KeyValues::fixupsTargetKeyword |
| << ":" |
| << spacePadding(KeyValues::fixupsTargetKeyword) |
| << refName |
| << "\n"; |
| } |
| if ( ref->addend() != 0 ) { |
| out << " " |
| << KeyValues::fixupsAddendKeyword |
| << ":" |
| << spacePadding(KeyValues::fixupsAddendKeyword) |
| << ref->addend() |
| << "\n"; |
| } |
| } |
| } |
| |
| |
| void writeUndefinedAtom(const UndefinedAtom &atom, raw_ostream &out) { |
| if ( _firstAtom ) { |
| out << "atoms:\n"; |
| _firstAtom = false; |
| } |
| else { |
| // add blank line between atoms for readability |
| out << "\n"; |
| } |
| |
| out << " - " |
| << KeyValues::nameKeyword |
| << ":" |
| << spacePadding(KeyValues::nameKeyword) |
| << atom.name() |
| << "\n"; |
| |
| out << " " |
| << KeyValues::definitionKeyword |
| << ":" |
| << spacePadding(KeyValues::definitionKeyword) |
| << KeyValues::definition(atom.definition()) |
| << "\n"; |
| |
| if ( atom.canBeNull() != KeyValues::canBeNullDefault ) { |
| out << " " |
| << KeyValues::canBeNullKeyword |
| << ":" |
| << spacePadding(KeyValues::canBeNullKeyword) |
| << KeyValues::canBeNull(atom.canBeNull()) |
| << "\n"; |
| } |
| } |
| |
| void writeSharedLibraryAtom(const SharedLibraryAtom &atom, raw_ostream &out) { |
| if ( _firstAtom ) { |
| out << "atoms:\n"; |
| _firstAtom = false; |
| } |
| else { |
| // add blank line between atoms for readability |
| out << "\n"; |
| } |
| |
| out << " - " |
| << KeyValues::nameKeyword |
| << ":" |
| << spacePadding(KeyValues::nameKeyword) |
| << atom.name() |
| << "\n"; |
| |
| out << " " |
| << KeyValues::definitionKeyword |
| << ":" |
| << spacePadding(KeyValues::definitionKeyword) |
| << KeyValues::definition(atom.definition()) |
| << "\n"; |
| |
| if ( !atom.loadName().empty() ) { |
| out << " " |
| << KeyValues::loadNameKeyword |
| << ":" |
| << spacePadding(KeyValues::loadNameKeyword) |
| << atom.loadName() |
| << "\n"; |
| } |
| |
| if ( atom.canBeNullAtRuntime() ) { |
| out << " " |
| << KeyValues::canBeNullKeyword |
| << ":" |
| << spacePadding(KeyValues::canBeNullKeyword) |
| << KeyValues::canBeNull(UndefinedAtom::canBeNullAtRuntime) |
| << "\n"; |
| } |
| } |
| |
| void writeAbsoluteAtom(const AbsoluteAtom &atom, raw_ostream &out) { |
| if ( _firstAtom ) { |
| out << "atoms:\n"; |
| _firstAtom = false; |
| } |
| else { |
| // add blank line between atoms for readability |
| out << "\n"; |
| } |
| |
| out << " - " |
| << KeyValues::nameKeyword |
| << ":" |
| << spacePadding(KeyValues::nameKeyword) |
| << atom.name() |
| << "\n"; |
| |
| out << " " |
| << KeyValues::definitionKeyword |
| << ":" |
| << spacePadding(KeyValues::definitionKeyword) |
| << KeyValues::definition(atom.definition()) |
| << "\n"; |
| |
| out << " " |
| << KeyValues::valueKeyword |
| << ":" |
| << spacePadding(KeyValues::valueKeyword) |
| << "0x"; |
| out.write_hex(atom.value()); |
| out << "\n"; |
| } |
| |
| |
| private: |
| // return a string of the correct number of spaces to align value |
| const char* spacePadding(const char* key) { |
| const char* spaces = " "; |
| assert(strlen(spaces) > strlen(key)); |
| return &spaces[strlen(key)]; |
| } |
| |
| char hexdigit(uint8_t nibble) { |
| if ( nibble < 0x0A ) |
| return '0' + nibble; |
| else |
| return 'A' + nibble - 0x0A; |
| } |
| |
| const File& _file; |
| Platform& _platform; |
| RefNameBuilder& _rnb; |
| bool _firstAtom; |
| }; |
| |
| } // anonymous namespace |
| |
| |
| |
| /// |
| /// writeObjectText - writes the lld::File object as in YAML |
| /// format to the specified stream. |
| /// |
| void writeObjectText(const File &file, Platform &platform, raw_ostream &out) { |
| // Figure what ref-name labels are needed |
| RefNameBuilder rnb(file); |
| |
| // Write out all atoms |
| AtomWriter writer(file, platform, rnb); |
| writer.write(out); |
| } |
| |
| } // namespace yaml |
| } // namespace lld |