diff --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h --- a/llvm/include/llvm/ObjectYAML/ELFYAML.h +++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -15,6 +15,8 @@ #ifndef LLVM_OBJECTYAML_ELFYAML_H #define LLVM_OBJECTYAML_ELFYAML_H +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/ObjectYAML/YAML.h" #include "llvm/Support/YAMLTraits.h" @@ -498,6 +500,11 @@ Optional> Symbols; Optional> DynamicSymbols; + DenseMap Overridden; + DenseSet SeenOverridden; + + Object(DenseMap Overridden) : Overridden(Overridden) {} + std::vector
getSections() { std::vector
Ret; for (const std::unique_ptr &Sec : Chunks) @@ -538,8 +545,9 @@ }; template <> -struct ScalarEnumerationTraits { - static void enumeration(IO &IO, ELFYAML::ELF_EM &Value); +struct ScalarEnumerationContextTraits { + static void enumeration(IO &IO, ELFYAML::ELF_EM &Value, + ELFYAML::Object *&Ctx); }; template <> diff --git a/llvm/include/llvm/ObjectYAML/ObjectYAML.h b/llvm/include/llvm/ObjectYAML/ObjectYAML.h --- a/llvm/include/llvm/ObjectYAML/ObjectYAML.h +++ b/llvm/include/llvm/ObjectYAML/ObjectYAML.h @@ -29,6 +29,7 @@ std::unique_ptr FatMachO; std::unique_ptr Minidump; std::unique_ptr Wasm; + DenseMap Overridden; }; template <> struct MappingTraits { diff --git a/llvm/include/llvm/ObjectYAML/yaml2obj.h b/llvm/include/llvm/ObjectYAML/yaml2obj.h --- a/llvm/include/llvm/ObjectYAML/yaml2obj.h +++ b/llvm/include/llvm/ObjectYAML/yaml2obj.h @@ -14,6 +14,8 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include +#include +#include namespace llvm { class raw_ostream; @@ -54,7 +56,8 @@ bool yaml2wasm(WasmYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH); bool convertYAML(Input &YIn, raw_ostream &Out, ErrorHandler ErrHandler, - unsigned DocNum = 1); + unsigned DocNum = 1, + std::vector Substitutions = {}); /// Convenience function for tests. std::unique_ptr diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h --- a/llvm/include/llvm/Support/YAMLTraits.h +++ b/llvm/include/llvm/Support/YAMLTraits.h @@ -106,6 +106,10 @@ // static void enumeration(IO &io, T &value); }; +/// Similar to ScalarEnumerationTraits, but allows you to pass in an +/// additional context. +template struct ScalarEnumerationContextTraits {}; + /// This class should be specialized by any integer type that is a union /// of bit values and the YAML representation is a flow sequence of /// strings. For example: @@ -306,20 +310,25 @@ template struct MissingTrait; -// Test if ScalarEnumerationTraits is defined on type T. -template -struct has_ScalarEnumerationTraits -{ - using Signature_enumeration = void (*)(class IO&, T&); - +// Test if ScalarEnumerationContextTraits is defined on type T. +template struct has_ScalarEnumerationTraits { template - static char test(SameType*); + static char + test(SameType *); + template static double test(...); + + static bool const value = + (sizeof(test>(nullptr)) == 1); +}; +// Test if ScalarEnumerationCTraits is defined on type T. +template struct has_ScalarEnumerationTraits { template - static double test(...); + static char test(SameType *); + template static double test(...); static bool const value = - (sizeof(test>(nullptr)) == 1); + (sizeof(test>(nullptr)) == 1); }; // Test if ScalarBitSetTraits is defined on type T. @@ -719,17 +728,17 @@ template struct missingTraits - : public std::integral_constant::value && - !has_ScalarBitSetTraits::value && - !has_ScalarTraits::value && - !has_BlockScalarTraits::value && - !has_TaggedScalarTraits::value && - !has_MappingTraits::value && - !has_SequenceTraits::value && - !has_CustomMappingTraits::value && - !has_DocumentListTraits::value && - !has_PolymorphicTraits::value> {}; + : public std::integral_constant< + bool, !has_ScalarEnumerationTraits::value && + !has_ScalarBitSetTraits::value && + !has_ScalarTraits::value && + !has_BlockScalarTraits::value && + !has_TaggedScalarTraits::value && + !has_MappingTraits::value && + !has_SequenceTraits::value && + !has_CustomMappingTraits::value && + !has_DocumentListTraits::value && + !has_PolymorphicTraits::value> {}; template struct validatedMappingTraits @@ -815,6 +824,17 @@ } } + template + void enumFallbackWithOverridden(StringRef Overridden, T &Val) { + if (matchEnumFallback()) { + FBT Res = static_cast(Val); + StringRef Err = ScalarTraits::input(Overridden, getContext(), Res); + if (!Err.empty()) + setError(Err); + Val = static_cast(static_cast(Res)); + } + } + template void bitSetCase(T &Val, const char* Str, const T ConstVal) { if ( bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal) ) { @@ -953,6 +973,15 @@ namespace detail { +template +void doEnumeration(IO &io, T &Val, Context &Ctx) { + ScalarEnumerationContextTraits::enumeration(io, Val, Ctx); +} + +template void doEnumeration(IO &io, T &Val, EmptyContext &) { + ScalarEnumerationTraits::enumeration(io, Val); +} + template void doMapping(IO &io, T &Val, Context &Ctx) { MappingContextTraits::mapping(io, Val, Ctx); @@ -964,11 +993,12 @@ } // end namespace detail -template -typename std::enable_if::value, void>::type -yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { +template +typename std::enable_if::value, + void>::type +yamlize(IO &io, T &Val, bool, Context &Ctx) { io.beginEnumScalar(); - ScalarEnumerationTraits::enumeration(io, Val); + detail::doEnumeration(io, Val, Ctx); io.endEnumScalar(); } @@ -1276,7 +1306,7 @@ support::detail::packed_endian_specific_integral, typename std::enable_if< - has_ScalarEnumerationTraits::value>::type> { + has_ScalarEnumerationTraits::value>::type> { using endian_type = support::detail::packed_endian_specific_integral; diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -59,9 +59,26 @@ IO.enumFallback(Value); } -void ScalarEnumerationTraits::enumeration( - IO &IO, ELFYAML::ELF_EM &Value) { -#define ECase(X) IO.enumCase(Value, #X, ELF::X) +void ScalarEnumerationContextTraits< + ELFYAML::ELF_EM, ELFYAML::Object *>::enumeration(IO &io, + ELFYAML::ELF_EM &Value, + ELFYAML::Object *&Ctx) { + bool Output = io.outputting(); + Optional OverriddenStr; + auto It = Ctx->Overridden.find("e_machine"); + if (It != Ctx->Overridden.end()) { + OverriddenStr = It->second; + Ctx->SeenOverridden.insert("e_machine"); + } + +#define ECase(X) \ + if (!Output && OverriddenStr && *OverriddenStr == #X) { \ + Value = ELF::X; \ + io.matchEnumFallback(); \ + return; \ + } \ + io.enumCase(Value, #X, ELF::X); + ECase(EM_NONE); ECase(EM_M32); ECase(EM_SPARC); @@ -222,7 +239,11 @@ ECase(EM_LANAI); ECase(EM_BPF); #undef ECase - IO.enumFallback(Value); + + if (!Output && OverriddenStr) + io.enumFallbackWithOverridden(*OverriddenStr, Value); + else + io.enumFallback(Value); } void ScalarEnumerationTraits::enumeration( @@ -827,12 +848,13 @@ void MappingTraits::mapping(IO &IO, ELFYAML::FileHeader &FileHdr) { + auto *Ctx = static_cast(IO.getContext()); IO.mapRequired("Class", FileHdr.Class); IO.mapRequired("Data", FileHdr.Data); IO.mapOptional("OSABI", FileHdr.OSABI, ELFYAML::ELF_ELFOSABI(0)); IO.mapOptional("ABIVersion", FileHdr.ABIVersion, Hex8(0)); IO.mapRequired("Type", FileHdr.Type); - IO.mapRequired("Machine", FileHdr.Machine); + IO.mapRequired("Machine", FileHdr.Machine, Ctx); IO.mapOptional("Flags", FileHdr.Flags, ELFYAML::ELF_EF(0)); IO.mapOptional("Entry", FileHdr.Entry, Hex64(0)); diff --git a/llvm/lib/ObjectYAML/ObjectYAML.cpp b/llvm/lib/ObjectYAML/ObjectYAML.cpp --- a/llvm/lib/ObjectYAML/ObjectYAML.cpp +++ b/llvm/lib/ObjectYAML/ObjectYAML.cpp @@ -32,10 +32,15 @@ MappingTraits::mapping(IO, *ObjectFile.FatMachO); } else { + DenseMap Overridden; + DenseSet SeenOverridden; Input &In = (Input &)IO; if (IO.mapTag("!ELF")) { - ObjectFile.Elf.reset(new ELFYAML::Object()); + ObjectFile.Elf.reset( + new ELFYAML::Object(std::move(ObjectFile.Overridden))); MappingTraits::mapping(IO, *ObjectFile.Elf); + Overridden = std::move(ObjectFile.Elf->Overridden); + SeenOverridden = std::move(ObjectFile.Elf->SeenOverridden); } else if (IO.mapTag("!COFF")) { ObjectFile.Coff.reset(new COFFYAML::Object()); MappingTraits::mapping(IO, *ObjectFile.Coff); @@ -59,5 +64,11 @@ IO.setError("YAML Object File unsupported document type tag '" + N->getRawTag() + "'!"); } + + for (auto &It : Overridden) + if (!SeenOverridden.count(It.first)) { + IO.setError("'" + It.first + "' is overridden but not used"); + break; + } } } diff --git a/llvm/lib/ObjectYAML/yaml2obj.cpp b/llvm/lib/ObjectYAML/yaml2obj.cpp --- a/llvm/lib/ObjectYAML/yaml2obj.cpp +++ b/llvm/lib/ObjectYAML/yaml2obj.cpp @@ -19,13 +19,23 @@ namespace yaml { bool convertYAML(yaml::Input &YIn, raw_ostream &Out, ErrorHandler ErrHandler, - unsigned DocNum) { + unsigned DocNum, std::vector Overridden) { + yaml::YamlObjectFile Doc; + for (StringRef O : Overridden) { + StringRef F, V; + std::tie(F, V) = O.split('='); + if (!O.count('=') || F.empty()) { + ErrHandler("bad override, missing field name: " + O); + return false; + } + Doc.Overridden.try_emplace(F, V); + } + unsigned CurDocNum = 0; do { if (++CurDocNum != DocNum) continue; - yaml::YamlObjectFile Doc; YIn >> Doc; if (std::error_code EC = YIn.error()) { ErrHandler("failed to parse YAML input: " + EC.message()); diff --git a/llvm/test/tools/yaml2obj/ELF/emachine.yaml b/llvm/test/tools/yaml2obj/ELF/emachine.yaml --- a/llvm/test/tools/yaml2obj/ELF/emachine.yaml +++ b/llvm/test/tools/yaml2obj/ELF/emachine.yaml @@ -53,3 +53,21 @@ Data: ELFDATA2MSB Type: ET_REL Machine: EM_UNKNOWN_FOO + +## Test we can override e_machine with -D. + +# RUN: yaml2obj --docnum=1 -D e_machine=0x4321 %s -o %t1.4321 +# RUN: llvm-readobj --file-headers %t1.4321 | FileCheck %s --check-prefix=4321 + +# 4321: Machine: 0x4321 + +# RUN: yaml2obj --docnum=1 -D e_machine=EM_386 %s -o %t1.386 +# RUN: llvm-readobj --file-headers %t1.386 | FileCheck %s --check-prefix=386 + +# 386: Machine: EM_386 + +## TODO Report that the overridden value is invalid. +# RUN: not yaml2obj --docnum=1 -D e_machine=invalid %s -o /dev/null 2>&1 | FileCheck --check-prefix=INVALID %s + +# INVALID: error: invalid hex16 number +# INVALID-NEXT: Machine: 0x1234 diff --git a/llvm/test/tools/yaml2obj/ELF/unused-overridden.yaml b/llvm/test/tools/yaml2obj/ELF/unused-overridden.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/yaml2obj/ELF/unused-overridden.yaml @@ -0,0 +1,20 @@ +## Test that an unused -D option emits a diagnostic. + +# RUN: not yaml2obj -D unused=1 %s -o /dev/null 2>&1 | FileCheck --check-prefix=UNUSED %s + +# UNUSED: error: 'unused' is overridden but not used + +## Test that an unused -D option emits an error. + +## TODO Report that the overridden value is invalid. +# RUN: not yaml2obj -D e_machine=invalid %s -o /dev/null 2>&1 | FileCheck --check-prefix=INVALID %s + +# INVALID: error: invalid hex16 number +# INVALID-NEXT: Machine: EM_386 + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_386 diff --git a/llvm/test/tools/yaml2obj/invalid-override.yaml b/llvm/test/tools/yaml2obj/invalid-override.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/yaml2obj/invalid-override.yaml @@ -0,0 +1,5 @@ +# RUN: not yaml2obj -D field %s 2>&1 | FileCheck --check-prefix=ERR1 %s +# ERR1: error: bad override, missing field name: field + +# RUN: not yaml2obj -D =value %s 2>&1 | FileCheck --check-prefix=ERR2 %s +# ERR2: error: bad override, missing field name: =value diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -155,7 +155,7 @@ } template Expected ELFDumper::dump() { - auto Y = std::make_unique(); + auto Y = std::make_unique(DenseMap()); // Dump header. We do not dump SHEntSize, SHOff, SHNum and SHStrNdx fields. // When not explicitly set, the values are set by yaml2obj automatically diff --git a/llvm/tools/yaml2obj/yaml2obj.cpp b/llvm/tools/yaml2obj/yaml2obj.cpp --- a/llvm/tools/yaml2obj/yaml2obj.cpp +++ b/llvm/tools/yaml2obj/yaml2obj.cpp @@ -31,6 +31,8 @@ static cl::opt Input(cl::Positional, cl::desc(""), cl::init("-")); +static cl::list D("D", cl::desc("list of overridden fields: field=value")); + static cl::opt DocNum("docnum", cl::init(1), cl::desc("Read specified document from input (default = 1)")); @@ -63,7 +65,7 @@ return 1; yaml::Input YIn(Buf.get()->getBuffer()); - if (!convertYAML(YIn, Out->os(), ErrHandler, DocNum)) + if (!convertYAML(YIn, Out->os(), ErrHandler, DocNum, D)) return 1; Out->keep();