//===- lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp -----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

///
/// \file For mach-o object files, this implementation uses YAML I/O to
/// provide the convert between YAML and the normalized mach-o (NM).
///
///                  +------------+         +------+
///                  | normalized |   <->   | yaml |
///                  +------------+         +------+

#include "MachONormalizedFile.h"
#include "lld/Common/LLVM.h"
#include "lld/Core/Error.h"
#include "lld/ReaderWriter/YamlContext.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include <system_error>

using llvm::StringRef;
using namespace llvm::yaml;
using namespace llvm::MachO;
using namespace lld::mach_o::normalized;
using lld::YamlContext;

LLVM_YAML_IS_SEQUENCE_VECTOR(Segment)
LLVM_YAML_IS_SEQUENCE_VECTOR(DependentDylib)
LLVM_YAML_IS_SEQUENCE_VECTOR(RebaseLocation)
LLVM_YAML_IS_SEQUENCE_VECTOR(BindLocation)
LLVM_YAML_IS_SEQUENCE_VECTOR(Export)
LLVM_YAML_IS_SEQUENCE_VECTOR(DataInCode)


// for compatibility with gcc-4.7 in C++11 mode, add extra namespace
namespace llvm {
namespace yaml {

// A vector of Sections is a sequence.
template<>
struct SequenceTraits< std::vector<Section> > {
  static size_t size(IO &io, std::vector<Section> &seq) {
    return seq.size();
  }
  static Section& element(IO &io, std::vector<Section> &seq, size_t index) {
    if ( index >= seq.size() )
      seq.resize(index+1);
    return seq[index];
  }
};

template<>
struct SequenceTraits< std::vector<Symbol> > {
  static size_t size(IO &io, std::vector<Symbol> &seq) {
    return seq.size();
  }
  static Symbol& element(IO &io, std::vector<Symbol> &seq, size_t index) {
    if ( index >= seq.size() )
      seq.resize(index+1);
    return seq[index];
  }
};

// A vector of Relocations is a sequence.
template<>
struct SequenceTraits< Relocations > {
  static size_t size(IO &io, Relocations &seq) {
    return seq.size();
  }
  static Relocation& element(IO &io, Relocations &seq, size_t index) {
    if ( index >= seq.size() )
      seq.resize(index+1);
    return seq[index];
  }
};

// The content for a section is represented as a flow sequence of hex bytes.
template<>
struct SequenceTraits< ContentBytes > {
  static size_t size(IO &io, ContentBytes &seq) {
    return seq.size();
  }
  static Hex8& element(IO &io, ContentBytes &seq, size_t index) {
    if ( index >= seq.size() )
      seq.resize(index+1);
    return seq[index];
  }
  static const bool flow = true;
};

// The indirect symbols for a section is represented as a flow sequence
// of numbers (symbol table indexes).
template<>
struct SequenceTraits< IndirectSymbols > {
  static size_t size(IO &io, IndirectSymbols &seq) {
    return seq.size();
  }
  static uint32_t& element(IO &io, IndirectSymbols &seq, size_t index) {
    if ( index >= seq.size() )
      seq.resize(index+1);
    return seq[index];
  }
  static const bool flow = true;
};

template <>
struct ScalarEnumerationTraits<lld::MachOLinkingContext::Arch> {
  static void enumeration(IO &io, lld::MachOLinkingContext::Arch &value) {
    io.enumCase(value, "unknown",lld::MachOLinkingContext::arch_unknown);
    io.enumCase(value, "ppc",    lld::MachOLinkingContext::arch_ppc);
    io.enumCase(value, "x86",    lld::MachOLinkingContext::arch_x86);
    io.enumCase(value, "x86_64", lld::MachOLinkingContext::arch_x86_64);
    io.enumCase(value, "armv6",  lld::MachOLinkingContext::arch_armv6);
    io.enumCase(value, "armv7",  lld::MachOLinkingContext::arch_armv7);
    io.enumCase(value, "armv7s", lld::MachOLinkingContext::arch_armv7s);
    io.enumCase(value, "arm64",  lld::MachOLinkingContext::arch_arm64);
  }
};

template <>
struct ScalarEnumerationTraits<lld::MachOLinkingContext::OS> {
  static void enumeration(IO &io, lld::MachOLinkingContext::OS &value) {
    io.enumCase(value, "unknown",
                          lld::MachOLinkingContext::OS::unknown);
    io.enumCase(value, "Mac OS X",
                          lld::MachOLinkingContext::OS::macOSX);
    io.enumCase(value, "iOS",
                          lld::MachOLinkingContext::OS::iOS);
    io.enumCase(value, "iOS Simulator",
                          lld::MachOLinkingContext::OS::iOS_simulator);
  }
};


template <>
struct ScalarEnumerationTraits<HeaderFileType> {
  static void enumeration(IO &io, HeaderFileType &value) {
    io.enumCase(value, "MH_OBJECT",   llvm::MachO::MH_OBJECT);
    io.enumCase(value, "MH_DYLIB",    llvm::MachO::MH_DYLIB);
    io.enumCase(value, "MH_EXECUTE",  llvm::MachO::MH_EXECUTE);
    io.enumCase(value, "MH_BUNDLE",   llvm::MachO::MH_BUNDLE);
  }
};


template <>
struct ScalarBitSetTraits<FileFlags> {
  static void bitset(IO &io, FileFlags &value) {
    io.bitSetCase(value, "MH_TWOLEVEL",
                          llvm::MachO::MH_TWOLEVEL);
    io.bitSetCase(value, "MH_SUBSECTIONS_VIA_SYMBOLS",
                          llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
  }
};


template <>
struct ScalarEnumerationTraits<SectionType> {
  static void enumeration(IO &io, SectionType &value) {
    io.enumCase(value, "S_REGULAR",
                        llvm::MachO::S_REGULAR);
    io.enumCase(value, "S_ZEROFILL",
                        llvm::MachO::S_ZEROFILL);
    io.enumCase(value, "S_CSTRING_LITERALS",
                        llvm::MachO::S_CSTRING_LITERALS);
    io.enumCase(value, "S_4BYTE_LITERALS",
                        llvm::MachO::S_4BYTE_LITERALS);
    io.enumCase(value, "S_8BYTE_LITERALS",
                        llvm::MachO::S_8BYTE_LITERALS);
    io.enumCase(value, "S_LITERAL_POINTERS",
                        llvm::MachO::S_LITERAL_POINTERS);
    io.enumCase(value, "S_NON_LAZY_SYMBOL_POINTERS",
                        llvm::MachO::S_NON_LAZY_SYMBOL_POINTERS);
    io.enumCase(value, "S_LAZY_SYMBOL_POINTERS",
                        llvm::MachO::S_LAZY_SYMBOL_POINTERS);
    io.enumCase(value, "S_SYMBOL_STUBS",
                        llvm::MachO::S_SYMBOL_STUBS);
    io.enumCase(value, "S_MOD_INIT_FUNC_POINTERS",
                        llvm::MachO::S_MOD_INIT_FUNC_POINTERS);
    io.enumCase(value, "S_MOD_TERM_FUNC_POINTERS",
                        llvm::MachO::S_MOD_TERM_FUNC_POINTERS);
    io.enumCase(value, "S_COALESCED",
                        llvm::MachO::S_COALESCED);
    io.enumCase(value, "S_GB_ZEROFILL",
                        llvm::MachO::S_GB_ZEROFILL);
    io.enumCase(value, "S_INTERPOSING",
                        llvm::MachO::S_INTERPOSING);
    io.enumCase(value, "S_16BYTE_LITERALS",
                        llvm::MachO::S_16BYTE_LITERALS);
    io.enumCase(value, "S_DTRACE_DOF",
                        llvm::MachO::S_DTRACE_DOF);
    io.enumCase(value, "S_LAZY_DYLIB_SYMBOL_POINTERS",
                        llvm::MachO::S_LAZY_DYLIB_SYMBOL_POINTERS);
    io.enumCase(value, "S_THREAD_LOCAL_REGULAR",
                        llvm::MachO::S_THREAD_LOCAL_REGULAR);
    io.enumCase(value, "S_THREAD_LOCAL_ZEROFILL",
                        llvm::MachO::S_THREAD_LOCAL_ZEROFILL);
    io.enumCase(value, "S_THREAD_LOCAL_VARIABLES",
                        llvm::MachO::S_THREAD_LOCAL_VARIABLES);
    io.enumCase(value, "S_THREAD_LOCAL_VARIABLE_POINTERS",
                        llvm::MachO::S_THREAD_LOCAL_VARIABLE_POINTERS);
    io.enumCase(value, "S_THREAD_LOCAL_INIT_FUNCTION_POINTERS",
                        llvm::MachO::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS);
  }
};

template <>
struct ScalarBitSetTraits<SectionAttr> {
  static void bitset(IO &io, SectionAttr &value) {
    io.bitSetCase(value, "S_ATTR_PURE_INSTRUCTIONS",
                          llvm::MachO::S_ATTR_PURE_INSTRUCTIONS);
    io.bitSetCase(value, "S_ATTR_SOME_INSTRUCTIONS",
                          llvm::MachO::S_ATTR_SOME_INSTRUCTIONS);
    io.bitSetCase(value, "S_ATTR_NO_DEAD_STRIP",
                          llvm::MachO::S_ATTR_NO_DEAD_STRIP);
    io.bitSetCase(value, "S_ATTR_EXT_RELOC",
                          llvm::MachO::S_ATTR_EXT_RELOC);
    io.bitSetCase(value, "S_ATTR_LOC_RELOC",
                          llvm::MachO::S_ATTR_LOC_RELOC);
    io.bitSetCase(value, "S_ATTR_DEBUG",
                         llvm::MachO::S_ATTR_DEBUG);
  }
};

/// This is a custom formatter for SectionAlignment.  Values are
/// the power to raise by, ie, the n in 2^n.
template <> struct ScalarTraits<SectionAlignment> {
  static void output(const SectionAlignment &value, void *ctxt,
                     raw_ostream &out) {
    out << llvm::format("%d", (uint32_t)value);
  }

  static StringRef input(StringRef scalar, void *ctxt,
                         SectionAlignment &value) {
    uint32_t alignment;
    if (scalar.getAsInteger(0, alignment)) {
      return "malformed alignment value";
    }
    if (!llvm::isPowerOf2_32(alignment))
      return "alignment must be a power of 2";
    value = alignment;
    return StringRef(); // returning empty string means success
  }

  static QuotingType mustQuote(StringRef) { return QuotingType::None; }
};

template <>
struct ScalarEnumerationTraits<NListType> {
  static void enumeration(IO &io, NListType &value) {
    io.enumCase(value, "N_UNDF",  llvm::MachO::N_UNDF);
    io.enumCase(value, "N_ABS",   llvm::MachO::N_ABS);
    io.enumCase(value, "N_SECT",  llvm::MachO::N_SECT);
    io.enumCase(value, "N_PBUD",  llvm::MachO::N_PBUD);
    io.enumCase(value, "N_INDR",  llvm::MachO::N_INDR);
  }
};

template <>
struct ScalarBitSetTraits<SymbolScope> {
  static void bitset(IO &io, SymbolScope &value) {
    io.bitSetCase(value, "N_EXT",   llvm::MachO::N_EXT);
    io.bitSetCase(value, "N_PEXT",  llvm::MachO::N_PEXT);
  }
};

template <>
struct ScalarBitSetTraits<SymbolDesc> {
  static void bitset(IO &io, SymbolDesc &value) {
    io.bitSetCase(value, "N_NO_DEAD_STRIP",   llvm::MachO::N_NO_DEAD_STRIP);
    io.bitSetCase(value, "N_WEAK_REF",        llvm::MachO::N_WEAK_REF);
    io.bitSetCase(value, "N_WEAK_DEF",        llvm::MachO::N_WEAK_DEF);
    io.bitSetCase(value, "N_ARM_THUMB_DEF",   llvm::MachO::N_ARM_THUMB_DEF);
    io.bitSetCase(value, "N_SYMBOL_RESOLVER", llvm::MachO::N_SYMBOL_RESOLVER);
  }
};


template <>
struct MappingTraits<Section> {
  struct NormalizedContentBytes;
  static void mapping(IO &io, Section &sect) {
    io.mapRequired("segment",         sect.segmentName);
    io.mapRequired("section",         sect.sectionName);
    io.mapRequired("type",            sect.type);
    io.mapOptional("attributes",      sect.attributes);
    io.mapOptional("alignment",       sect.alignment, (SectionAlignment)1);
    io.mapRequired("address",         sect.address);
    if (isZeroFillSection(sect.type)) {
      // S_ZEROFILL sections use "size:" instead of "content:"
      uint64_t size = sect.content.size();
      io.mapOptional("size",          size);
      if (!io.outputting()) {
        uint8_t *bytes = nullptr;
        sect.content = makeArrayRef(bytes, size);
      }
    } else {
      MappingNormalization<NormalizedContent, ArrayRef<uint8_t>> content(
        io, sect.content);
      io.mapOptional("content",         content->_normalizedContent);
    }
    io.mapOptional("relocations",     sect.relocations);
    io.mapOptional("indirect-syms",   sect.indirectSymbols);
  }

  struct NormalizedContent {
    NormalizedContent(IO &io) : _io(io) {}
    NormalizedContent(IO &io, ArrayRef<uint8_t> content) : _io(io) {
      // When writing yaml, copy content byte array to Hex8 vector.
      for (auto &c : content) {
        _normalizedContent.push_back(c);
      }
    }
    ArrayRef<uint8_t> denormalize(IO &io) {
      // When reading yaml, allocate byte array owned by NormalizedFile and
      // copy Hex8 vector to byte array.
      YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
      assert(info != nullptr);
      NormalizedFile *file = info->_normalizeMachOFile;
      assert(file != nullptr);
      size_t size = _normalizedContent.size();
      if (!size)
        return None;
      uint8_t *bytes = file->ownedAllocations.Allocate<uint8_t>(size);
      std::copy(_normalizedContent.begin(), _normalizedContent.end(), bytes);
      return makeArrayRef(bytes, size);
    }

    IO                &_io;
    ContentBytes       _normalizedContent;
  };
};


template <>
struct MappingTraits<Relocation> {
  static void mapping(IO &io, Relocation &reloc) {
    io.mapRequired("offset",    reloc.offset);
    io.mapOptional("scattered", reloc.scattered, false);
    io.mapRequired("type",      reloc.type);
    io.mapRequired("length",    reloc.length);
    io.mapRequired("pc-rel",    reloc.pcRel);
    if ( !reloc.scattered )
     io.mapRequired("extern",   reloc.isExtern);
    if ( reloc.scattered )
     io.mapRequired("value",    reloc.value);
    if ( !reloc.scattered )
     io.mapRequired("symbol",   reloc.symbol);
  }
};


template <>
struct ScalarEnumerationTraits<RelocationInfoType> {
  static void enumeration(IO &io, RelocationInfoType &value) {
    YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
    assert(info != nullptr);
    NormalizedFile *file = info->_normalizeMachOFile;
    assert(file != nullptr);
    switch (file->arch) {
    case lld::MachOLinkingContext::arch_x86_64:
      io.enumCase(value, "X86_64_RELOC_UNSIGNED",
                                  llvm::MachO::X86_64_RELOC_UNSIGNED);
      io.enumCase(value, "X86_64_RELOC_SIGNED",
                                  llvm::MachO::X86_64_RELOC_SIGNED);
      io.enumCase(value, "X86_64_RELOC_BRANCH",
                                  llvm::MachO::X86_64_RELOC_BRANCH);
      io.enumCase(value, "X86_64_RELOC_GOT_LOAD",
                                  llvm::MachO::X86_64_RELOC_GOT_LOAD);
      io.enumCase(value, "X86_64_RELOC_GOT",
                                  llvm::MachO::X86_64_RELOC_GOT);
      io.enumCase(value, "X86_64_RELOC_SUBTRACTOR",
                                  llvm::MachO::X86_64_RELOC_SUBTRACTOR);
      io.enumCase(value, "X86_64_RELOC_SIGNED_1",
                                  llvm::MachO::X86_64_RELOC_SIGNED_1);
      io.enumCase(value, "X86_64_RELOC_SIGNED_2",
                                  llvm::MachO::X86_64_RELOC_SIGNED_2);
      io.enumCase(value, "X86_64_RELOC_SIGNED_4",
                                  llvm::MachO::X86_64_RELOC_SIGNED_4);
      io.enumCase(value, "X86_64_RELOC_TLV",
                                  llvm::MachO::X86_64_RELOC_TLV);
      break;
    case lld::MachOLinkingContext::arch_x86:
      io.enumCase(value, "GENERIC_RELOC_VANILLA",
                                  llvm::MachO::GENERIC_RELOC_VANILLA);
      io.enumCase(value, "GENERIC_RELOC_PAIR",
                                  llvm::MachO::GENERIC_RELOC_PAIR);
      io.enumCase(value, "GENERIC_RELOC_SECTDIFF",
                                  llvm::MachO::GENERIC_RELOC_SECTDIFF);
      io.enumCase(value, "GENERIC_RELOC_LOCAL_SECTDIFF",
                                  llvm::MachO::GENERIC_RELOC_LOCAL_SECTDIFF);
      io.enumCase(value, "GENERIC_RELOC_TLV",
                                  llvm::MachO::GENERIC_RELOC_TLV);
      break;
    case lld::MachOLinkingContext::arch_armv6:
    case lld::MachOLinkingContext::arch_armv7:
    case lld::MachOLinkingContext::arch_armv7s:
       io.enumCase(value, "ARM_RELOC_VANILLA",
                                  llvm::MachO::ARM_RELOC_VANILLA);
      io.enumCase(value, "ARM_RELOC_PAIR",
                                  llvm::MachO::ARM_RELOC_PAIR);
      io.enumCase(value, "ARM_RELOC_SECTDIFF",
                                  llvm::MachO::ARM_RELOC_SECTDIFF);
      io.enumCase(value, "ARM_RELOC_LOCAL_SECTDIFF",
                                  llvm::MachO::ARM_RELOC_LOCAL_SECTDIFF);
      io.enumCase(value, "ARM_RELOC_BR24",
                                  llvm::MachO::ARM_RELOC_BR24);
      io.enumCase(value, "ARM_THUMB_RELOC_BR22",
                                  llvm::MachO::ARM_THUMB_RELOC_BR22);
      io.enumCase(value, "ARM_RELOC_HALF",
                                  llvm::MachO::ARM_RELOC_HALF);
      io.enumCase(value, "ARM_RELOC_HALF_SECTDIFF",
                                  llvm::MachO::ARM_RELOC_HALF_SECTDIFF);
      break;
    case lld::MachOLinkingContext::arch_arm64:
      io.enumCase(value, "ARM64_RELOC_UNSIGNED",
                                  llvm::MachO::ARM64_RELOC_UNSIGNED);
      io.enumCase(value, "ARM64_RELOC_SUBTRACTOR",
                                  llvm::MachO::ARM64_RELOC_SUBTRACTOR);
      io.enumCase(value, "ARM64_RELOC_BRANCH26",
                                  llvm::MachO::ARM64_RELOC_BRANCH26);
      io.enumCase(value, "ARM64_RELOC_PAGE21",
                                  llvm::MachO::ARM64_RELOC_PAGE21);
      io.enumCase(value, "ARM64_RELOC_PAGEOFF12",
                                  llvm::MachO::ARM64_RELOC_PAGEOFF12);
      io.enumCase(value, "ARM64_RELOC_GOT_LOAD_PAGE21",
                                  llvm::MachO::ARM64_RELOC_GOT_LOAD_PAGE21);
      io.enumCase(value, "ARM64_RELOC_GOT_LOAD_PAGEOFF12",
                                  llvm::MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12);
      io.enumCase(value, "ARM64_RELOC_POINTER_TO_GOT",
                                  llvm::MachO::ARM64_RELOC_POINTER_TO_GOT);
      io.enumCase(value, "ARM64_RELOC_TLVP_LOAD_PAGE21",
                                  llvm::MachO::ARM64_RELOC_TLVP_LOAD_PAGE21);
      io.enumCase(value, "ARM64_RELOC_TLVP_LOAD_PAGEOFF12",
                                  llvm::MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12);
      io.enumCase(value, "ARM64_RELOC_ADDEND",
                                  llvm::MachO::ARM64_RELOC_ADDEND);
      break;
    default:
      llvm_unreachable("unknown architecture");
    }
 }
};


template <>
struct MappingTraits<Symbol> {
  static void mapping(IO &io, Symbol& sym) {
    io.mapRequired("name",    sym.name);
    io.mapRequired("type",    sym.type);
    io.mapOptional("scope",   sym.scope, SymbolScope(0));
    io.mapOptional("sect",    sym.sect, (uint8_t)0);
    if (sym.type == llvm::MachO::N_UNDF) {
      // In undef symbols, desc field contains alignment/ordinal info
      // which is better represented as a hex vaule.
      uint16_t t1 = sym.desc;
      Hex16 t2 = t1;
      io.mapOptional("desc",  t2, Hex16(0));
      sym.desc = t2;
    } else {
      // In defined symbols, desc fit is a set of option bits.
      io.mapOptional("desc",    sym.desc, SymbolDesc(0));
    }
    io.mapRequired("value",  sym.value);
  }
};

// Custom mapping for VMProtect (e.g. "r-x").
template <>
struct ScalarTraits<VMProtect> {
  static void output(const VMProtect &value, void*, raw_ostream &out) {
    out << ( (value & llvm::MachO::VM_PROT_READ)    ? 'r' : '-');
    out << ( (value & llvm::MachO::VM_PROT_WRITE)   ? 'w' : '-');
    out << ( (value & llvm::MachO::VM_PROT_EXECUTE) ? 'x' : '-');
  }
  static StringRef input(StringRef scalar, void*, VMProtect &value) {
    value = 0;
    if (scalar.size() != 3)
      return "segment access protection must be three chars (e.g. \"r-x\")";
    switch (scalar[0]) {
    case 'r':
      value = llvm::MachO::VM_PROT_READ;
      break;
    case '-':
      break;
    default:
      return "segment access protection first char must be 'r' or '-'";
    }
    switch (scalar[1]) {
    case 'w':
      value = value | llvm::MachO::VM_PROT_WRITE;
      break;
    case '-':
      break;
    default:
      return "segment access protection second char must be 'w' or '-'";
    }
    switch (scalar[2]) {
    case 'x':
      value = value | llvm::MachO::VM_PROT_EXECUTE;
      break;
    case '-':
      break;
    default:
      return "segment access protection third char must be 'x' or '-'";
    }
    // Return the empty string on success,
    return StringRef();
  }
  static QuotingType mustQuote(StringRef) { return QuotingType::None; }
};


template <>
struct MappingTraits<Segment> {
  static void mapping(IO &io, Segment& seg) {
    io.mapRequired("name",            seg.name);
    io.mapRequired("address",         seg.address);
    io.mapRequired("size",            seg.size);
    io.mapRequired("init-access",     seg.init_access);
    io.mapRequired("max-access",      seg.max_access);
  }
};

template <>
struct ScalarEnumerationTraits<LoadCommandType> {
  static void enumeration(IO &io, LoadCommandType &value) {
    io.enumCase(value, "LC_LOAD_DYLIB",
                        llvm::MachO::LC_LOAD_DYLIB);
    io.enumCase(value, "LC_LOAD_WEAK_DYLIB",
                        llvm::MachO::LC_LOAD_WEAK_DYLIB);
    io.enumCase(value, "LC_REEXPORT_DYLIB",
                        llvm::MachO::LC_REEXPORT_DYLIB);
    io.enumCase(value, "LC_LOAD_UPWARD_DYLIB",
                        llvm::MachO::LC_LOAD_UPWARD_DYLIB);
    io.enumCase(value, "LC_LAZY_LOAD_DYLIB",
                        llvm::MachO::LC_LAZY_LOAD_DYLIB);
    io.enumCase(value, "LC_VERSION_MIN_MACOSX",
                        llvm::MachO::LC_VERSION_MIN_MACOSX);
    io.enumCase(value, "LC_VERSION_MIN_IPHONEOS",
                        llvm::MachO::LC_VERSION_MIN_IPHONEOS);
    io.enumCase(value, "LC_VERSION_MIN_TVOS",
                        llvm::MachO::LC_VERSION_MIN_TVOS);
    io.enumCase(value, "LC_VERSION_MIN_WATCHOS",
                        llvm::MachO::LC_VERSION_MIN_WATCHOS);
  }
};

template <>
struct MappingTraits<DependentDylib> {
  static void mapping(IO &io, DependentDylib& dylib) {
    io.mapRequired("path",            dylib.path);
    io.mapOptional("kind",            dylib.kind,
                                      llvm::MachO::LC_LOAD_DYLIB);
    io.mapOptional("compat-version",  dylib.compatVersion,
                                      PackedVersion(0x10000));
    io.mapOptional("current-version", dylib.currentVersion,
                                      PackedVersion(0x10000));
  }
};

template <>
struct ScalarEnumerationTraits<RebaseType> {
  static void enumeration(IO &io, RebaseType &value) {
    io.enumCase(value, "REBASE_TYPE_POINTER",
                        llvm::MachO::REBASE_TYPE_POINTER);
    io.enumCase(value, "REBASE_TYPE_TEXT_PCREL32",
                        llvm::MachO::REBASE_TYPE_TEXT_PCREL32);
    io.enumCase(value, "REBASE_TYPE_TEXT_ABSOLUTE32",
                        llvm::MachO::REBASE_TYPE_TEXT_ABSOLUTE32);
  }
};


template <>
struct MappingTraits<RebaseLocation> {
  static void mapping(IO &io, RebaseLocation& rebase) {
    io.mapRequired("segment-index",   rebase.segIndex);
    io.mapRequired("segment-offset",  rebase.segOffset);
    io.mapOptional("kind",            rebase.kind,
                                      llvm::MachO::REBASE_TYPE_POINTER);
  }
};



template <>
struct ScalarEnumerationTraits<BindType> {
  static void enumeration(IO &io, BindType &value) {
    io.enumCase(value, "BIND_TYPE_POINTER",
                        llvm::MachO::BIND_TYPE_POINTER);
    io.enumCase(value, "BIND_TYPE_TEXT_ABSOLUTE32",
                        llvm::MachO::BIND_TYPE_TEXT_ABSOLUTE32);
    io.enumCase(value, "BIND_TYPE_TEXT_PCREL32",
                        llvm::MachO::BIND_TYPE_TEXT_PCREL32);
  }
};

template <>
struct MappingTraits<BindLocation> {
  static void mapping(IO &io, BindLocation &bind) {
    io.mapRequired("segment-index",   bind.segIndex);
    io.mapRequired("segment-offset",  bind.segOffset);
    io.mapOptional("kind",            bind.kind,
                                      llvm::MachO::BIND_TYPE_POINTER);
    io.mapOptional("can-be-null",     bind.canBeNull, false);
    io.mapRequired("ordinal",         bind.ordinal);
    io.mapRequired("symbol-name",     bind.symbolName);
    io.mapOptional("addend",          bind.addend, Hex64(0));
  }
};


template <>
struct ScalarEnumerationTraits<ExportSymbolKind> {
  static void enumeration(IO &io, ExportSymbolKind &value) {
    io.enumCase(value, "EXPORT_SYMBOL_FLAGS_KIND_REGULAR",
                        llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR);
    io.enumCase(value, "EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL",
                        llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL);
    io.enumCase(value, "EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE",
                        llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE);
  }
};

template <>
struct ScalarBitSetTraits<ExportFlags> {
  static void bitset(IO &io, ExportFlags &value) {
    io.bitSetCase(value, "EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION",
                          llvm::MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION);
    io.bitSetCase(value, "EXPORT_SYMBOL_FLAGS_REEXPORT",
                          llvm::MachO::EXPORT_SYMBOL_FLAGS_REEXPORT);
    io.bitSetCase(value, "EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER",
                          llvm::MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER);
  }
};


template <>
struct MappingTraits<Export> {
  static void mapping(IO &io, Export &exp) {
    io.mapRequired("name",         exp.name);
    io.mapOptional("offset",       exp.offset);
    io.mapOptional("kind",         exp.kind,
                                llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR);
    if (!io.outputting() || exp.flags)
      io.mapOptional("flags",      exp.flags);
    io.mapOptional("other",        exp.otherOffset, Hex32(0));
    io.mapOptional("other-name",   exp.otherName, StringRef());
  }
};

template <>
struct ScalarEnumerationTraits<DataRegionType> {
  static void enumeration(IO &io, DataRegionType &value) {
    io.enumCase(value, "DICE_KIND_DATA",
                        llvm::MachO::DICE_KIND_DATA);
    io.enumCase(value, "DICE_KIND_JUMP_TABLE8",
                        llvm::MachO::DICE_KIND_JUMP_TABLE8);
    io.enumCase(value, "DICE_KIND_JUMP_TABLE16",
                        llvm::MachO::DICE_KIND_JUMP_TABLE16);
    io.enumCase(value, "DICE_KIND_JUMP_TABLE32",
                        llvm::MachO::DICE_KIND_JUMP_TABLE32);
    io.enumCase(value, "DICE_KIND_ABS_JUMP_TABLE32",
                        llvm::MachO::DICE_KIND_ABS_JUMP_TABLE32);
  }
};

template <>
struct MappingTraits<DataInCode> {
  static void mapping(IO &io, DataInCode &entry) {
    io.mapRequired("offset",       entry.offset);
    io.mapRequired("length",       entry.length);
    io.mapRequired("kind",         entry.kind);
  }
};

template <>
struct ScalarTraits<PackedVersion> {
  static void output(const PackedVersion &value, void*, raw_ostream &out) {
    out << llvm::format("%d.%d", (value >> 16), (value >> 8) & 0xFF);
    if (value & 0xFF) {
      out << llvm::format(".%d", (value & 0xFF));
    }
  }
  static StringRef input(StringRef scalar, void*, PackedVersion &result) {
    uint32_t value;
    if (lld::MachOLinkingContext::parsePackedVersion(scalar, value))
      return "malformed version number";
    result = value;
    // Return the empty string on success,
    return StringRef();
  }
  static QuotingType mustQuote(StringRef) { return QuotingType::None; }
};

template <>
struct MappingTraits<NormalizedFile> {
  static void mapping(IO &io, NormalizedFile &file) {
    io.mapRequired("arch",             file.arch);
    io.mapRequired("file-type",        file.fileType);
    io.mapOptional("flags",            file.flags);
    io.mapOptional("dependents",       file.dependentDylibs);
    io.mapOptional("install-name",     file.installName,    StringRef());
    io.mapOptional("compat-version",   file.compatVersion,  PackedVersion(0x10000));
    io.mapOptional("current-version",  file.currentVersion, PackedVersion(0x10000));
    io.mapOptional("has-UUID",         file.hasUUID,        true);
    io.mapOptional("rpaths",           file.rpaths);
    io.mapOptional("entry-point",      file.entryAddress,   Hex64(0));
    io.mapOptional("stack-size",       file.stackSize,      Hex64(0));
    io.mapOptional("source-version",   file.sourceVersion,  Hex64(0));
    io.mapOptional("OS",               file.os);
    io.mapOptional("min-os-version",   file.minOSverson,    PackedVersion(0));
    io.mapOptional("min-os-version-kind",   file.minOSVersionKind, (LoadCommandType)0);
    io.mapOptional("sdk-version",      file.sdkVersion,     PackedVersion(0));
    io.mapOptional("segments",         file.segments);
    io.mapOptional("sections",         file.sections);
    io.mapOptional("local-symbols",    file.localSymbols);
    io.mapOptional("global-symbols",   file.globalSymbols);
    io.mapOptional("undefined-symbols",file.undefinedSymbols);
    io.mapOptional("page-size",        file.pageSize,       Hex32(4096));
    io.mapOptional("rebasings",        file.rebasingInfo);
    io.mapOptional("bindings",         file.bindingInfo);
    io.mapOptional("weak-bindings",    file.weakBindingInfo);
    io.mapOptional("lazy-bindings",    file.lazyBindingInfo);
    io.mapOptional("exports",          file.exportInfo);
    io.mapOptional("dataInCode",       file.dataInCode);
  }
  static StringRef validate(IO &io, NormalizedFile &file) {
    return StringRef();
  }
};

} // namespace llvm
} // namespace yaml


namespace lld {
namespace mach_o {

/// Handles !mach-o tagged yaml documents.
bool MachOYamlIOTaggedDocumentHandler::handledDocTag(llvm::yaml::IO &io,
                                                 const lld::File *&file) const {
  if (!io.mapTag("!mach-o"))
    return false;
  // Step 1: parse yaml into normalized mach-o struct.
  NormalizedFile nf;
  YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
  assert(info != nullptr);
  assert(info->_normalizeMachOFile == nullptr);
  info->_normalizeMachOFile = &nf;
  MappingTraits<NormalizedFile>::mapping(io, nf);
  // Step 2: parse normalized mach-o struct into atoms.
  auto fileOrError = normalizedToAtoms(nf, info->_path, true);

  // Check that we parsed successfully.
  if (!fileOrError) {
    std::string buffer;
    llvm::raw_string_ostream stream(buffer);
    handleAllErrors(fileOrError.takeError(),
                    [&](const llvm::ErrorInfoBase &EI) {
      EI.log(stream);
      stream << "\n";
    });
    io.setError(stream.str());
    return false;
  }

  if (nf.arch != _arch) {
    io.setError(Twine("file is wrong architecture. Expected ("
                      + MachOLinkingContext::nameFromArch(_arch)
                      + ") found ("
                      + MachOLinkingContext::nameFromArch(nf.arch)
                      + ")"));
    return false;
  }
  info->_normalizeMachOFile = nullptr;
  file = fileOrError->release();
  return true;
}



namespace normalized {

/// Parses a yaml encoded mach-o file to produce an in-memory normalized view.
llvm::Expected<std::unique_ptr<NormalizedFile>>
readYaml(std::unique_ptr<MemoryBuffer> &mb) {
  // Make empty NormalizedFile.
  std::unique_ptr<NormalizedFile> f(new NormalizedFile());

  // Create YAML Input parser.
  YamlContext yamlContext;
  yamlContext._normalizeMachOFile = f.get();
  llvm::yaml::Input yin(mb->getBuffer(), &yamlContext);

  // Fill NormalizedFile by parsing yaml.
  yin >> *f;

  // Return error if there were parsing problems.
  if (auto ec = yin.error())
    return llvm::make_error<GenericError>(Twine("YAML parsing error: ")
                                          + ec.message());

  // Hand ownership of instantiated NormalizedFile to caller.
  return std::move(f);
}


/// Writes a yaml encoded mach-o files from an in-memory normalized view.
std::error_code writeYaml(const NormalizedFile &file, raw_ostream &out) {
  // YAML I/O is not const aware, so need to cast away ;-(
  NormalizedFile *f = const_cast<NormalizedFile*>(&file);

  // Create yaml Output writer, using yaml options for context.
  YamlContext yamlContext;
  yamlContext._normalizeMachOFile = f;
  llvm::yaml::Output yout(out, &yamlContext);

  // Stream out yaml.
  yout << *f;

  return std::error_code();
}

} // namespace normalized
} // namespace mach_o
} // namespace lld
