forked from OSchip/llvm-project
491 lines
14 KiB
C++
491 lines
14 KiB
C++
//===- 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
|
|
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, 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
|