[llvm] b013bbf - [llvm-nm][GOFF] Display archive attributes in GOFF archives through --print-armap (#214527)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 18 20:54:49 PDT 2026
Author: Amy Kwan
Date: 2026-08-18T23:54:45-04:00
New Revision: b013bbf63a304f263c839e1ad2b079bad3b77cae
URL: https://github.com/llvm/llvm-project/commit/b013bbf63a304f263c839e1ad2b079bad3b77cae
DIFF: https://github.com/llvm/llvm-project/commit/b013bbf63a304f263c839e1ad2b079bad3b77cae.diff
LOG: [llvm-nm][GOFF] Display archive attributes in GOFF archives through --print-armap (#214527)
GOFF archive symbol table entries contain an attribute word in addition
to the archive member offset. The low three bits describe whether the
symbol is 64-bit, uses XPLink, or belongs to the WSA namespace (which
was briefly mentioned in e2c8fa09872cfacba7f73599dcf8557971ebe865).
This patch extends `llvm-nm --print-armap` to print the attribute value
(in hex)
and its decoded description beside a symbol and its corresponding member
when processing a GOFF archive. This will functionality will be used to
help
validate full support for writing GOFF archives in a subsequent llvm-ar
patch.
The output for non-z/OS archives is unchanged.
Added:
llvm/test/tools/llvm-nm/zos-armap.test
Modified:
llvm/include/llvm/Object/Archive.h
llvm/lib/Object/Archive.cpp
llvm/tools/llvm-nm/llvm-nm.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/Object/Archive.h b/llvm/include/llvm/Object/Archive.h
index 45877335b0778..3b7bae296e60f 100644
--- a/llvm/include/llvm/Object/Archive.h
+++ b/llvm/include/llvm/Object/Archive.h
@@ -348,6 +348,18 @@ class LLVM_ABI Archive : public Binary {
LLVM_ABI Expected<Child> getMember() const;
LLVM_ABI Symbol getNext() const;
LLVM_ABI bool isECSymbol() const;
+
+ /// Archive attribute bit masks for K_ZOS archive symbol table entries.
+ static constexpr uint32_t ZOSAttrWSA = 0x1;
+ static constexpr uint32_t ZOSAttrXPLink = 0x2;
+ static constexpr uint32_t ZOSAttr64Bit = 0x4;
+ static constexpr uint32_t ZOSKnownAttrMask =
+ ZOSAttrWSA | ZOSAttrXPLink | ZOSAttr64Bit;
+
+ /// For K_ZOS archives, returns the 32-bit attribute word stored alongside
+ /// the symbol table entry. The low bits are described by the ZOSAttr*
+ /// constants above. Returns 0 for non-z/OS archives.
+ LLVM_ABI uint32_t getZOSAttributes() const;
};
class symbol_iterator {
diff --git a/llvm/lib/Object/Archive.cpp b/llvm/lib/Object/Archive.cpp
index ae54338bb9a71..be0dcaab9ba5e 100644
--- a/llvm/lib/Object/Archive.cpp
+++ b/llvm/lib/Object/Archive.cpp
@@ -1140,6 +1140,18 @@ bool Archive::Symbol::isECSymbol() const {
SymbolIndex < SymbolCount + Parent->getNumberOfECSymbols();
}
+uint32_t Archive::Symbol::getZOSAttributes() const {
+ assert(Parent->kind() == K_ZOS && "Cannot get z/OS attributes for non-z/OS "
+ "archives");
+ if (SymbolIndex >= Parent->getNumberOfSymbols())
+ return 0;
+
+ // The z/OS symbol table layout is:
+ // NumSyms * { uint32_t member_offset, uint32_t attrs } (big-endian)
+ const char *Buf = Parent->getSymbolTable().begin();
+ return read32be(Buf + sizeof(uint32_t) + SymbolIndex * 8 + sizeof(uint32_t));
+}
+
StringRef Archive::Symbol::getName() const {
if (isECSymbol())
return Parent->ECSymbolTable.begin() + StringIndex;
diff --git a/llvm/test/tools/llvm-nm/zos-armap.test b/llvm/test/tools/llvm-nm/zos-armap.test
new file mode 100644
index 0000000000000..800d5c00eb15b
--- /dev/null
+++ b/llvm/test/tools/llvm-nm/zos-armap.test
@@ -0,0 +1,48 @@
+## Test that llvm-nm --print-armap prints the archive map for GOFF archives,
+## including inline z/OS attribute flags on each symbol line.
+##
+## All 8 combinations of the 3 known attribute bits are exercised, plus two
+## symbols with unknown bits set to cover the '?' flag:
+## bit 2 (0x4): 64-bit (AMODE == ESD_AMODE_64)
+## bit 1 (0x2): XPLink (LinkageType == ESD_LT_XPLink)
+## bit 0 (0x1): WSA (parent ED namespace == ESD_NS_Parts)
+## bits above 0x7: unknown, printed as '?'
+##
+## symunk uses the highest bit (0x80000000) — the least likely to ever become
+## a known value — so that if new low bits are defined in the future, this
+## test still covers a genuinely unknown bit.
+## symall uses 0xffffffff so that adding any new known value will cause this
+## CHECK line to fail.
+##
+## The archive is generated directly using generate_zos_archive.py.
+
+# RUN: rm -rf %t.dir && mkdir -p %t.dir
+
+# RUN: %python %S/../../Object/Inputs/generate_zos_archive.py \
+# RUN: --output %t.dir/test.a \
+# RUN: --member "test.o:hex:abcdabcd" \
+# RUN: --symtab "sym000:0:0" \
+# RUN: --symtab "sym001:0:1" \
+# RUN: --symtab "sym010:0:2" \
+# RUN: --symtab "sym011:0:3" \
+# RUN: --symtab "sym100:0:4" \
+# RUN: --symtab "sym101:0:5" \
+# RUN: --symtab "sym110:0:6" \
+# RUN: --symtab "sym111:0:7" \
+# RUN: --symtab "symunk:0:2147483648" \
+# RUN: --symtab "symall:0:4294967295"
+
+# RUN: llvm-nm --print-armap %t.dir/test.a | FileCheck %s
+
+## For z/OS archives, each symbol line includes (flags: <hex> [description]).
+# CHECK: Archive map
+# CHECK-NEXT: sym000 in test.o (flags: 0x00000000 [none])
+# CHECK-NEXT: sym001 in test.o (flags: 0x00000001 [WSA])
+# CHECK-NEXT: sym010 in test.o (flags: 0x00000002 [XPLink])
+# CHECK-NEXT: sym011 in test.o (flags: 0x00000003 [XPLink + WSA])
+# CHECK-NEXT: sym100 in test.o (flags: 0x00000004 [64-bit])
+# CHECK-NEXT: sym101 in test.o (flags: 0x00000005 [64-bit + WSA])
+# CHECK-NEXT: sym110 in test.o (flags: 0x00000006 [64-bit + XPLink])
+# CHECK-NEXT: sym111 in test.o (flags: 0x00000007 [64-bit + XPLink + WSA])
+# CHECK-NEXT: symunk in test.o (flags: 0x80000000 [?])
+# CHECK-NEXT: symall in test.o (flags: 0xffffffff [64-bit + XPLink + WSA + ?])
diff --git a/llvm/tools/llvm-nm/llvm-nm.cpp b/llvm/tools/llvm-nm/llvm-nm.cpp
index 4f3f833a50a5f..170bf0b7fc22e 100644
--- a/llvm/tools/llvm-nm/llvm-nm.cpp
+++ b/llvm/tools/llvm-nm/llvm-nm.cpp
@@ -2069,9 +2069,39 @@ static bool checkMachOAndArchFlags(SymbolicFile *O, StringRef Filename) {
return true;
}
-static void printArchiveMap(iterator_range<Archive::symbol_iterator> &map,
- StringRef Filename) {
- for (auto I : map) {
+/// Decode the low 3 bits of a z/OS archive symbol attribute word into a
+/// human-readable description written to OS, e.g. "[64-bit + XPLink]".
+/// Any bits above the known 3-bit mask produce a trailing "?" flag.
+static void decodeZOSAttributes(raw_ostream &OS, uint32_t Attrs) {
+ bool Unknown = (Attrs & ~Archive::Symbol::ZOSKnownAttrMask) != 0;
+ bool Is64Bit = (Attrs & Archive::Symbol::ZOSAttr64Bit) != 0;
+ bool IsXPLink = (Attrs & Archive::Symbol::ZOSAttrXPLink) != 0;
+ bool IsWSA = (Attrs & Archive::Symbol::ZOSAttrWSA) != 0;
+
+ OS << "[";
+ bool NeedPlus = false;
+ auto Append = [&](const char *S) {
+ if (NeedPlus)
+ OS << " + ";
+ OS << S;
+ NeedPlus = true;
+ };
+ if (Is64Bit)
+ Append("64-bit");
+ if (IsXPLink)
+ Append("XPLink");
+ if (IsWSA)
+ Append("WSA");
+ if (Unknown)
+ Append("?");
+ if (!NeedPlus)
+ Append("none");
+ OS << "]";
+}
+
+static void printArchiveMap(iterator_range<Archive::symbol_iterator> &Map,
+ StringRef Filename, Archive::Kind Kind) {
+ for (auto I : Map) {
Expected<Archive::Child> C = I.getMember();
if (!C) {
error(C.takeError(), Filename);
@@ -2083,7 +2113,14 @@ static void printArchiveMap(iterator_range<Archive::symbol_iterator> &map,
break;
}
StringRef SymName = I.getName();
- outs() << SymName << " in " << FileNameOrErr.get() << "\n";
+ outs() << SymName << " in " << FileNameOrErr.get();
+ if (Kind == Archive::K_ZOS) {
+ uint32_t Attrs = I.getZOSAttributes();
+ outs() << format(" (flags: 0x%08x ", Attrs);
+ decodeZOSAttributes(outs(), Attrs);
+ outs() << ")";
+ }
+ outs() << "\n";
}
outs() << "\n";
@@ -2093,7 +2130,7 @@ static void dumpArchiveMap(Archive *A, StringRef Filename) {
auto Map = A->symbols();
if (!Map.empty()) {
outs() << "Archive map\n";
- printArchiveMap(Map, Filename);
+ printArchiveMap(Map, Filename, A->kind());
}
auto ECMap = A->ec_symbols();
@@ -2101,7 +2138,7 @@ static void dumpArchiveMap(Archive *A, StringRef Filename) {
warn(ECMap.takeError(), Filename);
} else if (!ECMap->empty()) {
outs() << "Archive EC map\n";
- printArchiveMap(*ECMap, Filename);
+ printArchiveMap(*ECMap, Filename, A->kind());
}
}
More information about the llvm-commits
mailing list