-
Notifications
You must be signed in to change notification settings - Fork 18k
[z/OS] Add z/OS archive reading support #187110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
12727a9
12e3e4b
1d9da96
1b28b03
8cef225
469bbc5
6fd2aae
bda2dff
ea2dbe2
bd62997
40363d9
d18485a
967636e
c44535d
ef764e2
c224124
92d74f7
37d19d4
d439b06
0ed1d1f
c375f50
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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') && | ||
|
jh7370 marked this conversation as resolved.
Outdated
|
||
| (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,118 @@ Expected<uint64_t> BigArchiveMemberHeader::getSize() const { | |
| return *SizeOrErr + alignTo(*NameLenOrErr, 2); | ||
| } | ||
|
|
||
| template <std::size_t N> | ||
| StringRef getFieldRawStringE2A(const char (&Field)[N], SmallString<64> &Dst) { | ||
|
jh7370 marked this conversation as resolved.
Outdated
|
||
| Dst.clear(); | ||
| StringRef Src = StringRef(Field, N); | ||
|
uyoyo0 marked this conversation as resolved.
|
||
| ConverterEBCDIC::convertToUTF8(Src, Dst); | ||
|
uyoyo0 marked this conversation as resolved.
|
||
| 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 { | ||
|
uyoyo0 marked this conversation as resolved.
Outdated
|
||
| 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); | ||
|
jh7370 marked this conversation as resolved.
Outdated
|
||
| if (RawNameSR.empty() || RawNameSR[0] == ' ') { | ||
|
uyoyo0 marked this conversation as resolved.
Outdated
|
||
| *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 | ||
|
jh7370 marked this conversation as resolved.
Outdated
|
||
| MemberName = RawMemberName; | ||
|
|
||
| // LastModified | ||
| StringRef LastModifiedSR = getFieldRawStringE2A(ArMemHdr->LastModified, Dst); | ||
|
uyoyo0 marked this conversation as resolved.
Outdated
|
||
| if (LastModifiedSR.empty()) { | ||
| *Err = malformedError("problem converting LastModified field in " | ||
|
jh7370 marked this conversation as resolved.
Outdated
|
||
| "header at offset " + | ||
| Twine(Offset)); | ||
| return; | ||
| } | ||
| LastModified.append(LastModifiedSR); | ||
|
|
||
| // UID | ||
| 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 | ||
| 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 | ||
| 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 +785,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 +799,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 +818,6 @@ uint64_t Archive::getArchiveMagicLen() const { | |
|
|
||
| return sizeof(ArchiveMagic) - 1; | ||
| } | ||
|
|
||
|
jh7370 marked this conversation as resolved.
|
||
| void Archive::setFirstRegular(const Child &C) { | ||
| FirstRegularData = C.Data; | ||
| FirstRegularStartOfFile = C.StartOfFile; | ||
|
|
@@ -714,6 +836,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 +1097,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 +1170,12 @@ 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) { | ||
| // 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); | ||
|
uyoyo0 marked this conversation as resolved.
uyoyo0 marked this conversation as resolved.
|
||
| } else { | ||
| // Skip offsets. | ||
| uint32_t MemberCount = read32le(Buf); | ||
|
|
@@ -1171,6 +1305,15 @@ Archive::symbol_iterator Archive::symbol_begin() const { | |
| buf += ran_strx; | ||
| } 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); | ||
|
jh7370 marked this conversation as resolved.
Outdated
|
||
| buf += sizeof(uint32_t) + (symbol_count * (sizeof(uint64_t))); | ||
|
uyoyo0 marked this conversation as resolved.
Outdated
|
||
| } else { | ||
| uint32_t member_count = 0; | ||
| uint32_t symbol_count = 0; | ||
|
|
@@ -1244,6 +1387,9 @@ uint32_t Archive::getNumberOfSymbols() const { | |
| return read32le(buf) / 8; | ||
| if (kind() == K_DARWIN64) | ||
| return read64le(buf) / 16; | ||
| if (kind() == K_ZOS) { | ||
|
uyoyo0 marked this conversation as resolved.
Outdated
|
||
| return read32be(buf); | ||
| } | ||
| uint32_t member_count = 0; | ||
| member_count = read32le(buf); | ||
| buf += 4 + (member_count * 4); // Skip offsets. | ||
|
|
@@ -1448,3 +1594,72 @@ 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) | ||
|
jh7370 marked this conversation as resolved.
|
||
| 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; | ||
|
|
||
| Expected<StringRef> NameOrErr = C->getRawName(); | ||
| if (!NameOrErr) { | ||
| Err = NameOrErr.takeError(); | ||
| return; | ||
| } | ||
| StringRef Name = NameOrErr.get(); | ||
|
|
||
| if (Name == "__.SYMDEF") { | ||
|
jh7370 marked this conversation as resolved.
|
||
| // 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; | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the reason I made that comment is because of the code comment above which implies that we don't really need to worry about this error case for some reason, which I took to mean that it can never be hit. Is that not the case then? If it isn't, the comment probably just could be removed.
jh7370 marked this conversation as resolved.
Outdated
|
||
|
|
||
| // Copy symbol table converting embedded EBCDIC names to ASCII. | ||
| StringRef ESymbolTable = BufOrErr.get(); | ||
|
jh7370 marked this conversation as resolved.
Outdated
|
||
| uint32_t ESymbolCount = read32be(ESymbolTable.data()); | ||
|
jh7370 marked this conversation as resolved.
Outdated
|
||
| uint32_t OffsetToENames = | ||
|
jh7370 marked this conversation as resolved.
Outdated
|
||
| sizeof(uint32_t) + (ESymbolCount * (sizeof(uint64_t))); | ||
| uint32_t ENamesSize = (uint32_t)ESymbolTable.size() - OffsetToENames; | ||
| const char *ENamesPtr = (const char *)ESymbolTable.data() + OffsetToENames; | ||
|
jh7370 marked this conversation as resolved.
Outdated
|
||
| 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()); | ||
|
|
||
| auto Increment = [&]() { | ||
|
jh7370 marked this conversation as resolved.
Outdated
|
||
| ++I; | ||
| if (Err) | ||
| return false; | ||
| C = &*I; | ||
| return true; | ||
| }; | ||
|
|
||
| if (!Increment()) | ||
| return; | ||
| setFirstRegular(*C); | ||
|
|
||
| Err = Error::success(); | ||
| return; | ||
| } | ||
|
|
||
| setFirstRegular(*C); | ||
| Err = Error::success(); | ||
| return; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.