diff --git a/llvm/include/llvm/Object/Archive.h b/llvm/include/llvm/Object/Archive.h --- a/llvm/include/llvm/Object/Archive.h +++ b/llvm/include/llvm/Object/Archive.h @@ -34,35 +34,59 @@ class Archive; -class ArchiveMemberHeader { +class AbstractArchiveMemberHeader { public: friend class Archive; - - ArchiveMemberHeader(Archive const *Parent, const char *RawHeaderPtr, - uint64_t Size, Error *Err); - // ArchiveMemberHeader() = default; - + virtual std::unique_ptr clone() const = 0; + virtual ~AbstractArchiveMemberHeader(){}; /// Get the name without looking up long names. - Expected getRawName() const; + virtual Expected getRawName() const = 0; /// Get the name looking up long names. - Expected getName(uint64_t Size) const; + virtual Expected getName(uint64_t Size) const = 0; + + virtual Expected getSize() const = 0; + virtual uint64_t getOffset() const = 0; - Expected getSize() const; + virtual StringRef getRawAccessMode() const = 0; + virtual StringRef getRawLastModified() const = 0; + virtual StringRef getRawUID() const = 0; + virtual StringRef getRawGID() const = 0; Expected getAccessMode() const; Expected> getLastModified() const; - StringRef getRawLastModified() const { - return StringRef(ArMemHdr->LastModified, sizeof(ArMemHdr->LastModified)) - .rtrim(' '); - } - Expected getUID() const; Expected getGID() const; - // This returns the size of the private struct ArMemHdrType - uint64_t getSizeOf() const { return sizeof(ArMemHdrType); } + // Returns the size in bytes of the format-defined header of derived class. + virtual uint64_t getSizeOf() const { return 0; } + + Archive const *Parent; + AbstractArchiveMemberHeader(const Archive *Parent) : Parent(Parent){}; +}; + +class ArchiveMemberHeader : public AbstractArchiveMemberHeader { +public: + ArchiveMemberHeader(const Archive *Parent, const char *RawHeaderPtr, + uint64_t Size, Error *Err); + + std::unique_ptr clone() const override { + return std::unique_ptr( + new ArchiveMemberHeader(*this)); + } + + Expected getRawName() const override; + Expected getName(uint64_t Size) const override; + Expected getSize() const override; + + uint64_t getOffset() const override; + StringRef getRawAccessMode() const override; + StringRef getRawLastModified() const override; + StringRef getRawUID() const override; + StringRef getRawGID() const override; + + uint64_t getSizeOf() const override { return sizeof(ArMemHdrType); } private: struct ArMemHdrType { @@ -74,7 +98,49 @@ char Size[10]; ///< Size of data, not including header or padding. char Terminator[2]; }; - Archive const *Parent; + ArMemHdrType const *ArMemHdr; +}; + +class BigArchiveMemberHeader : public AbstractArchiveMemberHeader { +public: + BigArchiveMemberHeader(Archive const *Parent, const char *RawHeaderPtr, + uint64_t Size, Error *Err); + std::unique_ptr clone() const override { + return std::unique_ptr( + new BigArchiveMemberHeader(*this)); + } + + Expected getRawName() const override; + Expected getName(uint64_t Size) const override; + Expected getSize() const override; + + uint64_t getOffset() const override; + StringRef getRawAccessMode() const override; + StringRef getRawLastModified() const override; + StringRef getRawUID() const override; + StringRef getRawGID() const override; + + uint64_t getSizeOf() const override { return sizeof(ArMemHdrType); } + Expected getNextOffset() const; + static bool classof(AbstractArchiveMemberHeader const *Header); + +private: + // File Member Header + struct ArMemHdrType { + char Size[20]; // File member size in decimal + char NextOffset[20]; // Next member offset in decimal + char PrevOffset[20]; // Previous member offset in decimal + char LastModified[12]; + char UID[12]; + char GID[12]; + char AccessMode[12]; + char NameLen[4]; // File member name length in decimal + union { + char Name[2]; // Start of member name + char Terminator[2]; + }; + }; + ArMemHdrType const *ArMemHdr; }; @@ -84,10 +150,10 @@ public: class Child { friend Archive; - friend ArchiveMemberHeader; + friend AbstractArchiveMemberHeader; const Archive *Parent; - ArchiveMemberHeader Header; + std::unique_ptr Header; /// Includes header but not padding byte. StringRef Data; /// Offset from Data to the start of the file. @@ -99,6 +165,44 @@ Child(const Archive *Parent, const char *Start, Error *Err); Child(const Archive *Parent, StringRef Data, uint16_t StartOfFile); + Child(const Child &C) + : Parent(C.Parent), Data(C.Data), StartOfFile(C.StartOfFile) { + if (C.Header) + Header = C.Header->clone(); + } + + Child(Child &&C) { + Parent = std::move(C.Parent); + Header = std::move(C.Header); + Data = C.Data; + StartOfFile = C.StartOfFile; + } + + Child &operator=(Child &&C) noexcept { + if (&C == this) + return *this; + + Parent = std::move(C.Parent); + Header = std::move(C.Header); + Data = C.Data; + StartOfFile = C.StartOfFile; + + return *this; + } + + Child &operator=(const Child &C) { + if (&C == this) + return *this; + + Parent = C.Parent; + if (C.Header) + Header = C.Header->clone(); + Data = C.Data; + StartOfFile = C.StartOfFile; + + return *this; + } + bool operator==(const Child &other) const { assert(!Parent || !other.Parent || Parent == other.Parent); return Data.begin() == other.Data.begin(); @@ -109,19 +213,21 @@ Expected getName() const; Expected getFullName() const; - Expected getRawName() const { return Header.getRawName(); } + Expected getRawName() const { return Header->getRawName(); } Expected> getLastModified() const { - return Header.getLastModified(); + return Header->getLastModified(); } - StringRef getRawLastModified() const { return Header.getRawLastModified(); } + StringRef getRawLastModified() const { + return Header->getRawLastModified(); + } - Expected getUID() const { return Header.getUID(); } - Expected getGID() const { return Header.getGID(); } + Expected getUID() const { return Header->getUID(); } + Expected getGID() const { return Header->getGID(); } Expected getAccessMode() const { - return Header.getAccessMode(); + return Header->getAccessMode(); } /// \return the size of the archive member without the header or padding. @@ -218,7 +324,7 @@ /// 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 }; + enum Kind { K_GNU, K_GNU64, K_BSD, K_DARWIN, K_DARWIN64, K_COFF, K_AIXBIG }; Kind kind() const { return (Kind)Format; } bool isThin() const { return IsThin; } @@ -236,7 +342,6 @@ return make_range(symbol_begin(), symbol_end()); } - // Cast methods. static bool classof(Binary const *v) { return v->isArchive(); } // check if a symbol is in the archive @@ -247,24 +352,51 @@ StringRef getSymbolTable() const { return SymbolTable; } StringRef getStringTable() const { return StringTable; } uint32_t getNumberOfSymbols() const; + virtual uint64_t getFirstChildOffset() const { return ArchiveMagicLen; } std::vector> takeThinBuffers() { return std::move(ThinBuffers); } +protected: + static constexpr int ArchiveMagicLen = 8; + void setFirstRegular(const Child &C); + private: StringRef SymbolTable; StringRef StringTable; StringRef FirstRegularData; uint16_t FirstRegularStartOfFile = -1; - void setFirstRegular(const Child &C); unsigned Format : 3; unsigned IsThin : 1; mutable std::vector> ThinBuffers; }; +class BigArchive : public Archive { + /// Fixed-Length Header. + struct BigArFixLenHdrType { + char Magic[ArchiveMagicLen]; ///< Big archive magic string. + char MemOffset[20]; ///< Offset to member table. + char GlobSymOffset[20]; ///< Offset to global symbol table. + char + GlobSym64Offset[20]; ///< Offset global symbol table for 64-bit objects. + char FirstMemberOffset[20]; ///< Offset to first archive member. + char LastMemberOffset[20]; ///< Offset to last archive member. + char FreeOffset[20]; ///< Offset to first mem on free list. + }; + + const BigArFixLenHdrType *ArFixLenHdr; + uint64_t FirstMemberOffset = 0; + uint64_t LastMemberOffset = 0; + +public: + BigArchive(MemoryBufferRef Source, Error &Err); + uint64_t getFirstChildOffset() const override { return FirstMemberOffset; } + uint64_t getLastChildOffset() const { return LastMemberOffset; } +}; + } // end namespace object } // end namespace llvm diff --git a/llvm/lib/BinaryFormat/Magic.cpp b/llvm/lib/BinaryFormat/Magic.cpp --- a/llvm/lib/BinaryFormat/Magic.cpp +++ b/llvm/lib/BinaryFormat/Magic.cpp @@ -88,6 +88,11 @@ if (startswith(Magic, "!\n") || startswith(Magic, "!\n")) return file_magic::archive; break; + case '<': + if (startswith(Magic, "\n")) + return file_magic::archive; + break; + case '\177': if (startswith(Magic, "\177ELF") && Magic.size() >= 18) { bool Data2MSB = Magic[5] == 2; diff --git a/llvm/lib/Object/Archive.cpp b/llvm/lib/Object/Archive.cpp --- a/llvm/lib/Object/Archive.cpp +++ b/llvm/lib/Object/Archive.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/Object/Binary.h" #include "llvm/Object/Error.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/Chrono.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" @@ -31,6 +32,7 @@ #include #include #include +#include #include #include @@ -40,6 +42,7 @@ const char Magic[] = "!\n"; const char ThinMagic[] = "!\n"; +const char BigMagic[] = "\n"; void Archive::anchor() {} @@ -52,12 +55,13 @@ ArchiveMemberHeader::ArchiveMemberHeader(const Archive *Parent, const char *RawHeaderPtr, uint64_t Size, Error *Err) - : Parent(Parent), - ArMemHdr(reinterpret_cast(RawHeaderPtr)) { + : AbstractArchiveMemberHeader(Parent) { if (RawHeaderPtr == nullptr) return; ErrorAsOutParameter ErrAsOutParam(Err); + ArMemHdr = reinterpret_cast(RawHeaderPtr); + if (Size < sizeof(ArMemHdrType)) { if (Err) { std::string Msg("remaining size of archive too small for next archive " @@ -94,6 +98,33 @@ } } +BigArchiveMemberHeader::BigArchiveMemberHeader(const Archive *Parent, + const char *RawHeaderPtr, + uint64_t Size, Error *Err) + : AbstractArchiveMemberHeader(Parent) { + if (RawHeaderPtr == nullptr) + return; + ErrorAsOutParameter ErrAsOutParam(Err); + + ArMemHdr = reinterpret_cast(RawHeaderPtr); + + if (Size < sizeof(ArMemHdrType)) { + if (Err) { + std::string Msg("remaining size of archive too small for next archive " + "member header "); + Expected NameOrErr = getName(Size); + if (!NameOrErr) { + consumeError(NameOrErr.takeError()); + uint64_t Offset = RawHeaderPtr - Parent->getData().data(); + *Err = malformedError(Msg + "at offset " + Twine(Offset)); + } else + *Err = malformedError(Msg + "for " + NameOrErr.get()); + } + return; + } + +} + // This gets the raw name from the ArMemHdr->Name field and checks that it is // valid for the kind of archive. If it is not valid it returns an Error. Expected ArchiveMemberHeader::getRawName() const { @@ -121,7 +152,16 @@ return StringRef(ArMemHdr->Name, end); } -// This gets the name looking up long names. Size is the size of the archive +bool BigArchiveMemberHeader::classof( + const AbstractArchiveMemberHeader *Header) { + return Header->Parent->kind() == Archive::K_AIXBIG; +} + +Expected BigArchiveMemberHeader::getRawName() const { + StringRef::size_type NameSize = strtol(ArMemHdr->NameLen, NULL, 10); + return StringRef(ArMemHdr->Name, NameSize); +} + // member including the header, so the size of any name following the header // is checked to make sure it does not overflow. Expected ArchiveMemberHeader::getName(uint64_t Size) const { @@ -224,75 +264,155 @@ return Name.drop_back(1); } +Expected BigArchiveMemberHeader::getName(uint64_t Size) const { + // The raw name itself can be invalid. + Expected NameOrErr = getRawName(); + if (!NameOrErr) + return NameOrErr.takeError(); + return NameOrErr.get(); +} + Expected ArchiveMemberHeader::getSize() const { - uint64_t Ret; - if (StringRef(ArMemHdr->Size, sizeof(ArMemHdr->Size)) - .rtrim(" ") - .getAsInteger(10, Ret)) { - std::string Buf; - raw_string_ostream OS(Buf); - OS.write_escaped( - StringRef(ArMemHdr->Size, sizeof(ArMemHdr->Size)).rtrim(" ")); - OS.flush(); + uint64_t Size; + StringRef RawSize = + StringRef(ArMemHdr->Size, sizeof(ArMemHdr->Size)).rtrim(" "); + if (RawSize.getAsInteger(10, Size)) { uint64_t Offset = reinterpret_cast(ArMemHdr) - Parent->getData().data(); return malformedError("characters in size field in archive header are not " "all decimal numbers: '" + - Buf + + RawSize + "' for archive " "member header at offset " + Twine(Offset)); } - return Ret; + return Size; } -Expected ArchiveMemberHeader::getAccessMode() const { - unsigned Ret; - if (StringRef(ArMemHdr->AccessMode, sizeof(ArMemHdr->AccessMode)) - .rtrim(' ') - .getAsInteger(8, Ret)) { - std::string Buf; - raw_string_ostream OS(Buf); - OS.write_escaped( - StringRef(ArMemHdr->AccessMode, sizeof(ArMemHdr->AccessMode)) - .rtrim(" ")); - OS.flush(); +Expected BigArchiveMemberHeader::getSize() const { + uint64_t Size; + uint64_t NameLen; + StringRef RawSize = + StringRef(ArMemHdr->Size, sizeof(ArMemHdr->Size)).rtrim(" "); + if (RawSize.getAsInteger(10, Size)) { uint64_t Offset = reinterpret_cast(ArMemHdr) - Parent->getData().data(); + return malformedError("characters in size field in archive header are not " + "all decimal numbers: '" + + RawSize + + "' for archive " + "member header at offset " + + Twine(Offset)); + } + + StringRef RawNameLen = + StringRef(ArMemHdr->NameLen, sizeof(ArMemHdr->NameLen)).rtrim(" "); + if (RawNameLen.getAsInteger(10, NameLen)) { + uint64_t Offset = + reinterpret_cast(ArMemHdr) - Parent->getData().data(); + return malformedError( + "characters in name length field in archive header are not " + "all decimal numbers: '" + + RawNameLen + + "' for archive " + "member header at offset " + + Twine(Offset)); + } + return Size + (((NameLen + 1) >> 1) << 1); +} + +Expected BigArchiveMemberHeader::getNextOffset() const { + uint64_t NextOffset; + StringRef RawStringRef = + StringRef(ArMemHdr->NextOffset, sizeof(ArMemHdr->NextOffset)).rtrim(" "); + + if (RawStringRef.getAsInteger(10, NextOffset)) { + uint64_t Offset = reinterpret_cast(ArMemHdr->NextOffset) - + Parent->getData().data(); + return malformedError("characters in size field in archive header are not " + "all decimal numbers: '" + + RawStringRef + + "' for archive " + "member header at offset " + + Twine(Offset)); + } + return NextOffset; +} + +uint64_t ArchiveMemberHeader::getOffset() const { + uint64_t Offset = + reinterpret_cast(ArMemHdr) - Parent->getData().data(); + return Offset; +} + +uint64_t BigArchiveMemberHeader::getOffset() const { + uint64_t Offset = + reinterpret_cast(ArMemHdr) - Parent->getData().data(); + return Offset; +} + +// This gets the raw access mode from the ArMemHdr->AccessMode field. +StringRef ArchiveMemberHeader::getRawAccessMode() const { + return StringRef(ArMemHdr->AccessMode, sizeof(ArMemHdr->AccessMode)) + .rtrim(' '); +} + +StringRef BigArchiveMemberHeader::getRawAccessMode() const { + return StringRef(ArMemHdr->AccessMode, sizeof(ArMemHdr->AccessMode)) + .rtrim(' '); +} + +Expected AbstractArchiveMemberHeader::getAccessMode() const { + unsigned Ret; + if (getRawAccessMode().getAsInteger(8, Ret)) { + uint64_t Offset = getOffset(); return malformedError("characters in AccessMode field in archive header " "are not all decimal numbers: '" + - Buf + "' for the archive member header at offset " + + getRawAccessMode() + + "' for the archive member header at offset " + Twine(Offset)); } return static_cast(Ret); } +// This gets ArMemHdr->LastModified field. +StringRef ArchiveMemberHeader::getRawLastModified() const { + return StringRef(ArMemHdr->LastModified, sizeof(ArMemHdr->LastModified)) + .rtrim(' '); +} + +StringRef BigArchiveMemberHeader::getRawLastModified() const { + return StringRef(ArMemHdr->LastModified, sizeof(ArMemHdr->LastModified)) + .rtrim(' '); +} + Expected> -ArchiveMemberHeader::getLastModified() const { +AbstractArchiveMemberHeader::getLastModified() const { unsigned Seconds; - if (StringRef(ArMemHdr->LastModified, sizeof(ArMemHdr->LastModified)) - .rtrim(' ') - .getAsInteger(10, Seconds)) { - std::string Buf; - raw_string_ostream OS(Buf); - OS.write_escaped( - StringRef(ArMemHdr->LastModified, sizeof(ArMemHdr->LastModified)) - .rtrim(" ")); - OS.flush(); - uint64_t Offset = - reinterpret_cast(ArMemHdr) - Parent->getData().data(); + if (getRawLastModified().getAsInteger(10, Seconds)) { + uint64_t Offset = getOffset(); return malformedError("characters in LastModified field in archive header " "are not all decimal numbers: '" + - Buf + "' for the archive member header at offset " + + getRawLastModified() + + "' for the archive member header at offset " + Twine(Offset)); } return sys::toTimePoint(Seconds); } -Expected ArchiveMemberHeader::getUID() const { +// This gets the raw UID from the ArMemHdr->UID field. +StringRef ArchiveMemberHeader::getRawUID() const { + return StringRef(ArMemHdr->UID, sizeof(ArMemHdr->UID)).rtrim(' '); +} + +StringRef BigArchiveMemberHeader::getRawUID() const { + return StringRef(ArMemHdr->UID, sizeof(ArMemHdr->UID)).rtrim(' '); +} + +Expected AbstractArchiveMemberHeader::getUID() const { unsigned Ret; - StringRef User = StringRef(ArMemHdr->UID, sizeof(ArMemHdr->UID)).rtrim(' '); + StringRef User = getRawUID(); if (User.empty()) return 0; if (User.getAsInteger(10, Ret)) { @@ -300,8 +420,7 @@ raw_string_ostream OS(Buf); OS.write_escaped(User); OS.flush(); - uint64_t Offset = - reinterpret_cast(ArMemHdr) - Parent->getData().data(); + uint64_t Offset = getOffset(); return malformedError("characters in UID field in archive header " "are not all decimal numbers: '" + Buf + "' for the archive member header at offset " + @@ -310,21 +429,25 @@ return Ret; } -Expected ArchiveMemberHeader::getGID() const { +// This gets the raw GID from the ArMemHdr->GID field. +StringRef ArchiveMemberHeader::getRawGID() const { + return StringRef(ArMemHdr->GID, sizeof(ArMemHdr->GID)).rtrim(' '); +} + +StringRef BigArchiveMemberHeader::getRawGID() const { + return StringRef(ArMemHdr->GID, sizeof(ArMemHdr->GID)).rtrim(' '); +} + +Expected AbstractArchiveMemberHeader::getGID() const { unsigned Ret; - StringRef Group = StringRef(ArMemHdr->GID, sizeof(ArMemHdr->GID)).rtrim(' '); + StringRef Group = getRawGID(); if (Group.empty()) return 0; if (Group.getAsInteger(10, Ret)) { - std::string Buf; - raw_string_ostream OS(Buf); - OS.write_escaped(Group); - OS.flush(); - uint64_t Offset = - reinterpret_cast(ArMemHdr) - Parent->getData().data(); + uint64_t Offset = getOffset(); return malformedError("characters in GID field in archive header " "are not all decimal numbers: '" + - Buf + "' for the archive member header at offset " + + Group + "' for the archive member header at offset " + Twine(Offset)); } return Ret; @@ -332,18 +455,38 @@ Archive::Child::Child(const Archive *Parent, StringRef Data, uint16_t StartOfFile) - : Parent(Parent), Header(Parent, Data.data(), Data.size(), nullptr), - Data(Data), StartOfFile(StartOfFile) {} + : Parent(Parent), Data(Data), StartOfFile(StartOfFile) { + // Create the right concrete archive member as a function of Kind. + if (Parent->kind() != K_AIXBIG) { + Header = std::make_unique( + ArchiveMemberHeader(Parent, Data.data(), Data.size(), nullptr)); + } else { + Header = std::make_unique( + BigArchiveMemberHeader(Parent, Data.data(), Data.size(), nullptr)); + } +} Archive::Child::Child(const Archive *Parent, const char *Start, Error *Err) - : Parent(Parent), - Header(Parent, Start, - Parent - ? Parent->getData().size() - (Start - Parent->getData().data()) - : 0, - Err) { - if (!Start) + : Parent(Parent) { + if (!Start) { + Header = nullptr; return; + } + + // Create the right concrete archive member as a function of Kind. + if (Parent->kind() != K_AIXBIG) { + Header = std::make_unique(ArchiveMemberHeader( + Parent, Start, + Parent ? Parent->getData().size() - (Start - Parent->getData().data()) + : 0, + Err)); + } else { + Header = std::make_unique(BigArchiveMemberHeader( + Parent, Start, + Parent ? Parent->getData().size() - (Start - Parent->getData().data()) + : 0, + Err)); + } // If we are pointed to real data, Start is not a nullptr, then there must be // a non-null Err pointer available to report malformed data on. Only in @@ -358,7 +501,7 @@ if (*Err) return; - uint64_t Size = Header.getSizeOf(); + uint64_t Size = Header->getSizeOf(); Data = StringRef(Start, Size); Expected isThinOrErr = isThinMember(); if (!isThinOrErr) { @@ -377,7 +520,7 @@ } // Setup StartOfFile and PaddingBytes. - StartOfFile = Header.getSizeOf(); + StartOfFile = Header->getSizeOf(); // Don't include attached name. Expected NameOrErr = getRawName(); if (!NameOrErr) { @@ -385,17 +528,20 @@ return; } StringRef Name = NameOrErr.get(); - if (Name.startswith("#1/")) { + + if (Parent->kind() == Archive::K_AIXBIG) { + // Add name to found the real start of child object, the real start is even + // alignment. + StartOfFile += ((Name.size() + 1) >> 1) << 1; + } else if (Name.startswith("#1/")) { uint64_t NameSize; - if (Name.substr(3).rtrim(' ').getAsInteger(10, NameSize)) { - std::string Buf; - raw_string_ostream OS(Buf); - OS.write_escaped(Name.substr(3).rtrim(' ')); - OS.flush(); + StringRef RawNameSize = Name.substr(3).rtrim(' '); + if (RawNameSize.getAsInteger(10, NameSize)) { uint64_t Offset = Start - Parent->getData().data(); *Err = malformedError("long name length characters after the #1/ are " "not all decimal numbers: '" + - Buf + "' for archive member header at offset " + + RawNameSize + + "' for archive member header at offset " + Twine(Offset)); return; } @@ -405,16 +551,16 @@ Expected Archive::Child::getSize() const { if (Parent->IsThin) - return Header.getSize(); + return Header->getSize(); return Data.size() - StartOfFile; } Expected Archive::Child::getRawSize() const { - return Header.getSize(); + return Header->getSize(); } Expected Archive::Child::isThinMember() const { - Expected NameOrErr = Header.getRawName(); + Expected NameOrErr = Header->getRawName(); if (!NameOrErr) return NameOrErr.takeError(); StringRef Name = NameOrErr.get(); @@ -462,16 +608,35 @@ } Expected Archive::Child::getNext() const { - size_t SpaceToSkip = Data.size(); - // If it's odd, add 1 to make it even. - if (SpaceToSkip & 1) - ++SpaceToSkip; - const char *NextLoc = Data.data() + SpaceToSkip; + const char *NextLoc = nullptr; + + if (Parent->kind() != K_AIXBIG) { + size_t SpaceToSkip = Data.size(); + // If it's odd, add 1 to make it even. + if (SpaceToSkip & 1) + ++SpaceToSkip; + + NextLoc = Data.data() + SpaceToSkip; - // Check to see if this is at the end of the archive. - if (NextLoc == Parent->Data.getBufferEnd()) - return Child(nullptr, nullptr, nullptr); + // Check to see if this is at the end of the archive. + if (NextLoc == Parent->Data.getBufferEnd()) + return Child(nullptr, nullptr, nullptr); + } else { + // If current child offset is equal to the last ar offset, current child is + // the last one. + if (Header->getOffset() == + static_cast(Parent)->getLastChildOffset()) + return Child(nullptr, nullptr, nullptr); + else { + Expected NextOffSetOrErr = + dyn_cast(Header.get())->getNextOffset(); + if (NextOffSetOrErr) + NextLoc = Parent->getData().data() + NextOffSetOrErr.get(); + else + return NextOffSetOrErr.takeError(); + } + } // Check to see if this is past the end of the archive. if (NextLoc > Parent->Data.getBufferEnd()) { @@ -505,7 +670,8 @@ if (!RawSizeOrErr) return RawSizeOrErr.takeError(); uint64_t RawSize = RawSizeOrErr.get(); - Expected NameOrErr = Header.getName(Header.getSizeOf() + RawSize); + Expected NameOrErr = + Header->getName(Header->getSizeOf() + RawSize); if (!NameOrErr) return NameOrErr.takeError(); StringRef Name = NameOrErr.get(); @@ -537,10 +703,19 @@ Expected> Archive::create(MemoryBufferRef Source) { Error Err = Error::success(); - std::unique_ptr Ret(new Archive(Source, Err)); + + Archive *Ret; + + StringRef Buffer = Source.getBuffer(); + + if (Buffer.startswith(BigMagic)) + Ret = new BigArchive(Source, Err); + else + Ret = new Archive(Source, Err); + if (Err) return std::move(Err); - return std::move(Ret); + return std::unique_ptr(Ret); } void Archive::setFirstRegular(const Child &C) { @@ -557,6 +732,10 @@ IsThin = true; } else if (Buffer.startswith(Magic)) { IsThin = false; + } else if (Buffer.startswith(BigMagic)) { + Format = K_AIXBIG; + IsThin = false; + return; } else { Err = make_error("file too small to be an archive", object_error::invalid_file_type); @@ -564,10 +743,10 @@ } // Make sure Format is initialized before any call to - // ArchiveMemberHeader::getName() is made. This could be a valid empty - // archive which is the same in all formats. So claiming it to be gnu to is - // fine if not totally correct before we look for a string table or table of - // contents. + // ArchiveMemberHeader::getName() is made. Read Magic to deal with AIX Big + // Archive. Else, this could be a valid empty archive which is the same in all + // formats. So claiming it to be gnu to is fine if not totally correct before + // we look for a string table or table of contents. Format = K_GNU; // Get the special members. @@ -788,7 +967,7 @@ return child_iterator::itr( Child(this, FirstRegularData, FirstRegularStartOfFile), Err); - const char *Loc = Data.getBufferStart() + strlen(Magic); + const char *Loc = Data.getBufferStart() + getFirstChildOffset(); Child C(this, Loc, &Err); if (Err) return child_end(); @@ -997,6 +1176,46 @@ } // Returns true if archive file contains no member file. -bool Archive::isEmpty() const { return Data.getBufferSize() == 8; } +bool Archive::isEmpty() const { + return Data.getBufferSize() == ArchiveMagicLen; +} bool Archive::hasSymbolTable() const { return !SymbolTable.empty(); } + +BigArchive::BigArchive(MemoryBufferRef Source, Error &Err) + : Archive(Source, Err) { + ErrorAsOutParameter ErrAsOutParam(&Err); + StringRef Buffer = Data.getBuffer(); + ArFixLenHdr = reinterpret_cast(Buffer.data()); + + StringRef RawStringRef = StringRef(ArFixLenHdr->FirstMemberOffset, + sizeof(ArFixLenHdr->FirstMemberOffset)) + .rtrim(' '); + + if (RawStringRef.rtrim(' ').getAsInteger(10, FirstMemberOffset)) { + Err = malformedError("malformed aix big archive: first member offset \"" + + RawStringRef + "\" is not a number"); + } + + RawStringRef = StringRef(ArFixLenHdr->LastMemberOffset, + sizeof(ArFixLenHdr->LastMemberOffset)) + .rtrim(' '); + + if (RawStringRef.getAsInteger(10, LastMemberOffset)) { + Err = malformedError("malformed aix big archive: last member offset \"" + + RawStringRef + "\" is not a number"); + } + + child_iterator I = child_begin(Err, false); + if (Err) + return; + child_iterator E = child_end(); + if (I == E) { + Err = Error::success(); + return; + } + const Child *C = &*I; + setFirstRegular(*C); + Err = Error::success(); + return; +} diff --git a/llvm/lib/Object/ArchiveWriter.cpp b/llvm/lib/Object/ArchiveWriter.cpp --- a/llvm/lib/Object/ArchiveWriter.cpp +++ b/llvm/lib/Object/ArchiveWriter.cpp @@ -137,6 +137,7 @@ case object::Archive::K_DARWIN: case object::Archive::K_DARWIN64: return true; + case object::Archive::K_AIXBIG: case object::Archive::K_COFF: break; } @@ -199,6 +200,7 @@ case object::Archive::K_BSD: case object::Archive::K_DARWIN: case object::Archive::K_COFF: + case object::Archive::K_AIXBIG: return false; case object::Archive::K_DARWIN64: case object::Archive::K_GNU64: diff --git a/llvm/test/Object/Inputs/bigFormat.a b/llvm/test/Object/Inputs/bigFormat.a new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@getMemBufferRef(), Err); - failIfError(std::move(Err), "unable to load '" + ArchiveName + "'"); - if (Archive.isThin()) + Expected> ArchiveOrError = + object::Archive::create(Buf.get()->getMemBufferRef()); + if (!ArchiveOrError) + failIfError(ArchiveOrError.takeError(), + "unable to load '" + ArchiveName + "'"); + + std::unique_ptr Archive = std::move(ArchiveOrError.get()); + + if (Archive->isThin()) CompareFullPath = true; - performOperation(Operation, &Archive, std::move(Buf.get()), NewMembers); + performOperation(Operation, Archive.get(), std::move(Buf.get()), NewMembers); return 0; }