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

1312 lines
51 KiB
C++

//===- 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/Reader.h"
#include "lld/Core/Reference.h"
#include "lld/Core/Simple.h"
#include "lld/Core/Writer.h"
#include "lld/ReaderWriter/YamlContext.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <string>
#include <system_error>
using llvm::yaml::MappingTraits;
using llvm::yaml::ScalarEnumerationTraits;
using llvm::yaml::ScalarTraits;
using llvm::yaml::IO;
using llvm::yaml::SequenceTraits;
using llvm::yaml::DocumentListTraits;
using namespace lld;
/// 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 {
/// 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) {
// 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++);
StringRef newName = copyString(buffer.str());
_refNames[target] = newName;
DEBUG_WITH_TYPE("WriterYAML",
llvm::dbgs() << "unnamed atom: creating ref-name: '"
<< newName << "' ("
<< (const 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()) {
if (!absAtom->name().empty())
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);
StringRef newName = copyString(buffer.str());
_refNames[&atom] = newName;
DEBUG_WITH_TYPE("WriterYAML",
llvm::dbgs() << "name collsion: creating ref-name: '"
<< newName << "' ("
<< (const 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);
StringRef newName2 = copyString(buffer2.str());
_refNames[prevAtom] = newName2;
DEBUG_WITH_TYPE("WriterYAML",
llvm::dbgs() << "name collsion: creating ref-name: '"
<< newName2 << "' ("
<< (const 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() << "' ("
<< (const void *)atom.name().data()
<< ", " << atom.name().size() << ")\n");
}
}
bool hasRefName(const lld::Atom *atom) { return _refNames.count(atom); }
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 in _storage, so the strings
// can be freed when RefNameBuilder is destroyed.
StringRef copyString(StringRef str) {
char *s = _storage.Allocate<char>(str.size());
memcpy(s, str.data(), str.size());
return StringRef(s, str.size());
}
unsigned int _collisionCount;
unsigned int _unnamedCounter;
NameToAtom _nameMap;
AtomToRefName _refNames;
llvm::BumpPtrAllocator _storage;
};
/// 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(StringRef name) const {
NameToAtom::const_iterator pos = _nameMap.find(name);
if (pos != _nameMap.end())
return pos->second;
_io.setError(Twine("no such atom name: ") + name);
return nullptr;
}
private:
typedef llvm::StringMap<const lld::Atom *> NameToAtom;
void add(StringRef name, const lld::Atom *atom) {
if (_nameMap.count(name)) {
_io.setError(Twine("duplicate atom name: ") + name);
} else {
_nameMap[name] = atom;
}
}
IO &_io;
NameToAtom _nameMap;
};
/// Mapping of Atoms.
template <typename T> class AtomList {
typedef lld::File::AtomVector<T> Ty;
public:
typename Ty::iterator begin() { return _atoms.begin(); }
typename Ty::iterator end() { return _atoms.end(); }
Ty _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
fileKindObjectMachO // mach-o object files encoded in yaml
};
struct ArchMember {
FileKinds _kind;
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 tuple of <namespace, arch, value>.
// For yaml, we just want one string that encapsulates the tuple.
struct RefKind {
Reference::KindNamespace ns;
Reference::KindArch arch;
Reference::KindValue value;
};
} // anonymous namespace
LLVM_YAML_IS_SEQUENCE_VECTOR(ArchMember)
LLVM_YAML_IS_SEQUENCE_VECTOR(const lld::Reference *)
// Always write DefinedAtoms content bytes as a flow sequence.
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(ImplicitHex8)
// for compatibility with gcc-4.7 in C++11 mode, add extra namespace
namespace llvm {
namespace yaml {
// This is a custom formatter for RefKind
template <> struct ScalarTraits<RefKind> {
static void output(const RefKind &kind, void *ctxt, raw_ostream &out) {
assert(ctxt != nullptr);
YamlContext *info = reinterpret_cast<YamlContext *>(ctxt);
assert(info->_registry);
StringRef str;
if (info->_registry->referenceKindToString(kind.ns, kind.arch, kind.value,
str))
out << str;
else
out << (int)(kind.ns) << "-" << (int)(kind.arch) << "-" << kind.value;
}
static StringRef input(StringRef scalar, void *ctxt, RefKind &kind) {
assert(ctxt != nullptr);
YamlContext *info = reinterpret_cast<YamlContext *>(ctxt);
assert(info->_registry);
if (info->_registry->referenceKindFromString(scalar, kind.ns, kind.arch,
kind.value))
return StringRef();
return StringRef("unknown reference kind");
}
static bool mustQuote(StringRef) { return false; }
};
template <> struct ScalarEnumerationTraits<lld::File::Kind> {
static void enumeration(IO &io, lld::File::Kind &value) {
io.enumCase(value, "error-object", lld::File::kindErrorObject);
io.enumCase(value, "object", lld::File::kindMachObject);
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", DefinedAtom::interposeNo);
io.enumCase(value, "yes", DefinedAtom::interposeYes);
io.enumCase(value, "yes-and-weak", 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);
io.enumCase(value, "by-content", lld::DefinedAtom::mergeByContent);
io.enumCase(value, "same-name-and-size",
lld::DefinedAtom::mergeSameNameAndSize);
io.enumCase(value, "largest", lld::DefinedAtom::mergeByLargestSection);
}
};
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::DynamicExport> {
static void enumeration(IO &io, lld::DefinedAtom::DynamicExport &value) {
io.enumCase(value, "normal", lld::DefinedAtom::dynamicExportNormal);
io.enumCase(value, "always", lld::DefinedAtom::dynamicExportAlways);
}
};
template <> struct ScalarEnumerationTraits<lld::DefinedAtom::CodeModel> {
static void enumeration(IO &io, lld::DefinedAtom::CodeModel &value) {
io.enumCase(value, "none", lld::DefinedAtom::codeNA);
io.enumCase(value, "mips-pic", lld::DefinedAtom::codeMipsPIC);
io.enumCase(value, "mips-micro", lld::DefinedAtom::codeMipsMicro);
io.enumCase(value, "mips-micro-pic", lld::DefinedAtom::codeMipsMicroPIC);
io.enumCase(value, "mips-16", lld::DefinedAtom::codeMips16);
io.enumCase(value, "arm-thumb", lld::DefinedAtom::codeARMThumb);
io.enumCase(value, "arm-a", lld::DefinedAtom::codeARM_a);
io.enumCase(value, "arm-d", lld::DefinedAtom::codeARM_d);
io.enumCase(value, "arm-t", lld::DefinedAtom::codeARM_t);
}
};
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);
io.enumCase(value, "unknown", lld::DefinedAtom::permUnknown);
}
};
template <> struct ScalarEnumerationTraits<lld::DefinedAtom::ContentType> {
static void enumeration(IO &io, lld::DefinedAtom::ContentType &value) {
io.enumCase(value, "unknown", DefinedAtom::typeUnknown);
io.enumCase(value, "code", DefinedAtom::typeCode);
io.enumCase(value, "stub", DefinedAtom::typeStub);
io.enumCase(value, "constant", DefinedAtom::typeConstant);
io.enumCase(value, "data", DefinedAtom::typeData);
io.enumCase(value, "quick-data", DefinedAtom::typeDataFast);
io.enumCase(value, "zero-fill", DefinedAtom::typeZeroFill);
io.enumCase(value, "zero-fill-quick", DefinedAtom::typeZeroFillFast);
io.enumCase(value, "const-data", DefinedAtom::typeConstData);
io.enumCase(value, "got", DefinedAtom::typeGOT);
io.enumCase(value, "resolver", DefinedAtom::typeResolver);
io.enumCase(value, "branch-island", DefinedAtom::typeBranchIsland);
io.enumCase(value, "branch-shim", DefinedAtom::typeBranchShim);
io.enumCase(value, "stub-helper", DefinedAtom::typeStubHelper);
io.enumCase(value, "c-string", DefinedAtom::typeCString);
io.enumCase(value, "utf16-string", DefinedAtom::typeUTF16String);
io.enumCase(value, "unwind-cfi", DefinedAtom::typeCFI);
io.enumCase(value, "unwind-lsda", DefinedAtom::typeLSDA);
io.enumCase(value, "const-4-byte", DefinedAtom::typeLiteral4);
io.enumCase(value, "const-8-byte", DefinedAtom::typeLiteral8);
io.enumCase(value, "const-16-byte", DefinedAtom::typeLiteral16);
io.enumCase(value, "lazy-pointer", DefinedAtom::typeLazyPointer);
io.enumCase(value, "lazy-dylib-pointer",
DefinedAtom::typeLazyDylibPointer);
io.enumCase(value, "cfstring", DefinedAtom::typeCFString);
io.enumCase(value, "initializer-pointer",
DefinedAtom::typeInitializerPtr);
io.enumCase(value, "terminator-pointer",
DefinedAtom::typeTerminatorPtr);
io.enumCase(value, "c-string-pointer",DefinedAtom::typeCStringPtr);
io.enumCase(value, "objc-class-pointer",
DefinedAtom::typeObjCClassPtr);
io.enumCase(value, "objc-category-list",
DefinedAtom::typeObjC2CategoryList);
io.enumCase(value, "objc-image-info",
DefinedAtom::typeObjCImageInfo);
io.enumCase(value, "objc-method-list",
DefinedAtom::typeObjCMethodList);
io.enumCase(value, "objc-class1", DefinedAtom::typeObjC1Class);
io.enumCase(value, "dtraceDOF", DefinedAtom::typeDTraceDOF);
io.enumCase(value, "interposing-tuples",
DefinedAtom::typeInterposingTuples);
io.enumCase(value, "lto-temp", DefinedAtom::typeTempLTO);
io.enumCase(value, "compact-unwind", DefinedAtom::typeCompactUnwindInfo);
io.enumCase(value, "unwind-info", DefinedAtom::typeProcessedUnwindInfo);
io.enumCase(value, "tlv-thunk", DefinedAtom::typeThunkTLV);
io.enumCase(value, "tlv-data", DefinedAtom::typeTLVInitialData);
io.enumCase(value, "tlv-zero-fill", DefinedAtom::typeTLVInitialZeroFill);
io.enumCase(value, "tlv-initializer-ptr",
DefinedAtom::typeTLVInitializerPtr);
io.enumCase(value, "mach_header", DefinedAtom::typeMachHeader);
io.enumCase(value, "dso_handle", DefinedAtom::typeDSOHandle);
io.enumCase(value, "sectcreate", DefinedAtom::typeSectCreate);
}
};
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);
}
};
template <>
struct ScalarEnumerationTraits<lld::SharedLibraryAtom::Type> {
static void enumeration(IO &io, lld::SharedLibraryAtom::Type &value) {
io.enumCase(value, "code", lld::SharedLibraryAtom::Type::Code);
io.enumCase(value, "data", lld::SharedLibraryAtom::Type::Data);
io.enumCase(value, "unknown", lld::SharedLibraryAtom::Type::Unknown);
}
};
/// This is a custom formatter for lld::DefinedAtom::Alignment. Values look
/// like:
/// 8 # 8-byte aligned
/// 7 mod 16 # 16-byte aligned plus 7 bytes
template <> struct ScalarTraits<lld::DefinedAtom::Alignment> {
static void output(const lld::DefinedAtom::Alignment &value, void *ctxt,
raw_ostream &out) {
if (value.modulus == 0) {
out << llvm::format("%d", value.value);
} else {
out << llvm::format("%d mod %d", value.modulus, value.value);
}
}
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();
}
unsigned int power;
if (scalar.getAsInteger(0, power)) {
return "malformed alignment power";
}
value.value = power;
if (value.modulus >= power) {
return "malformed alignment, modulus too large for power";
}
return StringRef(); // returning empty string means success
}
static bool mustQuote(StringRef) { return false; }
};
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-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);
}
};
// 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 *, raw_ostream &out) {
uint8_t num = val;
out << llvm::format("%02X", num);
}
static StringRef input(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
}
static bool mustQuote(StringRef) { return false; }
};
// 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; }
const AtomVector<lld::DefinedAtom> &defined() const override {
return _noDefinedAtoms;
}
const AtomVector<lld::UndefinedAtom> &undefined() const override {
return _noUndefinedAtoms;
}
const AtomVector<lld::SharedLibraryAtom> &
sharedLibrary() const override {
return _noSharedLibraryAtoms;
}
const AtomVector<lld::AbsoluteAtom> &absolute() const override {
return _noAbsoluteAtoms;
}
File *find(StringRef name, bool dataSymbolOnly) override {
for (const ArchMember &member : _members) {
for (const lld::DefinedAtom *atom : member._content->defined()) {
if (name == atom->name()) {
if (!dataSymbolOnly)
return const_cast<File *>(member._content);
switch (atom->contentType()) {
case lld::DefinedAtom::typeData:
case lld::DefinedAtom::typeZeroFill:
return const_cast<File *>(member._content);
default:
break;
}
}
}
}
return nullptr;
}
std::error_code
parseAllMembers(std::vector<std::unique_ptr<File>> &result) override {
return std::error_code();
}
StringRef _path;
std::vector<ArchMember> _members;
};
class NormalizedFile : public lld::File {
public:
NormalizedFile(IO &io)
: File("", kindNormalizedObject), _io(io), _rnb(nullptr) {}
NormalizedFile(IO &io, const lld::File *file)
: File(file->path(), kindNormalizedObject), _io(io),
_rnb(new RefNameBuilder(*file)), _path(file->path()) {
for (const lld::DefinedAtom *a : file->defined())
_definedAtoms._atoms.push_back(a);
for (const lld::UndefinedAtom *a : file->undefined())
_undefinedAtoms._atoms.push_back(a);
for (const lld::SharedLibraryAtom *a : file->sharedLibrary())
_sharedLibraryAtoms._atoms.push_back(a);
for (const lld::AbsoluteAtom *a : file->absolute())
_absoluteAtoms._atoms.push_back(a);
}
const lld::File *denormalize(IO &io);
const AtomVector<lld::DefinedAtom> &defined() const override {
return _definedAtoms._atoms;
}
const AtomVector<lld::UndefinedAtom> &undefined() const override {
return _undefinedAtoms._atoms;
}
const AtomVector<lld::SharedLibraryAtom> &
sharedLibrary() const override {
return _sharedLibraryAtoms._atoms;
}
const AtomVector<lld::AbsoluteAtom> &absolute() const override {
return _absoluteAtoms._atoms;
}
// Allocate a new copy of this string in _storage, so the strings
// can be freed when File is destroyed.
StringRef copyString(StringRef str) {
char *s = _storage.Allocate<char>(str.size());
memcpy(s, str.data(), str.size());
return StringRef(s, str.size());
}
IO &_io;
std::unique_ptr<RefNameBuilder> _rnb;
StringRef _path;
AtomList<lld::DefinedAtom> _definedAtoms;
AtomList<lld::UndefinedAtom> _undefinedAtoms;
AtomList<lld::SharedLibraryAtom> _sharedLibraryAtoms;
AtomList<lld::AbsoluteAtom> _absoluteAtoms;
llvm::BumpPtrAllocator _storage;
};
static void mapping(IO &io, const lld::File *&file) {
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
assert(info != nullptr);
// Let any register tag handler process this.
if (info->_registry && info->_registry->handleTaggedDoc(io, file))
return;
// If no registered handler claims this tag and there is no tag,
// grandfather in as "!native".
if (io.mapTag("!native", true) || io.mapTag("tag:yaml.org,2002:map"))
mappingAtoms(io, file);
}
static void mappingAtoms(IO &io, const lld::File *&file) {
MappingNormalizationHeap<NormalizedFile, const lld::File *> keys(io, file);
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
assert(info != nullptr);
info->_file = 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)
: lld::Reference(lld::Reference::KindNamespace::all,
lld::Reference::KindArch::all, 0),
_target(nullptr), _targetName(), _offset(0), _addend(0), _tag(0) {}
NormalizedReference(IO &io, const lld::Reference *ref)
: lld::Reference(ref->kindNamespace(), ref->kindArch(),
ref->kindValue()),
_target(nullptr), _targetName(targetName(io, ref)),
_offset(ref->offsetInAtom()), _addend(ref->addend()),
_tag(ref->tag()) {
_mappedKind.ns = ref->kindNamespace();
_mappedKind.arch = ref->kindArch();
_mappedKind.value = ref->kindValue();
}
const lld::Reference *denormalize(IO &io) {
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
assert(info != nullptr);
typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
if (!_targetName.empty())
_targetName = f->copyString(_targetName);
DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs()
<< "created Reference to name: '"
<< _targetName << "' ("
<< (const void *)_targetName.data()
<< ", " << _targetName.size() << ")\n");
setKindNamespace(_mappedKind.ns);
setKindArch(_mappedKind.arch);
setKindValue(_mappedKind.value);
return this;
}
void bind(const RefNameResolver &);
static StringRef targetName(IO &io, const lld::Reference *ref);
uint64_t offsetInAtom() const override { return _offset; }
const lld::Atom *target() const override { return _target; }
Addend addend() const override { return _addend; }
void setAddend(Addend a) override { _addend = a; }
void setTarget(const lld::Atom *a) override { _target = a; }
const lld::Atom *_target;
StringRef _targetName;
uint32_t _offset;
Addend _addend;
RefKind _mappedKind;
uint32_t _tag;
};
static void mapping(IO &io, const lld::Reference *&ref) {
MappingNormalizationHeap<NormalizedReference, const lld::Reference *> keys(
io, ref);
io.mapRequired("kind", keys->_mappedKind);
io.mapOptional("offset", keys->_offset);
io.mapOptional("target", keys->_targetName);
io.mapOptional("addend", keys->_addend, (lld::Reference::Addend)0);
io.mapOptional("tag", keys->_tag, 0u);
}
};
// 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(), _contentType(),
_alignment(1), _content(), _references() {
static uint32_t ordinalCounter = 1;
_ordinal = ordinalCounter++;
}
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()), _dynamicExport(atom->dynamicExport()),
_codeModel(atom->codeModel()),
_permissions(atom->permissions()), _size(atom->size()),
_sectionName(atom->customSectionName()),
_sectionSize(atom->sectionSize()) {
for (const lld::Reference *r : *atom)
_references.push_back(r);
if (!atom->occupiesDiskSpace())
return;
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) {
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
assert(info != nullptr);
typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
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
<< "' (" << (const 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) {
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
assert(info != nullptr);
assert(info->_file != nullptr);
return *info->_file;
}
const lld::File &file() const override { return _file; }
StringRef name() const override { return _name; }
uint64_t size() const override { return _size; }
Scope scope() const override { return _scope; }
Interposable interposable() const override { return _interpose; }
Merge merge() const override { return _merge; }
ContentType contentType() const override { return _contentType; }
Alignment alignment() const override { return _alignment; }
SectionChoice sectionChoice() const override { return _sectionChoice; }
StringRef customSectionName() const override { return _sectionName; }
uint64_t sectionSize() const override { return _sectionSize; }
DeadStripKind deadStrip() const override { return _deadStrip; }
DynamicExport dynamicExport() const override { return _dynamicExport; }
CodeModel codeModel() const override { return _codeModel; }
ContentPermissions permissions() const override { return _permissions; }
ArrayRef<uint8_t> rawContent() const override {
if (!occupiesDiskSpace())
return ArrayRef<uint8_t>();
return ArrayRef<uint8_t>(
reinterpret_cast<const uint8_t *>(_content.data()), _content.size());
}
uint64_t ordinal() const override { return _ordinal; }
reference_iterator begin() const override {
uintptr_t index = 0;
const void *it = reinterpret_cast<const void *>(index);
return reference_iterator(*this, it);
}
reference_iterator end() const override {
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 override {
uintptr_t index = reinterpret_cast<uintptr_t>(it);
assert(index < _references.size());
return _references[index];
}
void incrementIterator(const void *&it) const override {
uintptr_t index = reinterpret_cast<uintptr_t>(it);
++index;
it = reinterpret_cast<const void *>(index);
}
void addReference(Reference::KindNamespace ns,
Reference::KindArch arch,
Reference::KindValue kindValue, uint64_t off,
const Atom *target, Reference::Addend a) override {
assert(target && "trying to create reference to nothing");
auto node = new (file().allocator()) SimpleReference(ns, arch, kindValue,
off, target, a);
_references.push_back(node);
}
const lld::File &_file;
StringRef _name;
StringRef _refName;
Scope _scope;
Interposable _interpose;
Merge _merge;
ContentType _contentType;
Alignment _alignment;
SectionChoice _sectionChoice;
DeadStripKind _deadStrip;
DynamicExport _dynamicExport;
CodeModel _codeModel;
ContentPermissions _permissions;
uint32_t _ordinal;
std::vector<ImplicitHex8> _content;
uint64_t _size;
StringRef _sectionName;
uint64_t _sectionSize;
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;
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
assert(info != nullptr);
NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
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,
DefinedAtom::scopeTranslationUnit);
io.mapOptional("type", keys->_contentType,
DefinedAtom::typeCode);
io.mapOptional("content", keys->_content);
io.mapOptional("size", keys->_size, (uint64_t)keys->_content.size());
io.mapOptional("interposable", keys->_interpose,
DefinedAtom::interposeNo);
io.mapOptional("merge", keys->_merge, DefinedAtom::mergeNo);
io.mapOptional("alignment", keys->_alignment,
DefinedAtom::Alignment(1));
io.mapOptional("section-choice", keys->_sectionChoice,
DefinedAtom::sectionBasedOnContent);
io.mapOptional("section-name", keys->_sectionName, StringRef());
io.mapOptional("section-size", keys->_sectionSize, (uint64_t)0);
io.mapOptional("dead-strip", keys->_deadStrip,
DefinedAtom::deadStripNormal);
io.mapOptional("dynamic-export", keys->_dynamicExport,
DefinedAtom::dynamicExportNormal);
io.mapOptional("code-model", keys->_codeModel, DefinedAtom::codeNA);
// default permissions based on content type
io.mapOptional("permissions", keys->_permissions,
DefinedAtom::permissions(
keys->_contentType));
io.mapOptional("references", keys->_references);
}
};
// 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) {
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
assert(info != nullptr);
typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
if (!_name.empty())
_name = f->copyString(_name);
DEBUG_WITH_TYPE("WriterYAML",
llvm::dbgs() << "created UndefinedAtom named: '" << _name
<< "' (" << (const void *)_name.data() << ", "
<< _name.size() << ")\n");
return this;
}
// Extract current File object from YAML I/O parsing context
const lld::File &fileFromContext(IO &io) {
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
assert(info != nullptr);
assert(info->_file != nullptr);
return *info->_file;
}
const lld::File &file() const override { return _file; }
StringRef name() const override { return _name; }
CanBeNull canBeNull() const override { 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),
_type(Type::Unknown), _size(0) {}
NormalizedAtom(IO &io, const lld::SharedLibraryAtom *atom)
: _file(fileFromContext(io)), _name(atom->name()),
_loadName(atom->loadName()), _canBeNull(atom->canBeNullAtRuntime()),
_type(atom->type()), _size(atom->size()) {}
const lld::SharedLibraryAtom *denormalize(IO &io) {
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
assert(info != nullptr);
typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
if (!_name.empty())
_name = f->copyString(_name);
if (!_loadName.empty())
_loadName = f->copyString(_loadName);
DEBUG_WITH_TYPE("WriterYAML",
llvm::dbgs() << "created SharedLibraryAtom named: '"
<< _name << "' ("
<< (const void *)_name.data()
<< ", " << _name.size() << ")\n");
return this;
}
// Extract current File object from YAML I/O parsing context
const lld::File &fileFromContext(IO &io) {
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
assert(info != nullptr);
assert(info->_file != nullptr);
return *info->_file;
}
const lld::File &file() const override { return _file; }
StringRef name() const override { return _name; }
StringRef loadName() const override { return _loadName; }
bool canBeNullAtRuntime() const override { return _canBeNull; }
Type type() const override { return _type; }
uint64_t size() const override { return _size; }
const lld::File &_file;
StringRef _name;
StringRef _loadName;
ShlibCanBeNull _canBeNull;
Type _type;
uint64_t _size;
};
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);
io.mapOptional("type", keys->_type, SharedLibraryAtom::Type::Code);
io.mapOptional("size", keys->_size, uint64_t(0));
}
};
// 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) {
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
assert(info != nullptr);
typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
if (!_name.empty())
_name = f->copyString(_name);
DEBUG_WITH_TYPE("WriterYAML",
llvm::dbgs() << "created AbsoluteAtom named: '" << _name
<< "' (" << (const void *)_name.data()
<< ", " << _name.size() << ")\n");
return this;
}
// Extract current File object from YAML I/O parsing context
const lld::File &fileFromContext(IO &io) {
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
assert(info != nullptr);
assert(info->_file != nullptr);
return *info->_file;
}
const lld::File &file() const override { return _file; }
StringRef name() const override { return _name; }
uint64_t value() const override { return _value; }
Scope scope() const override { 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;
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
assert(info != nullptr);
NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
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);
}
};
} // namespace llvm
} // namespace yaml
RefNameResolver::RefNameResolver(const lld::File *file, IO &io) : _io(io) {
typedef MappingTraits<const lld::DefinedAtom *>::NormalizedAtom
NormalizedAtom;
for (const lld::DefinedAtom *a : file->defined()) {
const auto *na = (const NormalizedAtom *)a;
if (!na->_refName.empty())
add(na->_refName, a);
else if (!na->_name.empty())
add(na->_name, 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()) {
const auto *na = (const NormAbsAtom *)a;
if (na->_refName.empty())
add(na->_name, a);
else
add(na->_refName, a);
}
}
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()) {
auto *normAtom = (NormalizedAtom *)const_cast<DefinedAtom *>(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) {
auto *normRef = (NormalizedReference *)const_cast<Reference *>(ref);
normRef->bind(resolver);
}
}
inline void MappingTraits<const lld::Reference *>::NormalizedReference::bind(
const RefNameResolver &resolver) {
_target = resolver.lookup(_targetName);
}
inline StringRef
MappingTraits<const lld::Reference *>::NormalizedReference::targetName(
IO &io, const lld::Reference *ref) {
if (ref->target() == nullptr)
return StringRef();
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
assert(info != nullptr);
typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
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 LinkingContext &context) : _ctx(context) {}
std::error_code writeFile(const lld::File &file, StringRef outPath) override {
// Create stream to path.
std::error_code ec;
llvm::raw_fd_ostream out(outPath, ec, llvm::sys::fs::F_Text);
if (ec)
return ec;
// Create yaml Output writer, using yaml options for context.
YamlContext yamlContext;
yamlContext._ctx = &_ctx;
yamlContext._registry = &_ctx.registry();
llvm::yaml::Output yout(out, &yamlContext);
// Write yaml output.
const lld::File *fileRef = &file;
yout << fileRef;
return std::error_code();
}
private:
const LinkingContext &_ctx;
};
} // end namespace yaml
namespace {
/// Handles !native tagged yaml documents.
class NativeYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler {
bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override {
if (io.mapTag("!native")) {
MappingTraits<const lld::File *>::mappingAtoms(io, file);
return true;
}
return false;
}
};
/// Handles !archive tagged yaml documents.
class ArchiveYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler {
bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override {
if (io.mapTag("!archive")) {
MappingTraits<const lld::File *>::mappingArchive(io, file);
return true;
}
return false;
}
};
class YAMLReader : public Reader {
public:
YAMLReader(const Registry &registry) : _registry(registry) {}
bool canParse(file_magic magic, MemoryBufferRef mb) const override {
StringRef name = mb.getBufferIdentifier();
return name.endswith(".objtxt") || name.endswith(".yaml");
}
ErrorOr<std::unique_ptr<File>>
loadFile(std::unique_ptr<MemoryBuffer> mb,
const class Registry &) const override {
// Create YAML Input Reader.
YamlContext yamlContext;
yamlContext._registry = &_registry;
yamlContext._path = mb->getBufferIdentifier();
llvm::yaml::Input yin(mb->getBuffer(), &yamlContext);
// Fill vector with File objects created by parsing yaml.
std::vector<const lld::File *> createdFiles;
yin >> createdFiles;
assert(createdFiles.size() == 1);
// Error out now if there were parsing errors.
if (yin.error())
return make_error_code(lld::YamlReaderError::illegal_value);
std::shared_ptr<MemoryBuffer> smb(mb.release());
const File *file = createdFiles[0];
// Note: loadFile() should return vector of *const* File
File *f = const_cast<File *>(file);
f->setLastError(std::error_code());
f->setSharedMemoryBuffer(smb);
return std::unique_ptr<File>(f);
}
private:
const Registry &_registry;
};
} // anonymous namespace
void Registry::addSupportYamlFiles() {
add(std::unique_ptr<Reader>(new YAMLReader(*this)));
add(std::unique_ptr<YamlIOTaggedDocumentHandler>(
new NativeYamlIOTaggedDocumentHandler()));
add(std::unique_ptr<YamlIOTaggedDocumentHandler>(
new ArchiveYamlIOTaggedDocumentHandler()));
}
std::unique_ptr<Writer> createWriterYAML(const LinkingContext &context) {
return std::unique_ptr<Writer>(new lld::yaml::Writer(context));
}
} // end namespace lld