llvm-project/lld/lib/ReaderWriter/YAML/WriterYAML.cpp

498 lines
14 KiB
C++

//===- 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";
}
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