[llvm] [SystemZ][z/OS] Keep weak references weak (PR #226840)
MMS IT GmbH via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 27 14:51:14 PDT 2026
https://github.com/mms-it-ch created https://github.com/llvm/llvm-project/pull/226840
Fixes #226835.
An `extern_weak` reference must stay unresolved without an error when the symbol does not exist. On z/OS two kinds of references were always strong:
- Taking the address of an external function goes through the indirect symbol `<name>@indirect` (ADA slot `MO_ADA_INDIRECT_FUNC_DESC`). It never got the weak attribute of the function symbol. The weak attribute is already set on the function symbol when the ADA is emitted, because `AsmPrinter::doFinalization` emits the weak references before `emitEndOfAsmFile`. So the indirect symbol now becomes a weak reference too.
- An external data reference is a part reference (PR). `GOFF::PRAttr` had no binding strength, and the PR constructor in `GOFFObjectWriter` did not set it. `PRAttr` gets a `BindingStrength` field, and `defineExtern` passes the strength of the symbol.
Without this change, a C program with `extern int wv __attribute__((weak));` and `extern void wf(void) __attribute__((weak));` that takes `&wv` and `wf` fails to bind on z/OS 3.1 (IEW2456E for `wv` and `wf`). With the change it binds, and at run time both addresses are null, as on other targets.
Tests: new `zos-extern-weak.ll` checks `WXTRN wf at indirect` in the HLASM output and the binding strength of the ER and PR symbols in the object file. `llvm-lit test/CodeGen/SystemZ test/MC/SystemZ test/MC/GOFF` passes (1304 passed, 19 unsupported). Without the fix, both checks of the new test fail.
Found while porting Free Pascal to z/OS (`weakexternal`).
Assisted-by: Claude Code (Anthropic)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
>From c737d5b34ad701809446d4c22fc8bd7d01297fbd Mon Sep 17 00:00:00 2001
From: mms-it-ch <info at mms-it.ch>
Date: Sun, 27 Sep 2026 23:37:55 +0200
Subject: [PATCH] [SystemZ][z/OS] Keep weak references weak
An extern_weak reference must stay unresolved without an error when the
symbol does not exist. On z/OS two kinds of references were always
strong:
- Taking the address of an external function goes through the indirect
symbol <name>@indirect, which never got the weak attribute of the
function symbol. The attribute is already set when the ADA is emitted,
because AsmPrinter::doFinalization emits the weak references before
emitEndOfAsmFile.
- An external data reference is a part reference (PR). GOFF::PRAttr had
no binding strength, and the PR constructor did not set it.
Without this change the binder reports IEW2456E for such references.
Fixes #226835.
Assisted-by: Claude Code (Anthropic)
---
llvm/include/llvm/MC/MCGOFFAttributes.h | 1 +
llvm/lib/MC/GOFFObjectWriter.cpp | 5 ++-
.../SystemZ/SystemZXPLINKAsmPrinter.cpp | 4 ++
llvm/test/CodeGen/SystemZ/zos-extern-weak.ll | 37 +++++++++++++++++++
4 files changed, 45 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/CodeGen/SystemZ/zos-extern-weak.ll
diff --git a/llvm/include/llvm/MC/MCGOFFAttributes.h b/llvm/include/llvm/MC/MCGOFFAttributes.h
index 289d962e993b8..eb2abd4cf806b 100644
--- a/llvm/include/llvm/MC/MCGOFFAttributes.h
+++ b/llvm/include/llvm/MC/MCGOFFAttributes.h
@@ -77,6 +77,7 @@ struct PRAttr {
GOFF::ESDLinkageType Linkage = GOFF::ESD_LT_XPLink;
GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified;
uint32_t SortKey = 0;
+ GOFF::ESDBindingStrength BindingStrength = GOFF::ESD_BST_Strong;
};
// Attributes for ER symbols.
diff --git a/llvm/lib/MC/GOFFObjectWriter.cpp b/llvm/lib/MC/GOFFObjectWriter.cpp
index 36407759dbc50..435e144f828bd 100644
--- a/llvm/lib/MC/GOFFObjectWriter.cpp
+++ b/llvm/lib/MC/GOFFObjectWriter.cpp
@@ -265,6 +265,7 @@ class GOFFSymbol {
SymbolType(GOFF::ESD_ST_PartReference), NameSpace(EDAttr.NameSpace) {
SymbolFlags.setRenameable(Attr.IsRenamable);
BehavAttrs.setExecutable(Attr.Executable);
+ BehavAttrs.setBindingStrength(Attr.BindingStrength);
BehavAttrs.setLinkageType(Attr.Linkage);
BehavAttrs.setBindingScope(Attr.BindingScope);
BehavAttrs.setAlignment(Alignment);
@@ -401,8 +402,8 @@ void GOFFWriter::defineExtern(const MCSymbolGOFF &Symbol) {
GOFFSymbol PR(Symbol.getExternalName(), Symbol.getIndex(), ED->getOrdinal(),
ED->getEDAttributes(), ED->getEDAlignment(),
GOFF::PRAttr{/*IsRenamable*/ false, Symbol.getCodeData(),
- Symbol.getLinkage(), Symbol.getBindingScope(),
- 0});
+ Symbol.getLinkage(), Symbol.getBindingScope(), 0,
+ Symbol.getBindingStrength()});
writeSymbol(PR);
} else {
GOFFSymbol ER(Symbol.getExternalName(), Symbol.getIndex(),
diff --git a/llvm/lib/Target/SystemZ/SystemZXPLINKAsmPrinter.cpp b/llvm/lib/Target/SystemZ/SystemZXPLINKAsmPrinter.cpp
index 3ab3dfc4a0bcb..166185fa0967d 100644
--- a/llvm/lib/Target/SystemZ/SystemZXPLINKAsmPrinter.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZXPLINKAsmPrinter.cpp
@@ -341,6 +341,10 @@ void SystemZXPLINKAsmPrinter::emitADASection() {
OutStreamer->emitSymbolAttribute(Alias, MCSA_Extern);
MCSymbolGOFF *GOFFSym =
static_cast<llvm::MCSymbolGOFF *>(const_cast<llvm::MCSymbol *>(Sym));
+ // A weak reference (extern_weak) stays weak through the indirect
+ // symbol, otherwise the binder fails on the unresolved reference.
+ if (GOFFSym->isWeak())
+ OutStreamer->emitSymbolAttribute(Alias, MCSA_WeakReference);
ZOS->emitExternalName(Alias, GOFFSym->getExternalName());
EMIT_COMMENT("pointer to function descriptor");
OutStreamer->emitValue(
diff --git a/llvm/test/CodeGen/SystemZ/zos-extern-weak.ll b/llvm/test/CodeGen/SystemZ/zos-extern-weak.ll
new file mode 100644
index 0000000000000..d8b1d1e9789f7
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/zos-extern-weak.ll
@@ -0,0 +1,37 @@
+; Weak references (extern_weak) must be weak in the object file: the indirect
+; symbol through which the address of a function is taken, and the part
+; reference for external data. Otherwise the binder fails when the symbol
+; does not exist.
+; RUN: llc < %s -mtriple=s390x-ibm-zos | FileCheck %s
+; RUN: llc < %s -mtriple=s390x-ibm-zos --filetype=obj | \
+; RUN: od -Ax -tx1 -v | FileCheck --check-prefix=CHECKOBJ --ignore-case %s
+
+; CHECK-DAG: WXTRN wf at indirect
+; CHECK-DAG: WXTRN wf{{ *$}}
+; CHECK-NOT: {{^ EXTRN wf}}
+
+; The last bytes of the ESD records: behavioral attributes 4-9 (binding
+; strength 1 = weak in byte 4), name length and name.
+; PR wv (external data):
+; CHECKOBJ: {{^[0-9a-f]+}} 01 04 24 00 00 00 00 02 a6 a5
+; ER wf at indirect (external name wf, indirect reference):
+; CHECKOBJ: {{^[0-9a-f]+}} 01 14 20 00 00 00 00 02 a6 86
+; ER wf:
+; CHECKOBJ: {{^[0-9a-f]+}} 01 04 20 00 00 00 00 02 a6 86
+
+ at wv = extern_weak global i32
+
+declare extern_weak void @wf()
+
+define ptr @getv() {
+ ret ptr @wv
+}
+
+define ptr @getf() {
+ ret ptr @wf
+}
+
+define void @callf() {
+ call void @wf()
+ ret void
+}
More information about the llvm-commits
mailing list