[llvm] [SystemZ][z/OS] Handle constants with initializers with relocations (PR #223055)

Kai Nacke via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 11 13:51:42 PDT 2026


https://github.com/redstar created https://github.com/llvm/llvm-project/pull/223055

When the initializer of a constant needs relocations, the constant cannot be placed into the code section, because the loader needs a writable section to fixup the relocation.
This change
 - sets the relocation model for z/OS to `DynamicNoPIC`, which better fits the relocation handling than `Static`, and
 - treats section kind `ReadOnlyWithRel` like a variable.

Assisted by IBM Bob.

>From e03e9ee4567c8ab9e189405410d2fae1734d10f8 Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Fri, 11 Sep 2026 16:46:12 -0400
Subject: [PATCH] [SystemZ][z/OS] Handle constants with initializers with
 relocations

When the initializer of a constant needs relocations, the constant
cannot be placed into the code section, because the loader needs
a writable section to fixup the relocation.
This change
 - sets the relocation model for z/OS to `DynamicNoPIC`, which better
   fits the relocation handling than `Static`, and
 - treats section kind `ReadOnlyWithRel` like a variable.
---
 .../CodeGen/TargetLoweringObjectFileImpl.cpp  |  2 +-
 .../Target/SystemZ/SystemZTargetMachine.cpp   | 21 +++++++++++----
 .../SystemZ/zos-section-readonlywithrel.ll    | 27 +++++++++++++++++++
 3 files changed, 44 insertions(+), 6 deletions(-)
 create mode 100644 llvm/test/CodeGen/SystemZ/zos-section-readonlywithrel.ll

diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index 2fb44041c164e..9f7a038374792 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -2893,7 +2893,7 @@ MCSection *TargetLoweringObjectFileGOFF::SelectSectionForGlobal(
     const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
   auto *Symbol = TM.getSymbol(GO);
 
-  if (Kind.isBSS() || Kind.isData()) {
+  if (Kind.isBSS() || Kind.isData() || Kind.isReadOnlyWithRel()) {
     GOFF::ESDBindingScope PRBindingScope =
         GO->hasExternalLinkage()
             ? (GO->hasDefaultVisibility() ? GOFF::ESD_BSC_ImportExport
diff --git a/llvm/lib/Target/SystemZ/SystemZTargetMachine.cpp b/llvm/lib/Target/SystemZ/SystemZTargetMachine.cpp
index 5158077716d9f..ccf3b07b0bfab 100644
--- a/llvm/lib/Target/SystemZ/SystemZTargetMachine.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZTargetMachine.cpp
@@ -68,9 +68,20 @@ static std::unique_ptr<TargetLoweringObjectFile> createTLOF(const Triple &TT) {
   return std::make_unique<SystemZELFTargetObjectFile>();
 }
 
-static Reloc::Model getEffectiveRelocModel(std::optional<Reloc::Model> RM) {
-  // Static code is suitable for use in a dynamic executable; there is no
-  // separate DynamicNoPIC model.
+static Reloc::Model getEffectiveRelocModel(const Triple &TT,
+                                           std::optional<Reloc::Model> RM) {
+  if (TT.isOSzOS()) {
+    // On z/OS, constant globals whose initializers contain pointer relocations
+    // (e.g. vtables) must be placed in a writable section (C_WSA64) so the
+    // GOFF binder can apply them at link time.  Using DynamicNoPIC causes
+    // getKindForGlobal() to classify such globals as ReadOnlyWithRel instead
+    // of ReadOnly, which routes them to C_WSA64 rather than C_CODE64.
+    if (!RM || *RM == Reloc::DynamicNoPIC)
+      return Reloc::DynamicNoPIC;
+    return *RM;
+  }
+  // For ELF/Linux, static code is suitable for use in a dynamic executable;
+  // there is no separate DynamicNoPIC model.
   if (!RM || *RM == Reloc::DynamicNoPIC)
     return Reloc::Static;
   return *RM;
@@ -128,8 +139,8 @@ SystemZTargetMachine::SystemZTargetMachine(const Target &T, const Triple &TT,
                                            CodeGenOptLevel OL, bool JIT)
     : CodeGenTargetMachineImpl(
           T, TT.computeDataLayout(), TT, CPU, FS, Options,
-          getEffectiveRelocModel(RM),
-          getEffectiveSystemZCodeModel(CM, getEffectiveRelocModel(RM), JIT),
+          getEffectiveRelocModel(TT, RM),
+          getEffectiveSystemZCodeModel(CM, getEffectiveRelocModel(TT, RM), JIT),
           OL),
       TLOF(createTLOF(getTargetTriple())) {
   initAsmInfo();
diff --git a/llvm/test/CodeGen/SystemZ/zos-section-readonlywithrel.ll b/llvm/test/CodeGen/SystemZ/zos-section-readonlywithrel.ll
new file mode 100644
index 0000000000000..5f856df3fc4ce
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/zos-section-readonlywithrel.ll
@@ -0,0 +1,27 @@
+; Test that a read-only global containing pointer relocations (e.g. a C++
+; vtable) is placed in the writable static area (C_WSA64) and NOT in the
+; constant/text section (C_CODE64) on z/OS.
+;
+; z/OS uses DynamicNoPIC as its effective relocation model.  This causes
+; getKindForGlobal() to skip the Reloc::Static short-circuit and classify
+; constant globals that needsDynamicRelocation() as ReadOnlyWithRel instead
+; of ReadOnly.  SelectSectionForGlobal then routes ReadOnlyWithRel to a
+; per-symbol C_WSA64 PR section (ESD_LB_Deferred) rather than C_CODE64
+; (ESD_LB_Initial, read-only/executable).
+;
+; RUN: llc < %s -mtriple=s390x-ibm-zos | FileCheck %s
+
+ at _ZTV4Base = hidden constant [2 x ptr] [ptr null, ptr @_ZTI4Base], align 8
+ at _ZTI4Base = external hidden global ptr
+
+; The vtable gets its own SD and a C_WSA64 ED (DEFLOAD, NOTEXECUTABLE).
+; CHECK: _ZTV4Base CSECT
+; CHECK-NEXT: C_WSA64 CATTR {{.*}}DEFLOAD,NOTEXECUTABLE
+; CHECK: _ZTV4Base XATTR LINKAGE(XPLINK),REFERENCE(DATA)
+
+; The vtable data (null + reloc to _ZTI4Base) appears inside the WSA section.
+; CHECK: DC XL8'0000000000000000'
+; CHECK-NEXT: DC AD(_ZTI4Base)
+
+; Crucially, the vtable must NOT be emitted directly inside C_CODE64.
+; CHECK-NOT: C_CODE64{{.*}}_ZTV4Base



More information about the llvm-commits mailing list