Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SystemZ][z/OS] Implement yaml2obj for GOFF Object File Format #71445

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

ysyeda
Copy link
Contributor

@ysyeda ysyeda commented Nov 6, 2023

This PR implements yaml2obj for the GOFF Object File Format.

@llvmbot
Copy link
Collaborator

llvmbot commented Nov 6, 2023

@llvm/pr-subscribers-objectyaml

@llvm/pr-subscribers-llvm-binary-utilities

Author: Yusra Syeda (ysyeda)

Changes

This PR implements yaml2obj for the GOFF Object File Format.


Patch is 58.02 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/71445.diff

14 Files Affected:

  • (modified) llvm/include/llvm/BinaryFormat/GOFF.h (+27)
  • (added) llvm/include/llvm/ObjectYAML/GOFFYAML.h (+245)
  • (modified) llvm/include/llvm/ObjectYAML/ObjectYAML.h (+2)
  • (modified) llvm/include/llvm/ObjectYAML/yaml2obj.h (+5)
  • (modified) llvm/lib/ObjectYAML/CMakeLists.txt (+2)
  • (added) llvm/lib/ObjectYAML/GOFFEmitter.cpp (+451)
  • (added) llvm/lib/ObjectYAML/GOFFYAML.cpp (+375)
  • (modified) llvm/lib/ObjectYAML/ObjectYAML.cpp (+5)
  • (modified) llvm/lib/ObjectYAML/yaml2obj.cpp (+2)
  • (added) llvm/test/ObjectYAML/GOFF/GOFF-Basic.yaml (+59)
  • (added) llvm/test/ObjectYAML/GOFF/GOFF-LongSymbol.yaml (+92)
  • (added) llvm/test/ObjectYAML/GOFF/GOFF-OneSymb.yaml (+40)
  • (added) llvm/test/ObjectYAML/GOFF/GOFF-Relocation.yaml (+145)
  • (added) llvm/test/ObjectYAML/GOFF/GOFF-TXTSection.yaml (+49)
diff --git a/llvm/include/llvm/BinaryFormat/GOFF.h b/llvm/include/llvm/BinaryFormat/GOFF.h
index f1a30e41b736bda..061b1b83370c3b6 100644
--- a/llvm/include/llvm/BinaryFormat/GOFF.h
+++ b/llvm/include/llvm/BinaryFormat/GOFF.h
@@ -157,6 +157,33 @@ enum ESDAlignment : uint8_t {
   ESD_ALIGN_4Kpage = 12,
 };
 
+enum TXTRecordStyle : uint8_t {
+  TXT_RS_Byte = 0,
+  TXT_RS_Structured = 1,
+  TXT_RS_Unstructured = 2,
+};
+
+enum RLDReferenceType : uint8_t {
+  RLD_RT_RAddress = 0,
+  RLD_RT_ROffset = 1,
+  RLD_RT_RLength = 2,
+  RLD_RT_RRelativeImmediate = 6,
+  RLD_RT_RTypeConstant = 7, // XPLink ADA
+  RLD_RT_RLongDisplacement = 9,
+};
+
+enum RLDReferentType : uint8_t {
+  RLD_RO_Label = 0,
+  RLD_RO_Element = 1,
+  RLD_RO_Class = 2,
+  RLD_RO_Part = 3,
+};
+
+enum RLDAction : uint8_t {
+  RLD_ACT_Add = 0,
+  RLD_ACT_Subtract = 1,
+};
+
 enum ENDEntryPointRequest : uint8_t {
   END_EPR_None = 0,
   END_EPR_EsdidOffset = 1,
diff --git a/llvm/include/llvm/ObjectYAML/GOFFYAML.h b/llvm/include/llvm/ObjectYAML/GOFFYAML.h
new file mode 100644
index 000000000000000..9b58afe61e97864
--- /dev/null
+++ b/llvm/include/llvm/ObjectYAML/GOFFYAML.h
@@ -0,0 +1,245 @@
+//===- GOFFYAML.h - GOFF YAMLIO implementation ------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares classes for handling the YAML representation of GOFF.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_OBJECTYAML_GOFFYAML_H
+#define LLVM_OBJECTYAML_GOFFYAML_H
+
+//#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/GOFF.h"
+#include "llvm/ObjectYAML/YAML.h"
+#include <cstdint>
+#include <vector>
+
+namespace llvm {
+
+namespace GOFF {
+
+enum ESDFlags {
+  ESD_FillByteValuePresent = 1 << 7,
+  ESD_SymbolDisplayFlag = 1 << 6,
+  ESD_SymbolRenamingFlag = 1 << 5,
+  ESD_RemovableClass = 1 << 4
+};
+
+enum {
+  ESD_Mask_ERST = 0x07,
+  ESD_Mask_RQW = 0x07,
+  ESD_Mask_TextStyle = 0xf0,
+  ESD_Mask_BindingAlgorithm = 0x0f,
+};
+
+enum ESDBAFlags {
+  ESD_BA_Movable = 0x01,
+  ESD_BA_ReadOnly = 0x2,
+  ESD_BA_NoPrime = 0x4,
+  ESD_BA_COMMON = 0x8,
+  ESD_BA_Indirect = 0x10,
+};
+
+enum RLDFlags {
+  RLD_Same_RID = 0x80,
+  RLD_Same_PID = 0x40,
+  RLD_Same_Offset = 0x20,
+  RLD_EA_Present = 0x04,
+  RLD_Offset_Length = 0x02,
+  RLD_Adressing_Mode_Sensitivity = 0x01,
+  RLD_FetchStore = 0x100,
+};
+
+} // end namespace GOFF
+
+// The structure of the yaml files is not an exact 1:1 match to GOFF. In order
+// to use yaml::IO, we use these structures which are closer to the source.
+namespace GOFFYAML {
+
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_ESDSYMBOLTYPE)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_ESDNAMESPACEID)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_ESDFlags)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_ESDAMODE)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_ESDRMODE)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_ESDTEXTSTYLE)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_ESDBINDINGALGORITHM)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_ESDTASKINGBEHAVIOR)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_ESDEXECUTABLE)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_ESDLINKAGETYPE)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_ESDBINDINGSTRENGTH)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_ESDLOADINGBEHAVIOR)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_ESDBINDINGSCOPE)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_ESDALIGNMENT)
+LLVM_YAML_STRONG_TYPEDEF(uint64_t, GOFF_BAFLAGS)
+
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_TEXTRECORDSTYLE)
+
+LLVM_YAML_STRONG_TYPEDEF(uint16_t, GOFF_RLDFLAGS)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_RLDREFERENCETYPE)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_RLDREFERENTTYPE)
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, GOFF_RLDACTION)
+
+struct RecordBase {
+  enum RecordBaseKind { RBK_Relocations, RBK_Symbol, RBK_Section };
+
+private:
+  const RecordBaseKind Kind;
+
+protected:
+  RecordBase(RecordBaseKind Kind) : Kind(Kind) {}
+
+public:
+  RecordBaseKind getKind() const { return Kind; }
+};
+typedef std::unique_ptr<RecordBase> RecordPtr;
+
+struct Relocation {
+  GOFF_RLDFLAGS Flags;
+  GOFF_RLDREFERENCETYPE ReferenceType;
+  GOFF_RLDREFERENTTYPE ReferentType;
+  GOFF_RLDACTION Action;
+  uint32_t RPointer;
+  uint32_t PPointer;
+  uint64_t Offset;
+  uint32_t ExtAttrID;
+  uint32_t ExtAttrOffset;
+  uint8_t TargetFieldByteLength;
+  uint8_t BitLength;
+  uint8_t BitOffset;
+};
+
+struct Relocations : public RecordBase {
+  Relocations() : RecordBase(RBK_Relocations) {}
+
+  std::vector<Relocation> Relocs;
+
+  static bool classof(const RecordBase *Rec) {
+    return Rec->getKind() == RBK_Relocations;
+  }
+};
+
+struct Section : public RecordBase {
+  Section() : RecordBase(RBK_Section) {}
+
+  StringRef SymbolName;
+  uint32_t SymbolID;
+  uint32_t Offset;
+  uint32_t TrueLength;
+  uint16_t TextEncoding;
+  uint16_t DataLength;
+  GOFF_TEXTRECORDSTYLE TextStyle;
+
+  std::optional<yaml::BinaryRef> Data;
+
+  static bool classof(const RecordBase *Rec) {
+    return Rec->getKind() == RBK_Section;
+  }
+};
+
+struct Symbol : public RecordBase {
+  Symbol() : RecordBase(RBK_Symbol) {}
+
+  StringRef Name;
+  GOFF_ESDSYMBOLTYPE Type;
+  uint32_t ID;
+  uint32_t OwnerID;
+  uint32_t Address;
+  uint32_t Length;
+  uint32_t ExtAttrID;
+  uint32_t ExtAttrOffset;
+  GOFF_ESDNAMESPACEID NameSpace;
+  GOFF_ESDFlags Flags;
+  uint8_t FillByteValue;
+  uint32_t PSectID;
+  uint32_t Priority;
+  std::optional<llvm::yaml::Hex64> Signature;
+  GOFF_ESDAMODE Amode;
+  GOFF_ESDRMODE Rmode;
+  GOFF_ESDTEXTSTYLE TextStyle;
+  GOFF_ESDBINDINGALGORITHM BindingAlgorithm;
+  GOFF_ESDTASKINGBEHAVIOR TaskingBehavior;
+  GOFF_ESDEXECUTABLE Executable;
+  GOFF_ESDLINKAGETYPE LinkageType;
+  GOFF_ESDBINDINGSTRENGTH BindingStrength;
+  GOFF_ESDLOADINGBEHAVIOR LoadingBehavior;
+  GOFF_ESDBINDINGSCOPE BindingScope;
+  GOFF_ESDALIGNMENT Alignment;
+  GOFF_BAFLAGS BAFlags;
+
+  static bool classof(const RecordBase *Rec) {
+    return Rec->getKind() == RBK_Symbol;
+  }
+};
+
+struct FileHeader {
+  uint32_t TargetEnvironment;
+  uint32_t TargetOperatingSystem;
+  uint16_t CCSID;
+  StringRef CharacterSetName;
+  StringRef LanguageProductIdentifier;
+  uint32_t ArchitectureLevel;
+  std::optional<uint16_t> InternalCCSID;
+  std::optional<uint8_t> TargetSoftwareEnvironment;
+};
+
+struct Object {
+  FileHeader Header;
+  std::vector<RecordPtr> Records;
+
+  Object();
+};
+
+} // end namespace GOFFYAML
+
+} // end namespace llvm
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(GOFFYAML::Relocation)
+LLVM_YAML_IS_SEQUENCE_VECTOR(GOFFYAML::RecordPtr)
+
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_ESDSYMBOLTYPE)
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_ESDNAMESPACEID)
+LLVM_YAML_DECLARE_BITSET_TRAITS(GOFFYAML::GOFF_ESDFlags)
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_ESDAMODE)
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_ESDRMODE)
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_ESDTEXTSTYLE)
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_ESDBINDINGALGORITHM)
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_ESDTASKINGBEHAVIOR)
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_ESDEXECUTABLE)
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_ESDLINKAGETYPE)
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_ESDBINDINGSTRENGTH)
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_ESDLOADINGBEHAVIOR)
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_ESDBINDINGSCOPE)
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_ESDALIGNMENT)
+LLVM_YAML_DECLARE_BITSET_TRAITS(GOFFYAML::GOFF_BAFLAGS)
+
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_TEXTRECORDSTYLE)
+
+LLVM_YAML_DECLARE_BITSET_TRAITS(GOFFYAML::GOFF_RLDFLAGS)
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_RLDREFERENCETYPE)
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_RLDREFERENTTYPE)
+LLVM_YAML_DECLARE_ENUM_TRAITS(GOFFYAML::GOFF_RLDACTION)
+
+LLVM_YAML_DECLARE_MAPPING_TRAITS(GOFFYAML::Relocation)
+LLVM_YAML_DECLARE_MAPPING_TRAITS(GOFFYAML::Section)
+LLVM_YAML_DECLARE_MAPPING_TRAITS(GOFFYAML::Symbol)
+LLVM_YAML_DECLARE_MAPPING_TRAITS(GOFFYAML::FileHeader)
+LLVM_YAML_DECLARE_MAPPING_TRAITS(GOFFYAML::Object)
+
+namespace llvm {
+namespace yaml {
+
+template <> struct CustomMappingTraits<GOFFYAML::RecordPtr> {
+  static void inputOne(IO &IO, StringRef Key, GOFFYAML::RecordPtr &Elem);
+  static void output(IO &IO, GOFFYAML::RecordPtr &Elem);
+};
+
+} // end namespace yaml
+} // end namespace llvm
+
+#endif // LLVM_OBJECTYAML_GOFFYAML_H
diff --git a/llvm/include/llvm/ObjectYAML/ObjectYAML.h b/llvm/include/llvm/ObjectYAML/ObjectYAML.h
index b63607e6796b0b7..7fd15cf290f4a4b 100644
--- a/llvm/include/llvm/ObjectYAML/ObjectYAML.h
+++ b/llvm/include/llvm/ObjectYAML/ObjectYAML.h
@@ -13,6 +13,7 @@
 #include "llvm/ObjectYAML/COFFYAML.h"
 #include "llvm/ObjectYAML/DXContainerYAML.h"
 #include "llvm/ObjectYAML/ELFYAML.h"
+#include "llvm/ObjectYAML/GOFFYAML.h"
 #include "llvm/ObjectYAML/MachOYAML.h"
 #include "llvm/ObjectYAML/MinidumpYAML.h"
 #include "llvm/ObjectYAML/OffloadYAML.h"
@@ -30,6 +31,7 @@ struct YamlObjectFile {
   std::unique_ptr<ArchYAML::Archive> Arch;
   std::unique_ptr<ELFYAML::Object> Elf;
   std::unique_ptr<COFFYAML::Object> Coff;
+  std::unique_ptr<GOFFYAML::Object> Goff;
   std::unique_ptr<MachOYAML::Object> MachO;
   std::unique_ptr<MachOYAML::UniversalBinary> FatMachO;
   std::unique_ptr<MinidumpYAML::Object> Minidump;
diff --git a/llvm/include/llvm/ObjectYAML/yaml2obj.h b/llvm/include/llvm/ObjectYAML/yaml2obj.h
index 000da077bb18c6a..3b458c3cd890b12 100644
--- a/llvm/include/llvm/ObjectYAML/yaml2obj.h
+++ b/llvm/include/llvm/ObjectYAML/yaml2obj.h
@@ -32,6 +32,10 @@ namespace ELFYAML {
 struct Object;
 }
 
+namespace GOFFYAML {
+struct Object;
+}
+
 namespace MinidumpYAML {
 struct Object;
 }
@@ -64,6 +68,7 @@ using ErrorHandler = llvm::function_ref<void(const Twine &Msg)>;
 
 bool yaml2archive(ArchYAML::Archive &Doc, raw_ostream &Out, ErrorHandler EH);
 bool yaml2coff(COFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH);
+bool yaml2goff(GOFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH);
 bool yaml2elf(ELFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH,
               uint64_t MaxSize);
 bool yaml2macho(YamlObjectFile &Doc, raw_ostream &Out, ErrorHandler EH);
diff --git a/llvm/lib/ObjectYAML/CMakeLists.txt b/llvm/lib/ObjectYAML/CMakeLists.txt
index c081009653d4f8c..b36974d47d9f897 100644
--- a/llvm/lib/ObjectYAML/CMakeLists.txt
+++ b/llvm/lib/ObjectYAML/CMakeLists.txt
@@ -13,6 +13,8 @@ add_llvm_component_library(LLVMObjectYAML
   DXContainerYAML.cpp
   ELFEmitter.cpp
   ELFYAML.cpp
+  GOFFEmitter.cpp
+  GOFFYAML.cpp
   MachOEmitter.cpp
   MachOYAML.cpp
   ObjectYAML.cpp
diff --git a/llvm/lib/ObjectYAML/GOFFEmitter.cpp b/llvm/lib/ObjectYAML/GOFFEmitter.cpp
new file mode 100644
index 000000000000000..e3c4f7bce21cf5e
--- /dev/null
+++ b/llvm/lib/ObjectYAML/GOFFEmitter.cpp
@@ -0,0 +1,451 @@
+//===- yaml2goff - Convert YAML to a GOFF object file ---------------------===//
+//
+// 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
+/// The GOFF component of yaml2obj.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/IndexedMap.h"
+#include "llvm/ObjectYAML/ObjectYAML.h"
+#include "llvm/ObjectYAML/yaml2obj.h"
+#include "llvm/Support/ConvertEBCDIC.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+namespace {
+
+static const uint8_t TXTMaxDataLength = 56;
+
+// Common flag values on records.
+enum {
+  // Flag: This record is continued.
+  Rec_Continued = 1 << (8 - 7 - 1),
+
+  // Flag: This record is a continuation.
+  Rec_Continuation = 1 << (8 - 6 - 1),
+};
+
+template <typename value_type> struct BinaryBeImpl {
+  value_type Value;
+  BinaryBeImpl(value_type V) : Value(V) {}
+};
+
+template <typename value_type>
+raw_ostream &operator<<(raw_ostream &OS, const BinaryBeImpl<value_type> &BBE) {
+  char Buffer[sizeof(BBE.Value)];
+  support::endian::write<value_type, llvm::endianness::big, support::unaligned>(
+      Buffer, BBE.Value);
+  OS.write(Buffer, sizeof(BBE.Value));
+  return OS;
+}
+
+template <typename value_type> BinaryBeImpl<value_type> binaryBe(value_type V) {
+  return BinaryBeImpl<value_type>(V);
+}
+
+struct ZerosImpl {
+  size_t NumBytes;
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const ZerosImpl &Z) {
+  OS.write_zeros(Z.NumBytes);
+  return OS;
+}
+
+ZerosImpl zeros(const size_t NumBytes) { return ZerosImpl{NumBytes}; }
+
+raw_ostream &operator<<(raw_ostream &OS, const yaml::BinaryRef &Data) {
+  Data.writeAsBinary(OS);
+  return OS;
+}
+
+// The GOFFOstream is responsible to write the data into the fixed physical
+// records of the format. A user of this class announces the the begin 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 {
+  /// The underlying raw_ostream.
+  raw_ostream &OS;
+
+  /// The number of logical records emitted to 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 begin of new record.
+  bool NewLogicalRecord;
+
+  // Return the number of bytes left to write until next physical record.
+  // Please note that we maintain the total numbers of byte 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);
+
+  /// Fill the last physical record of a logical record with zero bytes.
+  void fillRecord();
+
+  /// See raw_ostream::write_impl.
+  void write_impl(const char *Ptr, size_t Size) override;
+
+  /// Return the current position within the stream, not counting the bytes
+  /// currently in the buffer.
+  uint64_t current_pos() const override { return OS.tell(); }
+
+public:
+  explicit GOFFOstream(raw_ostream &OS)
+      : OS(OS), LogicalRecords(0), RemainingSize(0), NewLogicalRecord(false) {
+    SetBufferSize(GOFF::PayloadLength);
+  }
+
+  ~GOFFOstream() { finalize(); }
+
+  void newRecord(GOFF::RecordType Type, size_t Size);
+
+  void finalize() { fillRecord(); }
+
+  uint32_t logicalRecords() { return LogicalRecords; }
+};
+
+void GOFFOstream::writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type,
+                                    size_t RemainingSize, uint8_t Flags) {
+  uint8_t TypeAndFlags = Flags | (Type << 4);
+  if (RemainingSize > GOFF::RecordLength)
+    TypeAndFlags |= Rec_Continued;
+  OS << binaryBe(static_cast<unsigned char>(GOFF::PTVPrefix))
+     << binaryBe(static_cast<unsigned char>(TypeAndFlags))
+     << binaryBe(static_cast<unsigned char>(0));
+}
+
+void GOFFOstream::newRecord(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;
+}
+
+void GOFFOstream::fillRecord() {
+  assert((GetNumBytesInBuffer() <= RemainingSize) &&
+         "More bytes in buffer than expected");
+  size_t Remains = RemainingSize - GetNumBytesInBuffer();
+  if (Remains) {
+    assert((Remains < GOFF::RecordLength) &&
+           "Attempt 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");
+}
+
+void GOFFOstream::write_impl(const char *Ptr, size_t Size) {
+  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;
+  }
+  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);
+    }
+  }
+}
+
+class GOFFState {
+  void writeHeader(GOFFYAML::FileHeader &FileHdr);
+  void writeEnd();
+  void writeSymbol(GOFFYAML::Symbol Sym);
+  void writeSection(GOFFYAML::Section Sec);
+  void writeRelocationDirectory(GOFFYAML::Relocations Rel);
+
+  void reportError(const Twine &Msg) {
+    ErrHandler(Msg);
+    HasError = true;
+  }
+
+  GOFFState(raw_ostream &OS, GOFFYAML::Object &Doc,
+            yaml::ErrorHandler ErrHandler)
+      : GW(OS), Doc(Doc), ErrHandler(ErrHandler), SymbolID(0), HasError(false) {
+  }
+
+  ~GOFFState() { GW.finalize(); }
+
+  bool writeObject();
+
+public:
+  static bool writeGOFF(raw_ostream &OS, GOFFYAML::Object &Doc,
+                        yaml::ErrorHandler ErrHandler);
+
+private:
+  GOFFOstream GW;
+  GOFFYAML::Object &Doc;
+  yaml::ErrorHandler ErrHandler;
+  uint16_t SymbolID;
+  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.newRecord(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::writeSymbol(GOFFYAML::Symbol Sym) {
+  if (Sym.ID != SymbolID + 1)
+    reportError("Symbol IDs not monoton " + Sym.Name);
+  else
+    ++SymbolID;
+  if (Sym.OwnerID >= SymbolID)
+    reportError("Owner ID not defined " + Sym.Name);
+  SmallString<80> SymName;
+  if (std::error_code EC = ConverterEBCDIC::convertToEBCDIC(Sym.Name, Sy...
[truncated]

Copy link

github-actions bot commented Nov 6, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Collaborator

@jh7370 jh7370 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the patch. I've skimmed through it and noted a number of nits. A GOFF expert will want to review the core functionality. Some high-level comments:

  1. Could this be broken into smaller pieces of incremental functionality? For example, could you start with an object with a file header and no symbols/sections etc?
  2. From experience with ELF yaml2obj, to facilitate testing edge cases, it's important to be able to customise as much as possible. For example, I noted one case where you say you follow a strict order of components. It's not likely important to be able to change that order at this point, but if the components don't HAVE to be in that order, you should consider whether your code is flexible enough to allow changing it based on user input at runtime.
  3. To avoid unnecessarily large chunks of boiler plate YAML, try to provide reasonable defaults wherever possible. So prefer mapOptional over mapRequired wherever possible, and auto-generate values either with fixed values (e.g. 0) or derived from the rest of the content.
  4. Try to be as tolerant as possible of "invalid" input: if it doesn't actively prevent generating an object file, then it's important to allow it as otherwise you may not be able to use the tool to generate test inputs that exercise error paths in llvm-readobj.

llvm/include/llvm/ObjectYAML/GOFFYAML.h Outdated Show resolved Hide resolved
llvm/lib/ObjectYAML/GOFFEmitter.cpp Outdated Show resolved Hide resolved
llvm/lib/ObjectYAML/GOFFEmitter.cpp Outdated Show resolved Hide resolved
llvm/lib/ObjectYAML/GOFFEmitter.cpp Outdated Show resolved Hide resolved
llvm/lib/ObjectYAML/GOFFEmitter.cpp Outdated Show resolved Hide resolved
llvm/lib/ObjectYAML/GOFFEmitter.cpp Outdated Show resolved Hide resolved
llvm/lib/ObjectYAML/GOFFEmitter.cpp Outdated Show resolved Hide resolved
llvm/lib/ObjectYAML/GOFFEmitter.cpp Outdated Show resolved Hide resolved
llvm/lib/ObjectYAML/GOFFEmitter.cpp Outdated Show resolved Hide resolved
llvm/lib/ObjectYAML/GOFFEmitter.cpp Outdated Show resolved Hide resolved
@jh7370
Copy link
Collaborator

jh7370 commented Nov 13, 2023

@ysyeda, please acknowledge all feedback (https://llvm.org/docs/CodeReview.html#acknowledge-all-reviewer-feedback).

Copy link
Collaborator

@jh7370 jh7370 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you see my out-of-line high-level comments, because you haven't responded to them at all, as far as I can tell.

@@ -73,6 +73,22 @@ raw_ostream &operator<<(raw_ostream &OS, const yaml::BinaryRef &Data) {
// 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 {

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: no blank line here to start the class.


~GOFFOstream() { finalize(); }

void newRecord(GOFF::RecordType Type, size_t Size);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


~GOFFOstream() { finalize(); }

void newRecord(GOFF::RecordType Type, size_t Size);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -352,7 +354,7 @@ void GOFFState::writeSection(GOFFYAML::Section Sec) {

void GOFFState::writeRelocationDirectory(GOFFYAML::Relocations Rels) {
size_t Size = 0;
for (auto &Rel : Rels.Relocs) {
for (const llvm::GOFFYAML::Relocation &Rel : Rels.Relocs) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'm fairly confident here and below you don't need llvm::.

}

bool GOFFState::writeObject() {
// We follow a strict, recommended order:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's true that GOFF files should follow that order, but this tool doesn't actually obey that rule (which is good, because it lets us test error states) so imo we can actually delete this comment.

@ysyeda
Copy link
Contributor Author

ysyeda commented Nov 29, 2023

@jh7370 I've broken this PR down to support just header and the end records as you suggested. #73859

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants