[llvm] 2fa6bcf - [SystemZ][z/OS] Add GOFFObjectFile class support for HDR, ESD and END records

Neumann Hon via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 28 16:08:53 PDT 2023


Author: Yusra Syeda
Date: 2023-04-28T19:08:26-04:00
New Revision: 2fa6bcf02f1585df3af6d0081215e23c07421f43

URL: https://github.com/llvm/llvm-project/commit/2fa6bcf02f1585df3af6d0081215e23c07421f43
DIFF: https://github.com/llvm/llvm-project/commit/2fa6bcf02f1585df3af6d0081215e23c07421f43.diff

LOG: [SystemZ][z/OS] Add GOFFObjectFile class support for HDR, ESD and END records

This patch details the GOFF file format and implements the GOFFObjectfile class
with support for only the HDR, ESD and END GOFF records.

Reviewed By: jhenderson, kpn

Differential Revision: https://reviews.llvm.org/D98437

Added: 
    llvm/include/llvm/Object/GOFF.h
    llvm/include/llvm/Object/GOFFObjectFile.h
    llvm/lib/Object/GOFFObjectFile.cpp
    llvm/unittests/Object/GOFFObjectFileTest.cpp

Modified: 
    llvm/include/llvm/BinaryFormat/GOFF.h
    llvm/include/llvm/Object/Binary.h
    llvm/include/llvm/Object/ObjectFile.h
    llvm/lib/Object/CMakeLists.txt
    llvm/lib/Object/ObjectFile.cpp
    llvm/unittests/Object/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/BinaryFormat/GOFF.h b/llvm/include/llvm/BinaryFormat/GOFF.h
index 96992414c6ccf..b4ddbabdf1e4e 100644
--- a/llvm/include/llvm/BinaryFormat/GOFF.h
+++ b/llvm/include/llvm/BinaryFormat/GOFF.h
@@ -9,7 +9,8 @@
 // This header contains common, non-processor-specific data structures and
 // constants for the GOFF file format.
 //
-// GOFF specifics can be found in MVS Program Management: Advanced Facilities
+// GOFF specifics can be found in MVS Program Management: Advanced Facilities.
+//
 //===----------------------------------------------------------------------===//
 
 #ifndef LLVM_BINARYFORMAT_GOFF_H
@@ -18,14 +19,142 @@
 #include "llvm/Support/DataTypes.h"
 
 namespace llvm {
-
 namespace GOFF {
 
+constexpr uint8_t RecordLength = 80;
+constexpr uint8_t RecordPrefixLength = 3;
+constexpr uint8_t PayloadLength = 77;
+
+// Prefix byte on every record. This indicates GOFF format.
+constexpr uint8_t PTVPrefix = 0x03;
+
+enum RecordType : uint8_t {
+  RT_ESD = 0,
+  RT_TXT = 1,
+  RT_RLD = 2,
+  RT_LEN = 3,
+  RT_END = 4,
+  RT_HDR = 15,
+};
+
+enum ESDSymbolType : uint8_t {
+  ESD_ST_SectionDefinition = 0,
+  ESD_ST_ElementDefinition = 1,
+  ESD_ST_LabelDefinition = 2,
+  ESD_ST_PartReference = 3,
+  ESD_ST_ExternalReference = 4,
+};
+
+enum ESDNameSpaceId : uint8_t {
+  ESD_NS_ProgramManagementBinder = 0,
+  ESD_NS_NormalName = 1,
+  ESD_NS_PseudoRegister = 2,
+  ESD_NS_Parts = 3
+};
+
+enum ESDReserveQwords : uint8_t {
+  ESD_RQ_0 = 0,
+  ESD_RQ_1 = 1,
+  ESD_RQ_2 = 2,
+  ESD_RQ_3 = 3
+};
+
+enum ESDAmode : uint8_t {
+  ESD_AMODE_None = 0,
+  ESD_AMODE_24 = 1,
+  ESD_AMODE_31 = 2,
+  ESD_AMODE_ANY = 3,
+  ESD_AMODE_64 = 4,
+  ESD_AMODE_MIN = 16,
+};
+
+enum ESDRmode : uint8_t {
+  ESD_RMODE_None = 0,
+  ESD_RMODE_24 = 1,
+  ESD_RMODE_31 = 3,
+  ESD_RMODE_64 = 4,
+};
+
+enum ESDTextStyle : uint8_t {
+  ESD_TS_ByteOriented = 0,
+  ESD_TS_Structured = 1,
+  ESD_TS_Unstructured = 2,
+};
+
+enum ESDBindingAlgorithm : uint8_t {
+  ESD_BA_Concatenate = 0,
+  ESD_BA_Merge = 1,
+};
+
+enum ESDTaskingBehavior : uint8_t {
+  ESD_TA_Unspecified = 0,
+  ESD_TA_NonReus = 1,
+  ESD_TA_Reus = 2,
+  ESD_TA_Rent = 3,
+};
+
+enum ESDExecutable : uint8_t {
+  ESD_EXE_Unspecified = 0,
+  ESD_EXE_DATA = 1,
+  ESD_EXE_CODE = 2,
+};
+
+enum ESDDuplicateSymbolSeverity : uint8_t {
+  ESD_DSS_NoWarning = 0,
+  ESD_DSS_Warning = 1,
+  ESD_DSS_Error = 2,
+  ESD_DSS_Reserved = 3,
+};
+
+enum ESDBindingStrength : uint8_t {
+  ESD_BST_Strong = 0,
+  ESD_BST_Weak = 1,
+};
+
+enum ESDLoadingBehavior : uint8_t {
+  ESD_LB_Initial = 0,
+  ESD_LB_Deferred = 1,
+  ESD_LB_NoLoad = 2,
+  ESD_LB_Reserved = 3,
+};
+
+enum ESDBindingScope : uint8_t {
+  ESD_BSC_Unspecified = 0,
+  ESD_BSC_Section = 1,
+  ESD_BSC_Module = 2,
+  ESD_BSC_Library = 3,
+  ESD_BSC_ImportExport = 4,
+};
+
+enum ESDLinkageType : uint8_t { ESD_LT_OS = 0, ESD_LT_XPLink = 1 };
+
+enum ESDAlignment : uint8_t {
+  ESD_ALIGN_Byte = 0,
+  ESD_ALIGN_Halfword = 1,
+  ESD_ALIGN_Fullword = 2,
+  ESD_ALIGN_Doubleword = 3,
+  ESD_ALIGN_Quadword = 4,
+  ESD_ALIGN_32byte = 5,
+  ESD_ALIGN_64byte = 6,
+  ESD_ALIGN_128byte = 7,
+  ESD_ALIGN_256byte = 8,
+  ESD_ALIGN_512byte = 9,
+  ESD_ALIGN_1024byte = 10,
+  ESD_ALIGN_2Kpage = 11,
+  ESD_ALIGN_4Kpage = 12,
+};
+
+enum ENDEntryPointRequest : uint8_t {
+  END_EPR_None = 0,
+  END_EPR_EsdidOffset = 1,
+  END_EPR_ExternalName = 2,
+  END_EPR_Reserved = 3,
+};
+
 // \brief Subsections of the primary C_CODE section in the object file.
 enum SubsectionKind : uint8_t {
   SK_PPA1 = 2,
 };
-
 } // end namespace GOFF
 
 } // end namespace llvm

diff  --git a/llvm/include/llvm/Object/Binary.h b/llvm/include/llvm/Object/Binary.h
index 955b0b0ce2817..ce870e25acafe 100644
--- a/llvm/include/llvm/Object/Binary.h
+++ b/llvm/include/llvm/Object/Binary.h
@@ -69,6 +69,7 @@ class Binary {
     ID_MachO64L, // MachO 64-bit, little endian
     ID_MachO64B, // MachO 64-bit, big endian
 
+    ID_GOFF,
     ID_Wasm,
 
     ID_EndObjects
@@ -145,6 +146,8 @@ class Binary {
     return TypeID == ID_IR;
   }
 
+  bool isGOFF() const { return TypeID == ID_GOFF; }
+
   bool isMinidump() const { return TypeID == ID_Minidump; }
 
   bool isTapiFile() const { return TypeID == ID_TapiFile; }
@@ -164,6 +167,8 @@ class Binary {
       return Triple::MachO;
     if (isELF())
       return Triple::ELF;
+    if (isGOFF())
+      return Triple::GOFF;
     return Triple::UnknownObjectFormat;
   }
 

diff  --git a/llvm/include/llvm/Object/GOFF.h b/llvm/include/llvm/Object/GOFF.h
new file mode 100644
index 0000000000000..f4aa04cd99fcf
--- /dev/null
+++ b/llvm/include/llvm/Object/GOFF.h
@@ -0,0 +1,284 @@
+//===- GOFF.h - GOFF object file 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 the GOFFObjectFile class.
+// Record classes and derivatives are also declared and implemented.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_OBJECT_GOFF_H
+#define LLVM_OBJECT_GOFF_H
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/BinaryFormat/GOFF.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace object {
+
+/// \brief Represents a GOFF physical record.
+///
+/// Specifies protected member functions to manipulate the record. These should
+/// be called from deriving classes to change values as that record specifies.
+class Record {
+public:
+  static Error getContinuousData(const uint8_t *Record, uint16_t DataLength,
+                                 int DataIndex, SmallString<256> &CompleteData);
+
+  static bool isContinued(const uint8_t *Record) {
+    uint8_t IsContinued;
+    getBits(Record, 1, 7, 1, IsContinued);
+    return IsContinued;
+  }
+
+  static bool isContinuation(const uint8_t *Record) {
+    uint8_t IsContinuation;
+    getBits(Record, 1, 6, 1, IsContinuation);
+    return IsContinuation;
+  }
+
+protected:
+  /// \brief Get bit field of specified byte.
+  ///
+  /// Used to pack bit fields into one byte. Fields are packed left to right.
+  /// Bit index zero is the most significant bit of the byte.
+  ///
+  /// \param ByteIndex index of byte the field is in.
+  /// \param BitIndex index of first bit of field.
+  /// \param Length length of bit field.
+  /// \param Value value of bit field.
+  static void getBits(const uint8_t *Bytes, uint8_t ByteIndex, uint8_t BitIndex,
+                      uint8_t Length, uint8_t &Value) {
+    assert(ByteIndex < GOFF::RecordLength && "Byte index out of bounds!");
+    assert(BitIndex < 8 && "Bit index out of bounds!");
+    assert(Length + BitIndex <= 8 && "Bit length too long!");
+
+    get<uint8_t>(Bytes, ByteIndex, Value);
+    Value = (Value >> (8 - BitIndex - Length)) & ((1 << Length) - 1);
+  }
+
+  template <class T>
+  static void get(const uint8_t *Bytes, uint8_t ByteIndex, T &Value) {
+    assert(ByteIndex + sizeof(T) <= GOFF::RecordLength &&
+           "Byte index out of bounds!");
+    Value = support::endian::read<T, support::big, support::unaligned>(
+        &Bytes[ByteIndex]);
+  }
+};
+
+class HDRRecord : public Record {
+public:
+  static Error getData(const uint8_t *Record, SmallString<256> &CompleteData);
+
+  static uint16_t getPropertyModuleLength(const uint8_t *Record) {
+    uint16_t Length;
+    get<uint16_t>(Record, 52, Length);
+    return Length;
+  }
+};
+
+class ESDRecord : public Record {
+public:
+  /// \brief Number of bytes for name; any more must go in continuation.
+  /// This is the number of bytes that can fit into the data field of an ESD
+  /// record.
+  static const uint8_t ESDMaxUncontinuedNameLength = 8;
+
+  /// \brief Maximum name length for ESD records and continuations.
+  /// This is the number of bytes that can fit into the data field of an ESD
+  /// record AND following continuations. This is limited fundamentally by the
+  /// 16 bit SIGNED length field.
+  static const uint16_t MaxNameLength = 32 * 1024;
+
+public:
+  static Error getData(const uint8_t *Record, SmallString<256> &CompleteData);
+
+  // ESD Get routines.
+  static void getSymbolType(const uint8_t *Record,
+                            GOFF::ESDSymbolType &SymbolType) {
+    uint8_t Value;
+    get<uint8_t>(Record, 3, Value);
+    SymbolType = (GOFF::ESDSymbolType)Value;
+  }
+
+  static void getEsdId(const uint8_t *Record, uint32_t &EsdId) {
+    get<uint32_t>(Record, 4, EsdId);
+  }
+
+  static void getParentEsdId(const uint8_t *Record, uint32_t &EsdId) {
+    get<uint32_t>(Record, 8, EsdId);
+  }
+
+  static void getOffset(const uint8_t *Record, uint32_t &Offset) {
+    get<uint32_t>(Record, 16, Offset);
+  }
+
+  static void getLength(const uint8_t *Record, uint32_t &Length) {
+    get<uint32_t>(Record, 24, Length);
+  }
+
+  static void getNameSpaceId(const uint8_t *Record, GOFF::ESDNameSpaceId &Id) {
+    uint8_t Value;
+    get<uint8_t>(Record, 40, Value);
+    Id = (GOFF::ESDNameSpaceId)Value;
+  }
+
+  static void getFillBytePresent(const uint8_t *Record, bool &Present) {
+    uint8_t Value;
+    getBits(Record, 41, 0, 1, Value);
+    Present = (bool)Value;
+  }
+
+  static void getNameMangled(const uint8_t *Record, bool &Mangled) {
+    uint8_t Value;
+    getBits(Record, 41, 1, 1, Value);
+    Mangled = (bool)Value;
+  }
+
+  static void getRenamable(const uint8_t *Record, bool &Renamable) {
+    uint8_t Value;
+    getBits(Record, 41, 2, 1, Value);
+    Renamable = (bool)Value;
+  }
+
+  static void getRemovable(const uint8_t *Record, bool &Removable) {
+    uint8_t Value;
+    getBits(Record, 41, 3, 1, Value);
+    Removable = (bool)Value;
+  }
+
+  static void getFillByteValue(const uint8_t *Record, uint8_t &Fill) {
+    get<uint8_t>(Record, 42, Fill);
+  }
+
+  static void getAdaEsdId(const uint8_t *Record, uint32_t &EsdId) {
+    get<uint32_t>(Record, 44, EsdId);
+  }
+
+  static void getSortPriority(const uint8_t *Record, uint32_t &Priority) {
+    get<uint32_t>(Record, 48, Priority);
+  }
+
+  static void getAmode(const uint8_t *Record, GOFF::ESDAmode &Amode) {
+    uint8_t Value;
+    get<uint8_t>(Record, 60, Value);
+    Amode = (GOFF::ESDAmode)Value;
+  }
+
+  static void getRmode(const uint8_t *Record, GOFF::ESDRmode &Rmode) {
+    uint8_t Value;
+    get<uint8_t>(Record, 61, Value);
+    Rmode = (GOFF::ESDRmode)Value;
+  }
+
+  static void getTextStyle(const uint8_t *Record, GOFF::ESDTextStyle &Style) {
+    uint8_t Value;
+    getBits(Record, 62, 0, 4, Value);
+    Style = (GOFF::ESDTextStyle)Value;
+  }
+
+  static void getBindingAlgorithm(const uint8_t *Record,
+                                  GOFF::ESDBindingAlgorithm &Algorithm) {
+    uint8_t Value;
+    getBits(Record, 62, 4, 4, Value);
+    Algorithm = (GOFF::ESDBindingAlgorithm)Value;
+  }
+
+  static void getTaskingBehavior(const uint8_t *Record,
+                                 GOFF::ESDTaskingBehavior &TaskingBehavior) {
+    uint8_t Value;
+    getBits(Record, 63, 0, 3, Value);
+    TaskingBehavior = (GOFF::ESDTaskingBehavior)Value;
+  }
+
+  static void getReadOnly(const uint8_t *Record, bool &ReadOnly) {
+    uint8_t Value;
+    getBits(Record, 63, 4, 1, Value);
+    ReadOnly = (bool)Value;
+  }
+
+  static void getExecutable(const uint8_t *Record,
+                            GOFF::ESDExecutable &Executable) {
+    uint8_t Value;
+    getBits(Record, 63, 5, 3, Value);
+    Executable = (GOFF::ESDExecutable)Value;
+  }
+
+  static void getDuplicateSeverity(const uint8_t *Record,
+                                   GOFF::ESDDuplicateSymbolSeverity &DSS) {
+    uint8_t Value;
+    getBits(Record, 64, 2, 2, Value);
+    DSS = (GOFF::ESDDuplicateSymbolSeverity)Value;
+  }
+
+  static void getBindingStrength(const uint8_t *Record,
+                                 GOFF::ESDBindingStrength &Strength) {
+    uint8_t Value;
+    getBits(Record, 64, 4, 4, Value);
+    Strength = (GOFF::ESDBindingStrength)Value;
+  }
+
+  static void getLoadingBehavior(const uint8_t *Record,
+                                 GOFF::ESDLoadingBehavior &Behavior) {
+    uint8_t Value;
+    getBits(Record, 65, 0, 2, Value);
+    Behavior = (GOFF::ESDLoadingBehavior)Value;
+  }
+
+  static void getIndirectReference(const uint8_t *Record, bool &Indirect) {
+    uint8_t Value;
+    getBits(Record, 65, 3, 1, Value);
+    Indirect = (bool)Value;
+  }
+
+  static void getBindingScope(const uint8_t *Record,
+                              GOFF::ESDBindingScope &Scope) {
+    uint8_t Value;
+    getBits(Record, 65, 4, 4, Value);
+    Scope = (GOFF::ESDBindingScope)Value;
+  }
+
+  static void getLinkageType(const uint8_t *Record,
+                             GOFF::ESDLinkageType &Type) {
+    uint8_t Value;
+    getBits(Record, 66, 2, 1, Value);
+    Type = (GOFF::ESDLinkageType)Value;
+  }
+
+  static void getAlignment(const uint8_t *Record,
+                           GOFF::ESDAlignment &Alignment) {
+    uint8_t Value;
+    getBits(Record, 66, 3, 5, Value);
+    Alignment = (GOFF::ESDAlignment)Value;
+  }
+
+  static uint16_t getNameLength(const uint8_t *Record) {
+    uint16_t Length;
+    get<uint16_t>(Record, 70, Length);
+    return Length;
+  }
+};
+
+class ENDRecord : public Record {
+public:
+  static Error getData(const uint8_t *Record, SmallString<256> &CompleteData);
+
+  static uint16_t getNameLength(const uint8_t *Record) {
+    uint16_t Length;
+    get<uint16_t>(Record, 24, Length);
+    return Length;
+  }
+};
+
+} // end namespace object
+} // end namespace llvm
+
+#endif

diff  --git a/llvm/include/llvm/Object/GOFFObjectFile.h b/llvm/include/llvm/Object/GOFFObjectFile.h
new file mode 100644
index 0000000000000..37b6b1ec659a0
--- /dev/null
+++ b/llvm/include/llvm/Object/GOFFObjectFile.h
@@ -0,0 +1,130 @@
+//===- GOFFObjectFile.h - GOFF object file 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 the GOFFObjectFile class.
+// Record classes and derivatives are also declared and implemented.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_OBJECT_GOFFOBJECTFILE_H
+#define LLVM_OBJECT_GOFFOBJECTFILE_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/IndexedMap.h"
+#include "llvm/BinaryFormat/GOFF.h"
+#include "llvm/MC/SubtargetFeature.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/ConvertEBCDIC.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/Triple.h"
+
+namespace llvm {
+
+namespace object {
+
+class GOFFObjectFile : public ObjectFile {
+  IndexedMap<const uint8_t *> EsdPtrs; // Indexed by EsdId.
+
+  mutable DenseMap<uint32_t, std::pair<size_t, std::unique_ptr<char[]>>>
+      EsdNamesCache;
+
+  typedef DataRefImpl SectionEntryImpl;
+  // (EDID, 0)               code, r/o data section
+  // (EDID,PRID)             r/w data section
+  SmallVector<SectionEntryImpl, 256> SectionList;
+  mutable DenseMap<uint32_t, std::string> SectionDataCache;
+
+public:
+  Expected<StringRef> getSymbolName(SymbolRef Symbol) const;
+
+  GOFFObjectFile(MemoryBufferRef Object, Error &Err);
+  static inline bool classof(const Binary *V) { return V->isGOFF(); }
+  section_iterator section_begin() const override;
+  section_iterator section_end() const override;
+
+  uint8_t getBytesInAddress() const override { return 8; }
+
+  StringRef getFileFormatName() const override { return "GOFF-SystemZ"; }
+
+  Triple::ArchType getArch() const override { return Triple::systemz; }
+
+  Expected<SubtargetFeatures> getFeatures() const override { return SubtargetFeatures(); }
+
+  bool isRelocatableObject() const override { return true; }
+
+  void moveSymbolNext(DataRefImpl &Symb) const override;
+  basic_symbol_iterator symbol_begin() const override;
+  basic_symbol_iterator symbol_end() const override;
+
+  bool is64Bit() const override {
+    return true;
+  }
+
+private:
+  // SymbolRef.
+  Expected<StringRef> getSymbolName(DataRefImpl Symb) const override;
+  Expected<uint64_t> getSymbolAddress(DataRefImpl Symb) const override;
+  uint64_t getSymbolValueImpl(DataRefImpl Symb) const override;
+  uint64_t getCommonSymbolSizeImpl(DataRefImpl Symb) const override;
+  Expected<uint32_t> getSymbolFlags(DataRefImpl Symb) const override;
+  Expected<SymbolRef::Type> getSymbolType(DataRefImpl Symb) const override;
+  Expected<section_iterator> getSymbolSection(DataRefImpl Symb) const override;
+
+  const uint8_t *getSymbolEsdRecord(DataRefImpl Symb) const;
+  bool isSymbolUnresolved(DataRefImpl Symb) const;
+  bool isSymbolIndirect(DataRefImpl Symb) const;
+
+  // SectionRef.
+  void moveSectionNext(DataRefImpl &Sec) const override{};
+  virtual Expected<StringRef> getSectionName(DataRefImpl Sec) const override {
+    return StringRef();
+  }
+  uint64_t getSectionAddress(DataRefImpl Sec) const override { return 0; }
+  uint64_t getSectionSize(DataRefImpl Sec) const override { return 0; }
+  virtual Expected<ArrayRef<uint8_t>>
+  getSectionContents(DataRefImpl Sec) const override {
+    return ArrayRef<uint8_t>();
+  }
+  uint64_t getSectionIndex(DataRefImpl Sec) const override { return 0; }
+  uint64_t getSectionAlignment(DataRefImpl Sec) const override { return 0; }
+  bool isSectionCompressed(DataRefImpl Sec) const override { return false; }
+  bool isSectionText(DataRefImpl Sec) const override { return false; }
+  bool isSectionData(DataRefImpl Sec) const override { return false; }
+  bool isSectionBSS(DataRefImpl Sec) const override { return false; }
+  bool isSectionVirtual(DataRefImpl Sec) const override { return false; }
+  relocation_iterator section_rel_begin(DataRefImpl Sec) const override {
+    return relocation_iterator(RelocationRef(Sec, this));
+  }
+  relocation_iterator section_rel_end(DataRefImpl Sec) const override {
+    return relocation_iterator(RelocationRef(Sec, this));
+  }
+
+  const uint8_t *getSectionEdEsdRecord(DataRefImpl &Sec) const;
+  const uint8_t *getSectionPrEsdRecord(DataRefImpl &Sec) const;
+  const uint8_t *getSectionEdEsdRecord(uint32_t SectionIndex) const;
+  const uint8_t *getSectionPrEsdRecord(uint32_t SectionIndex) const;
+
+  // RelocationRef.
+  void moveRelocationNext(DataRefImpl &Rel) const override{};
+  uint64_t getRelocationOffset(DataRefImpl Rel) const override { return 0; }
+  symbol_iterator getRelocationSymbol(DataRefImpl Rel) const override {
+    DataRefImpl Temp;
+    return basic_symbol_iterator(SymbolRef(Temp, this));
+  }
+  uint64_t getRelocationType(DataRefImpl Rel) const override { return 0; }
+  void getRelocationTypeName(DataRefImpl Rel,
+                             SmallVectorImpl<char> &Result) const override{};
+};
+
+} // namespace object
+
+} // namespace llvm
+
+#endif

diff  --git a/llvm/include/llvm/Object/ObjectFile.h b/llvm/include/llvm/Object/ObjectFile.h
index f8c2eea2fcaba..684e7b500478b 100644
--- a/llvm/include/llvm/Object/ObjectFile.h
+++ b/llvm/include/llvm/Object/ObjectFile.h
@@ -392,6 +392,9 @@ class ObjectFile : public SymbolicFile {
                         uint32_t UniversalCputype = 0,
                         uint32_t UniversalIndex = 0);
 
+  static Expected<std::unique_ptr<ObjectFile>>
+  createGOFFObjectFile(MemoryBufferRef Object);
+
   static Expected<std::unique_ptr<WasmObjectFile>>
   createWasmObjectFile(MemoryBufferRef Object);
 };

diff  --git a/llvm/lib/Object/CMakeLists.txt b/llvm/lib/Object/CMakeLists.txt
index c9a93f398a9f2..bfb420e57a7f4 100644
--- a/llvm/lib/Object/CMakeLists.txt
+++ b/llvm/lib/Object/CMakeLists.txt
@@ -11,6 +11,7 @@ add_llvm_component_library(LLVMObject
   ELF.cpp
   ELFObjectFile.cpp
   Error.cpp
+  GOFFObjectFile.cpp
   FaultMapParser.cpp
   IRObjectFile.cpp
   IRSymtab.cpp

diff  --git a/llvm/lib/Object/GOFFObjectFile.cpp b/llvm/lib/Object/GOFFObjectFile.cpp
new file mode 100644
index 0000000000000..34b74386cbdae
--- /dev/null
+++ b/llvm/lib/Object/GOFFObjectFile.cpp
@@ -0,0 +1,479 @@
+//===- GOFFObjectFile.cpp - GOFF object file 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of the GOFFObjectFile class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Object/GOFFObjectFile.h"
+#include "llvm/BinaryFormat/GOFF.h"
+#include "llvm/Object/GOFF.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/raw_ostream.h"
+
+#ifndef DEBUG_TYPE
+#define DEBUG_TYPE "goff"
+#endif
+
+using namespace llvm::object;
+using namespace llvm;
+
+Expected<std::unique_ptr<ObjectFile>>
+ObjectFile::createGOFFObjectFile(MemoryBufferRef Object) {
+  Error Err = Error::success();
+  std::unique_ptr<GOFFObjectFile> Ret(new GOFFObjectFile(Object, Err));
+  if (Err)
+    return std::move(Err);
+  return std::move(Ret);
+}
+
+GOFFObjectFile::GOFFObjectFile(MemoryBufferRef Object, Error &Err)
+    : ObjectFile(Binary::ID_GOFF, Object) {
+  ErrorAsOutParameter ErrAsOutParam(&Err);
+  // Object file isn't the right size, bail out early.
+  if ((Object.getBufferSize() % GOFF::RecordLength) != 0) {
+    Err = createStringError(
+        object_error::unexpected_eof,
+        "object file is not the right size. Must be a multiple "
+        "of 80 bytes, but is " +
+            std::to_string(Object.getBufferSize()) + " bytes");
+    return;
+  }
+  // Object file doesn't start/end with HDR/END records.
+  // Bail out early.
+  if (Object.getBufferSize() != 0) {
+    if ((base()[1] & 0xF0) >> 4 != GOFF::RT_HDR) {
+      Err = createStringError(object_error::parse_failed,
+                              "object file must start with HDR record");
+      return;
+    }
+    if ((base()[Object.getBufferSize() - GOFF::RecordLength + 1] & 0xF0) >> 4 !=
+        GOFF::RT_END) {
+      Err = createStringError(object_error::parse_failed,
+                              "object file must end with END record");
+      return;
+    }
+  }
+
+  SectionEntryImpl DummySection;
+  SectionList.emplace_back(DummySection); // Dummy entry at index 0.
+
+  uint8_t PrevRecordType = 0;
+  uint8_t PrevContinuationBits = 0;
+  const uint8_t *End = reinterpret_cast<const uint8_t *>(Data.getBufferEnd());
+  for (const uint8_t *I = base(); I < End; I += GOFF::RecordLength) {
+    uint8_t RecordType = (I[1] & 0xF0) >> 4;
+    bool IsContinuation = I[1] & 0x02;
+    bool PrevWasContinued = PrevContinuationBits & 0x01;
+    size_t RecordNum = (I - base()) / GOFF::RecordLength;
+
+    // If the previous record was continued, the current record should be a
+    // continuation.
+    if (PrevWasContinued && !IsContinuation) {
+      if (PrevRecordType == RecordType) {
+        Err = createStringError(object_error::parse_failed,
+                                "record " + std::to_string(RecordNum) +
+                                    " is not a continuation record but the "
+                                    "preceding record is continued");
+        return;
+      }
+    }
+    // Don't parse continuations records, only parse initial record.
+    if (IsContinuation) {
+      if (RecordType != PrevRecordType) {
+        Err = createStringError(object_error::parse_failed,
+                                "record " + std::to_string(RecordNum) +
+                                    " is a continuation record that does not "
+                                    "match the type of the previous record");
+        return;
+      }
+      if (!PrevWasContinued) {
+        Err = createStringError(object_error::parse_failed,
+                                "record " + std::to_string(RecordNum) +
+                                    " is a continuation record that is not "
+                                    "preceded by a continued record");
+        return;
+      }
+      PrevRecordType = RecordType;
+      PrevContinuationBits = I[1] & 0x03;
+      continue;
+    }
+
+    for (size_t J = 0; J < GOFF::RecordLength; ++J) {
+      const uint8_t *P = I + J;
+      if (J % 8 == 0)
+        LLVM_DEBUG(dbgs() << "  ");
+
+      LLVM_DEBUG(dbgs() << format("%02hhX", *P));
+    }
+    switch (RecordType) {
+    case GOFF::RT_ESD: {
+      // Save ESD record.
+      uint32_t EsdId;
+      ESDRecord::getEsdId(I, EsdId);
+      EsdPtrs.grow(EsdId);
+      EsdPtrs[EsdId] = I;
+
+      // Determine and save the "sections" in GOFF.
+      // A section is saved as a tuple of the form
+      // case (1): (ED,child PR)
+      //    - where the PR must have non-zero length.
+      // case (2a) (ED,0)
+      //   - where the ED is of non-zero length.
+      // case (2b) (ED,0)
+      //   - where the ED is zero length but
+      //     contains a label (LD).
+      GOFF::ESDSymbolType SymbolType;
+      ESDRecord::getSymbolType(I, SymbolType);
+      SectionEntryImpl Section;
+      uint32_t Length;
+      ESDRecord::getLength(I, Length);
+      if (SymbolType == GOFF::ESD_ST_ElementDefinition) {
+        // case (2a)
+        if (Length != 0) {
+          Section.d.a = EsdId;
+          SectionList.emplace_back(Section);
+        }
+      } else if (SymbolType == GOFF::ESD_ST_PartReference) {
+        // case (1)
+        if (Length != 0) {
+          uint32_t SymEdId;
+          ESDRecord::getParentEsdId(I, SymEdId);
+          Section.d.a = SymEdId;
+          Section.d.b = EsdId;
+          SectionList.emplace_back(Section);
+        }
+      } else if (SymbolType == GOFF::ESD_ST_LabelDefinition) {
+        // case (2b)
+        uint32_t SymEdId;
+        ESDRecord::getParentEsdId(I, SymEdId);
+        const uint8_t *SymEdRecord = EsdPtrs[SymEdId];
+        uint32_t EdLength;
+        ESDRecord::getLength(SymEdRecord, EdLength);
+        if (!EdLength) { // [ EDID, PRID ]
+          // LD child of a zero length parent ED.
+          // Add the section ED which was previously ignored.
+          Section.d.a = SymEdId;
+          SectionList.emplace_back(Section);
+        }
+      }
+      LLVM_DEBUG(dbgs() << "  --  ESD " << EsdId << "\n");
+      break;
+    }
+    case GOFF::RT_END:
+      LLVM_DEBUG(dbgs() << "  --  END (GOFF record type) unhandled\n");
+      break;
+    case GOFF::RT_HDR:
+      LLVM_DEBUG(dbgs() << "  --  HDR (GOFF record type) unhandled\n");
+      break;
+    default:
+      llvm_unreachable("Unknown record type");
+    }
+    PrevRecordType = RecordType;
+    PrevContinuationBits = I[1] & 0x03;
+  }
+}
+
+const uint8_t *GOFFObjectFile::getSymbolEsdRecord(DataRefImpl Symb) const {
+  const uint8_t *EsdRecord = EsdPtrs[Symb.d.a];
+  return EsdRecord;
+}
+
+Expected<StringRef> GOFFObjectFile::getSymbolName(DataRefImpl Symb) const {
+  if (EsdNamesCache.count(Symb.d.a)) {
+    auto &StrPtr = EsdNamesCache[Symb.d.a];
+    return StringRef(StrPtr.second.get(), StrPtr.first);
+  }
+
+  SmallString<256> SymbolName;
+  if (auto Err = ESDRecord::getData(getSymbolEsdRecord(Symb), SymbolName))
+    return std::move(Err);
+
+  SmallString<256> SymbolNameConverted;
+  EC = ConverterEBCDIC::convertToUTF8(SymbolName, SymbolNameConverted);
+
+  size_t Size = SymbolNameConverted.size();
+  auto StrPtr = std::make_pair(Size, std::make_unique<char[]>(Size));
+  char *Buf = StrPtr.second.get();
+  memcpy(Buf, SymbolNameConverted.data(), Size);
+  EsdNamesCache[Symb.d.a] = std::move(StrPtr);
+  return StringRef(Buf, Size);
+}
+
+Expected<StringRef> GOFFObjectFile::getSymbolName(SymbolRef Symbol) const {
+  return getSymbolName(Symbol.getRawDataRefImpl());
+}
+
+Expected<uint64_t> GOFFObjectFile::getSymbolAddress(DataRefImpl Symb) const {
+  uint32_t Offset;
+  const uint8_t *EsdRecord = getSymbolEsdRecord(Symb);
+  ESDRecord::getOffset(EsdRecord, Offset);
+  return static_cast<uint64_t>(Offset);
+}
+
+uint64_t GOFFObjectFile::getSymbolValueImpl(DataRefImpl Symb) const {
+  uint32_t Offset;
+  const uint8_t *EsdRecord = getSymbolEsdRecord(Symb);
+  ESDRecord::getOffset(EsdRecord, Offset);
+  return static_cast<uint64_t>(Offset);
+}
+
+uint64_t GOFFObjectFile::getCommonSymbolSizeImpl(DataRefImpl Symb) const {
+  return 0;
+}
+
+bool GOFFObjectFile::isSymbolUnresolved(DataRefImpl Symb) const {
+  const uint8_t *Record = getSymbolEsdRecord(Symb);
+  GOFF::ESDSymbolType SymbolType;
+  ESDRecord::getSymbolType(Record, SymbolType);
+
+  if (SymbolType == GOFF::ESD_ST_ExternalReference)
+    return true;
+  if (SymbolType == GOFF::ESD_ST_PartReference) {
+    uint32_t Length;
+    ESDRecord::getLength(Record, Length);
+    if (Length == 0)
+      return true;
+  }
+  return false;
+}
+
+bool GOFFObjectFile::isSymbolIndirect(DataRefImpl Symb) const {
+  const uint8_t *Record = getSymbolEsdRecord(Symb);
+  bool Indirect;
+  ESDRecord::getIndirectReference(Record, Indirect);
+  return Indirect;
+}
+
+Expected<uint32_t> GOFFObjectFile::getSymbolFlags(DataRefImpl Symb) const {
+  uint32_t Flags = 0;
+  if (isSymbolUnresolved(Symb))
+    Flags |= SymbolRef::SF_Undefined;
+
+  const uint8_t *Record = getSymbolEsdRecord(Symb);
+
+  GOFF::ESDBindingStrength BindingStrength;
+  ESDRecord::getBindingStrength(Record, BindingStrength);
+  if (BindingStrength == GOFF::ESD_BST_Weak)
+    Flags |= SymbolRef::SF_Weak;
+
+  GOFF::ESDBindingScope BindingScope;
+  ESDRecord::getBindingScope(Record, BindingScope);
+
+  if (BindingScope != GOFF::ESD_BSC_Section) {
+    Expected<StringRef> Name = getSymbolName(Symb);
+    if (Name && *Name != " ") { // Blank name is local.
+      Flags |= SymbolRef::SF_Global;
+      if (BindingScope == GOFF::ESD_BSC_ImportExport)
+        Flags |= SymbolRef::SF_Exported;
+      else if (!(Flags & SymbolRef::SF_Undefined))
+        Flags |= SymbolRef::SF_Hidden;
+    }
+  }
+
+  return Flags;
+}
+
+Expected<SymbolRef::Type>
+GOFFObjectFile::getSymbolType(DataRefImpl Symb) const {
+  const uint8_t *Record = getSymbolEsdRecord(Symb);
+  GOFF::ESDSymbolType SymbolType;
+  ESDRecord::getSymbolType(Record, SymbolType);
+  GOFF::ESDExecutable Executable;
+  ESDRecord::getExecutable(Record, Executable);
+
+  if (SymbolType != GOFF::ESD_ST_SectionDefinition &&
+      SymbolType != GOFF::ESD_ST_ElementDefinition &&
+      SymbolType != GOFF::ESD_ST_LabelDefinition &&
+      SymbolType != GOFF::ESD_ST_PartReference &&
+      SymbolType != GOFF::ESD_ST_ExternalReference) {
+    uint32_t EsdId;
+    ESDRecord::getEsdId(Record, EsdId);
+    return createStringError(llvm::errc::invalid_argument,
+                             "ESD record %" PRIu32
+                             " has invalid symbol type 0x%02" PRIX8,
+                             EsdId, SymbolType);
+  }
+  switch (SymbolType) {
+  case GOFF::ESD_ST_SectionDefinition:
+  case GOFF::ESD_ST_ElementDefinition:
+    return SymbolRef::ST_Other;
+  case GOFF::ESD_ST_LabelDefinition:
+  case GOFF::ESD_ST_PartReference:
+  case GOFF::ESD_ST_ExternalReference:
+    if (Executable != GOFF::ESD_EXE_CODE && Executable != GOFF::ESD_EXE_DATA &&
+        Executable != GOFF::ESD_EXE_Unspecified) {
+      uint32_t EsdId;
+      ESDRecord::getEsdId(Record, EsdId);
+      return createStringError(llvm::errc::invalid_argument,
+                               "ESD record %" PRIu32
+                               " has unknown Executable type 0x%02X",
+                               EsdId, Executable);
+    }
+    switch (Executable) {
+    case GOFF::ESD_EXE_CODE:
+      return SymbolRef::ST_Function;
+    case GOFF::ESD_EXE_DATA:
+      return SymbolRef::ST_Data;
+    case GOFF::ESD_EXE_Unspecified:
+      return SymbolRef::ST_Unknown;
+    }
+  }
+}
+
+Expected<section_iterator>
+GOFFObjectFile::getSymbolSection(DataRefImpl Symb) const {
+  DataRefImpl Sec;
+
+  if (isSymbolUnresolved(Symb))
+    return section_iterator(SectionRef(Sec, this));
+
+  const uint8_t *SymEsdRecord = EsdPtrs[Symb.d.a];
+  uint32_t SymEdId;
+  ESDRecord::getParentEsdId(SymEsdRecord, SymEdId);
+  const uint8_t *SymEdRecord = EsdPtrs[SymEdId];
+
+  for (size_t I = 0, E = SectionList.size(); I < E; ++I) {
+    bool Found;
+    const uint8_t *SectionPrRecord = getSectionPrEsdRecord(I);
+    if (SectionPrRecord) {
+      Found = SymEsdRecord == SectionPrRecord;
+    } else {
+      const uint8_t *SectionEdRecord = getSectionEdEsdRecord(I);
+      Found = SymEdRecord == SectionEdRecord;
+    }
+
+    if (Found) {
+      Sec.d.a = I;
+      return section_iterator(SectionRef(Sec, this));
+    }
+  }
+  return createStringError(llvm::errc::invalid_argument,
+                           "symbol with ESD id " + std::to_string(Symb.d.a) +
+                               " refers to invalid section with ESD id " +
+                               std::to_string(SymEdId));
+}
+
+const uint8_t *GOFFObjectFile::getSectionEdEsdRecord(DataRefImpl &Sec) const {
+  SectionEntryImpl EsdIds = SectionList[Sec.d.a];
+  const uint8_t *EsdRecord = EsdPtrs[EsdIds.d.a];
+  return EsdRecord;
+}
+
+const uint8_t *GOFFObjectFile::getSectionPrEsdRecord(DataRefImpl &Sec) const {
+  SectionEntryImpl EsdIds = SectionList[Sec.d.a];
+  const uint8_t *EsdRecord = nullptr;
+  if (EsdIds.d.b)
+    EsdRecord = EsdPtrs[EsdIds.d.b];
+  return EsdRecord;
+}
+
+const uint8_t *
+GOFFObjectFile::getSectionEdEsdRecord(uint32_t SectionIndex) const {
+  DataRefImpl Sec;
+  Sec.d.a = SectionIndex;
+  const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec);
+  return EsdRecord;
+}
+
+const uint8_t *
+GOFFObjectFile::getSectionPrEsdRecord(uint32_t SectionIndex) const {
+  DataRefImpl Sec;
+  Sec.d.a = SectionIndex;
+  const uint8_t *EsdRecord = getSectionPrEsdRecord(Sec);
+  return EsdRecord;
+}
+
+section_iterator GOFFObjectFile::section_begin() const {
+  DataRefImpl Sec;
+  moveSectionNext(Sec);
+  return section_iterator(SectionRef(Sec, this));
+}
+
+section_iterator GOFFObjectFile::section_end() const {
+  DataRefImpl Sec;
+  return section_iterator(SectionRef(Sec, this));
+}
+
+void GOFFObjectFile::moveSymbolNext(DataRefImpl &Symb) const {
+  for (uint32_t I = Symb.d.a + 1, E = EsdPtrs.size(); I < E; ++I) {
+    if (EsdPtrs[I]) {
+      const uint8_t *EsdRecord = EsdPtrs[I];
+      GOFF::ESDSymbolType SymbolType;
+      ESDRecord::getSymbolType(EsdRecord, SymbolType);
+      // Skip EDs - i.e. section symbols.
+      bool IgnoreSpecialGOFFSymbols = true;
+      bool SkipSymbol = ((SymbolType == GOFF::ESD_ST_ElementDefinition) ||
+                         (SymbolType == GOFF::ESD_ST_SectionDefinition)) &&
+                        IgnoreSpecialGOFFSymbols;
+      if (!SkipSymbol) {
+        Symb.d.a = I;
+        return;
+      }
+    }
+  }
+  Symb.d.a = 0;
+}
+
+basic_symbol_iterator GOFFObjectFile::symbol_begin() const {
+  DataRefImpl Symb;
+  moveSymbolNext(Symb);
+  return basic_symbol_iterator(SymbolRef(Symb, this));
+}
+
+basic_symbol_iterator GOFFObjectFile::symbol_end() const {
+  DataRefImpl Symb;
+  return basic_symbol_iterator(SymbolRef(Symb, this));
+}
+
+Error Record::getContinuousData(const uint8_t *Record, uint16_t DataLength,
+                                int DataIndex, SmallString<256> &CompleteData) {
+  // First record.
+  const uint8_t *Slice = Record + DataIndex;
+  size_t SliceLength =
+      std::min(DataLength, (uint16_t)(GOFF::RecordLength - DataIndex));
+  CompleteData.append(Slice, Slice + SliceLength);
+  DataLength -= SliceLength;
+  Slice += SliceLength;
+
+  // Continuation records.
+  for (; DataLength > 0;
+       DataLength -= SliceLength, Slice += GOFF::PayloadLength) {
+    // Slice points to the start of the new record.
+    // Check that this block is a Continuation.
+    assert(Record::isContinuation(Slice) && "Continuation bit must be set");
+    // Check that the last Continuation is terminated correctly.
+    if (DataLength <= 77 && Record::isContinued(Slice))
+      return createStringError(object_error::parse_failed,
+                               "continued bit should not be set");
+
+    SliceLength = std::min(DataLength, (uint16_t)GOFF::PayloadLength);
+    Slice += GOFF::RecordPrefixLength;
+    CompleteData.append(Slice, Slice + SliceLength);
+  }
+  return Error::success();
+}
+
+Error HDRRecord::getData(const uint8_t *Record,
+                         SmallString<256> &CompleteData) {
+  uint16_t Length = getPropertyModuleLength(Record);
+  return getContinuousData(Record, Length, 60, CompleteData);
+}
+
+Error ESDRecord::getData(const uint8_t *Record,
+                         SmallString<256> &CompleteData) {
+  uint16_t DataSize = getNameLength(Record);
+  return getContinuousData(Record, DataSize, 72, CompleteData);
+}
+
+Error ENDRecord::getData(const uint8_t *Record,
+                         SmallString<256> &CompleteData) {
+  uint16_t Length = getNameLength(Record);
+  return getContinuousData(Record, Length, 26, CompleteData);
+}

diff  --git a/llvm/lib/Object/ObjectFile.cpp b/llvm/lib/Object/ObjectFile.cpp
index 56a1d09097d42..c953a5692cbb4 100644
--- a/llvm/lib/Object/ObjectFile.cpp
+++ b/llvm/lib/Object/ObjectFile.cpp
@@ -130,6 +130,10 @@ Triple ObjectFile::makeTriple() const {
     TheTriple.setOS(Triple::AIX);
     TheTriple.setObjectFormat(Triple::XCOFF);
   }
+  else if (isGOFF()) {
+    TheTriple.setOS(Triple::ZOS);
+    TheTriple.setObjectFormat(Triple::GOFF);
+  }
 
   return TheTriple;
 }

diff  --git a/llvm/unittests/Object/CMakeLists.txt b/llvm/unittests/Object/CMakeLists.txt
index 2de3e046cff03..2a361d4d71156 100644
--- a/llvm/unittests/Object/CMakeLists.txt
+++ b/llvm/unittests/Object/CMakeLists.txt
@@ -11,6 +11,7 @@ add_llvm_unittest(ObjectTests
   ELFObjectFileTest.cpp
   ELFTypesTest.cpp
   ELFTest.cpp
+  GOFFObjectFileTest.cpp
   MinidumpTest.cpp
   ObjectFileTest.cpp
   OffloadingTest.cpp

diff  --git a/llvm/unittests/Object/GOFFObjectFileTest.cpp b/llvm/unittests/Object/GOFFObjectFileTest.cpp
new file mode 100644
index 0000000000000..747e05846a231
--- /dev/null
+++ b/llvm/unittests/Object/GOFFObjectFileTest.cpp
@@ -0,0 +1,504 @@
+//===- GOFFObjectFileTest.cpp - Tests for GOFFObjectFile ------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Object/GOFFObjectFile.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::GOFF;
+
+namespace {
+char GOFFData[GOFF::RecordLength * 3] = {0x00};
+
+void constructValidGOFF(size_t Size) {
+  StringRef ValidSize(GOFFData, Size);
+  Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
+      object::ObjectFile::createGOFFObjectFile(
+          MemoryBufferRef(ValidSize, "dummyGOFF"));
+
+  ASSERT_THAT_EXPECTED(GOFFObjOrErr, Succeeded());
+}
+
+void constructInvalidGOFF(size_t Size) {
+  // Construct GOFFObject with record of length != multiple of 80.
+  StringRef InvalidData(GOFFData, Size);
+  Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
+      object::ObjectFile::createGOFFObjectFile(
+          MemoryBufferRef(InvalidData, "dummyGOFF"));
+
+  ASSERT_THAT_EXPECTED(
+      GOFFObjOrErr,
+      FailedWithMessage("object file is not the right size. Must be a multiple "
+                        "of 80 bytes, but is " +
+                        std::to_string(Size) + " bytes"));
+}
+} // namespace
+
+TEST(GOFFObjectFileTest, ConstructGOFFObjectValidSize) {
+  GOFFData[0] = 0x03;
+  GOFFData[1] = 0xF0;
+  GOFFData[80] = 0x03;
+  GOFFData[81] = 0x40;
+  constructValidGOFF(160);
+  constructValidGOFF(0);
+}
+
+TEST(GOFFObjectFileTest, ConstructGOFFObjectInvalidSize) {
+  constructInvalidGOFF(70);
+  constructInvalidGOFF(79);
+  constructInvalidGOFF(81);
+}
+
+TEST(GOFFObjectFileTest, MissingHDR) {
+  char GOFFData[GOFF::RecordLength * 2] = {0x00};
+
+  // ESD record.
+  GOFFData[0] = 0x03;
+
+  // END record.
+  GOFFData[GOFF::RecordLength] = 0x03;
+  GOFFData[GOFF::RecordLength + 1] = 0x40;
+
+  StringRef Data(GOFFData, GOFF::RecordLength * 2);
+
+  Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
+      object::ObjectFile::createGOFFObjectFile(
+          MemoryBufferRef(Data, "dummyGOFF"));
+
+  ASSERT_THAT_EXPECTED(
+      GOFFObjOrErr,
+      FailedWithMessage("object file must start with HDR record"));
+}
+
+TEST(GOFFObjectFileTest, MissingEND) {
+  char GOFFData[GOFF::RecordLength * 2] = {0x00};
+
+  // HDR record.
+  GOFFData[0] = 0x03;
+  GOFFData[1] = 0xF0;
+
+  // ESD record.
+  GOFFData[GOFF::RecordLength] = 0x03;
+
+  StringRef Data(GOFFData, GOFF::RecordLength * 2);
+
+  Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
+      object::ObjectFile::createGOFFObjectFile(
+          MemoryBufferRef(Data, "dummyGOFF"));
+
+  ASSERT_THAT_EXPECTED(
+      GOFFObjOrErr, FailedWithMessage("object file must end with END record"));
+}
+
+TEST(GOFFObjectFileTest, GetSymbolName) {
+  char GOFFData[GOFF::RecordLength * 3] = {0x00};
+
+  // HDR record.
+  GOFFData[0] = 0x03;
+  GOFFData[1] = 0xF0;
+
+  // ESD record.
+  GOFFData[GOFF::RecordLength] = 0x03;
+  GOFFData[GOFF::RecordLength + 3] = 0x02;
+  GOFFData[GOFF::RecordLength + 7] = 0x01;
+  GOFFData[GOFF::RecordLength + 11] = 0x01;
+  GOFFData[GOFF::RecordLength + 71] = 0x05; // Size of symbol name.
+  GOFFData[GOFF::RecordLength + 72] = 0xC8; // Symbol name is Hello.
+  GOFFData[GOFF::RecordLength + 73] = 0x85;
+  GOFFData[GOFF::RecordLength + 74] = 0x93;
+  GOFFData[GOFF::RecordLength + 75] = 0x93;
+  GOFFData[GOFF::RecordLength + 76] = 0x96;
+
+  // END record.
+  GOFFData[GOFF::RecordLength * 2] = 0x03;
+  GOFFData[GOFF::RecordLength * 2 + 1] = 0x40;
+
+  StringRef Data(GOFFData, GOFF::RecordLength * 3);
+
+  Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
+      object::ObjectFile::createGOFFObjectFile(
+          MemoryBufferRef(Data, "dummyGOFF"));
+
+  ASSERT_THAT_EXPECTED(GOFFObjOrErr, Succeeded());
+
+  GOFFObjectFile *GOFFObj = dyn_cast<GOFFObjectFile>((*GOFFObjOrErr).get());
+
+  for (SymbolRef Symbol : GOFFObj->symbols()) {
+    Expected<StringRef> SymbolNameOrErr = GOFFObj->getSymbolName(Symbol);
+    ASSERT_THAT_EXPECTED(SymbolNameOrErr, Succeeded());
+    StringRef SymbolName = SymbolNameOrErr.get();
+
+    EXPECT_EQ(SymbolName, "Hello");
+  }
+}
+
+TEST(GOFFObjectFileTest, ConcatenatedGOFFFile) {
+  char GOFFData[GOFF::RecordLength * 6] = {0x00};
+
+  // HDR record.
+  GOFFData[0] = 0x03;
+  GOFFData[1] = 0xF0;
+  // ESD record.
+  GOFFData[GOFF::RecordLength] = 0x03;
+  // END record.
+  GOFFData[GOFF::RecordLength * 2] = 0x03;
+  GOFFData[GOFF::RecordLength * 2 + 1] = 0x40;
+  // HDR record.
+  GOFFData[GOFF::RecordLength * 3] = 0x03;
+  GOFFData[GOFF::RecordLength * 3 + 1] = 0xF0;
+  // ESD record.
+  GOFFData[GOFF::RecordLength * 4] = 0x03;
+  // END record.
+  GOFFData[GOFF::RecordLength * 5] = 0x03;
+  GOFFData[GOFF::RecordLength * 5 + 1] = 0x40;
+
+  StringRef Data(GOFFData, GOFF::RecordLength * 6);
+
+  Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
+      object::ObjectFile::createGOFFObjectFile(
+          MemoryBufferRef(Data, "dummyGOFF"));
+
+  ASSERT_THAT_EXPECTED(GOFFObjOrErr, Succeeded());
+}
+
+TEST(GOFFObjectFileTest, ContinuationGetSymbolName) {
+  char GOFFContData[GOFF::RecordLength * 4] = {0x00};
+
+  // HDR record.
+  GOFFContData[0] = 0x03;
+  GOFFContData[1] = 0xF0;
+
+  // ESD record.
+  GOFFContData[GOFF::RecordLength] = 0x03;
+  GOFFContData[GOFF::RecordLength + 1] = 0x01;
+  GOFFContData[GOFF::RecordLength + 3] = 0x02;
+  GOFFContData[GOFF::RecordLength + 7] = 0x01;
+  GOFFContData[GOFF::RecordLength + 11] = 0x01;
+  GOFFContData[GOFF::RecordLength + 71] = 0x0A; // Size of symbol name.
+  GOFFContData[GOFF::RecordLength + 72] = 0xC8; // Symbol name is HelloWorld.
+  GOFFContData[GOFF::RecordLength + 73] = 0x85;
+  GOFFContData[GOFF::RecordLength + 74] = 0x93;
+  GOFFContData[GOFF::RecordLength + 75] = 0x93;
+  GOFFContData[GOFF::RecordLength + 76] = 0x96;
+  GOFFContData[GOFF::RecordLength + 77] = 0xA6;
+  GOFFContData[GOFF::RecordLength + 78] = 0x96;
+  GOFFContData[GOFF::RecordLength + 79] = 0x99;
+
+  // ESD continuation record.
+  GOFFContData[GOFF::RecordLength * 2] = 0x03;
+  GOFFContData[GOFF::RecordLength * 2 + 1] = 0x02; // No further continuations.
+  GOFFContData[GOFF::RecordLength * 2 + 3] = 0x93;
+  GOFFContData[GOFF::RecordLength * 2 + 4] = 0x84;
+
+  // END record.
+  GOFFContData[GOFF::RecordLength * 3] = 0x03;
+  GOFFContData[GOFF::RecordLength * 3 + 1] = 0x40;
+
+  StringRef Data(GOFFContData, GOFF::RecordLength * 4);
+
+  Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
+      object::ObjectFile::createGOFFObjectFile(
+          MemoryBufferRef(Data, "dummyGOFF"));
+
+  ASSERT_THAT_EXPECTED(GOFFObjOrErr, Succeeded());
+
+  GOFFObjectFile *GOFFObj = dyn_cast<GOFFObjectFile>((*GOFFObjOrErr).get());
+
+  for (SymbolRef Symbol : GOFFObj->symbols()) {
+    Expected<StringRef> SymbolNameOrErr = GOFFObj->getSymbolName(Symbol);
+    ASSERT_THAT_EXPECTED(SymbolNameOrErr, Succeeded());
+    StringRef SymbolName = SymbolNameOrErr.get();
+    EXPECT_EQ(SymbolName, "Helloworld");
+  }
+}
+
+TEST(GOFFObjectFileTest, ContinuationBitNotSet) {
+  char GOFFContData[GOFF::RecordLength * 4] = {0x00};
+
+  // HDR record.
+  GOFFContData[0] = 0x03;
+  GOFFContData[1] = 0xF0;
+
+  // ESD record.
+  GOFFContData[GOFF::RecordLength] = 0x03;
+  GOFFContData[GOFF::RecordLength + 1] = 0x01;
+  GOFFContData[GOFF::RecordLength + 3] = 0x02;
+  GOFFContData[GOFF::RecordLength + 7] = 0x01;
+  GOFFContData[GOFF::RecordLength + 11] = 0x01;
+  GOFFContData[GOFF::RecordLength + 71] = 0x0A; // Size of symbol name.
+  GOFFContData[GOFF::RecordLength + 72] = 0xC8; // Symbol name is HelloWorld.
+  GOFFContData[GOFF::RecordLength + 73] = 0x85;
+  GOFFContData[GOFF::RecordLength + 74] = 0x93;
+  GOFFContData[GOFF::RecordLength + 75] = 0x93;
+  GOFFContData[GOFF::RecordLength + 76] = 0x96;
+  GOFFContData[GOFF::RecordLength + 77] = 0xA6;
+  GOFFContData[GOFF::RecordLength + 78] = 0x96;
+  GOFFContData[GOFF::RecordLength + 79] = 0x99;
+
+  // ESD continuation record.
+  GOFFContData[GOFF::RecordLength * 2] = 0x03;
+  GOFFContData[GOFF::RecordLength * 2 + 1] = 0x00;
+  GOFFContData[GOFF::RecordLength * 2 + 3] = 0x93;
+  GOFFContData[GOFF::RecordLength * 2 + 4] = 0x84;
+
+  // END record.
+  GOFFContData[GOFF::RecordLength * 3] = 0x03;
+  GOFFContData[GOFF::RecordLength * 3 + 1] = 0x40;
+
+  StringRef Data(GOFFContData, GOFF::RecordLength * 4);
+
+  Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
+      object::ObjectFile::createGOFFObjectFile(
+          MemoryBufferRef(Data, "dummyGOFF"));
+  EXPECT_THAT_EXPECTED(
+      GOFFObjOrErr,
+      FailedWithMessage("record 2 is not a continuation record but the "
+                        "preceding record is continued"));
+}
+
+TEST(GOFFObjectFileTest, ContinuationRecordNotTerminated) {
+  char GOFFContData[GOFF::RecordLength * 4] = {0x00};
+
+  // HDR record.
+  GOFFContData[0] = 0x03;
+  GOFFContData[1] = 0xF0;
+
+  // ESD record.
+  GOFFContData[GOFF::RecordLength] = 0x03;
+  GOFFContData[GOFF::RecordLength + 1] = 0x01;
+  GOFFContData[GOFF::RecordLength + 3] = 0x02;
+  GOFFContData[GOFF::RecordLength + 7] = 0x01;
+  GOFFContData[GOFF::RecordLength + 11] = 0x01;
+  GOFFContData[GOFF::RecordLength + 71] = 0x0A; // Size of symbol name.
+  GOFFContData[GOFF::RecordLength + 72] = 0xC8; // Symbol name is HelloWorld.
+  GOFFContData[GOFF::RecordLength + 73] = 0x85;
+  GOFFContData[GOFF::RecordLength + 74] = 0x93;
+  GOFFContData[GOFF::RecordLength + 75] = 0x93;
+  GOFFContData[GOFF::RecordLength + 76] = 0x96;
+  GOFFContData[GOFF::RecordLength + 77] = 0xA6;
+  GOFFContData[GOFF::RecordLength + 78] = 0x96;
+  GOFFContData[GOFF::RecordLength + 79] = 0x99;
+
+  // ESD continuation record.
+  GOFFContData[GOFF::RecordLength * 2] = 0x03;
+  GOFFContData[GOFF::RecordLength * 2 + 1] = 0x03; // Continued bit set.
+  GOFFContData[GOFF::RecordLength * 2 + 3] = 0x93;
+  GOFFContData[GOFF::RecordLength * 2 + 4] = 0x84;
+
+  // END record.
+  GOFFContData[GOFF::RecordLength * 3] = 0x03;
+  GOFFContData[GOFF::RecordLength * 3 + 1] = 0x40;
+
+  StringRef Data(GOFFContData, GOFF::RecordLength * 4);
+
+  Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
+      object::ObjectFile::createGOFFObjectFile(
+          MemoryBufferRef(Data, "dummyGOFF"));
+  ASSERT_THAT_EXPECTED(GOFFObjOrErr, Succeeded());
+
+  GOFFObjectFile *GOFFObj = dyn_cast<GOFFObjectFile>((*GOFFObjOrErr).get());
+
+  for (SymbolRef Symbol : GOFFObj->symbols()) {
+    Expected<StringRef> SymbolNameOrErr = GOFFObj->getSymbolName(Symbol);
+    EXPECT_THAT_EXPECTED(SymbolNameOrErr,
+                         FailedWithMessage("continued bit should not be set"));
+  }
+}
+
+TEST(GOFFObjectFileTest, PrevNotContinued) {
+  char GOFFContData[GOFF::RecordLength * 4] = {0x00};
+
+  // HDR record.
+  GOFFContData[0] = 0x03;
+  GOFFContData[1] = 0xF0;
+
+  // ESD record, with continued bit not set.
+  GOFFContData[GOFF::RecordLength] = 0x03;
+
+  // ESD continuation record.
+  GOFFContData[GOFF::RecordLength * 2] = 0x03;
+  GOFFContData[GOFF::RecordLength * 2 + 1] = 0x02;
+
+  // END record.
+  GOFFContData[GOFF::RecordLength * 3] = 0x03;
+  GOFFContData[GOFF::RecordLength * 3 + 1] = 0x40;
+
+  StringRef Data(GOFFContData, GOFF::RecordLength * 4);
+
+  Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
+      object::ObjectFile::createGOFFObjectFile(
+          MemoryBufferRef(Data, "dummyGOFF"));
+
+  ASSERT_THAT_EXPECTED(
+      GOFFObjOrErr,
+      FailedWithMessage("record 2 is a continuation record that is not "
+                        "preceded by a continued record"));
+}
+
+TEST(GOFFObjectFileTest, ContinuationTypeMismatch) {
+  char GOFFContData[GOFF::RecordLength * 4] = {0x00};
+
+  // HDR record.
+  GOFFContData[0] = 0x03;
+  GOFFContData[1] = 0xF0;
+
+  // ESD record.
+  GOFFContData[GOFF::RecordLength] = 0x03;
+  GOFFContData[GOFF::RecordLength + 1] = 0x01; // Continued to next record.
+
+  // END continuation record.
+  GOFFContData[GOFF::RecordLength * 2] = 0x03;
+  GOFFContData[GOFF::RecordLength * 2 + 1] = 0x42;
+
+  // END record.
+  GOFFContData[GOFF::RecordLength * 3] = 0x03;
+  GOFFContData[GOFF::RecordLength * 3 + 1] = 0x40;
+
+  StringRef Data(GOFFContData, GOFF::RecordLength * 4);
+
+  Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
+      object::ObjectFile::createGOFFObjectFile(
+          MemoryBufferRef(Data, "dummyGOFF"));
+
+  ASSERT_THAT_EXPECTED(
+      GOFFObjOrErr,
+      FailedWithMessage("record 2 is a continuation record that does not match "
+                        "the type of the previous record"));
+}
+
+TEST(GOFFObjectFileTest, TwoSymbols) {
+  char GOFFData[GOFF::RecordLength * 4] = {0x00};
+
+  // HDR record.
+  GOFFData[0] = 0x03;
+  GOFFData[1] = 0xF0;
+
+  // ESD record 1.
+  GOFFData[GOFF::RecordLength] = 0x03;
+  GOFFData[GOFF::RecordLength + 3] = 0x00;
+  GOFFData[GOFF::RecordLength + 7] = 0x01;  // ESDID.
+  GOFFData[GOFF::RecordLength + 71] = 0x01; // Size of symbol name.
+  GOFFData[GOFF::RecordLength + 72] = 0xa7; // Symbol name is x.
+
+  // ESD record 2.
+  GOFFData[GOFF::RecordLength * 2] = 0x03;
+  GOFFData[GOFF::RecordLength * 2 + 3] = 0x03;
+  GOFFData[GOFF::RecordLength * 2 + 7] = 0x02;  // ESDID.
+  GOFFData[GOFF::RecordLength * 2 + 11] = 0x01; // Parent ESDID.
+  GOFFData[GOFF::RecordLength * 2 + 71] = 0x05; // Size of symbol name.
+  GOFFData[GOFF::RecordLength * 2 + 72] = 0xC8; // Symbol name is Hello.
+  GOFFData[GOFF::RecordLength * 2 + 73] = 0x85;
+  GOFFData[GOFF::RecordLength * 2 + 74] = 0x93;
+  GOFFData[GOFF::RecordLength * 2 + 75] = 0x93;
+  GOFFData[GOFF::RecordLength * 2 + 76] = 0x96;
+
+  // END record.
+  GOFFData[GOFF::RecordLength * 3] = 0x03;
+  GOFFData[GOFF::RecordLength * 3 + 1] = 0x40;
+
+  StringRef Data(GOFFData, GOFF::RecordLength * 4);
+
+  Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
+      object::ObjectFile::createGOFFObjectFile(
+          MemoryBufferRef(Data, "dummyGOFF"));
+
+  ASSERT_THAT_EXPECTED(GOFFObjOrErr, Succeeded());
+
+  GOFFObjectFile *GOFFObj = dyn_cast<GOFFObjectFile>((*GOFFObjOrErr).get());
+
+  for (SymbolRef Symbol : GOFFObj->symbols()) {
+    Expected<StringRef> SymbolNameOrErr = GOFFObj->getSymbolName(Symbol);
+    ASSERT_THAT_EXPECTED(SymbolNameOrErr, Succeeded());
+    StringRef SymbolName = SymbolNameOrErr.get();
+    EXPECT_EQ(SymbolName, "Hello");
+  }
+}
+
+TEST(GOFFObjectFileTest, InvalidSymbolType) {
+  char GOFFData[GOFF::RecordLength * 3] = {0x00};
+
+  // HDR record.
+  GOFFData[0] = 0x03;
+  GOFFData[1] = 0xF0;
+
+  // ESD record.
+  GOFFData[GOFF::RecordLength] = 0x03;
+  GOFFData[GOFF::RecordLength + 3] = 0x05;
+  GOFFData[GOFF::RecordLength + 7] = 0x01;
+  GOFFData[GOFF::RecordLength + 11] = 0x01;
+  GOFFData[GOFF::RecordLength + 71] = 0x01; // Size of symbol name.
+  GOFFData[GOFF::RecordLength + 72] = 0xC8; // Symbol name.
+
+  // END record.
+  GOFFData[GOFF::RecordLength * 2] = 0x03;
+  GOFFData[GOFF::RecordLength * 2 + 1] = 0x40;
+
+  StringRef Data(GOFFData, GOFF::RecordLength * 3);
+
+  Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
+      object::ObjectFile::createGOFFObjectFile(
+          MemoryBufferRef(Data, "dummyGOFF"));
+
+  ASSERT_THAT_EXPECTED(GOFFObjOrErr, Succeeded());
+
+  GOFFObjectFile *GOFFObj = dyn_cast<GOFFObjectFile>((*GOFFObjOrErr).get());
+
+  for (SymbolRef Symbol : GOFFObj->symbols()) {
+    Expected<SymbolRef::Type> SymbolType = Symbol.getType();
+    EXPECT_THAT_EXPECTED(
+        SymbolType,
+        FailedWithMessage("ESD record 1 has invalid symbol type 0x05"));
+
+    Expected<section_iterator> SymSI = Symbol.getSection();
+    ASSERT_THAT_EXPECTED(
+        SymSI,
+        FailedWithMessage(
+            "symbol with ESD id 1 refers to invalid section with ESD id 1"));
+  }
+}
+
+TEST(GOFFObjectFileTest, InvalidERSymbolType) {
+  char GOFFData[GOFF::RecordLength * 3] = {0x00};
+
+  // HDR record.
+  GOFFData[0] = 0x03;
+  GOFFData[1] = 0xF0;
+
+  // ESD record.
+  GOFFData[GOFF::RecordLength] = 0x03;
+  GOFFData[GOFF::RecordLength + 3] = 0x04;
+  GOFFData[GOFF::RecordLength + 7] = 0x01;
+  GOFFData[GOFF::RecordLength + 11] = 0x01;
+  GOFFData[GOFF::RecordLength + 63] = 0x03; // Unknown executable type.
+  GOFFData[GOFF::RecordLength + 71] = 0x01; // Size of symbol name.
+  GOFFData[GOFF::RecordLength + 72] = 0xC8; // Symbol name.
+
+  // END record.
+  GOFFData[GOFF::RecordLength * 2] = 0x03;
+  GOFFData[GOFF::RecordLength * 2 + 1] = 0x40;
+
+  StringRef Data(GOFFData, GOFF::RecordLength * 3);
+
+  Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
+      object::ObjectFile::createGOFFObjectFile(
+          MemoryBufferRef(Data, "dummyGOFF"));
+
+  ASSERT_THAT_EXPECTED(GOFFObjOrErr, Succeeded());
+
+  GOFFObjectFile *GOFFObj = dyn_cast<GOFFObjectFile>((*GOFFObjOrErr).get());
+
+  for (SymbolRef Symbol : GOFFObj->symbols()) {
+    Expected<SymbolRef::Type> SymbolType = Symbol.getType();
+    EXPECT_THAT_EXPECTED(
+        SymbolType,
+        FailedWithMessage("ESD record 1 has unknown Executable type 0x03"));
+  }
+}


        


More information about the llvm-commits mailing list