Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 62 additions & 1 deletion llvm/include/llvm/Object/Archive.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,46 @@ class LLVM_ABI BigArchiveMemberHeader
Expected<bool> isThin() const override { return false; }
};

// 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 */
Comment thread
jh7370 marked this conversation as resolved.
Outdated
// 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,
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();

Expand Down Expand Up @@ -343,7 +383,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; }
Expand Down Expand Up @@ -434,6 +483,18 @@ 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.
};

ZOSArchive(MemoryBufferRef Source, Error &Err);

private:
std::string SymbolTableBuf; // __.SYMDEF strings converted to ASCII.
Comment thread
uyoyo0 marked this conversation as resolved.
};
} // end namespace object
} // end namespace llvm

Expand Down
219 changes: 217 additions & 2 deletions llvm/lib/Object/Archive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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') &&
Comment thread
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);
Expand Down Expand Up @@ -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) {
Comment thread
jh7370 marked this conversation as resolved.
Outdated
Dst.clear();
StringRef Src = StringRef(Field, N);
Comment thread
uyoyo0 marked this conversation as resolved.
ConverterEBCDIC::convertToUTF8(Src, Dst);
Comment thread
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 {
Comment thread
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);
Comment thread
jh7370 marked this conversation as resolved.
Outdated
if (RawNameSR.empty() || RawNameSR[0] == ' ') {
Comment thread
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
Comment thread
jh7370 marked this conversation as resolved.
Outdated
MemberName = RawMemberName;

// LastModified
StringRef LastModifiedSR = getFieldRawStringE2A(ArMemHdr->LastModified, Dst);
Comment thread
uyoyo0 marked this conversation as resolved.
Outdated
if (LastModifiedSR.empty()) {
*Err = malformedError("problem converting LastModified field in "
Comment thread
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);
Expand Down Expand Up @@ -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);

Expand All @@ -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,
Expand All @@ -695,7 +818,6 @@ uint64_t Archive::getArchiveMagicLen() const {

return sizeof(ArchiveMagic) - 1;
}

Comment thread
jh7370 marked this conversation as resolved.
void Archive::setFirstRegular(const Child &C) {
FirstRegularData = C.Data;
FirstRegularStartOfFile = C.StartOfFile;
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Comment thread
uyoyo0 marked this conversation as resolved.
Comment thread
uyoyo0 marked this conversation as resolved.
} else {
// Skip offsets.
uint32_t MemberCount = read32le(Buf);
Expand Down Expand Up @@ -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);
Comment thread
jh7370 marked this conversation as resolved.
Outdated
buf += sizeof(uint32_t) + (symbol_count * (sizeof(uint64_t)));
Comment thread
uyoyo0 marked this conversation as resolved.
Outdated
} else {
uint32_t member_count = 0;
uint32_t symbol_count = 0;
Expand Down Expand Up @@ -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) {
Comment thread
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.
Expand Down Expand Up @@ -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)
Comment thread
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") {
Comment thread
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would cantFail be appropriate here? This can be used if the underlying code can never fail (even for malformed inputs).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe getBuffer() can legitimately fail on malformed or truncated archive members, so I've kept the error check.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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.

Comment thread
jh7370 marked this conversation as resolved.
Outdated

// Copy symbol table converting embedded EBCDIC names to ASCII.
StringRef ESymbolTable = BufOrErr.get();
Comment thread
jh7370 marked this conversation as resolved.
Outdated
uint32_t ESymbolCount = read32be(ESymbolTable.data());
Comment thread
jh7370 marked this conversation as resolved.
Outdated
uint32_t OffsetToENames =
Comment thread
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;
Comment thread
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 = [&]() {
Comment thread
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;
}
Loading