
This reverts commit 1b589f4d4db27e3fcd81fdc5abeb9407753ab790 and relands the D89463 with the fix: update `MappingTraits<FileFilter>::validate()` in ClangTidyOptions.cpp to match the new signature (change the return type to "std::string" from "StringRef"). Original commit message: This: Changes the return type of MappingTraits<T>>::validate to std::string instead of StringRef. It allows to create more complex error messages. It introduces std::vector<std::pair<StringRef, bool>> getEntries(): a new virtual method of Section, which is the base class for all sections. It returns names of special section specific keys (e.g. "Entries") and flags that says if them exist in a YAML. The code in validate() uses this list of entries descriptions to generalize validation. This approach was discussed in the D89039 thread. Differential revision: https://reviews.llvm.org/D89463
841 lines
31 KiB
C++
841 lines
31 KiB
C++
//===- 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 §) {
|
|
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 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<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
|