llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-llvm-binary-utilities Author: Amy Kwan (amy-kwan) <details> <summary>Changes</summary> Backport 847fb5cfde68fdea490171bc24087ebb0cd90738 This backport has been modified slightly by updating the LIT tests to remove the dependency on llvm-nm. llvm-nm added GOFF support after the LLVM 23.x release cut, and its new functionality is not available in LLVM 23. --- Full diff: https://github.com/llvm/llvm-project/pull/226705.diff 5 Files Affected: - (modified) llvm/include/llvm/Object/GOFFObjectFile.h (+6) - (modified) llvm/lib/Object/ArchiveWriter.cpp (+38-14) - (modified) llvm/lib/Object/GOFFObjectFile.cpp (+38) - (added) llvm/test/tools/llvm-ar/zos-symattrs.test (+81) - (modified) llvm/test/tools/llvm-ar/zos-write.test (+9-1) ``````````diff diff --git a/llvm/include/llvm/Object/GOFFObjectFile.h b/llvm/include/llvm/Object/GOFFObjectFile.h index 80da64ed3730d..4ffffc828e103 100644 --- a/llvm/include/llvm/Object/GOFFObjectFile.h +++ b/llvm/include/llvm/Object/GOFFObjectFile.h @@ -47,6 +47,12 @@ class LLVM_ABI GOFFObjectFile : public ObjectFile { public: Expected<StringRef> getSymbolName(SymbolRef Symbol) const; + // Returns the z/OS archive symbol attribute bits for a symbol: + // bit 2 (0x4): symbol is 64-bit + // bit 1 (0x2): symbol uses the XPLink calling convention + // bit 0 (0x1): symbol resides in a writable static area (WSA) + uint32_t getZOSSymbolArchiveAttributes(DataRefImpl Symb) const; + GOFFObjectFile(MemoryBufferRef Object, Error &Err); static inline bool classof(const Binary *V) { return V->isGOFF(); } section_iterator section_begin() const override; diff --git a/llvm/lib/Object/ArchiveWriter.cpp b/llvm/lib/Object/ArchiveWriter.cpp index 9af8e0efe1872..6ef79bb5ba44a 100644 --- a/llvm/lib/Object/ArchiveWriter.cpp +++ b/llvm/lib/Object/ArchiveWriter.cpp @@ -368,6 +368,9 @@ printMemberHeader(raw_ostream &Out, uint64_t Pos, raw_ostream &StringTable, namespace { struct MemberData { std::vector<unsigned> Symbols; + // z/OS archive attribute bits per symbol. Entry i of SymbolAttrs corresponds + // to Symbols[i]. These attributes are empty for non-z/OS archives. + std::vector<uint32_t> SymbolAttrs; std::string Header; StringRef Data; StringRef Padding; @@ -386,7 +389,7 @@ static MemberData computeStringTable(StringRef Names) { printWithSpacePadding(Out, "//", 48); printWithSpacePadding(Out, Size + Pad, 10); Out << "`\n"; - return {{}, std::move(Header), Names, Pad ? "\n" : ""}; + return {{}, {}, std::move(Header), Names, Pad ? "\n" : ""}; } static sys::TimePoint<std::chrono::seconds> now(bool Deterministic) { @@ -677,13 +680,14 @@ static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind, } } - for (unsigned StringOffset : M.Symbols) { + assert((!isZOSArchive(Kind) || M.Symbols.size() == M.SymbolAttrs.size()) && + "Incorrect number of symbol attributes!"); + for (size_t I = 0, E = M.Symbols.size(); I != E; ++I) { if (isBSDLike(Kind)) - printNBits(Out, Kind, StringOffset); + printNBits(Out, Kind, M.Symbols[I]); printNBits(Out, Kind, Pos); // member offset - // FIXME: Properly handle symbol attributes for z/OS archives. if (isZOSArchive(Kind)) - printNBits(Out, Kind, 0); // symbol flags + printNBits(Out, Kind, M.SymbolAttrs[I]); // symbol attributes } Pos += M.Header.size() + M.Data.size() + M.Padding.size(); } @@ -1078,17 +1082,37 @@ computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames, if (!SymbolsOrErr) return createFileError(MemberName, SymbolsOrErr.takeError()); D.Symbols = std::move(*SymbolsOrErr); + // For z/OS, populate SymbolAttrs in lockstep with Symbols so that + // writeSymbolTable() can emit the per-symbol attribute word. + if (isZOSArchive(Kind)) { + auto *GOFFObj = dyn_cast_or_null<GOFFObjectFile>(D.SymFile.get()); + if (GOFFObj) { + for (object::BasicSymbolRef S : GOFFObj->symbols()) { + if (!isArchiveSymbol(S)) + continue; + D.SymbolAttrs.push_back( + GOFFObj->getZOSSymbolArchiveAttributes(S.getRawDataRefImpl())); + } + } else { + // For non-GOFF symbolic files (e.g. bitcode/IR), there is no z/OS + // archive attribute data available. Pad SymbolAttrs to stay in sync + // with Symbols. + D.SymbolAttrs.resize(D.Symbols.size()); + } + } if (D.SymFile) HasObject = true; - } - // On z/OS, when there are no symbols, add a dummy blank symbol - // into the symbol table. This is done since the z/OS binder: - // - emits an error if there is no symbol table in the archive - // - emits an error if the symbol table has 0 symbols - // - should not find any references to a blank symbol - if ((LastZosObjIndex == Index) && (SymNames.tell() == 0)) { - D.Symbols.push_back(0); - SymNames << ' ' << '\0'; + // On z/OS, when there are no symbols, add a dummy blank symbol + // into the symbol table. This is done since the z/OS binder: + // - emits an error if there is no symbol table in the archive + // - emits an error if the symbol table has 0 symbols + // - should not find any references to a blank symbol + if (isZOSArchive(Kind) && (LastZosObjIndex == Index) && + (SymNames.tell() == 0)) { + D.Symbols.push_back(0); + D.SymbolAttrs.push_back(0); + SymNames << ' ' << '\0'; + } } Pos += D.Header.size() + D.Data.size() + D.Padding.size(); diff --git a/llvm/lib/Object/GOFFObjectFile.cpp b/llvm/lib/Object/GOFFObjectFile.cpp index d00ad71105fd5..2a12a80e3b108 100644 --- a/llvm/lib/Object/GOFFObjectFile.cpp +++ b/llvm/lib/Object/GOFFObjectFile.cpp @@ -381,6 +381,44 @@ GOFFObjectFile::getSymbolSection(DataRefImpl Symb) const { std::to_string(SymEdId)); } +uint32_t GOFFObjectFile::getZOSSymbolArchiveAttributes(DataRefImpl Symb) const { + const uint8_t *SymRecord = getSymbolEsdRecord(Symb); + uint32_t Attrs = 0; + + // Bit 2 (0x4): 64-bit AMODE. If the child AMODE is unspecified, + // query the parent ED. + // TODO: The parent-walk path (child ESD_AMODE_None with a parent that has + // ESD_AMODE_64) cannot currently be tested as GOFFObjectWriter always emits + // ESD_AMODE_64 directly on LD/ER records and does not set AMODE on ED + // records. Full coverage requires yaml2obj GOFF ESD record support. + GOFF::ESDAmode Amode; + ESDRecord::getAmode(SymRecord, Amode); + if (Amode == GOFF::ESD_AMODE_None) { + uint32_t ParentEsdId; + ESDRecord::getParentEsdId(SymRecord, ParentEsdId); + if (ParentEsdId) { + const uint8_t *EdRecord = EsdPtrs[ParentEsdId]; + ESDRecord::getAmode(EdRecord, Amode); + } + } + if (Amode == GOFF::ESD_AMODE_64) + Attrs |= 0x4; + + // Bit 1 (0x2): XPLink — LinkageType is ESD_LT_XPLink. + GOFF::ESDLinkageType LinkageType; + ESDRecord::getLinkageType(SymRecord, LinkageType); + if (LinkageType == GOFF::ESD_LT_XPLink) + Attrs |= 0x2; + + // Bit 0 (0x1): Writable Static Area. + GOFF::ESDNameSpaceId NameSpace; + ESDRecord::getNameSpaceId(SymRecord, NameSpace); + if (NameSpace == GOFF::ESD_NS_Parts) + Attrs |= 0x1; + + return Attrs; +} + uint64_t GOFFObjectFile::getSymbolSize(DataRefImpl Symb) const { const uint8_t *Record = getSymbolEsdRecord(Symb); uint32_t Length; diff --git a/llvm/test/tools/llvm-ar/zos-symattrs.test b/llvm/test/tools/llvm-ar/zos-symattrs.test new file mode 100644 index 0000000000000..d776922586d16 --- /dev/null +++ b/llvm/test/tools/llvm-ar/zos-symattrs.test @@ -0,0 +1,81 @@ +## Test that llvm-ar correctly writes z/OS archive symbol attribute bits. +## +## The three attribute bits come from GOFF ESD record fields: +## bit 2 (0x4): 64-bit - AMODE == ESD_AMODE_64 +## bit 1 (0x2): XPLink - LinkageType == ESD_LT_XPLink +## bit 0 (0x1): WSA - NameSpaceId == ESD_NS_Parts +## +## GOFF objects are produced by compiling embedded .ll files with llc. + +# REQUIRES: systemz-registered-target + +# RUN: rm -rf %t.dir && mkdir -p %t.dir +# RUN: split-file %s %t.dir + +## Compile each .ll file into a GOFF object. +# RUN: llc -mtriple=s390x-ibm-zos -filetype=obj %t.dir/func.ll -o %t.dir/func.o +# RUN: llc -mtriple=s390x-ibm-zos -filetype=obj %t.dir/data.ll -o %t.dir/data.o + +## Build a z/OS archive containing both objects. +# RUN: llvm-ar rcD --format=zos %t.dir/test.a %t.dir/func.o %t.dir/data.o + +## 1. Check the attribute words written into the archive symbol table. +## s_func: 0x6 (64-bit=0x4 + XPLink=0x2) +## s_data: 0x3 (XPLink=0x2 + WSA=0x1) +# RUN: %python -c " \ +# RUN: d = open(r'%t.dir/test.a', 'rb').read(); \ +# RUN: print(hex(int.from_bytes(d[76:80], 'big'))); \ +# RUN: print(hex(int.from_bytes(d[84:88], 'big'))) \ +# RUN: " | FileCheck %s --check-prefix=ATTRS + +# ATTRS: 0x6 +# ATTRS-NEXT: 0x3 + +## 2. Test that basic archive operations still work on a GOFF archive. +## llvm-ar t should list exactly the expected member names and nothing else. +# RUN: llvm-ar t %t.dir/test.a | FileCheck %s --check-prefix=LIST \ +# RUN: --implicit-check-not={{.}} + +# LIST: func.o +# LIST-NEXT: data.o + +## 3. Test mixed archive: one GOFF object and one bitcode object together. +## The bitcode member should have an attribute of 0 (no GOFF ESD data available). +# RUN: llc -mtriple=s390x-ibm-zos -filetype=obj %t.dir/func2.ll -o %t.dir/func2.o +# RUN: llvm-as %t.dir/mixed.ll -o %t.dir/mixed.bc +# RUN: llvm-ar rcD --format=zos %t.dir/mixed.a %t.dir/func2.o %t.dir/mixed.bc +# RUN: %python -c " \ +# RUN: d = open(r'%t.dir/mixed.a', 'rb').read(); \ +# RUN: print(hex(int.from_bytes(d[76:80], 'big'))); \ +# RUN: print(hex(int.from_bytes(d[84:88], 'big'))) \ +# RUN: " | FileCheck %s --check-prefix=MIXED-ATTRS + +# MIXED-ATTRS: 0x6 +# MIXED-ATTRS-NEXT: 0x0 + +#--- func.ll +target triple = "s390x-ibm-zos" + +define void @s_func() { +entry: + ret void +} + +#--- data.ll +target triple = "s390x-ibm-zos" + +@s_data = global i32 0, align 4 + +#--- func2.ll +target triple = "s390x-ibm-zos" + +define void @s_func2() { +entry: + ret void +} + +#--- mixed.ll +define i32 @mixed_sym() { +entry: + ret i32 0 +} diff --git a/llvm/test/tools/llvm-ar/zos-write.test b/llvm/test/tools/llvm-ar/zos-write.test index 0e390183f8d00..410d1ed020d60 100644 --- a/llvm/test/tools/llvm-ar/zos-write.test +++ b/llvm/test/tools/llvm-ar/zos-write.test @@ -1,6 +1,6 @@ ## Test writing a z/OS archive. -# RUN: rm -rf %t.dir %t.a %t.odd.a %t.long.a %t.sym.a %t.blank-sym.a && mkdir -p %t.dir +# RUN: rm -rf %t.dir %t.a %t.odd.a %t.long.a %t.sym.a %t.blank-sym.a %t.gnu-blank.a && mkdir -p %t.dir # RUN: split-file %s %t.dir ## Create a z/OS archive from a plain text file with symbol table writing disabled. @@ -62,6 +62,14 @@ # EMPTY-SYMTAB-NEXT: in blank.bc # EMPTY-SYMTAB-NOT: in blank.bc +## The blank-symbol injection is z/OS specific. A GNU archive with no real +## symbols should not have a blank symbol injected into its symbol table. +# RUN: llvm-ar rcD %t.gnu-blank.a %t.dir/blank.bc +# RUN: llvm-ar t %t.gnu-blank.a | FileCheck %s --check-prefix=GNU-NO-BLANK \ +# RUN: --implicit-check-not={{.}} + +# GNU-NO-BLANK: blank.bc + #--- mytest.ll define i32 @mytest() { entry: `````````` </details> https://github.com/llvm/llvm-project/pull/226705 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
