diff --git a/llvm/include/llvm/ObjectYAML/GOFFYAML.h b/llvm/include/llvm/ObjectYAML/GOFFYAML.h index f9bf45e95bd3a..0b3e1d6f04396 100644 --- a/llvm/include/llvm/ObjectYAML/GOFFYAML.h +++ b/llvm/include/llvm/ObjectYAML/GOFFYAML.h @@ -25,25 +25,87 @@ namespace llvm { // to use yaml::IO, we use these structures which are closer to the source. namespace GOFFYAML { -struct FileHeader { - uint32_t TargetEnvironment = 0; - uint32_t TargetOperatingSystem = 0; - uint16_t CCSID = 0; - StringRef CharacterSetName; - StringRef LanguageProductIdentifier; - uint32_t ArchitectureLevel = 0; - std::optional InternalCCSID; - std::optional TargetSoftwareEnvironment; +LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_AMODE) +LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_ENDFLAGS) + +// The GOFF format uses different kinds of logical records. The format imposes +// some rules on those records (e.g. the module header must come first, no +// forward references to records, etc.). However, to be able to specify invalid +// GOFF files, we treat all records the same way. +struct RecordBase { + enum class Kind { + ModuleHeader, + RelocationDirectory, + Symbol, + Text, + DeferredLength, + EndOfModule + }; + +private: + const Kind RecordKind; + +protected: + RecordBase(Kind RecordKind) : RecordKind(RecordKind) {} + +public: + Kind getKind() const { return RecordKind; } +}; +using RecordPtr = std::unique_ptr; + +struct ModuleHeader : public RecordBase { + ModuleHeader() : RecordBase(Kind::ModuleHeader) {} + + uint32_t ArchitectureLevel; + uint16_t PropertiesLength; + std::optional Properties; + + static bool classof(const RecordBase *S) { + return S->getKind() == Kind::ModuleHeader; + } +}; + +struct EndOfModule : public RecordBase { + EndOfModule() : RecordBase(Kind::EndOfModule) {} + + GOFF_ENDFLAGS Flags; + GOFF_AMODE AMODE; + uint32_t RecordCount; + uint32_t ESDID; + uint32_t Offset; + uint16_t NameLength; + StringRef EntryName; + + static bool classof(const RecordBase *S) { + return S->getKind() == Kind::EndOfModule; + } }; struct Object { - FileHeader Header; - Object(); + // A GOFF file is a sequence of records. + std::vector Records; }; } // end namespace GOFFYAML } // end namespace llvm -LLVM_YAML_DECLARE_MAPPING_TRAITS(GOFFYAML::FileHeader) +LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_AMODE) +LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_ENDFLAGS) + +LLVM_YAML_IS_SEQUENCE_VECTOR(GOFFYAML::RecordPtr) +LLVM_YAML_DECLARE_MAPPING_TRAITS(GOFFYAML::RecordPtr) + +LLVM_YAML_DECLARE_MAPPING_TRAITS(GOFFYAML::ModuleHeader) +LLVM_YAML_DECLARE_MAPPING_TRAITS(GOFFYAML::EndOfModule) LLVM_YAML_DECLARE_MAPPING_TRAITS(GOFFYAML::Object) +namespace llvm { +namespace yaml { + +template <> struct CustomMappingTraits { + static void inputOne(IO &IO, StringRef Key, GOFFYAML::RecordPtr &Elem); + static void output(IO &IO, GOFFYAML::RecordPtr &Elem); +}; + +} // namespace yaml +} // namespace llvm #endif // LLVM_OBJECTYAML_GOFFYAML_H diff --git a/llvm/lib/ObjectYAML/GOFFEmitter.cpp b/llvm/lib/ObjectYAML/GOFFEmitter.cpp index 345904407e1d2..9f54cd727f640 100644 --- a/llvm/lib/ObjectYAML/GOFFEmitter.cpp +++ b/llvm/lib/ObjectYAML/GOFFEmitter.cpp @@ -11,11 +11,12 @@ /// //===----------------------------------------------------------------------===// -#include "llvm/ADT/IndexedMap.h" -#include "llvm/ObjectYAML/ObjectYAML.h" +#include "llvm/BinaryFormat/GOFF.h" +#include "llvm/ObjectYAML/GOFFYAML.h" #include "llvm/ObjectYAML/yaml2obj.h" #include "llvm/Support/ConvertEBCDIC.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -25,10 +26,10 @@ namespace { // Common flag values on records. enum { // Flag: This record is continued. - Rec_Continued = 1, + Rec_Continued = 1 << 0, // Flag: This record is a continuation. - Rec_Continuation = 1 << (8 - 6 - 1), + Rec_Continuation = 1 << 1, }; template struct BinaryBeImpl { @@ -62,119 +63,91 @@ ZerosImpl zeros(const size_t NumBytes) { return ZerosImpl{NumBytes}; } // The GOFFOstream is responsible to write the data into the fixed physical // records of the format. A user of this class announces the start of a new -// logical record and the size of its payload. While writing the payload, the -// physical records are created for the data. Possible fill bytes at the end of -// a physical record are written automatically. -class GOFFOstream : public raw_ostream { +// logical record, and writes the full logical block. The physical records are +// created while the content is written to the underlying stream. Possible fill +// bytes at the end of a physical record are written automatically. +// The implementation aims at simplicity, not speed. +class GOFFOStream { public: - explicit GOFFOstream(raw_ostream &OS) - : OS(OS), LogicalRecords(0), RemainingSize(0), NewLogicalRecord(false) { - SetBufferSize(GOFF::PayloadLength); - } - - ~GOFFOstream() { finalize(); } + explicit GOFFOStream(raw_ostream &OS) : OS(OS), CurrentType() {} - void makeNewRecord(GOFF::RecordType Type, size_t Size) { - fillRecord(); - CurrentType = Type; - RemainingSize = Size; - if (size_t Gap = (RemainingSize % GOFF::PayloadLength)) - RemainingSize += GOFF::PayloadLength - Gap; - NewLogicalRecord = true; - ++LogicalRecords; + GOFFOStream &operator<<(StringRef Str) { + write(Str); + return *this; } - void finalize() { fillRecord(); } - - uint32_t logicalRecords() { return LogicalRecords; } + void newRecord(GOFF::RecordType Type) { CurrentType = Type; } private: // The underlying raw_ostream. raw_ostream &OS; - // The number of logical records emitted so far. - uint32_t LogicalRecords; - - // The remaining size of this logical record, including fill bytes. - size_t RemainingSize; - // The type of the current (logical) record. GOFF::RecordType CurrentType; - // Signals start of new record. - bool NewLogicalRecord; - - // Return the number of bytes left to write until next physical record. - // Please note that we maintain the total number of bytes left, not the - // written size. - size_t bytesToNextPhysicalRecord() { - size_t Bytes = RemainingSize % GOFF::PayloadLength; - return Bytes ? Bytes : GOFF::PayloadLength; - } - // Write the record prefix of a physical record, using the current record // type. - static void writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type, - size_t RemainingSize, - uint8_t Flags = Rec_Continuation) { - uint8_t TypeAndFlags = Flags | (Type << 4); - if (RemainingSize > GOFF::RecordLength) - TypeAndFlags |= Rec_Continued; - OS << binaryBe(static_cast(GOFF::PTVPrefix)) - << binaryBe(static_cast(TypeAndFlags)) - << binaryBe(static_cast(0)); - } + void writeRecordPrefix(uint8_t Flags); - // Fill the last physical record of a logical record with zero bytes. - void fillRecord() { - assert((GetNumBytesInBuffer() <= RemainingSize) && - "More bytes in buffer than expected"); - size_t Remains = RemainingSize - GetNumBytesInBuffer(); - if (Remains) { - assert((Remains < GOFF::RecordLength) && - "Attempting to fill more than one physical record"); - raw_ostream::write_zeros(Remains); - } - flush(); - assert(RemainingSize == 0 && "Not fully flushed"); - assert(GetNumBytesInBuffer() == 0 && "Buffer not fully empty"); - } + // Write a logical record. + void write(StringRef Str); +}; - // See raw_ostream::write_impl. - void write_impl(const char *Ptr, size_t Size) override { - assert((RemainingSize >= Size) && "Attempt to write too much data"); - assert(RemainingSize && "Logical record overflow"); - if (!(RemainingSize % GOFF::PayloadLength)) { - writeRecordPrefix(OS, CurrentType, RemainingSize, - NewLogicalRecord ? 0 : Rec_Continuation); - NewLogicalRecord = false; +void GOFFOStream::writeRecordPrefix(uint8_t Flags) { + // See https://www.ibm.com/docs/en/zos/3.1.0?topic=conventions-record-prefix. + uint8_t TypeAndFlags = Flags | (CurrentType << 4); + OS << binaryBe(uint8_t(GOFF::PTVPrefix)) // The prefix value. + << binaryBe(uint8_t(TypeAndFlags)) // The record type and the flags. + << binaryBe(uint8_t(0)); // The version. +} + +void GOFFOStream::write(StringRef Str) { + // The flags are determined by the flags of the prvious record, and by the + // size of the remaining data. + size_t Pos = 0; + size_t Size = Str.size(); + bool Continuation = false; + while (Size > 0) { + uint8_t Flags = 0; + if (Continuation) + Flags |= Rec_Continuation; + if (Size > GOFF::RecordContentLength) { + Flags |= Rec_Continued; + Continuation = true; } - assert(!NewLogicalRecord && - "New logical record not on physical record boundary"); - - size_t Idx = 0; - while (Size > 0) { - size_t BytesToWrite = bytesToNextPhysicalRecord(); - if (BytesToWrite > Size) - BytesToWrite = Size; - OS.write(Ptr + Idx, BytesToWrite); - Idx += BytesToWrite; - Size -= BytesToWrite; - RemainingSize -= BytesToWrite; - if (Size) { - writeRecordPrefix(OS, CurrentType, RemainingSize); - } + writeRecordPrefix(Flags); + if (Size < GOFF::RecordContentLength) { + OS.write(&Str.data()[Pos], Size); + OS.write_zeros(GOFF::RecordContentLength - Size); + Size = 0; + } else { + OS.write(&Str.data()[Pos], GOFF::RecordContentLength); + Size -= GOFF::RecordContentLength; } + Pos += GOFF::RecordContentLength; } +} - // Return the current position within the stream, not counting the bytes - // currently in the buffer. - uint64_t current_pos() const override { return OS.tell(); } +// A LogicalRecord buffers the data of a record. +class LogicalRecord : public raw_svector_ostream { + GOFFOStream &OS; + SmallVector Buffer; + + void anchor() override {}; + +public: + LogicalRecord(GOFFOStream &OS) : raw_svector_ostream(Buffer), OS(OS) {} + ~LogicalRecord() override { OS << str(); } + + LogicalRecord &operator<<(yaml::BinaryRef B) { + B.writeAsBinary(*this); + return *this; + } }; class GOFFState { - void writeHeader(GOFFYAML::FileHeader &FileHdr); - void writeEnd(); + void writeHeader(const GOFFYAML::ModuleHeader &ModHdr); + void writeEnd(const GOFFYAML::EndOfModule &EndMod); void reportError(const Twine &Msg) { ErrHandler(Msg); @@ -185,8 +158,6 @@ class GOFFState { yaml::ErrorHandler ErrHandler) : GW(OS), Doc(Doc), ErrHandler(ErrHandler), HasError(false) {} - ~GOFFState() { GW.finalize(); } - bool writeObject(); public: @@ -194,72 +165,64 @@ class GOFFState { yaml::ErrorHandler ErrHandler); private: - GOFFOstream GW; + GOFFOStream GW; GOFFYAML::Object &Doc; yaml::ErrorHandler ErrHandler; bool HasError; }; -void GOFFState::writeHeader(GOFFYAML::FileHeader &FileHdr) { - SmallString<16> CCSIDName; - if (std::error_code EC = - ConverterEBCDIC::convertToEBCDIC(FileHdr.CharacterSetName, CCSIDName)) - reportError("Conversion error on " + FileHdr.CharacterSetName); - if (CCSIDName.size() > 16) { - reportError("CharacterSetName too long"); - CCSIDName.resize(16); - } - SmallString<16> LangProd; - if (std::error_code EC = ConverterEBCDIC::convertToEBCDIC( - FileHdr.LanguageProductIdentifier, LangProd)) - reportError("Conversion error on " + FileHdr.LanguageProductIdentifier); - if (LangProd.size() > 16) { - reportError("LanguageProductIdentifier too long"); - LangProd.resize(16); - } - - GW.makeNewRecord(GOFF::RT_HDR, GOFF::PayloadLength); - GW << binaryBe(FileHdr.TargetEnvironment) // TargetEnvironment - << binaryBe(FileHdr.TargetOperatingSystem) // TargetOperatingSystem - << zeros(2) // Reserved - << binaryBe(FileHdr.CCSID) // CCSID - << CCSIDName // CharacterSetName - << zeros(16 - CCSIDName.size()) // Fill bytes - << LangProd // LanguageProductIdentifier - << zeros(16 - LangProd.size()) // Fill bytes - << binaryBe(FileHdr.ArchitectureLevel); // ArchitectureLevel - // The module propties are optional. Figure out if we need to write them. - uint16_t ModPropLen = 0; - if (FileHdr.TargetSoftwareEnvironment) - ModPropLen = 3; - else if (FileHdr.InternalCCSID) - ModPropLen = 2; - if (ModPropLen) { - GW << binaryBe(ModPropLen) << zeros(6); - if (ModPropLen >= 2) - GW << binaryBe(FileHdr.InternalCCSID ? *FileHdr.InternalCCSID : 0); - if (ModPropLen >= 3) - GW << binaryBe(FileHdr.TargetSoftwareEnvironment - ? *FileHdr.TargetSoftwareEnvironment - : 0); - } +void GOFFState::writeHeader(const GOFFYAML::ModuleHeader &ModHdr) { + // See + // https://www.ibm.com/docs/en/zos/3.1.0?topic=formats-module-header-record. + GW.newRecord(GOFF::RT_HDR); + LogicalRecord LR(GW); + LR << zeros(45) // Reserved. + << binaryBe(ModHdr.ArchitectureLevel) // The architecture level. + << binaryBe(ModHdr.PropertiesLength) // Length of module properties. + << zeros(6); // Reserved. + if (ModHdr.Properties) + LR << *ModHdr.Properties; // Module properties. } -void GOFFState::writeEnd() { - GW.makeNewRecord(GOFF::RT_END, GOFF::PayloadLength); - GW << binaryBe(uint8_t(0)) // No entry point - << binaryBe(uint8_t(0)) // No AMODE - << zeros(3) // Reserved - << binaryBe(GW.logicalRecords()); - // No entry point yet. Automatically fill remaining space with zero bytes. - GW.finalize(); +void GOFFState::writeEnd(const GOFFYAML::EndOfModule &EndMod) { + // See https://www.ibm.com/docs/en/zos/3.1.0?topic=formats-end-module-record. + SmallString<16> EntryName; + if (std::error_code EC = + ConverterEBCDIC::convertToEBCDIC(EndMod.EntryName, EntryName)) + reportError("failed to convert '" + EndMod.EntryName + "' to EBCDIC 1047"); + + GW.newRecord(GOFF::RT_END); + LogicalRecord LR(GW); + LR << binaryBe(uint8_t(EndMod.Flags)) // The flags. + << binaryBe(uint8_t(EndMod.AMODE)) // The addressing mode. + << zeros(3) // Reserved. + << binaryBe(EndMod.RecordCount) // The record count. + << binaryBe(EndMod.ESDID) // ESDID of the entry point. + << zeros(4) // Reserved. + << binaryBe(EndMod.Offset) // Offset of entry point. + << binaryBe(EndMod.NameLength) // Length of external name. + << EntryName; // Name of the entry point. } bool GOFFState::writeObject() { - writeHeader(Doc.Header); - if (HasError) - return false; - writeEnd(); + for (const std::unique_ptr &RecPtr : Doc.Records) { + const GOFFYAML::RecordBase *Rec = RecPtr.get(); + switch (Rec->getKind()) { + case GOFFYAML::RecordBase::Kind::ModuleHeader: + writeHeader(*static_cast(Rec)); + break; + case GOFFYAML::RecordBase::Kind::EndOfModule: + writeEnd(*static_cast(Rec)); + break; + case GOFFYAML::RecordBase::Kind::RelocationDirectory: + case GOFFYAML::RecordBase::Kind::Symbol: + case GOFFYAML::RecordBase::Kind::Text: + case GOFFYAML::RecordBase::Kind::DeferredLength: + llvm_unreachable("not yet implemented"); + } + if (HasError) + return false; + } return true; } diff --git a/llvm/lib/ObjectYAML/GOFFYAML.cpp b/llvm/lib/ObjectYAML/GOFFYAML.cpp index ae857980a521b..e48d5f452cc65 100644 --- a/llvm/lib/ObjectYAML/GOFFYAML.cpp +++ b/llvm/lib/ObjectYAML/GOFFYAML.cpp @@ -12,34 +12,92 @@ #include "llvm/ObjectYAML/GOFFYAML.h" #include "llvm/BinaryFormat/GOFF.h" -#include namespace llvm { -namespace GOFFYAML { -Object::Object() {} +namespace yaml { -} // namespace GOFFYAML +void ScalarEnumerationTraits::enumeration( + IO &IO, GOFFYAML::GOFF_AMODE &Value) { +#define ECase(X) IO.enumCase(Value, #X, GOFF::ESD_##X) + ECase(AMODE_None); + ECase(AMODE_24); + ECase(AMODE_31); + ECase(AMODE_ANY); + ECase(AMODE_64); + ECase(AMODE_MIN); +#undef ECase + IO.enumFallback(Value); +} -namespace yaml { +void ScalarEnumerationTraits::enumeration( + IO &IO, GOFFYAML::GOFF_ENDFLAGS &Value) { +#define ECase(X) IO.enumCase(Value, #X, unsigned(GOFF::END_##X) << 6) + ECase(EPR_None); + ECase(EPR_EsdidOffset); + ECase(EPR_ExternalName); + ECase(EPR_Reserved); +#undef ECase + IO.enumFallback(Value); +} + +void MappingTraits::mapping( + IO &IO, GOFFYAML::ModuleHeader &ModHdr) { + IO.mapOptional("ArchitectureLevel", ModHdr.ArchitectureLevel, 0); + IO.mapOptional("PropertiesLength", ModHdr.PropertiesLength, 0); + IO.mapOptional("Properties", ModHdr.Properties); +} + +void MappingTraits::mapping(IO &IO, + GOFFYAML::EndOfModule &End) { + IO.mapOptional("Flags", End.Flags, 0); + IO.mapOptional("AMODE", End.AMODE, 0); + IO.mapOptional("RecordCount", End.RecordCount, 0); + IO.mapOptional("ESDID", End.ESDID, 0); + IO.mapOptional("Offset", End.Offset, 0); + IO.mapOptional("NameLength", End.NameLength, 0); + IO.mapOptional("EntryName", End.EntryName); +} + +void CustomMappingTraits::inputOne( + IO &IO, StringRef Key, GOFFYAML::RecordPtr &Elem) { + if (Key == "ModuleHeader") { + GOFFYAML::ModuleHeader ModHdr; + IO.mapRequired("ModuleHeader", ModHdr); + Elem = std::make_unique(std::move(ModHdr)); + } else if (Key == "End") { + GOFFYAML::EndOfModule End; + IO.mapRequired("End", End); + Elem = std::make_unique(std::move(End)); + } else if (Key == "RelocationDirectory" || Key == "Symbol" || Key == "Text" || + Key == "Length") + IO.setError(Twine("not yet implemented '").concat(Key).concat("'")); + else + IO.setError(Twine("unknown record type name '").concat(Key).concat("'")); +} -void MappingTraits::mapping( - IO &IO, GOFFYAML::FileHeader &FileHdr) { - IO.mapOptional("TargetEnvironment", FileHdr.TargetEnvironment, 0); - IO.mapOptional("TargetOperatingSystem", FileHdr.TargetOperatingSystem, 0); - IO.mapOptional("CCSID", FileHdr.CCSID, 0); - IO.mapOptional("CharacterSetName", FileHdr.CharacterSetName, ""); - IO.mapOptional("LanguageProductIdentifier", FileHdr.LanguageProductIdentifier, - ""); - IO.mapOptional("ArchitectureLevel", FileHdr.ArchitectureLevel, 1); - IO.mapOptional("InternalCCSID", FileHdr.InternalCCSID); - IO.mapOptional("TargetSoftwareEnvironment", - FileHdr.TargetSoftwareEnvironment); +void CustomMappingTraits::output( + IO &IO, GOFFYAML::RecordPtr &Elem) { + switch (Elem->getKind()) { + case GOFFYAML::RecordBase::Kind::ModuleHeader: + IO.mapRequired("ModuleHeader", + *static_cast(Elem.get())); + break; + case GOFFYAML::RecordBase::Kind::EndOfModule: + IO.mapRequired("End", *static_cast(Elem.get())); + break; + case GOFFYAML::RecordBase::Kind::RelocationDirectory: + case GOFFYAML::RecordBase::Kind::Symbol: + case GOFFYAML::RecordBase::Kind::Text: + case GOFFYAML::RecordBase::Kind::DeferredLength: + llvm_unreachable("not yet implemented"); + } } void MappingTraits::mapping(IO &IO, GOFFYAML::Object &Obj) { IO.mapTag("!GOFF", true); - IO.mapRequired("FileHeader", Obj.Header); + EmptyContext Context; + yamlize(IO, Obj.Records, false, Context); } } // namespace yaml diff --git a/llvm/test/tools/yaml2obj/GOFF/GOFF-header-settings.yaml b/llvm/test/tools/yaml2obj/GOFF/GOFF-header-settings.yaml deleted file mode 100644 index 1971c407199fb..0000000000000 --- a/llvm/test/tools/yaml2obj/GOFF/GOFF-header-settings.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# RUN: yaml2obj %s | od -v -An -tx1 | FileCheck --ignore-case %s - -## Verify that GOFF Header is correct. -# CHECK: 03 f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -# CHECK-NEXT: 00 00 01 00 03 00 00 00 00 00 00 00 00 00 00 00 -# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - -## Verify GOFF Module end. -# CHECK-NEXT: 03 40 00 00 00 00 00 00 00 00 00 02 00 00 00 00 -# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ---- !GOFF -FileHeader: - TargetEnvironment: 0 - TargetOperatingSystem: 0 - CCSID: 0 - CharacterSetName: "" - LanguageProductIdentifier: "" - ArchitectureLevel: 1 - InternalCCSID: 0 - TargetSoftwareEnvironment: 0 diff --git a/llvm/test/tools/yaml2obj/GOFF/GOFF-no-header.yaml b/llvm/test/tools/yaml2obj/GOFF/GOFF-no-header.yaml deleted file mode 100644 index 55f0efaacdf89..0000000000000 --- a/llvm/test/tools/yaml2obj/GOFF/GOFF-no-header.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# RUN: not yaml2obj %s FileCheck --ignore-case %s - -# CHECK: yaml2obj: error: missing required key 'FileHeader' ---- !GOFF -## X: [] is an extra field required as workaround for -## 'document of unknown type' error -X: [] diff --git a/llvm/test/tools/yaml2obj/GOFF/end-amode-const.yaml b/llvm/test/tools/yaml2obj/GOFF/end-amode-const.yaml new file mode 100644 index 0000000000000..48d8b8dfe6c16 --- /dev/null +++ b/llvm/test/tools/yaml2obj/GOFF/end-amode-const.yaml @@ -0,0 +1,15 @@ +# RUN: yaml2obj %s | od -v -An -tx1 | FileCheck --ignore-case %s + +## Verify that a constant can be used for field AMODE. + +# CHECK: 03 40 00 00 04 00 00 00 00 00 00 00 00 00 00 00 +# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +# CHECK-EMPTY: + +--- !GOFF +- End: + AMODE: 4 diff --git a/llvm/test/tools/yaml2obj/GOFF/end-invalid-name.yaml b/llvm/test/tools/yaml2obj/GOFF/end-invalid-name.yaml new file mode 100644 index 0000000000000..6b1df34a97c31 --- /dev/null +++ b/llvm/test/tools/yaml2obj/GOFF/end-invalid-name.yaml @@ -0,0 +1,7 @@ +# RUN: not yaml2obj %s 2>&1 | FileCheck %s + +# CHECK: yaml2obj: error: failed to convert 'Euro symbol €' to EBCDIC 1047 + +--- !GOFF +- End: + EntryName: "Euro symbol €" diff --git a/llvm/test/tools/yaml2obj/GOFF/end-long-name.yaml b/llvm/test/tools/yaml2obj/GOFF/end-long-name.yaml new file mode 100644 index 0000000000000..feaa34c09bed1 --- /dev/null +++ b/llvm/test/tools/yaml2obj/GOFF/end-long-name.yaml @@ -0,0 +1,74 @@ +# RUN: awk 'BEGIN {str = sprintf("%47s", "")} {gsub("@FILL@", str); print}' < %s | \ +# RUN: yaml2obj | od -v -An -tx1 | \ +# RUN: FileCheck --ignore-case --check-prefix CHECK1 %s +# RUN: awk 'BEGIN {str = sprintf("%48s", "")} {gsub("@FILL@", str); print}' < %s | \ +# RUN: yaml2obj | od -v -An -tx1 | \ +# RUN: FileCheck --ignore-case --check-prefix CHECK2 %s +# RUN: awk 'BEGIN {str = sprintf("%125s", "")} {gsub("@FILL@", str); print}' < %s | \ +# RUN: yaml2obj | od -v -An -tx1 | \ +# RUN: FileCheck --ignore-case --check-prefix CHECK3 %s +# RUN: awk 'BEGIN {str = sprintf("%160s", "")} {gsub("@FILL@", str); print}' < %s | \ +# RUN: yaml2obj | od -v -An -tx1 | \ +# RUN: FileCheck --ignore-case --check-prefix CHECK4 %s + + +## Verify that the entry name is written correctly over multiple records. +## The continued/continuation flags are in the lower nibble of the 2nd byte +## of a record. + +## Last byte is a fill byte. +# CHECK1: 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# CHECK1-NEXT: 00 00 00 00 00 00 00 00 00 00 c6 96 96 c2 81 99 +# CHECK1-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK1-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK1-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 00 +# CHECK1-EMPTY: + +## 1 record fully used. +## Flags = 0 => not a continuation, not continued. +# CHECK2: 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# CHECK2-NEXT: 00 00 00 00 00 00 00 00 00 00 c6 96 96 c2 81 99 +# CHECK2-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK2-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK2-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK2-EMPTY: + +## 2 records fully used. +## Flags = 1 => not a continuation, but continued. +# CHECK3: 03 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# CHECK3-NEXT: 00 00 00 00 00 00 00 00 00 00 c6 96 96 c2 81 99 +# CHECK3-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK3-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK3-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +## Flags = 2 => continuation, not continued. +# CHECK3-NEXT: 03 42 00 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK3-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK3-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK3-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK3-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK3-EMPTY: + +## 3rd record used half. +## Flags = 1 => not a continuation, but continued. +# CHECK4: 03 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# CHECK4-NEXT: 00 00 00 00 00 00 00 00 00 00 c6 96 96 c2 81 99 +# CHECK4-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK4-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK4-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +## Flags = 3 => continuation, and continued. +# CHECK4-NEXT: 03 43 00 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK4-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK4-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK4-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK4-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +## Flags = 2 => continuation, not continued. +# CHECK4-NEXT: 03 42 00 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK4-NEXT: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 +# CHECK4-NEXT: 40 40 40 40 40 40 00 00 00 00 00 00 00 00 00 00 +# CHECK4-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# CHECK4-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# CHECK4-EMPTY: + +--- !GOFF +- End: + EntryName: "FooBar@FILL@" diff --git a/llvm/test/tools/yaml2obj/GOFF/end-only.yaml b/llvm/test/tools/yaml2obj/GOFF/end-only.yaml new file mode 100644 index 0000000000000..0f7af5530dc89 --- /dev/null +++ b/llvm/test/tools/yaml2obj/GOFF/end-only.yaml @@ -0,0 +1,22 @@ +# RUN: yaml2obj %s | od -v -An -tx1 | FileCheck --ignore-case %s + +## Verify that the GOFF end record is correct, with all fields having a value. +## No other record is written. + +# CHECK: 03 40 00 01 04 00 00 00 00 00 00 01 00 00 00 2a +# CHECK-NEXT: 00 00 00 00 00 00 02 01 00 06 c6 96 96 c2 81 99 +# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +# CHECK-EMPTY: + +--- !GOFF +- End: + Flags: 1 + AMODE: AMODE_64 + RecordCount: 1 + ESDID: 42 + Offset: 513 + NameLength: 6 + EntryName: FooBar diff --git a/llvm/test/tools/yaml2obj/GOFF/GOFF-header-end.yaml b/llvm/test/tools/yaml2obj/GOFF/header-end.yaml similarity index 69% rename from llvm/test/tools/yaml2obj/GOFF/GOFF-header-end.yaml rename to llvm/test/tools/yaml2obj/GOFF/header-end.yaml index a5e99c2da2c49..242a11462539a 100644 --- a/llvm/test/tools/yaml2obj/GOFF/GOFF-header-end.yaml +++ b/llvm/test/tools/yaml2obj/GOFF/header-end.yaml @@ -1,20 +1,22 @@ # RUN: yaml2obj %s | od -v -An -tx1 | FileCheck --ignore-case %s -## Verify that GOFF Header is correct. +## Verify that the GOFF Header and end record are written with the default +## values. No other record is written. + # CHECK: 03 f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -# CHECK-NEXT: 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 +# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -## Verify GOFF Module end. -# CHECK-NEXT: 03 40 00 00 00 00 00 00 00 00 00 02 00 00 00 00 +# CHECK-NEXT: 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + # CHECK-EMPTY: --- !GOFF -FileHeader: - ArchitectureLevel: 1 +- ModuleHeader: +- End: diff --git a/llvm/test/tools/yaml2obj/GOFF/header-only.yaml b/llvm/test/tools/yaml2obj/GOFF/header-only.yaml new file mode 100644 index 0000000000000..a4f121d9c8593 --- /dev/null +++ b/llvm/test/tools/yaml2obj/GOFF/header-only.yaml @@ -0,0 +1,18 @@ +# RUN: yaml2obj %s | od -v -An -tx1 | FileCheck --ignore-case %s + +## Verify that the GOFF header record is correct, with all fields having a +## value. No other record is written. + +# CHECK: 03 f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# CHECK-NEXT: 00 00 00 01 00 03 00 00 00 00 00 00 03 33 ff 00 +# CHECK-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +# CHECK-EMPTY: + +--- !GOFF +- ModuleHeader: + ArchitectureLevel: 1 + PropertiesLength: 3 + Properties: 0333ff diff --git a/llvm/test/tools/yaml2obj/GOFF/invalid-record.yaml b/llvm/test/tools/yaml2obj/GOFF/invalid-record.yaml new file mode 100644 index 0000000000000..fd8b9f4b562b5 --- /dev/null +++ b/llvm/test/tools/yaml2obj/GOFF/invalid-record.yaml @@ -0,0 +1,7 @@ +# RUN: not yaml2obj %s 2>&1 | FileCheck %s + +# CHECK: YAML:{{[0-9]*}}:{{[0-9]*}}: error: unknown record type name 'Foo' +# CHECk: yaml2obj: error: failed to parse YAML input: Invalid argument + +--- !GOFF +- Foo: