Major refactoring: Remove Platform concept. In its place there are
now Reader and Writer subclasses for each file format. Each Reader and
Writer subclass defines an "options" class which controls how that Reader
or Writer operates.
llvm-svn: 157774
diff --git a/lld/lib/ReaderWriter/YAML/WriterYAML.cpp b/lld/lib/ReaderWriter/YAML/WriterYAML.cpp
new file mode 100644
index 0000000..7b69f78
--- /dev/null
+++ b/lld/lib/ReaderWriter/YAML/WriterYAML.cpp
@@ -0,0 +1,498 @@
+//===- lib/ReaderWriter/YAML/WriterYAML.cpp - Writes 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/WriterYAML.h"
+
+#include "lld/Core/Atom.h"
+#include "lld/Core/File.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 "YamlKeyValues.h"
+
+#include <vector>
+
+namespace lld {
+namespace yaml {
+
+///
+/// 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
+ const 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++);
+ _refNames[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, const WriterOptionsYAML &options,
+ RefNameBuilder& rnb)
+ : _file(file), _options(options), _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 << " - "
+ << "name:"
+ << spacePadding(strlen("name"))
+ << atom.name()
+ << "\n";
+ hasDash = true;
+ }
+
+ if ( _rnb.hasRefName(&atom) ) {
+ out << (hasDash ? " " : " - ")
+ << "ref-name:"
+ << spacePadding(strlen("ref-name"))
+ << _rnb.refName(&atom)
+ << "\n";
+ hasDash = true;
+ }
+
+ if ( atom.definition() != KeyValues::definitionDefault ) {
+ out << (hasDash ? " " : " - ")
+ << "definition:"
+ << spacePadding(strlen("definition"))
+ << KeyValues::definition(atom.definition())
+ << "\n";
+ hasDash = true;
+ }
+
+ if ( atom.scope() != KeyValues::scopeDefault ) {
+ out << (hasDash ? " " : " - ")
+ << "scope:"
+ << spacePadding(strlen("scope"))
+ << KeyValues::scope(atom.scope())
+ << "\n";
+ hasDash = true;
+ }
+
+ if ( atom.interposable() != KeyValues::interposableDefault ) {
+ out << " "
+ << "interposable:"
+ << spacePadding(strlen("interposable"))
+ << KeyValues::interposable(atom.interposable())
+ << "\n";
+ }
+
+ if ( atom.merge() != KeyValues::mergeDefault ) {
+ out << " "
+ << "merge:"
+ << spacePadding(strlen("merge"))
+ << KeyValues::merge(atom.merge())
+ << "\n";
+ }
+
+ if ( atom.contentType() != KeyValues::contentTypeDefault ) {
+ out << " "
+ << "type:"
+ << spacePadding(strlen("type"))
+ << KeyValues::contentType(atom.contentType())
+ << "\n";
+ }
+
+ if ( atom.deadStrip() != KeyValues::deadStripKindDefault ) {
+ out << " "
+ << "dead-strip:"
+ << spacePadding(strlen("dead-strip"))
+ << KeyValues::deadStripKind(atom.deadStrip())
+ << "\n";
+ }
+
+ if ( atom.sectionChoice() != KeyValues::sectionChoiceDefault ) {
+ out << " "
+ << "section-choice:"
+ << spacePadding(strlen("section-choice"))
+ << KeyValues::sectionChoice(atom.sectionChoice())
+ << "\n";
+ assert( ! atom.customSectionName().empty() );
+ out << " "
+ << "section-name:"
+ << spacePadding(strlen("section-name"))
+ << atom.customSectionName()
+ << "\n";
+ }
+
+ if ( atom.isThumb() != KeyValues::isThumbDefault ) {
+ out << " "
+ << "is-thumb:"
+ << spacePadding(strlen("is-thumb"))
+ << KeyValues::isThumb(atom.isThumb())
+ << "\n";
+ }
+
+ if ( atom.isAlias() != KeyValues::isAliasDefault ) {
+ out << " "
+ << "is-alias:"
+ << spacePadding(strlen("is-alias"))
+ << KeyValues::isAlias(atom.isAlias())
+ << "\n";
+ }
+
+ if ( (atom.contentType() != DefinedAtom::typeZeroFill)
+ && (atom.size() != 0) ) {
+ out << " "
+ << "content:"
+ << spacePadding(strlen("content"))
+ << "[ ";
+ 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 << " - "
+ << "offset:"
+ << spacePadding(strlen("offset"))
+ << ref->offsetInAtom()
+ << "\n";
+ out << " "
+ << "kind:"
+ << spacePadding(strlen("kind"))
+ << _options.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 << " "
+ << "target:"
+ << spacePadding(strlen("target"))
+ << refName
+ << "\n";
+ }
+ if ( ref->addend() != 0 ) {
+ out << " "
+ << "addend:"
+ << spacePadding(strlen("addend"))
+ << 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 << " - "
+ << "name:"
+ << spacePadding(strlen("name"))
+ << atom.name()
+ << "\n";
+
+ out << " "
+ << "definition:"
+ << spacePadding(strlen("definition"))
+ << KeyValues::definition(atom.definition())
+ << "\n";
+
+ if ( atom.canBeNull() != KeyValues::canBeNullDefault ) {
+ out << " "
+ << "can-be-null:"
+ << spacePadding(strlen("can-be-null"))
+ << 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 << " - "
+ << "name:"
+ << spacePadding(strlen("name"))
+ << atom.name()
+ << "\n";
+
+ out << " "
+ << "definition:"
+ << spacePadding(strlen("definition"))
+ << KeyValues::definition(atom.definition())
+ << "\n";
+
+ if ( !atom.loadName().empty() ) {
+ out << " "
+ << "load-name:"
+ << spacePadding(strlen("load-name"))
+ << atom.loadName()
+ << "\n";
+ }
+
+ if ( atom.canBeNullAtRuntime() ) {
+ out << " "
+ << "can-be-null:"
+ << spacePadding(strlen("can-be-null"))
+ << 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 << " - "
+ << "name:"
+ << spacePadding(strlen("name"))
+ << atom.name()
+ << "\n";
+
+ out << " "
+ << "definition:"
+ << spacePadding(strlen("definition"))
+ << KeyValues::definition(atom.definition())
+ << "\n";
+
+ out << " "
+ << "value:"
+ << spacePadding(strlen("value"))
+ << "0x";
+ out.write_hex(atom.value());
+ out << "\n";
+ }
+
+
+private:
+ // return a string of the correct number of spaces to align value
+ const char* spacePadding(size_t keyLen) {
+ const char* spaces = " ";
+ assert(strlen(spaces) > keyLen);
+ return &spaces[keyLen];
+ }
+
+ char hexdigit(uint8_t nibble) {
+ if ( nibble < 0x0A )
+ return '0' + nibble;
+ else
+ return 'A' + nibble - 0x0A;
+ }
+
+ const File &_file;
+ const WriterOptionsYAML &_options;
+ RefNameBuilder &_rnb;
+ bool _firstAtom;
+};
+
+
+
+
+class Writer : public lld::Writer {
+public:
+ Writer(const WriterOptionsYAML &options) : _options(options) {
+ }
+
+ virtual error_code writeFile(const lld::File &file, StringRef path) {
+ // Create stream to path.
+ std::string errorInfo;
+ llvm::raw_fd_ostream out(path.data(), errorInfo);
+ if (!errorInfo.empty())
+ return llvm::make_error_code(llvm::errc::no_such_file_or_directory);
+
+ // Figure what ref-name labels are needed.
+ RefNameBuilder rnb(file);
+
+ // Write out all atoms.
+ AtomWriter writer(file, _options, rnb);
+ writer.write(out);
+ return error_code::success();
+ }
+
+ virtual StubsPass *stubPass() {
+ return _options.stubPass();
+ }
+
+ virtual GOTPass *gotPass() {
+ return _options.gotPass();
+ }
+
+
+private:
+ const WriterOptionsYAML &_options;
+};
+
+
+} // namespace yaml
+
+
+Writer* createWriterYAML(const WriterOptionsYAML &options) {
+ return new lld::yaml::Writer(options);
+}
+
+WriterOptionsYAML::WriterOptionsYAML() {
+}
+
+WriterOptionsYAML::~WriterOptionsYAML() {
+}
+
+
+} // namespace lld