[llvm] [JITLink] [GOFF] Initial support for GOFF (PR #224683)

Shimin Cui via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 1 06:47:36 PDT 2026


https://github.com/scui-ibm updated https://github.com/llvm/llvm-project/pull/224683

>From d4aac71d4a97dde2054f6989f6eeae684633bce5 Mon Sep 17 00:00:00 2001
From: Shimin Cui <scui at ca.ibm.com>
Date: Fri, 18 Sep 2026 16:32:03 +0000
Subject: [PATCH 1/3] [JITLink] [GOFF] Initial support for GOFF

---
 .../llvm/ExecutionEngine/JITLink/GOFF.h       |  37 +++
 .../ExecutionEngine/JITLink/GOFF_systemz.h    |  37 +++
 .../llvm/ExecutionEngine/JITLink/systemz.h    |  65 +++++
 llvm/include/llvm/Object/GOFFObjectFile.h     |   3 +
 .../ExecutionEngine/JITLink/CMakeLists.txt    |   5 +
 llvm/lib/ExecutionEngine/JITLink/GOFF.cpp     |  36 +++
 .../JITLink/GOFFLinkGraphBuilder.cpp          | 274 ++++++++++++++++++
 .../JITLink/GOFFLinkGraphBuilder.h            |  67 +++++
 .../ExecutionEngine/JITLink/GOFF_systemz.cpp  | 100 +++++++
 llvm/lib/ExecutionEngine/JITLink/JITLink.cpp  |   5 +
 llvm/lib/ExecutionEngine/JITLink/systemz.cpp  |   8 +
 .../ExecutionEngine/Orc/LoadLinkableFile.cpp  |  16 +
 .../Orc/ObjectFileInterface.cpp               |  44 +++
 llvm/lib/Object/GOFFObjectFile.cpp            |  48 +++
 .../JITLink/systemz/GOFF_systemz_reloc_ptr.ll |  15 +
 15 files changed, 760 insertions(+)
 create mode 100644 llvm/include/llvm/ExecutionEngine/JITLink/GOFF.h
 create mode 100644 llvm/include/llvm/ExecutionEngine/JITLink/GOFF_systemz.h
 create mode 100644 llvm/lib/ExecutionEngine/JITLink/GOFF.cpp
 create mode 100644 llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.cpp
 create mode 100644 llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.h
 create mode 100644 llvm/lib/ExecutionEngine/JITLink/GOFF_systemz.cpp
 create mode 100644 llvm/test/ExecutionEngine/JITLink/systemz/GOFF_systemz_reloc_ptr.ll

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/GOFF.h b/llvm/include/llvm/ExecutionEngine/JITLink/GOFF.h
new file mode 100644
index 0000000000000..3d554357b5c97
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/GOFF.h
@@ -0,0 +1,37 @@
+//===------- GOFF.h - Generic JIT link function for GOFF ------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// JIT-Link functions for GOFF.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_JITLINK_GOFF_H
+#define LLVM_EXECUTIONENGINE_JITLINK_GOFF_H
+
+#include "llvm/ExecutionEngine/JITLink/JITLink.h"
+
+namespace llvm {
+namespace jitlink {
+
+/// Create a LinkGraph from an GOFF relocatable object.
+///
+/// Note: The graph does not take ownership of the underlying buffer, nor copy
+/// its contents. The caller is responsible for ensuring that the object buffer
+/// outlives the graph.
+LLVM_ABI Expected<std::unique_ptr<LinkGraph>>
+createLinkGraphFromGOFFObject(MemoryBufferRef ObjectBuffer,
+                              std::shared_ptr<orc::SymbolStringPool> SSP);
+
+/// Link the given graph.
+LLVM_ABI void link_GOFF(std::unique_ptr<LinkGraph> G,
+                        std::unique_ptr<JITLinkContext> Ctx);
+
+} // namespace jitlink
+} // namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_JITLINK_GOFF_H
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/GOFF_systemz.h b/llvm/include/llvm/ExecutionEngine/JITLink/GOFF_systemz.h
new file mode 100644
index 0000000000000..eda4b049eb5c0
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/GOFF_systemz.h
@@ -0,0 +1,37 @@
+//===--- GOFF_systemz.h -  JIT link functions for GOFF/systemz ---*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// JIT-Link functions for GOFF/systemz.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_JITLINK_GOFF_SYSTEMZ_H
+#define LLVM_EXECUTIONENGINE_JITLINK_GOFF_SYSTEMZ_H
+
+#include "llvm/ExecutionEngine/JITLink/JITLink.h"
+
+namespace llvm::jitlink {
+
+/// Create a LinkGraph from an GOFF/systemz relocatable object.
+///
+/// Note: The graph does not take ownership of the underlying buffer, nor copy
+/// its contents. The caller is responsible for ensuring that the object buffer
+/// outlives the graph.
+///
+LLVM_ABI Expected<std::unique_ptr<LinkGraph>>
+createLinkGraphFromGOFFObject_systemz(
+    MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP);
+
+/// jit-link the given object buffer, which must be a GOFF systemz object file.
+///
+LLVM_ABI void link_GOFF_systemz(std::unique_ptr<LinkGraph> G,
+                                std::unique_ptr<JITLinkContext> Ctx);
+
+} // namespace llvm::jitlink
+
+#endif // LLVM_EXECUTIONENGINE_JITLINK_GOFF_SYSTEMZ_H
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/systemz.h b/llvm/include/llvm/ExecutionEngine/JITLink/systemz.h
index efc1bda8edda6..82361c1d83962 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/systemz.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/systemz.h
@@ -87,6 +87,14 @@ enum EdgeKind_systemz : Edge::Kind {
   ///
   Pointer8,
 
+  /// GOFF: Absolute add (field += value) for length 64 and 32.
+  Pointer64Add,
+  Pointer32Add,
+
+  /// GOFF: Absolute subtract (field -= value) for length 64 and 32.
+  Pointer64Sub,
+  Pointer32Sub,
+
   /// A 64-bit delta.
   ///
   /// Delta from the fixup to the target.
@@ -935,6 +943,63 @@ class PLTTableManager : public TableManager<PLTTableManager> {
 /// replace a indirect jump by plt stub with a direct jump to the target.
 LLVM_ABI Error optimizeGOTAndStubAccesses(LinkGraph &G);
 
+/// Apply fixup expression for edge to block content for GOFF.
+inline Error applyGOFFFixup(LinkGraph &G, Block &B, const Edge &E) {
+  using namespace support;
+
+  char *BlockWorkingMem = B.getAlreadyMutableContent().data();
+  char *FixupPtr = BlockWorkingMem + E.getOffset();
+  int64_t S = E.getTarget().getAddress().getValue();
+  int64_t A = E.getAddend();
+  assert(A == 0 && "Edge addend should be 0");
+
+  int64_t Value = S;
+  orc::ExecutorAddr FixupAddress = B.getAddress() + E.getOffset();
+  int64_t P = FixupAddress.getValue();
+  Edge::Kind K = E.getKind();
+
+  DEBUG_WITH_TYPE("jitlink", {
+    dbgs() << "    Applying fixup on " << G.getEdgeKindName(K)
+           << " edge, (S, P) = (" << formatv("{0:x}", S) << ", "
+           << formatv("{0:x}", P) << ")\n";
+  });
+
+  auto apply = [&](auto write, auto read, auto op) {
+    auto Current = read(FixupPtr);
+    DEBUG_WITH_TYPE("jitlink", {
+      dbgs() << "       Addend = " << formatv("{0:x}", Current) << "\n";
+    });
+    write(FixupPtr, static_cast<decltype(Current)>(op(Current, Value)));
+  };
+
+  switch (K) {
+  case Pointer64:
+    write64be(FixupPtr, Value);
+    break;
+  case Pointer32:
+    write32be(FixupPtr, Value);
+    break;
+  case Pointer64Add:
+    apply(write64be, read64be, std::plus<uint64_t>{});
+    break;
+  case Pointer32Add:
+    apply(write32be, read32be, std::plus<uint32_t>{});
+    break;
+  case Pointer64Sub:
+    apply(write64be, read64be, std::minus<uint64_t>{});
+    break;
+  case Pointer32Sub:
+    apply(write32be, read32be, std::minus<uint32_t>{});
+    break;
+  default:
+    return make_error<JITLinkError>(
+        "In graph " + G.getName() + ", section " + B.getSection().getName() +
+        " unsupported edge kind " + getEdgeKindName(E.getKind()));
+  }
+
+  return Error::success();
+}
+
 } // namespace systemz
 } // namespace jitlink
 } // namespace llvm
diff --git a/llvm/include/llvm/Object/GOFFObjectFile.h b/llvm/include/llvm/Object/GOFFObjectFile.h
index 080d18fc23bef..3d6a0da34c86b 100644
--- a/llvm/include/llvm/Object/GOFFObjectFile.h
+++ b/llvm/include/llvm/Object/GOFFObjectFile.h
@@ -139,6 +139,8 @@ class LLVM_ABI GOFFObjectFile : public ObjectFile {
   bool isSectionNoLoad(DataRefImpl Sec) const;
   bool isSectionReadOnlyData(DataRefImpl Sec) const;
   bool isSectionZeroInit(DataRefImpl Sec) const;
+  Error getSectionUniqueName(DataRefImpl Sec,
+                             SmallVectorImpl<char> &Result) const;
 
 private:
   // SymbolRef.
@@ -154,6 +156,7 @@ class LLVM_ABI GOFFObjectFile : public ObjectFile {
   const uint8_t *getSymbolEsdRecord(DataRefImpl Symb) const;
   bool isSymbolUnresolved(DataRefImpl Symb) const;
   bool isSymbolIndirect(DataRefImpl Symb) const;
+  Expected<StringRef> getSymbolName(uint32_t SymIndex) const;
 
   // SectionRef.
   void moveSectionNext(DataRefImpl &Sec) const override;
diff --git a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt
index c1456321327ef..329d19a341353 100644
--- a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt
+++ b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt
@@ -43,6 +43,11 @@ add_llvm_component_library(LLVMJITLink
   XCOFF_ppc64.cpp
   XCOFFLinkGraphBuilder.cpp
 
+  # GOFF
+  GOFF.cpp
+  GOFF_systemz.cpp
+  GOFFLinkGraphBuilder.cpp
+
   # Architectures:
   aarch32.cpp
   aarch64.cpp
diff --git a/llvm/lib/ExecutionEngine/JITLink/GOFF.cpp b/llvm/lib/ExecutionEngine/JITLink/GOFF.cpp
new file mode 100644
index 0000000000000..36f60b6de3f24
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/JITLink/GOFF.cpp
@@ -0,0 +1,36 @@
+//===--------------- GOFF.cpp - JIT linker function for GOFF --------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// JIT-link functions for GOFF.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ExecutionEngine/JITLink/GOFF.h"
+#include "llvm/ExecutionEngine/JITLink/GOFF_systemz.h"
+#include "llvm/Object/GOFFObjectFile.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "jitlink"
+
+namespace llvm {
+namespace jitlink {
+
+Expected<std::unique_ptr<LinkGraph>>
+createLinkGraphFromGOFFObject(MemoryBufferRef ObjectBuffer,
+                              std::shared_ptr<orc::SymbolStringPool> SSP) {
+  return createLinkGraphFromGOFFObject_systemz(ObjectBuffer, std::move(SSP));
+}
+
+void link_GOFF(std::unique_ptr<LinkGraph> G,
+               std::unique_ptr<JITLinkContext> Ctx) {
+  link_GOFF_systemz(std::move(G), std::move(Ctx));
+}
+
+} // namespace jitlink
+} // namespace llvm
diff --git a/llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.cpp b/llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.cpp
new file mode 100644
index 0000000000000..79146891825c5
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.cpp
@@ -0,0 +1,274 @@
+//===--- GOFFLinkGraphBuilder.cpp - GOFF LinkGraph Builder ----------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Generic GOFF LinkGraph building code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GOFFLinkGraphBuilder.h"
+#include "llvm/BinaryFormat/GOFF.h"
+#include "llvm/ExecutionEngine/JITLink/GOFF_systemz.h"
+#include "llvm/ExecutionEngine/JITLink/JITLink.h"
+#include "llvm/ExecutionEngine/JITLink/systemz.h"
+#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
+#include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h"
+#include "llvm/Object/GOFFObjectFile.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
+
+using namespace llvm;
+using namespace llvm::jitlink;
+using namespace llvm::object;
+
+#define DEBUG_TYPE "jitlink"
+
+namespace llvm {
+namespace jitlink {
+
+GOFFLinkGraphBuilder::GOFFLinkGraphBuilder(
+    const object::GOFFObjectFile &Obj,
+    std::shared_ptr<orc::SymbolStringPool> SSP, Triple TT,
+    SubtargetFeatures Features,
+    LinkGraph::GetEdgeKindNameFunction GetEdgeKindName)
+    : Obj(Obj),
+      G(std::make_unique<LinkGraph>(
+          std::string(Obj.getFileName()), std::move(SSP), std::move(TT),
+          std::move(Features), std::move(GetEdgeKindName))) {}
+
+Expected<std::unique_ptr<LinkGraph>> GOFFLinkGraphBuilder::buildGraph() {
+  LLVM_DEBUG(dbgs() << "Building GOFFLinkGraph...\n");
+
+  // Check to make sure the object is relocatable.
+  if (!Obj.isRelocatableObject())
+    return make_error<JITLinkError>("Object is not a relocatable COFF file");
+
+  if (auto Err = processSections())
+    return std::move(Err);
+  if (auto Err = processSymbols())
+    return std::move(Err);
+  if (auto Err = processRelocations())
+    return std::move(Err);
+
+  return std::move(G);
+}
+
+Error GOFFLinkGraphBuilder::processSections() {
+  LLVM_DEBUG(dbgs() << "Processing GOFF sections ...\n");
+
+  for (const object::SectionRef Sec : Obj.sections()) {
+    Expected<StringRef> NameOrErr = Sec.getName();
+    if (!NameOrErr) {
+      return NameOrErr.takeError();
+    }
+
+    // Skip empty sections.
+    if (Sec.getSize() == 0)
+      return Error::success();
+
+    StringRef SectionName = *NameOrErr;
+    DataRefImpl SecRawDataRef = Sec.getRawDataRefImpl();
+    SmallString<16> UniqSectionName;
+    if (Error Err = Obj.getSectionUniqueName(SecRawDataRef, UniqSectionName)) {
+      return Err;
+    }
+
+    LLVM_DEBUG({
+      dbgs() << "    section = " << SectionName
+             << ", uniq = " << UniqSectionName << ", idx = " << Sec.getIndex()
+             << ", size = " << format_hex_no_prefix(Sec.getSize(), 8)
+             << ", vma = " << format_hex(Sec.getAddress(), 16) << "\n";
+    });
+
+    // Skip debug sections.
+    if (Sec.isDebugSection())
+      continue;
+
+    // Get memory protection flags.
+    orc::MemProt Prot = orc::MemProt::Read;
+    if (Sec.isText())
+      Prot |= orc::MemProt::Exec;
+    else if (Sec.isData())
+      Prot |= orc::MemProt::Write;
+
+    // Get or create the section in the graph.
+    auto *GraphSec = G->findSectionByName(UniqSectionName);
+    assert(!GraphSec && "Should be named unique");
+    GraphSec = &G->createSection(UniqSectionName, Prot);
+
+    if (GraphSec->getMemProt() != Prot)
+      return make_error<JITLinkError>("MemProt should match");
+
+    uint32_t SecIndex = Sec.getIndex();
+    if (SectionMap.contains(SecIndex))
+      return make_error<JITLinkError>("Index already exists");
+
+    Expected<StringRef> ContentsOrErr = Sec.getContents();
+    if (!ContentsOrErr) {
+      return ContentsOrErr.takeError();
+    }
+
+    // Create the content block in the graph.
+    StringRef Contents = *ContentsOrErr;
+    uint64_t SecAddress = Sec.getAddress();
+    Block *B = &G->createContentBlock(*GraphSec, Contents,
+                                      orc::ExecutorAddr(SecAddress),
+                                      Sec.getAlignment().value(), 0);
+    SectionMap[SecIndex] = {GraphSec, B, Sec};
+  }
+
+  return Error::success();
+}
+
+Error GOFFLinkGraphBuilder::processSymbols() {
+  LLVM_DEBUG(dbgs() << "Processing GOFF symbols...\n");
+
+  for (object::GOFFSymbolRef Sym : Obj.symbols()) {
+    Expected<StringRef> NameOrErr = Sym.getName();
+    if (!NameOrErr)
+      return NameOrErr.takeError();
+
+    StringRef Name = *NameOrErr;
+    uint32_t SymEsdId = Sym.getRawDataRefImpl().d.a;
+    LLVM_DEBUG(dbgs() << "  Processing symbol [" << SymEsdId << "] " << Name
+                      << "\n");
+
+    Expected<uint32_t> SymFlagsOrErr = Sym.getSymbolGOFFFlags();
+    if (!SymFlagsOrErr)
+      return SymFlagsOrErr.takeError();
+
+    uint32_t Flags = *SymFlagsOrErr;
+    if (Flags & object::SymbolRef::SF_Undefined) {
+      LLVM_DEBUG(dbgs() << "      created external symbol\n");
+      SymbolMap[SymEsdId] = &G->addExternalSymbol(
+          Name, Sym.getSize(), Flags & object::SymbolRef::SF_Weak);
+      continue;
+    }
+
+    auto SymbolTypeOrErr = Sym.getSymbolGOFFType();
+    if (!SymbolTypeOrErr)
+      return SymbolTypeOrErr.takeError();
+
+    Expected<section_iterator> SectionOrErr = Sym.getSection();
+    if (!SectionOrErr)
+      return SectionOrErr.takeError();
+
+    section_iterator SI = *SectionOrErr;
+    if (SI == Obj.section_end())
+      return make_error<JITLinkError>("Symbol section not found");
+
+    Expected<uint64_t> OffsetOrErr = Sym.getAddress();
+    if (!OffsetOrErr)
+      return OffsetOrErr.takeError();
+
+    uint32_t SecIndex = SI->getIndex();
+    Block *B = SectionMap[SecIndex].Block;
+    uint64_t Offset = *OffsetOrErr;
+    Linkage L =
+        (Flags & object::SymbolRef::SF_Weak) ? Linkage::Weak : Linkage::Strong;
+    Scope S{Scope::Local};
+    if (Flags & object::SymbolRef::SF_Hidden)
+      S = Scope::Hidden;
+    else if (Flags & object::SymbolRef::SF_Global)
+      S = Scope::Default;
+    SymbolRef::Type SymbolType = *SymbolTypeOrErr;
+    bool IsCallable = (SymbolType == object::SymbolRef::ST_Function);
+
+    LLVM_DEBUG(dbgs() << "      creating with linkage = " << getLinkageName(L)
+                      << ", scope = " << getScopeName(S)
+                      << ", B = " << format_hex(B->getAddress().getValue(), 16)
+                      << (IsCallable ? " function" : " non-callable") << "\n");
+
+    SymbolMap[SymEsdId] = &G->addDefinedSymbol(*B, Offset, Name, Sym.getSize(),
+                                               L, S, IsCallable, true);
+  }
+
+  return Error::success();
+}
+
+static systemz::EdgeKind_systemz getRelEdgeKind(uint64_t RelType) {
+  GOFF::RLDReferenceType RldRefType = getRLDReferenceType(RelType);
+  GOFF::RLDAction RldAct = getRLDAction(RelType);
+  GOFF::RLDFetchStore RldFetch = getRLDFetchStore(RelType);
+  uint8_t RldLength = getRLDTargetLength(RelType);
+  uint8_t RldBitLength = getRLDBitLength(RelType);
+  uint8_t RldBitWidth = 8 * RldLength + RldBitLength;
+
+  switch (RldRefType) {
+  case GOFF::RLD_RT_RAddress:
+    switch (RldBitWidth) {
+    case 64:
+      if (RldFetch == GOFF::RLD_FS_Fetch)
+        return (RldAct == GOFF::RLD_ACT_Add ? systemz::Pointer64Add
+                                            : systemz::Pointer64Sub);
+      else
+        return systemz::Pointer64;
+      break;
+    case 32:
+      if (RldFetch == GOFF::RLD_FS_Fetch)
+        return (RldAct == GOFF::RLD_ACT_Add ? systemz::Pointer32Add
+                                            : systemz::Pointer32Sub);
+      else
+        return systemz::Pointer32;
+      break;
+    default:
+      llvm_unreachable("Unsuppoted rld reference type");
+    }
+    break;
+  default:
+    llvm_unreachable("Unsuppoted rld reference type");
+  }
+}
+
+Error GOFFLinkGraphBuilder::processRelocations() {
+  LLVM_DEBUG(dbgs() << "Processing GOFF relocations...\n");
+
+  for (const object::SectionRef Sec : Obj.sections()) {
+    uint32_t SecIndex = Sec.getIndex();
+    auto SectionName = Sec.getName();
+    if (!SectionName)
+      return SectionName.takeError();
+
+    LLVM_DEBUG(dbgs() << " Relocations for section " << *SectionName << "\n");
+
+    for (object::RelocationRef Relocation : Sec.relocations()) {
+      object::SymbolRef Sym = *Relocation.getSymbol();
+      auto TargetNameOrErr = Sym.getName();
+      if (!TargetNameOrErr) {
+        return TargetNameOrErr.takeError();
+      }
+
+      SmallString<16> RelTypeName;
+      Relocation.getTypeName(RelTypeName);
+      uint64_t RelType = Relocation.getType();
+      systemz::EdgeKind_systemz EK = getRelEdgeKind(RelType);
+      jitlink::Block *B = SectionMap[SecIndex].Block;
+      uint32_t TargetBlockOffset = Sec.getAddress() + Relocation.getOffset() -
+                                   B->getAddress().getValue();
+      uint32_t REsdId = Sym.getRawDataRefImpl().d.a;
+      jitlink::Symbol *S = SymbolMap[REsdId];
+
+      LLVM_DEBUG({
+        dbgs() << "    reloffset = " << format_hex(Relocation.getOffset(), 16)
+               << " typename =  " << RelTypeName << " idx =  " << REsdId
+               << " block = (" << B << ", "
+               << format_hex(B->getAddress().getValue(), 16) << ")"
+               << " targetname: " << *TargetNameOrErr << "\n";
+      });
+
+      B->addEdge(EK, TargetBlockOffset, *S, 0);
+    }
+  }
+  return Error::success();
+}
+
+} // namespace jitlink
+} // namespace llvm
diff --git a/llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.h b/llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.h
new file mode 100644
index 0000000000000..edc54ba99c1a9
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.h
@@ -0,0 +1,67 @@
+//===------ GOFFLinkGraphBuilder.h - GOFF LinkGraph builder -----*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Generic GOFF LinkGraph building code.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LIB_EXECUTIONENGINE_JITLINK_GOFFLINKGRAPHBUILDER_H
+#define LIB_EXECUTIONENGINE_JITLINK_GOFFLINKGRAPHBUILDER_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ExecutionEngine/JITLink/JITLink.h"
+#include "llvm/ExecutionEngine/Orc/SymbolStringPool.h"
+#include "llvm/Object/GOFFObjectFile.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/TargetParser/SubtargetFeature.h"
+
+namespace llvm {
+namespace jitlink {
+
+// Builder for GOFF LinkGraphs.
+class GOFFLinkGraphBuilder {
+public:
+  virtual ~GOFFLinkGraphBuilder() = default;
+  Expected<std::unique_ptr<LinkGraph>> buildGraph();
+
+public:
+  GOFFLinkGraphBuilder(const object::GOFFObjectFile &Obj,
+                       std::shared_ptr<orc::SymbolStringPool> SSP, Triple TT,
+                       SubtargetFeatures Features,
+                       LinkGraph::GetEdgeKindNameFunction GetEdgeKindName);
+  LinkGraph &getGraph() const { return *G; }
+  const object::GOFFObjectFile &getObject() const { return Obj; }
+
+private:
+  // Process all sections in the GOFF file.
+  Error processSections();
+
+  // Process ESD symbols.
+  Error processSymbols();
+
+  // Process all relocations for all sections.
+  Error processRelocations();
+
+private:
+  const object::GOFFObjectFile &Obj;
+  std::unique_ptr<LinkGraph> G;
+
+  struct SectionEntry {
+    jitlink::Section *Section;
+    jitlink::Block *Block;
+    object::SectionRef SectionData;
+  };
+
+  DenseMap<uint32_t, jitlink::Symbol *> SymbolMap;
+  DenseMap<uint32_t, SectionEntry> SectionMap;
+};
+
+} // namespace jitlink
+} // namespace llvm
+
+#endif // LIB_EXECUTIONENGINE_JITLINK_GOFFLINKGRAPHBUILDER_H
diff --git a/llvm/lib/ExecutionEngine/JITLink/GOFF_systemz.cpp b/llvm/lib/ExecutionEngine/JITLink/GOFF_systemz.cpp
new file mode 100644
index 0000000000000..b1086ee361ed0
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/JITLink/GOFF_systemz.cpp
@@ -0,0 +1,100 @@
+//===----------- GOFF_systemz.cpp - JIT linker function for GOFF ----------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// JIT-Link functions for GOFF/systemz.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ExecutionEngine/JITLink/GOFF_systemz.h"
+#include "GOFFLinkGraphBuilder.h"
+#include "JITLinkGeneric.h"
+#include "llvm/ExecutionEngine/JITLink/GOFF.h"
+#include "llvm/ExecutionEngine/JITLink/JITLink.h"
+#include "llvm/ExecutionEngine/JITLink/systemz.h"
+#include "llvm/Object/GOFFObjectFile.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <system_error>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "jitlink"
+
+namespace llvm {
+namespace jitlink {
+
+class GOFFJITLinker_systemz : public JITLinker<GOFFJITLinker_systemz> {
+  using JITLinkerBase = JITLinker<GOFFJITLinker_systemz>;
+  friend JITLinkerBase;
+
+public:
+  GOFFJITLinker_systemz(std::unique_ptr<JITLinkContext> Ctx,
+                        std::unique_ptr<LinkGraph> G,
+                        PassConfiguration PassConfig)
+      : JITLinkerBase(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
+
+private:
+  Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
+    if (auto Err = systemz::applyGOFFFixup(G, B, E)) {
+      return make_error<StringError>("Unsupported goff relocation type",
+                                     std::error_code());
+    }
+    return Error::success();
+  }
+};
+
+Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromGOFFObject_systemz(
+    MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP) {
+  LLVM_DEBUG({
+    dbgs() << "Building jitlink graph for new input "
+           << ObjectBuffer.getBufferIdentifier() << "...\n";
+  });
+
+  file_magic Magic = identify_magic(ObjectBuffer.getBuffer());
+  if (Magic != file_magic::goff_object)
+    return make_error<JITLinkError>("Invalid GOFF Header");
+
+  auto GOFFObj = object::ObjectFile::createObjectFile(ObjectBuffer);
+  if (!GOFFObj)
+    return GOFFObj.takeError();
+  assert((*GOFFObj)->isGOFF() && "Expects an GOFF Object");
+  assert((*GOFFObj)->getArch() == Triple::systemz && "Only support systemz");
+
+  auto Features = (*GOFFObj)->getFeatures();
+  if (!Features)
+    return Features.takeError();
+  LLVM_DEBUG({
+    dbgs() << " Features: ";
+    (*Features).print(dbgs());
+  });
+
+  return GOFFLinkGraphBuilder(cast<object::GOFFObjectFile>(**GOFFObj),
+                              std::move(SSP), (*GOFFObj)->makeTriple(),
+                              std::move(*Features), systemz::getEdgeKindName)
+      .buildGraph();
+}
+
+void link_GOFF_systemz(std::unique_ptr<LinkGraph> G,
+                       std::unique_ptr<JITLinkContext> Ctx) {
+
+  PassConfiguration PassCfg;
+  if (auto Err = Ctx->modifyPassConfig(*G, PassCfg))
+    return Ctx->notifyFailed(std::move(Err));
+
+  if (G->getTargetTriple().getArch() != Triple::systemz)
+    return Ctx->notifyFailed(make_error<JITLinkError>(
+        "Unsupported target machine architecture in GOFF link graph " +
+        G->getName()));
+
+  GOFFJITLinker_systemz::link(std::move(Ctx), std::move(G), std::move(PassCfg));
+  return;
+}
+
+} // namespace jitlink
+} // namespace llvm
diff --git a/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp b/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp
index 329414a5e1275..4b407470b4e98 100644
--- a/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp
@@ -12,6 +12,7 @@
 #include "llvm/BinaryFormat/Magic.h"
 #include "llvm/ExecutionEngine/JITLink/COFF.h"
 #include "llvm/ExecutionEngine/JITLink/ELF.h"
+#include "llvm/ExecutionEngine/JITLink/GOFF.h"
 #include "llvm/ExecutionEngine/JITLink/MachO.h"
 #include "llvm/ExecutionEngine/JITLink/XCOFF.h"
 #include "llvm/ExecutionEngine/JITLink/aarch64.h"
@@ -527,6 +528,8 @@ createLinkGraphFromObject(MemoryBufferRef ObjectBuffer,
     return createLinkGraphFromCOFFObject(ObjectBuffer, std::move(SSP));
   case file_magic::xcoff_object_64:
     return createLinkGraphFromXCOFFObject(ObjectBuffer, std::move(SSP));
+  case file_magic::goff_object:
+    return createLinkGraphFromGOFFObject(ObjectBuffer, std::move(SSP));
   default:
     return make_error<JITLinkError>("Unsupported file format");
   };
@@ -560,6 +563,8 @@ void link(std::unique_ptr<LinkGraph> G, std::unique_ptr<JITLinkContext> Ctx) {
     return link_COFF(std::move(G), std::move(Ctx));
   case Triple::XCOFF:
     return link_XCOFF(std::move(G), std::move(Ctx));
+  case Triple::GOFF:
+    return link_GOFF(std::move(G), std::move(Ctx));
   default:
     Ctx->notifyFailed(make_error<JITLinkError>("Unsupported object format"));
   };
diff --git a/llvm/lib/ExecutionEngine/JITLink/systemz.cpp b/llvm/lib/ExecutionEngine/JITLink/systemz.cpp
index f5d335a17cf8b..0fa4b507016c1 100644
--- a/llvm/lib/ExecutionEngine/JITLink/systemz.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/systemz.cpp
@@ -46,6 +46,14 @@ const char *getEdgeKindName(Edge::Kind R) {
     return "Pointer12";
   case Pointer8:
     return "Pointer8";
+  case Pointer64Add:
+    return "Pointer64Add";
+  case Pointer32Add:
+    return "Pointer32Add";
+  case Pointer64Sub:
+    return "Pointer64Sub";
+  case Pointer32Sub:
+    return "Pointer32Sub";
   case Delta64:
     return "Delta64";
   case Delta32:
diff --git a/llvm/lib/ExecutionEngine/Orc/LoadLinkableFile.cpp b/llvm/lib/ExecutionEngine/Orc/LoadLinkableFile.cpp
index aab7b40372205..2dc7134a83491 100644
--- a/llvm/lib/ExecutionEngine/Orc/LoadLinkableFile.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/LoadLinkableFile.cpp
@@ -38,6 +38,13 @@ checkELFRelocatableObject(std::unique_ptr<MemoryBuffer> Obj, const Triple &TT) {
   return std::move(Obj);
 }
 
+static Expected<std::unique_ptr<MemoryBuffer>>
+checkGOFFRelocatableObject(std::unique_ptr<MemoryBuffer> Obj,
+                           const Triple &TT) {
+  // TODO: Actually check the architecture of the file.
+  return std::move(Obj);
+}
+
 Expected<std::pair<std::unique_ptr<MemoryBuffer>, LinkableFileKind>>
 loadLinkableFile(StringRef Path, const Triple &TT, LoadArchives LA,
                  std::optional<StringRef> IdentifierOverride) {
@@ -121,6 +128,15 @@ loadLinkableFile(StringRef Path, const Triple &TT, LoadArchives LA,
                             LinkableFileKind::RelocatableObject);
     }
     break;
+  case file_magic::goff_object:
+    if (!RequireFormat || *RequireFormat == Triple::GOFF) {
+      auto CheckedBuf = checkGOFFRelocatableObject(std::move(*Buf), TT);
+      if (!CheckedBuf)
+        return CheckedBuf.takeError();
+      return std::make_pair(std::move(*CheckedBuf),
+                            LinkableFileKind::RelocatableObject);
+    }
+    break;
   default:
     break;
   }
diff --git a/llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp b/llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp
index b9cee98d45d77..664f10cf4ec63 100644
--- a/llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp
@@ -11,6 +11,7 @@
 #include "llvm/ExecutionEngine/Orc/Shared/ObjectFormats.h"
 #include "llvm/Object/COFF.h"
 #include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/GOFFObjectFile.h"
 #include "llvm/Object/MachO.h"
 #include "llvm/Object/ObjectFile.h"
 #include "llvm/Object/XCOFFObjectFile.h"
@@ -229,6 +230,47 @@ getCOFFObjectFileSymbolInfo(ExecutionSession &ES,
   return I;
 }
 
+static Expected<MaterializationUnit::Interface>
+getGOFFObjectFileSymbolInfo(ExecutionSession &ES,
+                            const object::GOFFObjectFile &Obj) {
+  MaterializationUnit::Interface I;
+  for (auto &Sym : Obj.symbols()) {
+    Expected<uint32_t> SymFlagsOrErr = Sym.getFlags();
+    if (!SymFlagsOrErr)
+      return SymFlagsOrErr.takeError();
+
+    uint32_t Flags = *SymFlagsOrErr;
+    // Skip symbols not defined in this object file.
+    if (Flags & object::SymbolRef::SF_Undefined)
+      continue;
+    // Skip symbols that are not global.
+    if (!(Flags & object::SymbolRef::SF_Global))
+      continue;
+
+    auto SymbolType = Sym.getType();
+    if (!SymbolType)
+      return SymbolType.takeError();
+
+    // Skip file symbols.
+    if (*SymbolType == object::SymbolRef::ST_File)
+      continue;
+
+    auto Name = Sym.getName();
+    if (!Name)
+      return Name.takeError();
+
+    auto SymFlags = JITSymbolFlags::fromObjectSymbol(Sym);
+    if (!SymFlags)
+      return SymFlags.takeError();
+
+    // TODO: weak symbol.
+
+    I.SymbolFlags[ES.intern(std::move(*Name))] = std::move(*SymFlags);
+  }
+
+  return I;
+}
+
 Expected<MaterializationUnit::Interface>
 getXCOFFObjectFileSymbolInfo(ExecutionSession &ES,
                              const object::ObjectFile &Obj) {
@@ -330,6 +372,8 @@ getObjectFileInterface(ExecutionSession &ES, MemoryBufferRef ObjBuffer) {
     return getCOFFObjectFileSymbolInfo(ES, *COFFObj);
   else if (auto *XCOFFObj = dyn_cast<object::XCOFFObjectFile>(Obj->get()))
     return getXCOFFObjectFileSymbolInfo(ES, *XCOFFObj);
+  else if (auto *GOFFObj = dyn_cast<object::GOFFObjectFile>(Obj->get()))
+    return getGOFFObjectFileSymbolInfo(ES, *GOFFObj);
 
   return getGenericObjectFileSymbolInfo(ES, **Obj);
 }
diff --git a/llvm/lib/Object/GOFFObjectFile.cpp b/llvm/lib/Object/GOFFObjectFile.cpp
index c7e89c86e4cfb..e5fc9f2f48fc3 100644
--- a/llvm/lib/Object/GOFFObjectFile.cpp
+++ b/llvm/lib/Object/GOFFObjectFile.cpp
@@ -384,6 +384,12 @@ Expected<StringRef> GOFFObjectFile::getSymbolName(SymbolRef Symbol) const {
   return getSymbolName(Symbol.getRawDataRefImpl());
 }
 
+Expected<StringRef> GOFFObjectFile::getSymbolName(uint32_t SymIndex) const {
+  DataRefImpl Symb;
+  Symb.d.a = SymIndex;
+  return getSymbolName(Symb);
+}
+
 Expected<uint64_t> GOFFObjectFile::getSymbolAddress(DataRefImpl Symb) const {
   uint32_t Offset;
   const uint8_t *EsdRecord = getSymbolEsdRecord(Symb);
@@ -653,6 +659,48 @@ Expected<StringRef> GOFFObjectFile::getSectionName(DataRefImpl Sec) const {
   return Name;
 }
 
+Error GOFFObjectFile::getSectionUniqueName(
+    DataRefImpl Sec, SmallVectorImpl<char> &Result) const {
+
+  SectionEntryImpl EsdIds = SectionList[Sec.d.a];
+
+  const uint8_t *EsdRecord = EsdPtrs[EsdIds.d.a];
+  uint32_t ParentEsdId = 0;
+  ESDRecord::getParentEsdId(EsdRecord, ParentEsdId);
+  assert(ParentEsdId && "Should have parent");
+
+  DataRefImpl ParentEdSym;
+  ParentEdSym.d.a = ParentEsdId;
+  const uint8_t *ParentRecord = getSymbolEsdRecord(ParentEdSym);
+  GOFF::ESDSymbolType ParentSymbolType;
+  ESDRecord::getSymbolType(ParentRecord, ParentSymbolType);
+  assert(ParentSymbolType == GOFF::ESD_ST_SectionDefinition && "Not SD");
+
+  Expected<StringRef> ParentNameOrErr = getSymbolName(ParentEdSym);
+  if (!ParentNameOrErr)
+    return ParentNameOrErr.takeError();
+
+  Result.append(ParentNameOrErr->begin(), ParentNameOrErr->end());
+
+  Expected<StringRef> NameOrErr = getSymbolName(EsdIds.d.a);
+  if (!NameOrErr)
+    return NameOrErr.takeError();
+
+  Result.append(1, '.');
+  Result.append(NameOrErr->begin(), NameOrErr->end());
+
+  if (EsdIds.d.b) {
+    Expected<StringRef> PrNameOrErr = getSymbolName(EsdIds.d.b);
+    if (!PrNameOrErr)
+      return PrNameOrErr.takeError();
+
+    Result.append(1, '.');
+    Result.append(PrNameOrErr->begin(), PrNameOrErr->end());
+  }
+
+  return Error::success();
+}
+
 uint64_t GOFFObjectFile::getSectionAddress(DataRefImpl Sec) const {
   uint32_t Offset;
   const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec);
diff --git a/llvm/test/ExecutionEngine/JITLink/systemz/GOFF_systemz_reloc_ptr.ll b/llvm/test/ExecutionEngine/JITLink/systemz/GOFF_systemz_reloc_ptr.ll
new file mode 100644
index 0000000000000..4ab8f50c47bf0
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/systemz/GOFF_systemz_reloc_ptr.ll
@@ -0,0 +1,15 @@
+; llvm-jitlink -check is not available as it requries implementation of registerXCOFFGraphInfo. 
+; Will revisit this testcase once support is more complete.
+
+; RUN: llc --filetype=obj -mtriple=s390x-ibm-zos -o GOFF_systemz_reloc_ptr.o < %s
+; RUN: llvm-jitlink -noexec -num-threads=0 --triple=s390x-ibm-zos GOFF_systemz_reloc_ptr.o \
+; RUN: -abs CELQSTRT=0x00400000
+
+;target datalayout = "E-m:z-p:64:64-i1:8:16-i8:8:16-i16:16-i32:32-i64:64-f32:32-f64:64-f128:64-a:8:16-n32:64"
+;target triple = "s390x-ibm-zos"
+
+define i32 @main() {
+entry:
+  ret i32 0
+}
+

>From 53435bc44b23b46fdcb29ccc2a063313d5b47ffd Mon Sep 17 00:00:00 2001
From: Shimin Cui <scui at ca.ibm.com>
Date: Mon, 28 Sep 2026 19:41:08 +0000
Subject: [PATCH 2/3] Move processRelocations to GOFF_systemz.cpp

---
 .../JITLink/GOFFLinkGraphBuilder.cpp          | 97 ++-----------------
 .../JITLink/GOFFLinkGraphBuilder.h            | 36 ++++++-
 .../ExecutionEngine/JITLink/GOFF_systemz.cpp  | 97 ++++++++++++++++++-
 3 files changed, 137 insertions(+), 93 deletions(-)

diff --git a/llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.cpp b/llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.cpp
index 79146891825c5..ea16eba65b2dd 100644
--- a/llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.cpp
@@ -12,9 +12,6 @@
 
 #include "GOFFLinkGraphBuilder.h"
 #include "llvm/BinaryFormat/GOFF.h"
-#include "llvm/ExecutionEngine/JITLink/GOFF_systemz.h"
-#include "llvm/ExecutionEngine/JITLink/JITLink.h"
-#include "llvm/ExecutionEngine/JITLink/systemz.h"
 #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
 #include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h"
 #include "llvm/Object/GOFFObjectFile.h"
@@ -64,7 +61,7 @@ Expected<std::unique_ptr<LinkGraph>> GOFFLinkGraphBuilder::buildGraph() {
 Error GOFFLinkGraphBuilder::processSections() {
   LLVM_DEBUG(dbgs() << "Processing GOFF sections ...\n");
 
-  for (const object::SectionRef Sec : Obj.sections()) {
+  for (const object::SectionRef Sec : sections()) {
     Expected<StringRef> NameOrErr = Sec.getName();
     if (!NameOrErr) {
       return NameOrErr.takeError();
@@ -108,7 +105,7 @@ Error GOFFLinkGraphBuilder::processSections() {
       return make_error<JITLinkError>("MemProt should match");
 
     uint32_t SecIndex = Sec.getIndex();
-    if (SectionMap.contains(SecIndex))
+    if (getGraphBlock(SecIndex))
       return make_error<JITLinkError>("Index already exists");
 
     Expected<StringRef> ContentsOrErr = Sec.getContents();
@@ -122,7 +119,7 @@ Error GOFFLinkGraphBuilder::processSections() {
     Block *B = &G->createContentBlock(*GraphSec, Contents,
                                       orc::ExecutorAddr(SecAddress),
                                       Sec.getAlignment().value(), 0);
-    SectionMap[SecIndex] = {GraphSec, B, Sec};
+    setGraphBlock(SecIndex, GraphSec, B, Sec);
   }
 
   return Error::success();
@@ -148,8 +145,9 @@ Error GOFFLinkGraphBuilder::processSymbols() {
     uint32_t Flags = *SymFlagsOrErr;
     if (Flags & object::SymbolRef::SF_Undefined) {
       LLVM_DEBUG(dbgs() << "      created external symbol\n");
-      SymbolMap[SymEsdId] = &G->addExternalSymbol(
-          Name, Sym.getSize(), Flags & object::SymbolRef::SF_Weak);
+      Symbol &GSym = G->addExternalSymbol(Name, Sym.getSize(),
+                                          Flags & object::SymbolRef::SF_Weak);
+      setGraphSymbol(SymEsdId, GSym);
       continue;
     }
 
@@ -170,7 +168,7 @@ Error GOFFLinkGraphBuilder::processSymbols() {
       return OffsetOrErr.takeError();
 
     uint32_t SecIndex = SI->getIndex();
-    Block *B = SectionMap[SecIndex].Block;
+    Block *B = getGraphBlock(SecIndex);
     uint64_t Offset = *OffsetOrErr;
     Linkage L =
         (Flags & object::SymbolRef::SF_Weak) ? Linkage::Weak : Linkage::Strong;
@@ -187,88 +185,13 @@ Error GOFFLinkGraphBuilder::processSymbols() {
                       << ", B = " << format_hex(B->getAddress().getValue(), 16)
                       << (IsCallable ? " function" : " non-callable") << "\n");
 
-    SymbolMap[SymEsdId] = &G->addDefinedSymbol(*B, Offset, Name, Sym.getSize(),
-                                               L, S, IsCallable, true);
+    Symbol &GSym = G->addDefinedSymbol(*B, Offset, Name, Sym.getSize(), L, S,
+                                       IsCallable, true);
+    setGraphSymbol(SymEsdId, GSym);
   }
 
   return Error::success();
 }
 
-static systemz::EdgeKind_systemz getRelEdgeKind(uint64_t RelType) {
-  GOFF::RLDReferenceType RldRefType = getRLDReferenceType(RelType);
-  GOFF::RLDAction RldAct = getRLDAction(RelType);
-  GOFF::RLDFetchStore RldFetch = getRLDFetchStore(RelType);
-  uint8_t RldLength = getRLDTargetLength(RelType);
-  uint8_t RldBitLength = getRLDBitLength(RelType);
-  uint8_t RldBitWidth = 8 * RldLength + RldBitLength;
-
-  switch (RldRefType) {
-  case GOFF::RLD_RT_RAddress:
-    switch (RldBitWidth) {
-    case 64:
-      if (RldFetch == GOFF::RLD_FS_Fetch)
-        return (RldAct == GOFF::RLD_ACT_Add ? systemz::Pointer64Add
-                                            : systemz::Pointer64Sub);
-      else
-        return systemz::Pointer64;
-      break;
-    case 32:
-      if (RldFetch == GOFF::RLD_FS_Fetch)
-        return (RldAct == GOFF::RLD_ACT_Add ? systemz::Pointer32Add
-                                            : systemz::Pointer32Sub);
-      else
-        return systemz::Pointer32;
-      break;
-    default:
-      llvm_unreachable("Unsuppoted rld reference type");
-    }
-    break;
-  default:
-    llvm_unreachable("Unsuppoted rld reference type");
-  }
-}
-
-Error GOFFLinkGraphBuilder::processRelocations() {
-  LLVM_DEBUG(dbgs() << "Processing GOFF relocations...\n");
-
-  for (const object::SectionRef Sec : Obj.sections()) {
-    uint32_t SecIndex = Sec.getIndex();
-    auto SectionName = Sec.getName();
-    if (!SectionName)
-      return SectionName.takeError();
-
-    LLVM_DEBUG(dbgs() << " Relocations for section " << *SectionName << "\n");
-
-    for (object::RelocationRef Relocation : Sec.relocations()) {
-      object::SymbolRef Sym = *Relocation.getSymbol();
-      auto TargetNameOrErr = Sym.getName();
-      if (!TargetNameOrErr) {
-        return TargetNameOrErr.takeError();
-      }
-
-      SmallString<16> RelTypeName;
-      Relocation.getTypeName(RelTypeName);
-      uint64_t RelType = Relocation.getType();
-      systemz::EdgeKind_systemz EK = getRelEdgeKind(RelType);
-      jitlink::Block *B = SectionMap[SecIndex].Block;
-      uint32_t TargetBlockOffset = Sec.getAddress() + Relocation.getOffset() -
-                                   B->getAddress().getValue();
-      uint32_t REsdId = Sym.getRawDataRefImpl().d.a;
-      jitlink::Symbol *S = SymbolMap[REsdId];
-
-      LLVM_DEBUG({
-        dbgs() << "    reloffset = " << format_hex(Relocation.getOffset(), 16)
-               << " typename =  " << RelTypeName << " idx =  " << REsdId
-               << " block = (" << B << ", "
-               << format_hex(B->getAddress().getValue(), 16) << ")"
-               << " targetname: " << *TargetNameOrErr << "\n";
-      });
-
-      B->addEdge(EK, TargetBlockOffset, *S, 0);
-    }
-  }
-  return Error::success();
-}
-
 } // namespace jitlink
 } // namespace llvm
diff --git a/llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.h b/llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.h
index edc54ba99c1a9..07fb4866a8ab2 100644
--- a/llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.h
+++ b/llvm/lib/ExecutionEngine/JITLink/GOFFLinkGraphBuilder.h
@@ -29,15 +29,15 @@ class GOFFLinkGraphBuilder {
   virtual ~GOFFLinkGraphBuilder() = default;
   Expected<std::unique_ptr<LinkGraph>> buildGraph();
 
-public:
+protected:
   GOFFLinkGraphBuilder(const object::GOFFObjectFile &Obj,
                        std::shared_ptr<orc::SymbolStringPool> SSP, Triple TT,
                        SubtargetFeatures Features,
                        LinkGraph::GetEdgeKindNameFunction GetEdgeKindName);
   LinkGraph &getGraph() const { return *G; }
+
   const object::GOFFObjectFile &getObject() const { return Obj; }
 
-private:
   // Process all sections in the GOFF file.
   Error processSections();
 
@@ -45,7 +45,37 @@ class GOFFLinkGraphBuilder {
   Error processSymbols();
 
   // Process all relocations for all sections.
-  Error processRelocations();
+  virtual Error processRelocations() = 0;
+
+  void setGraphSymbol(uint32_t SymIndex, Symbol &Sym) {
+    assert(!SymbolMap.contains(SymIndex) && "Duplicate symbol at index");
+    SymbolMap[SymIndex] = &Sym;
+  }
+
+  Symbol *getGraphSymbol(uint32_t SymIndex) const {
+    if (SymbolMap.contains(SymIndex))
+      return SymbolMap.at(SymIndex);
+
+    return nullptr;
+  }
+
+  void setGraphBlock(uint32_t SecIndex, Section *Section, Block *B,
+                     object::SectionRef SectionData) {
+    assert(!SectionMap.contains(SecIndex) &&
+           "Duplicate section block at index");
+    SectionMap[SecIndex] = {Section, B, SectionData};
+  }
+
+  Block *getGraphBlock(uint32_t SecIndex) const {
+    if (SectionMap.contains(SecIndex))
+      return SectionMap.at(SecIndex).Block;
+
+    return nullptr;
+  }
+
+  object::GOFFObjectFile::section_iterator_range sections() const {
+    return Obj.sections();
+  }
 
 private:
   const object::GOFFObjectFile &Obj;
diff --git a/llvm/lib/ExecutionEngine/JITLink/GOFF_systemz.cpp b/llvm/lib/ExecutionEngine/JITLink/GOFF_systemz.cpp
index b1086ee361ed0..554b806e6667b 100644
--- a/llvm/lib/ExecutionEngine/JITLink/GOFF_systemz.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/GOFF_systemz.cpp
@@ -49,6 +49,18 @@ class GOFFJITLinker_systemz : public JITLinker<GOFFJITLinker_systemz> {
   }
 };
 
+class GOFFLinkGraphBuilder_systemz : public GOFFLinkGraphBuilder {
+private:
+  Error processRelocations() override;
+
+public:
+  GOFFLinkGraphBuilder_systemz(const object::GOFFObjectFile &Obj,
+                               std::shared_ptr<orc::SymbolStringPool> SSP,
+                               const Triple T, const SubtargetFeatures Features)
+      : GOFFLinkGraphBuilder(Obj, std::move(SSP), std::move(T),
+                             std::move(Features), systemz::getEdgeKindName) {}
+};
+
 Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromGOFFObject_systemz(
     MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP) {
   LLVM_DEBUG({
@@ -74,9 +86,9 @@ Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromGOFFObject_systemz(
     (*Features).print(dbgs());
   });
 
-  return GOFFLinkGraphBuilder(cast<object::GOFFObjectFile>(**GOFFObj),
-                              std::move(SSP), (*GOFFObj)->makeTriple(),
-                              std::move(*Features), systemz::getEdgeKindName)
+  return GOFFLinkGraphBuilder_systemz(cast<object::GOFFObjectFile>(**GOFFObj),
+                                      std::move(SSP), (*GOFFObj)->makeTriple(),
+                                      std::move(*Features))
       .buildGraph();
 }
 
@@ -96,5 +108,84 @@ void link_GOFF_systemz(std::unique_ptr<LinkGraph> G,
   return;
 }
 
+static systemz::EdgeKind_systemz getRelEdgeKind(uint64_t RelType) {
+  GOFF::RLDReferenceType RldRefType = object::getRLDReferenceType(RelType);
+  GOFF::RLDAction RldAct = object::getRLDAction(RelType);
+  GOFF::RLDFetchStore RldFetch = object::getRLDFetchStore(RelType);
+  uint8_t RldLength = object::getRLDTargetLength(RelType);
+  uint8_t RldBitLength = object::getRLDBitLength(RelType);
+  uint8_t RldBitWidth = 8 * RldLength + RldBitLength;
+
+  switch (RldRefType) {
+  case GOFF::RLD_RT_RAddress:
+    switch (RldBitWidth) {
+    case 64:
+      if (RldFetch == GOFF::RLD_FS_Fetch)
+        return (RldAct == GOFF::RLD_ACT_Add ? systemz::Pointer64Add
+                                            : systemz::Pointer64Sub);
+      else
+        return systemz::Pointer64;
+      break;
+    case 32:
+      if (RldFetch == GOFF::RLD_FS_Fetch)
+        return (RldAct == GOFF::RLD_ACT_Add ? systemz::Pointer32Add
+                                            : systemz::Pointer32Sub);
+      else
+        return systemz::Pointer32;
+      break;
+    default:
+      llvm_unreachable("Unsuppoted rld reference type");
+    }
+    break;
+  default:
+    llvm_unreachable("Unsuppoted rld reference type");
+  }
+}
+
+Error GOFFLinkGraphBuilder_systemz::processRelocations() {
+  LLVM_DEBUG(dbgs() << "Processing GOFF relocations...\n");
+
+  for (const object::SectionRef Sec : sections()) {
+    uint32_t SecIndex = Sec.getIndex();
+    auto SectionName = Sec.getName();
+    if (!SectionName)
+      return SectionName.takeError();
+
+    LLVM_DEBUG(dbgs() << " Relocations for section " << *SectionName << "\n");
+
+    for (object::RelocationRef Relocation : Sec.relocations()) {
+      object::SymbolRef Sym = *Relocation.getSymbol();
+      auto TargetNameOrErr = Sym.getName();
+      if (!TargetNameOrErr) {
+        return TargetNameOrErr.takeError();
+      }
+
+      SmallString<16> RelTypeName;
+      Relocation.getTypeName(RelTypeName);
+      uint64_t RelType = Relocation.getType();
+      systemz::EdgeKind_systemz EK = getRelEdgeKind(RelType);
+      jitlink::Block *B = getGraphBlock(SecIndex);
+      assert(B && "Block not found");
+
+      uint32_t TargetBlockOffset = Sec.getAddress() + Relocation.getOffset() -
+                                   B->getAddress().getValue();
+      uint32_t REsdId = Sym.getRawDataRefImpl().d.a;
+      jitlink::Symbol *S = getGraphSymbol(REsdId);
+      assert(S && "Symbol not found");
+
+      LLVM_DEBUG({
+        dbgs() << "    reloffset = " << format_hex(Relocation.getOffset(), 16)
+               << " typename =  " << RelTypeName << " idx =  " << REsdId
+               << " block = (" << B << ", "
+               << format_hex(B->getAddress().getValue(), 16) << ")"
+               << " targetname: " << *TargetNameOrErr << "\n";
+      });
+
+      B->addEdge(EK, TargetBlockOffset, *S, 0);
+    }
+  }
+  return Error::success();
+}
+
 } // namespace jitlink
 } // namespace llvm

>From 79e875aa2678c2eb7cef8f86562094b3f4232e8a Mon Sep 17 00:00:00 2001
From: Shimin Cui <scui at ca.ibm.com>
Date: Thu, 1 Oct 2026 13:45:34 +0000
Subject: [PATCH 3/3] Fix the assert - need jitlink symbols for GOFF ED
 R-pointer

---
 llvm/include/llvm/Object/GOFFObjectFile.h     |  5 ++++
 .../ExecutionEngine/JITLink/GOFF_systemz.cpp  |  3 ++
 llvm/lib/Object/GOFFObjectFile.cpp            | 30 +++++++++++++++----
 3 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/llvm/include/llvm/Object/GOFFObjectFile.h b/llvm/include/llvm/Object/GOFFObjectFile.h
index 3d6a0da34c86b..2a0da45dae3a4 100644
--- a/llvm/include/llvm/Object/GOFFObjectFile.h
+++ b/llvm/include/llvm/Object/GOFFObjectFile.h
@@ -82,6 +82,9 @@ class LLVM_ABI GOFFObjectFile : public ObjectFile {
   SmallVector<SectionEntryImpl, 256> SectionList;
   mutable DenseMap<uint32_t, SmallVector<uint8_t>> SectionDataCache;
 
+  // The flag to skip GOFF ED symbols or not during symbol traversal.
+  bool SkipEDSymbols = true;
+
   // Flattened data for all logical records (record type + continuous data
   // without headers).
   SmallVector<std::pair<GOFF::RecordType, SmallVector<uint8_t>>> FlattenedData;
@@ -141,6 +144,7 @@ class LLVM_ABI GOFFObjectFile : public ObjectFile {
   bool isSectionZeroInit(DataRefImpl Sec) const;
   Error getSectionUniqueName(DataRefImpl Sec,
                              SmallVectorImpl<char> &Result) const;
+  void setSkipEDSymbols(bool Skip) { SkipEDSymbols = Skip; }
 
 private:
   // SymbolRef.
@@ -156,6 +160,7 @@ class LLVM_ABI GOFFObjectFile : public ObjectFile {
   const uint8_t *getSymbolEsdRecord(DataRefImpl Symb) const;
   bool isSymbolUnresolved(DataRefImpl Symb) const;
   bool isSymbolIndirect(DataRefImpl Symb) const;
+  bool isEDSymbol(DataRefImpl Symb) const;
   Expected<StringRef> getSymbolName(uint32_t SymIndex) const;
 
   // SectionRef.
diff --git a/llvm/lib/ExecutionEngine/JITLink/GOFF_systemz.cpp b/llvm/lib/ExecutionEngine/JITLink/GOFF_systemz.cpp
index 554b806e6667b..9616008287b9b 100644
--- a/llvm/lib/ExecutionEngine/JITLink/GOFF_systemz.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/GOFF_systemz.cpp
@@ -86,6 +86,9 @@ Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromGOFFObject_systemz(
     (*Features).print(dbgs());
   });
 
+  // Set the flag to preserve GOFF ED symbols for creating JITLink symbols.
+  cast<object::GOFFObjectFile>(**GOFFObj).setSkipEDSymbols(false);
+
   return GOFFLinkGraphBuilder_systemz(cast<object::GOFFObjectFile>(**GOFFObj),
                                       std::move(SSP), (*GOFFObj)->makeTriple(),
                                       std::move(*Features))
diff --git a/llvm/lib/Object/GOFFObjectFile.cpp b/llvm/lib/Object/GOFFObjectFile.cpp
index e5fc9f2f48fc3..5e0bc16f64a48 100644
--- a/llvm/lib/Object/GOFFObjectFile.cpp
+++ b/llvm/lib/Object/GOFFObjectFile.cpp
@@ -424,6 +424,13 @@ bool GOFFObjectFile::isSymbolUnresolved(DataRefImpl Symb) const {
   return false;
 }
 
+bool GOFFObjectFile::isEDSymbol(DataRefImpl Symb) const {
+  const uint8_t *Record = getSymbolEsdRecord(Symb);
+  GOFF::ESDSymbolType SymbolType;
+  ESDRecord::getSymbolType(Record, SymbolType);
+  return (SymbolType == GOFF::ESD_ST_ElementDefinition);
+}
+
 bool GOFFObjectFile::isSymbolIndirect(DataRefImpl Symb) const {
   const uint8_t *Record = getSymbolEsdRecord(Symb);
   bool Indirect;
@@ -523,6 +530,20 @@ GOFFObjectFile::getSymbolSection(DataRefImpl Symb) const {
     return section_iterator(SectionRef(Sec, this));
 
   const uint8_t *SymEsdRecord = EsdPtrs[Symb.d.a];
+  // check if this is a ED symbol.
+  if (!SkipEDSymbols && isEDSymbol(Symb)) {
+    for (size_t I = 0, E = SectionList.size(); I < E; ++I) {
+      const uint8_t *SectionEdRecord = getSectionEdEsdRecord(I);
+      if (SymEsdRecord == SectionEdRecord) {
+        Sec.d.a = I;
+        return section_iterator(SectionRef(Sec, this));
+      }
+    }
+    return createStringError(llvm::errc::invalid_argument,
+                             "No section found for ED symbol with id " +
+                                 std::to_string(Symb.d.a));
+  }
+
   uint32_t SymEdId;
   ESDRecord::getParentEsdId(SymEsdRecord, SymEdId);
   const uint8_t *SymEdRecord = EsdPtrs[SymEdId];
@@ -828,11 +849,10 @@ void GOFFObjectFile::moveSymbolNext(DataRefImpl &Symb) const {
     if (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;
+      // Skip section symbols, including SDs and, if flagged, EDs.
+      bool SkipSymbol =
+          ((SymbolType == GOFF::ESD_ST_SectionDefinition) ||
+           (SymbolType == GOFF::ESD_ST_ElementDefinition && SkipEDSymbols));
       if (!SkipSymbol) {
         Symb.d.a = I;
         return;



More information about the llvm-commits mailing list