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,58 @@ class Archive; -class ArchiveMemberHeader { +class AbstractArchiveMemberHeader { public: friend class Archive; - - ArchiveMemberHeader(Archive const *Parent, const char *RawHeaderPtr, - uint64_t Size, Error *Err); - // ArchiveMemberHeader() = default; - + // clone() is used to create a new object identical to original. + 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; - Expected getSize() const; + // Raw access and helper getters + virtual uint64_t getOffset() const = 0; + virtual StringRef getRawAccessMode() const = 0; + virtual StringRef getRawLastModified() const = 0; + virtual StringRef getRawUID() const = 0; + virtual StringRef getRawGID() const = 0; + // Non-Raw getters 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 of the private struct ArMemHdrType + virtual uint64_t getSizeOf() const = 0; +}; + +class ArchiveMemberHeader : public AbstractArchiveMemberHeader { +public: + ArchiveMemberHeader(Archive const *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,8 +97,50 @@ char Size[10]; ///< Size of data, not including header or padding. char Terminator[2]; }; + ArMemHdrType const *ArMemHdr; Archive const *Parent; +}; + +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); } + +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; + Archive const *Parent; }; class Archive : public Binary { @@ -84,10 +149,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. @@ -98,6 +163,42 @@ public: 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); @@ -109,19 +210,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,12 +321,13 @@ /// 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; } child_iterator child_begin(Error &Err, bool SkipInternal = true) const; + child_iterator child_begin_bigarchive(Error &Err) const; child_iterator child_end() const; iterator_range children(Error &Err, bool SkipInternal = true) const { @@ -252,6 +356,17 @@ return std::move(ThinBuffers); } + // Fixed-Length Header (without magic) + struct ArFixLenHdrType { + 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 FirstArOffset[20]; /*Offset to first archive member */ + char LastArOffset[20]; /*Offset to last archive member */ + char FreeOffset[20]; /*Offset to first mem on free list */ + }; + ArFixLenHdrType const *ArFixLenHdr; + private: StringRef SymbolTable; StringRef StringTable; 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 @@ -40,6 +40,10 @@ const char Magic[] = "!\n"; const char ThinMagic[] = "!\n"; +const char BigMagic[] = "\n"; + +// All magic are 8 caractere long +#define MAGIC_LEN (uint)8 void Archive::anchor() {} @@ -52,12 +56,13 @@ ArchiveMemberHeader::ArchiveMemberHeader(const Archive *Parent, const char *RawHeaderPtr, uint64_t Size, Error *Err) - : Parent(Parent), - ArMemHdr(reinterpret_cast(RawHeaderPtr)) { + : Parent(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 +99,34 @@ } } +BigArchiveMemberHeader::BigArchiveMemberHeader(const Archive *Parent, + const char *RawHeaderPtr, + uint64_t Size, Error *Err) + : Parent(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; + } + + // Terminator is cosmetic only for AIX Big Archive +} + // 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 { @@ -122,6 +155,15 @@ } // This gets the name looking up long names. Size is the size of the archive +Expected BigArchiveMemberHeader::getRawName() const { + // Name is outside ArMemHdr, and there is no end caracter + // name lenght is in NameLen field + // The two first char of name are already in ArMemHdrType + // but unused terminator '`\n' is after the name. + StringRef::size_type end = strtol(ArMemHdr->NameLen, NULL, 10); + return StringRef(ArMemHdr->Name, end); +} + // 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,11 +266,22 @@ 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(); + StringRef Name = NameOrErr.get(); + + // Trim the blanks at the end of the name. + return Name.rtrim(' '); +} + Expected ArchiveMemberHeader::getSize() const { - uint64_t Ret; + uint64_t Size; if (StringRef(ArMemHdr->Size, sizeof(ArMemHdr->Size)) .rtrim(" ") - .getAsInteger(10, Ret)) { + .getAsInteger(10, Size)) { std::string Buf; raw_string_ostream OS(Buf); OS.write_escaped( @@ -243,22 +296,81 @@ "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)) { +Expected BigArchiveMemberHeader::getSize() const { + uint64_t Size; + uint64_t NameLen; + if (StringRef(ArMemHdr->Size, sizeof(ArMemHdr->Size)) + .rtrim(" ") + .getAsInteger(10, Size)) { std::string Buf; raw_string_ostream OS(Buf); OS.write_escaped( - StringRef(ArMemHdr->AccessMode, sizeof(ArMemHdr->AccessMode)) - .rtrim(" ")); + StringRef(ArMemHdr->Size, sizeof(ArMemHdr->Size)).rtrim(" ")); OS.flush(); uint64_t Offset = reinterpret_cast(ArMemHdr) - Parent->getData().data(); + return malformedError("characters in size field in archive header are not " + "all decimal numbers: '" + + Buf + + "' for archive " + "member header at offset " + + Twine(Offset)); + } + if (StringRef(ArMemHdr->NameLen, sizeof(ArMemHdr->NameLen)) + .rtrim(" ") + .getAsInteger(10, NameLen)) { + std::string Buf; + raw_string_ostream OS(Buf); + OS.write_escaped( + StringRef(ArMemHdr->NameLen, sizeof(ArMemHdr->NameLen)).rtrim(" ")); + OS.flush(); + uint64_t Offset = + reinterpret_cast(ArMemHdr) - Parent->getData().data(); + return malformedError( + "characters in name length field in archive header are not " + "all decimal numbers: '" + + Buf + + "' for archive " + "member header at offset " + + Twine(Offset)); + } + return Size + NameLen + NameLen % 2; +} + +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)) { + std::string Buf; + raw_string_ostream OS(Buf); + OS.write_escaped(getRawAccessMode()); + OS.flush(); + 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 " + @@ -267,20 +379,26 @@ 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)) { + if (getRawLastModified().getAsInteger(10, Seconds)) { std::string Buf; raw_string_ostream OS(Buf); - OS.write_escaped( - StringRef(ArMemHdr->LastModified, sizeof(ArMemHdr->LastModified)) - .rtrim(" ")); + OS.write_escaped(StringRef(getRawLastModified())); OS.flush(); - uint64_t Offset = - reinterpret_cast(ArMemHdr) - Parent->getData().data(); + 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 " + @@ -290,9 +408,18 @@ 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 +427,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,9 +436,18 @@ 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)) { @@ -320,8 +455,7 @@ 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 " + @@ -332,18 +466,51 @@ 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 name field (two first bytes, or terminator) is terminator, we are in member table. + Expected NameOrErr = Header->getRawName(); + if (!NameOrErr) { + *Err = NameOrErr.takeError(); + return; + } + StringRef Name = NameOrErr.get(); + const char Terminator[2] = {0x60, 0x0a}; + if (strncmp(Name.data(), Terminator, 2) == 0) { + Header = nullptr; + return; + } + + } // 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 +525,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 +544,7 @@ } // Setup StartOfFile and PaddingBytes. - StartOfFile = Header.getSizeOf(); + StartOfFile = Header->getSizeOf(); // Don't include attached name. Expected NameOrErr = getRawName(); if (!NameOrErr) { @@ -385,6 +552,12 @@ return; } StringRef Name = NameOrErr.get(); + + if (Parent->kind() == Archive::K_AIXBIG) { + // Add name to found the real start + StartOfFile += Name.size() + Name.size() % 2; + } + if (Name.startswith("#1/")) { uint64_t NameSize; if (Name.substr(3).rtrim(' ').getAsInteger(10, NameSize)) { @@ -405,16 +578,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(); @@ -470,8 +643,9 @@ const char *NextLoc = Data.data() + SpaceToSkip; // Check to see if this is at the end of the archive. - if (NextLoc == Parent->Data.getBufferEnd()) + if (NextLoc == Parent->Data.getBufferEnd()) { return Child(nullptr, nullptr, nullptr); + } // Check to see if this is past the end of the archive. if (NextLoc > Parent->Data.getBufferEnd()) { @@ -505,7 +679,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(); @@ -557,6 +732,8 @@ IsThin = true; } else if (Buffer.startswith(Magic)) { IsThin = false; + } else if (Buffer.startswith(BigMagic)) { + IsThin = false; } else { Err = make_error("file too small to be an archive", object_error::invalid_file_type); @@ -564,11 +741,46 @@ } // 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. - Format = K_GNU; + // 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. + if (Buffer.startswith(BigMagic)) { + Format = K_AIXBIG; + } else + Format = K_GNU; + + // AIX Big Archive format + // Identified purely by magic bytes and uses a unique format. + if (Format == K_AIXBIG) { + ArFixLenHdr = reinterpret_cast(Buffer.data() + MAGIC_LEN); + + // Check if Free List exists. If so, stop: not supported yet. + StringRef RawFreeListOffset = StringRef(ArFixLenHdr->FreeOffset, sizeof(ArFixLenHdr->FreeOffset)).rtrim(' '); + uint64_t FreeListOffset; + if (RawFreeListOffset.getAsInteger(10, FreeListOffset)) { + std::string Buf; + raw_string_ostream OS(Buf); + OS.write_escaped(RawFreeListOffset); + OS.flush(); + report_fatal_error("Malformed AIX Big Archive: Free List Offset \"" + Buf + "\" is not a number."); + } + if (FreeListOffset != 0) + report_fatal_error("Free List found. Not supported yet."); + + child_iterator I = child_begin_bigarchive(Err); + 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; + } // Get the special members. child_iterator I = child_begin(Err, false); @@ -795,6 +1007,17 @@ return child_iterator::itr(C, Err); } +Archive::child_iterator Archive::child_begin_bigarchive(Error &Err) const { + if (isEmpty()) + return child_end(); + const char *Loc = Data.getBufferStart() + strlen(Magic) + sizeof(Archive::ArFixLenHdrType); + Child C(this, Loc, &Err); + if (Err) + return child_end(); + return child_iterator::itr(C, Err); +} + + Archive::child_iterator Archive::child_end() const { return child_iterator::end(Child(nullptr, nullptr, nullptr)); } 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,8 @@ case object::Archive::K_DARWIN: case object::Archive::K_DARWIN64: return true; + case object::Archive::K_AIXBIG: + report_fatal_error("Not handled yet"); case object::Archive::K_COFF: break; } @@ -203,6 +205,8 @@ case object::Archive::K_DARWIN64: case object::Archive::K_GNU64: return true; + case object::Archive::K_AIXBIG: + llvm_unreachable("Not handled yet"); } llvm_unreachable("not supported for writting"); } 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$@