//===- 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 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
> { static size_t size(IO &io, std::vector
&seq) { return seq.size(); } static Section& element(IO &io, std::vector
&seq, size_t index) { if ( index >= seq.size() ) seq.resize(index+1); return seq[index]; } }; template<> struct SequenceTraits< std::vector > { static size_t size(IO &io, std::vector &seq) { return seq.size(); } static Symbol& element(IO &io, std::vector &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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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
{ struct NormalizedContentBytes; static void mapping(IO &io, Section §) { 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> 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 content) : _io(io) { // When writing yaml, copy content byte array to Hex8 vector. for (auto &c : content) { _normalizedContent.push_back(c); } } ArrayRef denormalize(IO &io) { // When reading yaml, allocate byte array owned by NormalizedFile and // copy Hex8 vector to byte array. YamlContext *info = reinterpret_cast(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(size); std::copy(_normalizedContent.begin(), _normalizedContent.end(), bytes); return makeArrayRef(bytes, size); } IO &_io; ContentBytes _normalizedContent; }; }; template <> struct MappingTraits { 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 { static void enumeration(IO &io, RelocationInfoType &value) { YamlContext *info = reinterpret_cast(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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 std::string validate(IO &io, NormalizedFile &file) { return {}; } }; } // 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(io.getContext()); assert(info != nullptr); assert(info->_normalizeMachOFile == nullptr); info->_normalizeMachOFile = &nf; MappingTraits::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> readYaml(std::unique_ptr &mb) { // Make empty NormalizedFile. std::unique_ptr 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(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(&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