[llvm] [z/OS] Add z/OS archive reading support (PR #187110)
Uyiosa Iyekekpolor via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 13 06:56:34 PDT 2026
https://github.com/uyoyo0 updated https://github.com/llvm/llvm-project/pull/187110
>From 12727a9a353bf7dead4aba1086b58e05a9a92cdf Mon Sep 17 00:00:00 2001
From: Uyiosa Iyekekpolor <uyiosaben at gmail.com>
Date: Tue, 17 Mar 2026 15:29:38 -0400
Subject: [PATCH 1/6] [z/OS] Add z/OS archive reading support
---
llvm/include/llvm/Object/Archive.h | 53 +++++-
llvm/lib/Object/Archive.cpp | 211 ++++++++++++++++++++-
llvm/lib/Object/ArchiveWriter.cpp | 3 +
llvm/test/Object/Inputs/zos-archive-test.a | Bin 0 -> 1424 bytes
llvm/test/Object/zos-archive-read.test | 9 +
5 files changed, 273 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/Object/Inputs/zos-archive-test.a
create mode 100644 llvm/test/Object/zos-archive-read.test
diff --git a/llvm/include/llvm/Object/Archive.h b/llvm/include/llvm/Object/Archive.h
index c97018d3231d5..9a72460846502 100644
--- a/llvm/include/llvm/Object/Archive.h
+++ b/llvm/include/llvm/Object/Archive.h
@@ -158,6 +158,35 @@ class LLVM_ABI BigArchiveMemberHeader
Expected<bool> isThin() const override { return false; }
};
+// Define file member header of z/OS archive.
+class ZOSArchiveMemberHeader : public ArchiveMemberHeader {
+public:
+ ZOSArchiveMemberHeader(Archive const *Parent, const char *RawHeaderPtr,
+ uint64_t Size, Error *Err);
+ std::unique_ptr<AbstractArchiveMemberHeader> clone() const override {
+ return std::make_unique<ZOSArchiveMemberHeader>(*this);
+ }
+
+ // Converted EBCDIC to ASCII header string fields.
+ std::string RawMemberName;
+ std::string MemberName;
+ std::string LastModified;
+ std::string UID;
+ std::string GID;
+ std::string AccessMode;
+
+ void setMemberHeaderStrings(Error *Err, uint64_t Size);
+
+ Expected<StringRef> getRawName() const override;
+ Expected<StringRef> getName(uint64_t Size) const override;
+ StringRef getRawAccessMode() const override;
+ StringRef getRawLastModified() const override;
+ StringRef getRawUID() const override;
+ StringRef getRawGID() const override;
+ Expected<uint64_t> getSize() const override;
+ Expected<bool> isThin() const override { return false; }
+};
+
class LLVM_ABI Archive : public Binary {
virtual void anchor();
@@ -343,7 +372,16 @@ class LLVM_ABI Archive : public Binary {
/// Size field is 10 decimal digits long
static const uint64_t MaxMemberSize = 9999999999;
- enum Kind { K_GNU, K_GNU64, K_BSD, K_DARWIN, K_DARWIN64, K_COFF, K_AIXBIG };
+ enum Kind {
+ K_GNU,
+ K_GNU64,
+ K_BSD,
+ K_DARWIN,
+ K_DARWIN64,
+ K_COFF,
+ K_AIXBIG,
+ K_ZOS
+ };
Kind kind() const { return (Kind)Format; }
bool isThin() const { return IsThin; }
@@ -434,6 +472,19 @@ class BigArchive : public Archive {
bool has64BitGlobalSymtab() { return Has64BitGlobalSymtab; }
};
+class ZOSArchive : public Archive {
+public:
+ // Fixed-Length header.
+ struct FixLenHdr {
+ char Magic[sizeof(ZOSArchiveMagic) - 1]; ///< ZOS archive magic string.
+ };
+
+public:
+ ZOSArchive(MemoryBufferRef Source, Error &Err);
+
+private:
+ std::string SymbolTableBuf; // __.SYMDEF strings converted to ASCII.
+};
} // end namespace object
} // end namespace llvm
diff --git a/llvm/lib/Object/Archive.cpp b/llvm/lib/Object/Archive.cpp
index 17c926e621f36..c7a3e75f0f05e 100644
--- a/llvm/lib/Object/Archive.cpp
+++ b/llvm/lib/Object/Archive.cpp
@@ -17,6 +17,7 @@
#include "llvm/Object/Binary.h"
#include "llvm/Object/Error.h"
#include "llvm/Support/Chrono.h"
+#include "llvm/Support/ConvertEBCDIC.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/Error.h"
@@ -104,7 +105,11 @@ ArchiveMemberHeader::ArchiveMemberHeader(const Archive *Parent,
*Err = createMemberHeaderParseError(this, RawHeaderPtr, Size);
return;
}
- if (ArMemHdr->Terminator[0] != '`' || ArMemHdr->Terminator[1] != '\n') {
+ if ((ArMemHdr->Terminator[0] != '`' || ArMemHdr->Terminator[1] != '\n') &&
+ (ArMemHdr->Terminator[0] != '\x79' ||
+ ArMemHdr->Terminator[1] !=
+ '\x15') // '\x79\x15' is '`\n' in EBCDIC for z/OS archive terminator.
+ ) {
if (Err) {
std::string Buf;
raw_string_ostream OS(Buf);
@@ -368,6 +373,121 @@ Expected<uint64_t> BigArchiveMemberHeader::getSize() const {
return *SizeOrErr + alignTo(*NameLenOrErr, 2);
}
+template <class T, std::size_t N>
+StringRef getFieldRawStringE2A(const T (&Field)[N], SmallString<64> &Dst) {
+ StringRef Src = StringRef(Field, N);
+ ConverterEBCDIC::convertToUTF8(Src, Dst);
+ return Dst.str().rtrim(" ");
+}
+
+ZOSArchiveMemberHeader::ZOSArchiveMemberHeader(const Archive *Parent,
+ const char *RawHeaderPtr,
+ uint64_t Size, Error *Err)
+ : ArchiveMemberHeader(Parent, RawHeaderPtr, Size, Err) {
+ ErrorAsOutParameter ErrAsOutParam(Err);
+ setMemberHeaderStrings(Err, Size);
+}
+
+Expected<uint64_t> ZOSArchiveMemberHeader::getSize() const {
+ SmallString<64> Dst;
+ return getArchiveMemberDecField(
+ "size", getFieldRawStringE2A(ArMemHdr->Size, Dst), Parent, this);
+}
+
+Expected<StringRef> ZOSArchiveMemberHeader::getRawName() const {
+ return StringRef(RawMemberName);
+}
+
+Expected<StringRef> ZOSArchiveMemberHeader::getName(uint64_t Size) const {
+ return StringRef(MemberName);
+}
+
+StringRef ZOSArchiveMemberHeader::getRawAccessMode() const {
+ return StringRef(AccessMode);
+}
+
+StringRef ZOSArchiveMemberHeader::getRawLastModified() const {
+ return StringRef(LastModified);
+}
+
+StringRef ZOSArchiveMemberHeader::getRawUID() const { return StringRef(UID); }
+
+StringRef ZOSArchiveMemberHeader::getRawGID() const { return StringRef(GID); }
+
+void ZOSArchiveMemberHeader::setMemberHeaderStrings(Error *Err, uint64_t Size) {
+ SmallString<64> Dst;
+ uint64_t Offset =
+ reinterpret_cast<const char *>(ArMemHdr) - Parent->getData().data();
+
+ // Set RawMemberName.
+ StringRef RawNameSR = getFieldRawStringE2A(ArMemHdr->Name, Dst);
+ if (RawNameSR.empty() || RawNameSR[0] == ' ') {
+ *Err = malformedError("name contains a leading space for archive member "
+ "header at offset " +
+ Twine(Offset));
+ return;
+ }
+ RawMemberName.append(RawNameSR);
+
+ // Set MemberName.
+ if (RawNameSR.starts_with("#1/")) {
+ Expected<StringRef> NameOrErr = ArchiveMemberHeader::getName(Size);
+ if (!NameOrErr) {
+ *Err = NameOrErr.takeError();
+ return;
+ }
+ StringRef Name = NameOrErr.get();
+ Dst.clear();
+ ConverterEBCDIC::convertToUTF8(Name, Dst);
+ MemberName.append(Dst.str());
+ } else
+ MemberName = RawMemberName;
+
+ // LastModified
+ Dst.clear();
+ StringRef LastModifiedSR = getFieldRawStringE2A(ArMemHdr->LastModified, Dst);
+ if (LastModifiedSR.empty()) {
+ *Err = malformedError("problem converting LastModified field in "
+ "header at offset " +
+ Twine(Offset));
+ return;
+ }
+ LastModified.append(LastModifiedSR);
+
+ // UID
+ Dst.clear();
+ StringRef UIDSR = getFieldRawStringE2A(ArMemHdr->UID, Dst);
+ if (UIDSR.empty()) {
+ *Err = malformedError("problem converting UID field in "
+ "header at offset " +
+ Twine(Offset));
+ return;
+ }
+ UID.append(UIDSR);
+
+ // GID
+ Dst.clear();
+ StringRef GIDSR = getFieldRawStringE2A(ArMemHdr->GID, Dst);
+ if (GIDSR.empty()) {
+ *Err = malformedError("problem converting GID field in "
+ "header at offset " +
+ Twine(Offset));
+ return;
+ }
+ GID.append(GIDSR);
+
+ // AccessMode
+ Dst.clear();
+ StringRef AccessModeSR = getFieldRawStringE2A(ArMemHdr->AccessMode, Dst);
+ if (AccessModeSR.empty()) {
+ *Err = malformedError("problem converting AccessMode field in "
+ "header at offset " +
+ Twine(Offset));
+ return;
+ }
+ AccessMode.append(AccessModeSR);
+}
+
Expected<uint64_t> BigArchiveMemberHeader::getRawNameSize() const {
return getArchiveMemberDecField(
"NameLen", getFieldRawString(ArMemHdr->NameLen), Parent, this);
@@ -668,6 +788,8 @@ Expected<std::unique_ptr<Archive>> Archive::create(MemoryBufferRef Source) {
if (Buffer.starts_with(BigArchiveMagic))
Ret = std::make_unique<BigArchive>(Source, Err);
+ else if (Buffer.starts_with(ZOSArchiveMagic))
+ Ret = std::make_unique<ZOSArchive>(Source, Err);
else
Ret = std::make_unique<Archive>(Source, Err);
@@ -680,6 +802,10 @@ std::unique_ptr<AbstractArchiveMemberHeader>
Archive::createArchiveMemberHeader(const char *RawHeaderPtr, uint64_t Size,
Error *Err) const {
ErrorAsOutParameter ErrAsOutParam(Err);
+
+ if (kind() == K_ZOS)
+ return std::make_unique<ZOSArchiveMemberHeader>(this, RawHeaderPtr, Size,
+ Err);
if (kind() != K_AIXBIG)
return std::make_unique<ArchiveMemberHeader>(this, RawHeaderPtr, Size, Err);
return std::make_unique<BigArchiveMemberHeader>(this, RawHeaderPtr, Size,
@@ -695,7 +821,6 @@ uint64_t Archive::getArchiveMagicLen() const {
return sizeof(ArchiveMagic) - 1;
}
-
void Archive::setFirstRegular(const Child &C) {
FirstRegularData = C.Data;
FirstRegularStartOfFile = C.StartOfFile;
@@ -714,6 +839,10 @@ Archive::Archive(MemoryBufferRef Source, Error &Err)
Format = K_AIXBIG;
IsThin = false;
return;
+ } else if (Buffer.starts_with(ZOSArchiveMagic)) {
+ Format = K_ZOS;
+ IsThin = false;
+ return;
} else {
Err = make_error<GenericBinaryError>("file too small to be an archive",
object_error::invalid_file_type);
@@ -971,6 +1100,8 @@ object::Archive::Kind Archive::getDefaultKindForTriple(const Triple &T) {
return object::Archive::K_AIXBIG;
if (T.isOSWindows())
return object::Archive::K_COFF;
+ if (T.isOSzOS())
+ return object::Archive::K_ZOS;
return object::Archive::K_GNU;
}
@@ -1042,6 +1173,8 @@ Expected<Archive::Child> Archive::Symbol::getMember() const {
// the archive of the member that defines the symbol. Which is what
// is needed here.
Offset = read64le(Offsets + SymbolIndex * 16 + 8);
+ } else if (Parent->kind() == K_ZOS) {
+ Offset = read32be(Offsets + SymbolIndex * 8);
} else {
// Skip offsets.
uint32_t MemberCount = read32le(Buf);
@@ -1171,6 +1304,9 @@ Archive::symbol_iterator Archive::symbol_begin() const {
buf += ran_strx;
} else if (kind() == K_AIXBIG) {
buf = getStringTable().begin();
+ } else if (kind() == K_ZOS) {
+ uint32_t symbol_count = read32be(buf);
+ buf += sizeof(uint32_t) + (symbol_count * (sizeof(uint64_t)));
} else {
uint32_t member_count = 0;
uint32_t symbol_count = 0;
@@ -1244,6 +1380,9 @@ uint32_t Archive::getNumberOfSymbols() const {
return read32le(buf) / 8;
if (kind() == K_DARWIN64)
return read64le(buf) / 16;
+ if (kind() == K_ZOS) {
+ return read32be(buf);
+ }
uint32_t member_count = 0;
member_count = read32le(buf);
buf += 4 + (member_count * 4); // Skip offsets.
@@ -1448,3 +1587,71 @@ BigArchive::BigArchive(MemoryBufferRef Source, Error &Err)
setFirstRegular(*I);
Err = Error::success();
}
+
+ZOSArchive::ZOSArchive(MemoryBufferRef Source, Error &Err)
+ : Archive(Source, Err) {
+ ErrorAsOutParameter ErrAsOutParam(&Err);
+
+ // Get the special members.
+ child_iterator I = child_begin(Err, false);
+ if (Err)
+ return;
+ child_iterator E = child_end();
+
+ // See if this is a valid empty archive and if so return.
+ if (I == E) {
+ Err = Error::success();
+ return;
+ }
+ const Child *C = &*I;
+
+ auto Increment = [&]() {
+ ++I;
+ if (Err)
+ return true;
+ C = &*I;
+ return false;
+ };
+
+ Expected<StringRef> NameOrErr = C->getRawName();
+ if (!NameOrErr) {
+ Err = NameOrErr.takeError();
+ return;
+ }
+ StringRef Name = NameOrErr.get();
+
+ if (Name == "__.SYMDEF") {
+ // We know that the symbol table is not an external file, but we still must
+ // check any Expected<> return value.
+ Expected<StringRef> BufOrErr = C->getBuffer();
+ if (!BufOrErr) {
+ Err = BufOrErr.takeError();
+ return;
+ }
+
+ // Copy symbol table converting embedded EBCDIC names to ASCII.
+ StringRef ESymbolTable = BufOrErr.get();
+ uint32_t ESymbolCount = read32be(ESymbolTable.data());
+ uint32_t OffsetToENames =
+ sizeof(uint32_t) + (ESymbolCount * (sizeof(uint64_t)));
+ uint32_t ENamesSize = (uint32_t)ESymbolTable.size() - OffsetToENames;
+ const char *ENamesPtr = (const char *)ESymbolTable.data() + OffsetToENames;
+ StringRef ENames(ENamesPtr, ENamesSize);
+
+ SmallString<64> Dst;
+ ConverterEBCDIC::convertToUTF8(ENames, Dst);
+ SymbolTableBuf.append(ESymbolTable.data(), OffsetToENames);
+ SymbolTableBuf.append(Dst.str());
+ SymbolTable = StringRef(SymbolTableBuf.data(), SymbolTableBuf.size());
+ if (Increment())
+ return;
+ setFirstRegular(*C);
+
+ Err = Error::success();
+ return;
+ }
+
+ setFirstRegular(*C);
+ Err = Error::success();
+ return;
+}
\ No newline at end of file
diff --git a/llvm/lib/Object/ArchiveWriter.cpp b/llvm/lib/Object/ArchiveWriter.cpp
index 6d2bbca179836..4610fb4303274 100644
--- a/llvm/lib/Object/ArchiveWriter.cpp
+++ b/llvm/lib/Object/ArchiveWriter.cpp
@@ -192,6 +192,7 @@ static bool isBSDLike(object::Archive::Kind Kind) {
case object::Archive::K_GNU64:
case object::Archive::K_AIXBIG:
case object::Archive::K_COFF:
+ case object::Archive::K_ZOS:
return false;
case object::Archive::K_BSD:
case object::Archive::K_DARWIN:
@@ -287,6 +288,7 @@ static bool is64BitKind(object::Archive::Kind Kind) {
case object::Archive::K_BSD:
case object::Archive::K_DARWIN:
case object::Archive::K_COFF:
+ case object::Archive::K_ZOS:
return false;
case object::Archive::K_AIXBIG:
case object::Archive::K_DARWIN64:
@@ -517,6 +519,7 @@ getSymbolicFile(MemoryBufferRef Buf, LLVMContext &Context,
case object::Archive::K_COFF:
case object::Archive::K_DARWIN:
case object::Archive::K_DARWIN64:
+ case object::Archive::K_ZOS:
return ObjOrErr.takeError();
}
}
diff --git a/llvm/test/Object/Inputs/zos-archive-test.a b/llvm/test/Object/Inputs/zos-archive-test.a
new file mode 100644
index 0000000000000000000000000000000000000000..0244f176c64478cec3918b1ca6bed78b897eb2c1
GIT binary patch
literal 1424
zcmbtUPfO!K6n}C0?;uOvZJ{h8rFa%ULOkuEMS4&O;=wPFy^9_q3yVkbQ1K`T{Twyz
zQZEwNMe!n`dT>uWjcN2`_q~}U9b at CN_~6a^|C{$_CfR809e>;ZS~ku4;Qm*)_d}P&
zLrFsF>Z<F*AWGOS1AvCSM*O|i0_1ut3di-*p`rx7FPuZ~Dco+m%s8U_AJN{a#AM{n
zoYZ1~EioBD7H8VAF%j_yYX&)q*;jEiOSM9sK>;8`M$R_v%WhBL5f-&cGL at 2>Q6d)2
zr1VFdOe=};nH8%Og=yFywE_j(+}*vp>Ue08Ba18=r(j5!J0Vt`=!&L+Ch&6g=F!of
zC%~w$9CEV1no<YT&#zbjOX8iG=iTd{0+0I2Pr*~+UTr3V49`zaRj_-#!ENXI7SZV*
zXtwA+mR|scMzVQwi(1hhF!~OJZ%&8^OBlHH8PsH^^i-mN$Gw0*3?GMqKk|J3z>#pg
z{1?tk8aBy3QKp5wi7xHn&Y3k_r?CRAwGMWvy$GcyxQ)Z23r)w(RBjC3@!L<J8IF^|
z%vnQs7PAWmx92OX*+0NO5?<RM-+#V1$&JTjhisx}Dd2#l4n<s-Fn$`krHJQ at yo~bX
zdx=~mU%VAH at Gl+lqW7Yv*6^?wM!{(G`*J{k7(A!M$#FG(qO&UM%XGQ&;>GKx+sSOv
Xf{c`};-bXq^`{Wi<G_bldx!EDV(Pt2
literal 0
HcmV?d00001
diff --git a/llvm/test/Object/zos-archive-read.test b/llvm/test/Object/zos-archive-read.test
new file mode 100644
index 0000000000000..7adf2a5256697
--- /dev/null
+++ b/llvm/test/Object/zos-archive-read.test
@@ -0,0 +1,9 @@
+## Test reading a z/OS archive.
+
+# RUN: llvm-ar t %p/Inputs/zos-archive-test.a | FileCheck %s --check-prefix=LIST
+# RUN: llvm-nm --print-armap %p/Inputs/zos-archive-test.a | FileCheck %s --check-prefix=SYMS
+
+# LIST: foo.o
+
+# SYMS: Archive map
+# SYMS-NEXT: foo in foo.o
\ No newline at end of file
>From 12e3e4b6314642020707919ab0218bd31803d6ba Mon Sep 17 00:00:00 2001
From: Uyiosa Iyekekpolor <uyiosaben at gmail.com>
Date: Thu, 9 Apr 2026 10:51:04 -0400
Subject: [PATCH 2/6] Address review comments
---
llvm/include/llvm/Object/Archive.h | 1 -
llvm/lib/Object/Archive.cpp | 6 +++---
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/llvm/include/llvm/Object/Archive.h b/llvm/include/llvm/Object/Archive.h
index 9a72460846502..b45a8e4673402 100644
--- a/llvm/include/llvm/Object/Archive.h
+++ b/llvm/include/llvm/Object/Archive.h
@@ -479,7 +479,6 @@ class ZOSArchive : public Archive {
char Magic[sizeof(ZOSArchiveMagic) - 1]; ///< ZOS archive magic string.
};
-public:
ZOSArchive(MemoryBufferRef Source, Error &Err);
private:
diff --git a/llvm/lib/Object/Archive.cpp b/llvm/lib/Object/Archive.cpp
index c7a3e75f0f05e..1554161eec34b 100644
--- a/llvm/lib/Object/Archive.cpp
+++ b/llvm/lib/Object/Archive.cpp
@@ -373,8 +373,8 @@ Expected<uint64_t> BigArchiveMemberHeader::getSize() const {
return *SizeOrErr + alignTo(*NameLenOrErr, 2);
}
-template <class T, std::size_t N>
-StringRef getFieldRawStringE2A(const T (&Field)[N], SmallString<64> &Dst) {
+template <std::size_t N>
+StringRef getFieldRawStringE2A(const char (&Field)[N], SmallString<64> &Dst) {
StringRef Src = StringRef(Field, N);
ConverterEBCDIC::convertToUTF8(Src, Dst);
return Dst.str().rtrim(" ");
@@ -1654,4 +1654,4 @@ ZOSArchive::ZOSArchive(MemoryBufferRef Source, Error &Err)
setFirstRegular(*C);
Err = Error::success();
return;
-}
\ No newline at end of file
+}
>From 1d9da96ac58bab96f1f247d86b85c202e8b29502 Mon Sep 17 00:00:00 2001
From: Uyiosa Iyekekpolor <uyiosaben at gmail.com>
Date: Thu, 9 Apr 2026 13:36:25 -0400
Subject: [PATCH 3/6] Address additional review comments
---
llvm/include/llvm/Object/Archive.h | 10 ++++++++++
llvm/lib/Object/Archive.cpp | 17 ++++++++++-------
2 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/llvm/include/llvm/Object/Archive.h b/llvm/include/llvm/Object/Archive.h
index b45a8e4673402..59eca4babd60d 100644
--- a/llvm/include/llvm/Object/Archive.h
+++ b/llvm/include/llvm/Object/Archive.h
@@ -159,6 +159,16 @@ class LLVM_ABI BigArchiveMemberHeader
};
// Define file member header of z/OS archive.
+// The fixed part of the member header (in EBCDIC) is:
+// struct ar_hdr {
+// char ar_name[16]; /* blank terminated member name */
+// char ar_date[12]; /* date (decimal) */
+// char ar_uid[6]; /* user id (decimal) */
+// char ar_gid[6]; /* group id (decimal) */
+// char ar_mode[8]; /* access mode (octal) */
+// char ar_size[10]; /* length in bytes (decimal) */
+// char ar_fmag[2]; /* contains backtick (X'79'), followed by new line (X'15') */
+// };
class ZOSArchiveMemberHeader : public ArchiveMemberHeader {
public:
ZOSArchiveMemberHeader(Archive const *Parent, const char *RawHeaderPtr,
diff --git a/llvm/lib/Object/Archive.cpp b/llvm/lib/Object/Archive.cpp
index 1554161eec34b..98690a554356f 100644
--- a/llvm/lib/Object/Archive.cpp
+++ b/llvm/lib/Object/Archive.cpp
@@ -375,6 +375,7 @@ Expected<uint64_t> BigArchiveMemberHeader::getSize() const {
template <std::size_t N>
StringRef getFieldRawStringE2A(const char (&Field)[N], SmallString<64> &Dst) {
+ Dst.clear();
StringRef Src = StringRef(Field, N);
ConverterEBCDIC::convertToUTF8(Src, Dst);
return Dst.str().rtrim(" ");
@@ -444,7 +445,6 @@ void ZOSArchiveMemberHeader::setMemberHeaderStrings(Error *Err, uint64_t Size) {
MemberName = RawMemberName;
// LastModified
- Dst.clear();
StringRef LastModifiedSR = getFieldRawStringE2A(ArMemHdr->LastModified, Dst);
if (LastModifiedSR.empty()) {
*Err = malformedError("problem converting LastModified field in "
@@ -455,7 +455,6 @@ void ZOSArchiveMemberHeader::setMemberHeaderStrings(Error *Err, uint64_t Size) {
LastModified.append(LastModifiedSR);
// UID
- Dst.clear();
StringRef UIDSR = getFieldRawStringE2A(ArMemHdr->UID, Dst);
if (UIDSR.empty()) {
*Err = malformedError("problem converting UID field in "
@@ -466,7 +465,6 @@ void ZOSArchiveMemberHeader::setMemberHeaderStrings(Error *Err, uint64_t Size) {
UID.append(UIDSR);
// GID
- Dst.clear();
StringRef GIDSR = getFieldRawStringE2A(ArMemHdr->GID, Dst);
if (GIDSR.empty()) {
*Err = malformedError("problem converting GID field in "
@@ -477,7 +475,6 @@ void ZOSArchiveMemberHeader::setMemberHeaderStrings(Error *Err, uint64_t Size) {
GID.append(GIDSR);
// AccessMode
- Dst.clear();
StringRef AccessModeSR = getFieldRawStringE2A(ArMemHdr->AccessMode, Dst);
if (AccessModeSR.empty()) {
*Err = malformedError("problem converting AccessMode field in "
@@ -1174,6 +1171,12 @@ Expected<Archive::Child> Archive::Symbol::getMember() const {
// is needed here.
Offset = read64le(Offsets + SymbolIndex * 16 + 8);
} else if (Parent->kind() == K_ZOS) {
+ // The contents of the symbol table member in order are:
+ // 1. The number of symbols, NS (4 byte integer).
+ // 2. NS pairs of integers (the first in each pair of integers is the offset
+ // to the header of the entry... the second being coded attributes).
+ // Length is NS*(4+4) bytes.
+ // 3. NS null terminated strings of corresponding symbol names.
Offset = read32be(Offsets + SymbolIndex * 8);
} else {
// Skip offsets.
@@ -1608,9 +1611,9 @@ ZOSArchive::ZOSArchive(MemoryBufferRef Source, Error &Err)
auto Increment = [&]() {
++I;
if (Err)
- return true;
+ return false;
C = &*I;
- return false;
+ return true;
};
Expected<StringRef> NameOrErr = C->getRawName();
@@ -1643,7 +1646,7 @@ ZOSArchive::ZOSArchive(MemoryBufferRef Source, Error &Err)
SymbolTableBuf.append(ESymbolTable.data(), OffsetToENames);
SymbolTableBuf.append(Dst.str());
SymbolTable = StringRef(SymbolTableBuf.data(), SymbolTableBuf.size());
- if (Increment())
+ if (!Increment())
return;
setFirstRegular(*C);
>From 1b28b03be296581ef447824bf68c68acb920e015 Mon Sep 17 00:00:00 2001
From: Uyiosa Iyekekpolor <uyiosaben at gmail.com>
Date: Thu, 9 Apr 2026 13:51:06 -0400
Subject: [PATCH 4/6] Run clang-format
---
llvm/include/llvm/Object/Archive.h | 3 ++-
llvm/lib/Object/Archive.cpp | 4 ++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/llvm/include/llvm/Object/Archive.h b/llvm/include/llvm/Object/Archive.h
index 59eca4babd60d..cff8367e891ab 100644
--- a/llvm/include/llvm/Object/Archive.h
+++ b/llvm/include/llvm/Object/Archive.h
@@ -167,7 +167,8 @@ class LLVM_ABI BigArchiveMemberHeader
// char ar_gid[6]; /* group id (decimal) */
// char ar_mode[8]; /* access mode (octal) */
// char ar_size[10]; /* length in bytes (decimal) */
-// char ar_fmag[2]; /* contains backtick (X'79'), followed by new line (X'15') */
+// char ar_fmag[2]; /* contains backtick (X'79'), followed by new line
+// (X'15') */
// };
class ZOSArchiveMemberHeader : public ArchiveMemberHeader {
public:
diff --git a/llvm/lib/Object/Archive.cpp b/llvm/lib/Object/Archive.cpp
index 98690a554356f..375a57126bfad 100644
--- a/llvm/lib/Object/Archive.cpp
+++ b/llvm/lib/Object/Archive.cpp
@@ -1173,8 +1173,8 @@ Expected<Archive::Child> Archive::Symbol::getMember() const {
} else if (Parent->kind() == K_ZOS) {
// The contents of the symbol table member in order are:
// 1. The number of symbols, NS (4 byte integer).
- // 2. NS pairs of integers (the first in each pair of integers is the offset
- // to the header of the entry... the second being coded attributes).
+ // 2. NS pairs of integers (the first in each pair of integers is the offset
+ // to the header of the entry... the second being coded attributes).
// Length is NS*(4+4) bytes.
// 3. NS null terminated strings of corresponding symbol names.
Offset = read32be(Offsets + SymbolIndex * 8);
>From 8cef225f1d00739788ada50e8b506ea7f993057c Mon Sep 17 00:00:00 2001
From: Uyiosa Iyekekpolor <uyiosaben at gmail.com>
Date: Thu, 9 Apr 2026 15:05:54 -0400
Subject: [PATCH 5/6] Move Increment lambda closer to its first use
---
llvm/lib/Object/Archive.cpp | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Object/Archive.cpp b/llvm/lib/Object/Archive.cpp
index 375a57126bfad..90479b0330cbf 100644
--- a/llvm/lib/Object/Archive.cpp
+++ b/llvm/lib/Object/Archive.cpp
@@ -1608,14 +1608,6 @@ ZOSArchive::ZOSArchive(MemoryBufferRef Source, Error &Err)
}
const Child *C = &*I;
- auto Increment = [&]() {
- ++I;
- if (Err)
- return false;
- C = &*I;
- return true;
- };
-
Expected<StringRef> NameOrErr = C->getRawName();
if (!NameOrErr) {
Err = NameOrErr.takeError();
@@ -1646,6 +1638,15 @@ ZOSArchive::ZOSArchive(MemoryBufferRef Source, Error &Err)
SymbolTableBuf.append(ESymbolTable.data(), OffsetToENames);
SymbolTableBuf.append(Dst.str());
SymbolTable = StringRef(SymbolTableBuf.data(), SymbolTableBuf.size());
+
+ auto Increment = [&]() {
+ ++I;
+ if (Err)
+ return false;
+ C = &*I;
+ return true;
+ };
+
if (!Increment())
return;
setFirstRegular(*C);
>From 6fd2aae260b150d60f1d4f1d1c0e82fe1cf75fa1 Mon Sep 17 00:00:00 2001
From: Uyiosa Iyekekpolor <uyiosaben at gmail.com>
Date: Mon, 13 Apr 2026 09:53:02 -0400
Subject: [PATCH 6/6] Clarify z/OS symbol table offset math and layout comments
---
llvm/lib/Object/Archive.cpp | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Object/Archive.cpp b/llvm/lib/Object/Archive.cpp
index 90479b0330cbf..694a054aedae5 100644
--- a/llvm/lib/Object/Archive.cpp
+++ b/llvm/lib/Object/Archive.cpp
@@ -1171,12 +1171,10 @@ Expected<Archive::Child> Archive::Symbol::getMember() const {
// is needed here.
Offset = read64le(Offsets + SymbolIndex * 16 + 8);
} else if (Parent->kind() == K_ZOS) {
- // The contents of the symbol table member in order are:
- // 1. The number of symbols, NS (4 byte integer).
- // 2. NS pairs of integers (the first in each pair of integers is the offset
- // to the header of the entry... the second being coded attributes).
- // Length is NS*(4+4) bytes.
- // 3. NS null terminated strings of corresponding symbol names.
+ // Each entry in the offset array is 8 bytes long:
+ // A 4-byte offset followed by 4 bytes of coded attributes.
+ // We multiply the SymbolIndex by 8 to reach the correct entry,
+ // and read the first 4 bytes (the offset).
Offset = read32be(Offsets + SymbolIndex * 8);
} else {
// Skip offsets.
@@ -1308,6 +1306,12 @@ Archive::symbol_iterator Archive::symbol_begin() const {
} else if (kind() == K_AIXBIG) {
buf = getStringTable().begin();
} else if (kind() == K_ZOS) {
+ // The contents of the z/OS symbol table member are:
+ // 1. The number of symbols, NS (4-byte integer).
+ // 2. NS pairs of 4-byte integers (offset and attributes). Length is NS*8
+ // bytes.
+ // 3. NS null terminated strings of corresponding symbol names.
+ // Here we skip parts 1 and 2 to reach the start of the string table.
uint32_t symbol_count = read32be(buf);
buf += sizeof(uint32_t) + (symbol_count * (sizeof(uint64_t)));
} else {
More information about the llvm-commits
mailing list