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 @@ -127,10 +127,15 @@ Kind == object::Archive::K_DARWIN64; } +static bool isAIX(object::Archive::Kind Kind) { + return Kind == object::Archive::K_XCOFF; +} + static bool isBSDLike(object::Archive::Kind Kind) { switch (Kind) { case object::Archive::K_GNU: case object::Archive::K_GNU64: + case object::Archive::K_XCOFF: return false; case object::Archive::K_BSD: case object::Archive::K_DARWIN: @@ -189,6 +194,40 @@ Out.write(uint8_t(0)); } +static void +printBigMemberHeader(raw_ostream &Out, uint64_t Pos, StringRef Name, + const sys::TimePoint &ModTime, + unsigned UID, unsigned GID, unsigned Perms, uint64_t Size, + unsigned PrevOffset) { + // Big Archive has a different size for element of Member Header + // We cannot use printRestOfMemberHeader. + // Name is written at the end of the archive, + // followed by the cosmetic terminator "`\n". + unsigned NameLen = Name.size(); + + // Position after header is: Offset + ObjectHeaderSize(114) + + // + name length with padding + object size with padding + // If no name, special case: Member table; next member is Global, + // currently unsupported, so print 0. TODO in future. + unsigned PosAfterHeader = 0; + if (NameLen) + PosAfterHeader = Pos + 114 + NameLen + NameLen % 2 + Size + Size % 2; + + printWithSpacePadding(Out, Size, 20); // File member size + printWithSpacePadding(Out, PosAfterHeader, 20); // Next member offset + printWithSpacePadding(Out, PrevOffset, 20); // Previous member header + printWithSpacePadding(Out, sys::toTimeT(ModTime), 12); // File member date + printWithSpacePadding(Out, UID % 1000000000000, 12); // UID + printWithSpacePadding(Out, GID % 1000000000000, 12); // GID + printWithSpacePadding(Out, format("%o", Perms), 12); // Permision + printWithSpacePadding(Out, NameLen, 4); // Name length + if (NameLen) + printWithSpacePadding(Out, Name, NameLen); // Name + if (NameLen % 2) + Out << (char)0; // Padding with null caracter + printWithSpacePadding(Out, "`\n", 2); // Terminator +} + static bool useStringTable(bool Thin, StringRef Name) { return Thin || Name.size() >= 16 || Name.contains('/'); } @@ -317,6 +356,10 @@ const char *Name = is64BitKind(Kind) ? "__.SYMDEF_64" : "__.SYMDEF"; printBSDMemberHeader(Out, Out.tell(), Name, now(Deterministic), 0, 0, 0, Size); + } else if (isAIX(Kind)) { + const char *Name = ""; + printBigMemberHeader(Out, Out.tell(), Name, now(Deterministic), 0, 0, 0, + Size, 0); } else { const char *Name = is64BitKind(Kind) ? "/SYM64" : ""; printGNUSmallMemberHeader(Out, Name, now(Deterministic), 0, 0, 0, Size); @@ -471,6 +514,8 @@ Entry.second = Entry.second > 1 ? 1 : 0; } + // Big Archive needs to know offset and offset of the previous symbol + unsigned PrevOffset = 0; for (const NewArchiveMember &M : NewMembers) { std::string Header; raw_string_ostream Out(Header); @@ -503,8 +548,19 @@ std::move(StringMsg), object::object_error::parse_failed); } - printMemberHeader(Out, Pos, StringTable, MemberNames, Kind, Thin, M, - ModTime, Size); + // Big Archive needs more offset, we cannot use printMemberHeader. + if (isAIX(Kind)) { + if (Pos == 0) { + // For the first object, Fixed Length Header size + // must be added to offset. + Pos += 128; + } + printBigMemberHeader(Out, Pos, M.MemberName, ModTime, M.UID, M.GID, + M.Perms, Size, PrevOffset); + PrevOffset = Pos; + } else + printMemberHeader(Out, Pos, StringTable, MemberNames, Kind, Thin, M, + ModTime, Size); Out.flush(); std::vector Symbols; @@ -591,10 +647,35 @@ if (!StringTableBuf.empty()) Data.insert(Data.begin(), computeStringTable(StringTableBuf)); + // For BigArchive (AIX), compute a table of names and offset, + // used in Member Table. + uint64_t NamesSize = 0; // Total Length of names + NULL (without padding). + std::vector OffsetArray; + std::vector NameArray; + if (isAIX(Kind)) { + // Loop across object to find offset and names. + for (size_t it = 0; it != NewMembers.size(); ++it) { + NamesSize += NewMembers[it].MemberName.size() + 1; + // First member, offset is 128 + if (it == 0) + OffsetArray.push_back(128); + // If current member is not last, fulfill offset of next member + // Curent offset + Header size (114) + Object size + Name length + padding + // for name + if (it < NewMembers.size() - 1) + OffsetArray.push_back(OffsetArray[it] + 114 + Data[it].Data.size() + + NewMembers[it].MemberName.size() + + NewMembers[it].MemberName.size() % 2); + // Get the game + NameArray.push_back(NewMembers[it].MemberName); + } + } + // We would like to detect if we need to switch to a 64-bit symbol table. + uint64_t MaxOffset = 8; // For the file signature. + uint64_t LastOffset = MaxOffset; + if (WriteSymtab) { - uint64_t MaxOffset = 8; // For the file signature. - uint64_t LastOffset = MaxOffset; uint64_t NumSyms = 0; for (const auto &M : Data) { // Record the start of the member's offset @@ -613,7 +694,8 @@ writeSymbolTableHeader(Tmp, Kind, Deterministic, SymtabSize); return TmpBuf.size(); }; - LastOffset += computeSymbolTableHeaderSize() + SymtabSize; + if (!(isAIX(Kind))) + LastOffset += computeSymbolTableHeaderSize() + SymtabSize; // The SYM64 format is used when an archive's member offsets are larger than // 32-bits can hold. The need for this shift in format is detected by @@ -630,6 +712,7 @@ // If LastOffset isn't going to fit in a 32-bit varible we need to switch // to 64-bit. Note that the file can be larger than 4GB as long as the last // member starts before the 4GB offset. + // Big Archive not supported yet. if (LastOffset >= Sym64Threshold) { if (Kind == object::Archive::K_DARWIN) Kind = object::Archive::K_DARWIN64; @@ -640,14 +723,70 @@ if (Thin) Out << "!\n"; + else if (isAIX(Kind)) + Out << "\n"; else Out << "!\n"; - if (WriteSymtab) - writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf); + if (!isAIX(Kind)) { + if (WriteSymtab) + writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf); + for (const MemberData &M : Data) + Out << M.Header << M.Data << M.Padding; + } else { + // Fix Sized Header + unsigned FixedHeaderSize = 120; + unsigned MemberTableOffset = MaxOffset + FixedHeaderSize; + // If there is a global symbol table, offset will be: + // unsigned GlobalSymbolOffset = MemberTableOffset + 114 + NamesSize + + // (NewMembers.size()+1)*20 + 2; It is curently unsupported. Without global + // symbol table, offset is put to 0 + unsigned GlobalSymbolOffset = 0; + unsigned Global64SymbolOffset = 0; + + printWithSpacePadding(Out, MemberTableOffset, 20); // Offset to member table + printWithSpacePadding( + Out, GlobalSymbolOffset, + 20); // Offset to global symbol table - Not supported yet + printWithSpacePadding( + Out, Global64SymbolOffset, + 20); // Offset to 64 bits global symbol table - Not supported yet + printWithSpacePadding(Out, "128", + 20); // Offset to first archive member - always 128 + printWithSpacePadding(Out, LastOffset + FixedHeaderSize, + 20); // Offset to last Archive member + printWithSpacePadding( + Out, "0", + 20); // Offset to first member of free list - Not supported yet + + for (const MemberData &M : Data) { + Out << M.Header << M.Data; + if (M.Data.size() % 2) + Out << (char)0; + } - for (const MemberData &M : Data) - Out << M.Header << M.Data << M.Padding; + // Member Table Header + unsigned MemberTableSize = 20 * (OffsetArray.size() + 1) + + NamesSize; // Size of Global symbol table + printBigMemberHeader(Out, MemberTableOffset, "", sys::toTimePoint(0), 0, 0, + 0, MemberTableSize, LastOffset + FixedHeaderSize); + + // Member table + printWithSpacePadding(Out, OffsetArray.size(), 20); // Number of member + for (const int MemberOffset : OffsetArray) + printWithSpacePadding(Out, MemberOffset, 20); // All offset + for (const auto MemberName : NameArray) { + Out << MemberName; // All names + Out << (char)0; // And a null charcter + } + if (NamesSize % 2) + Out << (char)0; // Padding in names; + // final name has a null character included, so it ends by + // one or two null caracters + + // Global Symbol Table (32 bits and 64 bits) + // Not supported yet + } Out.flush(); return Error::success(); diff --git a/llvm/tools/llvm-ar/llvm-ar.cpp b/llvm/tools/llvm-ar/llvm-ar.cpp --- a/llvm/tools/llvm-ar/llvm-ar.cpp +++ b/llvm/tools/llvm-ar/llvm-ar.cpp @@ -86,6 +86,7 @@ =gnu - gnu =darwin - darwin =bsd - bsd + =aix - aix (big archive) --plugin= - ignored for compatibility -h --help - display this help and exit --rsp-quoting - quoting style for response files @@ -182,7 +183,7 @@ static bool MRI; namespace { -enum Format { Default, GNU, BSD, DARWIN, Unknown }; +enum Format { Default, GNU, BSD, DARWIN, AIX, Unknown }; } static Format FormatType = Default; @@ -960,6 +961,9 @@ fail("only the gnu format has a thin mode"); Kind = object::Archive::K_DARWIN; break; + case AIX: + Kind = object::Archive::K_XCOFF; + break; case Unknown: llvm_unreachable(""); } @@ -1231,6 +1235,7 @@ .Case("gnu", GNU) .Case("darwin", DARWIN) .Case("bsd", BSD) + .Case("aix", AIX) .Default(Unknown); if (FormatType == Unknown) fail(std::string("Invalid format ") + Match);