[llvm] 38d3c6c - [AIX] support 64bit global symbol table for big archive

via llvm-commits llvm-commits at lists.llvm.org
Thu May 18 07:55:15 PDT 2023


Author: zhijian
Date: 2023-05-18T10:54:14-04:00
New Revision: 38d3c6cb9b9dc67a8a5460aa241824be3240b81e

URL: https://github.com/llvm/llvm-project/commit/38d3c6cb9b9dc67a8a5460aa241824be3240b81e
DIFF: https://github.com/llvm/llvm-project/commit/38d3c6cb9b9dc67a8a5460aa241824be3240b81e.diff

LOG: [AIX] support 64bit global symbol table for big archive

Summary:

In big archive , there is 32bit global symbol table and 64 bit global symbol table. llvm-ar only support 32bit global symbol table this moment, we need to support the 64 bit global symbol table.

https://www.ibm.com/docs/en/aix/7.2?topic=formats-ar-file-format-big

Global Symbol Tables

Immediately following the member table, the archive file contains two global symbol tables. The first global symbol table locates 32-bit file members that define global symbols; the second global symbol table does the same for 64-bit file members. If the archive has no 32-bit or 64-bit file members, the respective global symbol table is omitted. The strip command can be used to delete one or both global symbol tables from the archive. The fl_gstoff field in the fixed-length header contains the offset to the 32-bit global symbol table, and the fl_gst64off contains the offset to the 64-bit global symbol table.

Reviewers: James Henderson,Stephen Peckham
Differential Revision: https://reviews.llvm.org/D142479

Added: 
    llvm/test/Object/Inputs/bitcode-sym32.ll
    llvm/test/Object/Inputs/bitcode-sym64.ll
    llvm/test/Object/bigarchive-malformed-global-symbol-table
    llvm/test/Object/bigarchive-malformed-header.test
    llvm/test/Object/bigarchive-symboltable.test

Modified: 
    llvm/include/llvm/Object/Archive.h
    llvm/lib/Object/Archive.cpp
    llvm/lib/Object/ArchiveWriter.cpp

Removed: 
    llvm/test/tools/llvm-ar/malformed-global-symbol-table-bigarchive.test


################################################################################
diff  --git a/llvm/include/llvm/Object/Archive.h b/llvm/include/llvm/Object/Archive.h
index 7722f851183f..27f504779c4f 100644
--- a/llvm/include/llvm/Object/Archive.h
+++ b/llvm/include/llvm/Object/Archive.h
@@ -410,6 +410,7 @@ class BigArchive : public Archive {
   const FixLenHdr *ArFixLenHdr;
   uint64_t FirstChildOffset = 0;
   uint64_t LastChildOffset = 0;
+  std::string MergedGlobalSymtabBuf;
 
 public:
   BigArchive(MemoryBufferRef Source, Error &Err);

diff  --git a/llvm/lib/Object/Archive.cpp b/llvm/lib/Object/Archive.cpp
index 081ff79ff550..9920145a2f3c 100644
--- a/llvm/lib/Object/Archive.cpp
+++ b/llvm/lib/Object/Archive.cpp
@@ -18,6 +18,7 @@
 #include "llvm/Object/Error.h"
 #include "llvm/Support/Chrono.h"
 #include "llvm/Support/Endian.h"
+#include "llvm/Support/EndianStream.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/ErrorOr.h"
 #include "llvm/Support/FileSystem.h"
@@ -1276,6 +1277,65 @@ bool Archive::isEmpty() const {
 
 bool Archive::hasSymbolTable() const { return !SymbolTable.empty(); }
 
+static Error getGlobalSymtabLocAndSize(const MemoryBufferRef &Data,
+                                       uint64_t GlobalSymtabOffset,
+                                       const char *&GlobalSymtabLoc,
+                                       uint64_t &Size, const char *BitMessage) {
+  uint64_t BufferSize = Data.getBufferSize();
+  uint64_t GlobalSymtabContentOffset =
+      GlobalSymtabOffset + sizeof(BigArMemHdrType);
+  if (GlobalSymtabContentOffset > BufferSize)
+    return malformedError(
+        Twine(BitMessage) + " global symbol table header at offset 0x" +
+        Twine::utohexstr(GlobalSymtabOffset) + " and size 0x" +
+        Twine::utohexstr(sizeof(BigArMemHdrType)) +
+        " goes past the end of file");
+
+  GlobalSymtabLoc = Data.getBufferStart() + GlobalSymtabOffset;
+  const BigArMemHdrType *GlobalSymHdr =
+      reinterpret_cast<const BigArMemHdrType *>(GlobalSymtabLoc);
+  StringRef RawOffset = getFieldRawString(GlobalSymHdr->Size);
+  if (RawOffset.getAsInteger(10, Size))
+    return malformedError(Twine(BitMessage) + " global symbol table size \"" +
+                          RawOffset + "\" is not a number");
+
+  if (GlobalSymtabContentOffset + Size > BufferSize)
+    return malformedError(
+        Twine(BitMessage) + " global symbol table content at offset 0x" +
+        Twine::utohexstr(GlobalSymtabContentOffset) + " and size 0x" +
+        Twine::utohexstr(Size) + " goes past the end of file");
+
+  return Error::success();
+}
+
+struct GlobalSymtabInfo {
+  uint64_t SymNum;
+  StringRef SymbolTable;
+  StringRef SymbolOffsetTable;
+  StringRef StringTable;
+};
+
+static void
+appendGlobalSymbolTableInfo(SmallVector<GlobalSymtabInfo> &SymtabInfos,
+                            const char *GlobalSymtabLoc, uint64_t Size) {
+  // In a big archive, a global symbol table contains the following information:
+  // - The number of symbols.
+  // - The array of offsets into the archive file. The length is eight
+  //   times the number of symbols.
+  // - The name-string table. The size is:
+  //   Size-(8*(the number of symbols + 1)).
+
+  StringRef SymbolTable =
+      StringRef(GlobalSymtabLoc + sizeof(BigArMemHdrType), Size);
+  uint64_t SymNum = read64be(GlobalSymtabLoc + sizeof(BigArMemHdrType));
+  StringRef SymbolOffsetTable = StringRef(SymbolTable.data() + 8, 8 * SymNum);
+  unsigned SymOffsetsSize = 8 * (SymNum + 1);
+  uint64_t SymbolTableStringSize = Size - SymOffsetsSize;
+  StringRef StringTable =
+      StringRef(SymbolTable.data() + SymOffsetsSize, SymbolTableStringSize);
+  SymtabInfos.push_back({SymNum, SymbolTable, SymbolOffsetTable, StringTable});
+}
+
 BigArchive::BigArchive(MemoryBufferRef Source, Error &Err)
     : Archive(Source, Err) {
   ErrorAsOutParameter ErrAsOutParam(&Err);
@@ -1302,55 +1362,73 @@ BigArchive::BigArchive(MemoryBufferRef Source, Error &Err)
     Err = malformedError("malformed AIX big archive: last member offset \"" +
                          RawOffset + "\" is not a number");
 
-  // Calculate the global symbol table.
-  uint64_t GlobSymOffset = 0;
+  uint64_t GlobSymtab32Offset = 0;
   RawOffset = getFieldRawString(ArFixLenHdr->GlobSymOffset);
-  if (RawOffset.getAsInteger(10, GlobSymOffset))
-    // TODO: add test case.
-    Err = malformedError(
-        "malformed AIX big archive: global symbol table offset \"" + RawOffset +
-        "\" is not a number");
+  if (RawOffset.getAsInteger(10, GlobSymtab32Offset)) {
+    Err = malformedError("global symbol table "
+                         "offset of 32-bit members \"" +
+                         RawOffset + "\" is not a number");
+    return;
+  }
 
-  if (Err)
+  uint64_t GlobSymtab64Offset = 0;
+  RawOffset = getFieldRawString(ArFixLenHdr->GlobSym64Offset);
+  if (RawOffset.getAsInteger(10, GlobSymtab64Offset)) {
+    Err = malformedError("global symbol table "
+                         "offset of 64-bit members\"" +
+                         RawOffset + "\" is not a number");
     return;
+  }
 
-  if (GlobSymOffset > 0) {
-    uint64_t GlobalSymTblContentOffset =
-        GlobSymOffset + sizeof(BigArMemHdrType);
-    if (GlobalSymTblContentOffset > BufferSize) {
-      Err = malformedError("global symbol table header at offset 0x" +
-                           Twine::utohexstr(GlobSymOffset) + " and size 0x" +
-                           Twine::utohexstr(sizeof(BigArMemHdrType)) +
-                           " goes past the end of file");
-      return;
-    }
+  const char *GlobSymtab32Loc = nullptr;
+  const char *GlobSymtab64Loc = nullptr;
+  uint64_t GlobSymtab32Size = 0;
+  uint64_t GlobSymtab64Size = 0;
+  const MemoryBufferRef &MemBuffRef = getMemoryBufferRef();
 
-    const char *GlobSymTblLoc = Data.getBufferStart() + GlobSymOffset;
-    const BigArMemHdrType *GlobalSymHdr =
-        reinterpret_cast<const BigArMemHdrType *>(GlobSymTblLoc);
-    RawOffset = getFieldRawString(GlobalSymHdr->Size);
-    uint64_t Size;
-    if (RawOffset.getAsInteger(10, Size)) {
-      // TODO: add test case.
-      Err = malformedError(
-          "malformed AIX big archive: global symbol table size \"" + RawOffset +
-          "\" is not a number");
+  if (GlobSymtab32Offset) {
+    Err =
+        getGlobalSymtabLocAndSize(MemBuffRef, GlobSymtab32Offset,
+                                  GlobSymtab32Loc, GlobSymtab32Size, "32-bit");
+    if (Err)
       return;
-    }
-    if (GlobalSymTblContentOffset + Size > BufferSize) {
-      Err = malformedError("global symbol table content at offset 0x" +
-                           Twine::utohexstr(GlobalSymTblContentOffset) +
-                           " and size 0x" + Twine::utohexstr(Size) +
-                           " goes past the end of file");
+  }
+
+  if (GlobSymtab64Offset) {
+    Err =
+        getGlobalSymtabLocAndSize(MemBuffRef, GlobSymtab64Offset,
+                                  GlobSymtab64Loc, GlobSymtab64Size, "64-bit");
+    if (Err)
       return;
-    }
-    SymbolTable = StringRef(GlobSymTblLoc + sizeof(BigArMemHdrType), Size);
-    unsigned SymNum = getNumberOfSymbols();
-    unsigned SymOffsetsSize = 8 * (SymNum + 1);
-    uint64_t SymbolTableStringSize = Size - SymOffsetsSize;
-    StringTable =
-        StringRef(GlobSymTblLoc + sizeof(BigArMemHdrType) + SymOffsetsSize,
-                  SymbolTableStringSize);
+  }
+
+  SmallVector<GlobalSymtabInfo> SymtabInfos;
+
+  if (GlobSymtab32Offset)
+    appendGlobalSymbolTableInfo(SymtabInfos, GlobSymtab32Loc, GlobSymtab32Size);
+  if (GlobSymtab64Offset)
+    appendGlobalSymbolTableInfo(SymtabInfos, GlobSymtab64Loc, GlobSymtab64Size);
+
+  if (SymtabInfos.size() == 1) {
+    SymbolTable = SymtabInfos[0].SymbolTable;
+    StringTable = SymtabInfos[0].StringTable;
+  } else if (SymtabInfos.size() == 2) {
+    // In order to let the Archive::Symbol::getNext() work for both 32-bit and
+    // 64-bit global symbol tables, we need to merge them into a single table.
+    raw_string_ostream Out(MergedGlobalSymtabBuf);
+    uint64_t SymNum = SymtabInfos[0].SymNum + SymtabInfos[1].SymNum;
+    write(Out, SymNum, support::big);
+    // Merge symbol offset.
+    Out << SymtabInfos[0].SymbolOffsetTable;
+    Out << SymtabInfos[1].SymbolOffsetTable;
+    // Merge string table.
+    Out << SymtabInfos[0].StringTable;
+    Out << SymtabInfos[1].StringTable;
+    SymbolTable = MergedGlobalSymtabBuf;
+    // The size of the symbol offset to the member file is 8 bytes.
+    StringTable = StringRef(SymbolTable.begin() + (SymNum + 1) * 8,
+                            SymtabInfos[0].StringTable.size() +
+                                SymtabInfos[1].StringTable.size());
   }
 
   child_iterator I = child_begin(Err, false);

diff  --git a/llvm/lib/Object/ArchiveWriter.cpp b/llvm/lib/Object/ArchiveWriter.cpp
index 16763a76237c..d79a5c6bef30 100644
--- a/llvm/lib/Object/ArchiveWriter.cpp
+++ b/llvm/lib/Object/ArchiveWriter.cpp
@@ -44,6 +44,7 @@
 #endif
 
 using namespace llvm;
+using namespace llvm::object;
 
 struct SymMap {
   bool UseECMap;
@@ -435,14 +436,15 @@ static uint64_t computeECSymbolsSize(SymMap &SymMap,
 
 static void writeSymbolTableHeader(raw_ostream &Out, object::Archive::Kind Kind,
                                    bool Deterministic, uint64_t Size,
-                                   uint64_t PrevMemberOffset = 0) {
+                                   uint64_t PrevMemberOffset = 0,
+                                   uint64_t NextMemberOffset = 0) {
   if (isBSDLike(Kind)) {
     const char *Name = is64BitKind(Kind) ? "__.SYMDEF_64" : "__.SYMDEF";
     printBSDMemberHeader(Out, Out.tell(), Name, now(Deterministic), 0, 0, 0,
                          Size);
   } else if (isAIXBigArchive(Kind)) {
-    printBigArchiveMemberHeader(Out, "", now(Deterministic), 0, 0,
-                                0, Size, PrevMemberOffset, 0);
+    printBigArchiveMemberHeader(Out, "", now(Deterministic), 0, 0, 0, Size,
+                                PrevMemberOffset, NextMemberOffset);
   } else {
     const char *Name = is64BitKind(Kind) ? "/SYM64" : "";
     printGNUSmallMemberHeader(Out, Name, now(Deterministic), 0, 0, 0, Size);
@@ -474,24 +476,60 @@ static uint64_t computeHeadersSize(object::Archive::Kind Kind,
   return Size + StringMemberSize;
 }
 
+static Expected<std::unique_ptr<SymbolicFile>>
+getSymbolicFile(MemoryBufferRef Buf, LLVMContext &Context) {
+  const file_magic Type = identify_magic(Buf.getBuffer());
+  // Don't attempt to read non-symbolic file types.
+  if (!object::SymbolicFile::isSymbolicFile(Type, &Context))
+    return nullptr;
+  if (Type == file_magic::bitcode) {
+    auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
+        Buf, file_magic::bitcode, &Context);
+    if (!ObjOrErr)
+      return ObjOrErr.takeError();
+    return std::move(*ObjOrErr);
+  } else {
+    auto ObjOrErr = object::SymbolicFile::createSymbolicFile(Buf);
+    if (!ObjOrErr)
+      return ObjOrErr.takeError();
+    return std::move(*ObjOrErr);
+  }
+}
+
+static Expected<bool> is64BitSymbolicFile(const StringRef &ObjStringRef) {
+  MemoryBufferRef ObjMbf(ObjStringRef, "");
+  // In the scenario when LLVMContext is populated SymbolicFile will contain a
+  // reference to it, thus SymbolicFile should be destroyed first.
+  LLVMContext Context;
+  Expected<std::unique_ptr<SymbolicFile>> ObjOrErr =
+      getSymbolicFile(ObjMbf, Context);
+  if (!ObjOrErr)
+    return ObjOrErr.takeError();
+
+  // Treat non-symbolic file types as not 64-bits.
+  if (!*ObjOrErr)
+    return false;
+
+  return (*ObjOrErr)->is64Bit();
+}
+
 static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind,
                              bool Deterministic, ArrayRef<MemberData> Members,
                              StringRef StringTable, uint64_t MembersOffset,
-                             uint64_t PrevMemberOffset = 0) {
+                             unsigned NumSyms, uint64_t PrevMemberOffset = 0,
+                             uint64_t NextMemberOffset = 0,
+                             bool Is64Bit = false) {
   // We don't write a symbol table on an archive with no members -- except on
   // Darwin, where the linker will abort unless the archive has a symbol table.
   if (StringTable.empty() && !isDarwin(Kind) && !isCOFFArchive(Kind))
     return;
 
-  unsigned NumSyms = 0;
-  for (const MemberData &M : Members)
-    NumSyms += M.Symbols.size();
-
   uint64_t OffsetSize = is64BitKind(Kind) ? 8 : 4;
   uint32_t Pad;
   uint64_t Size = computeSymbolTableSize(Kind, NumSyms, OffsetSize,
                                          StringTable.size(), &Pad);
-  writeSymbolTableHeader(Out, Kind, Deterministic, Size, PrevMemberOffset);
+  writeSymbolTableHeader(Out, Kind, Deterministic, Size, PrevMemberOffset,
+                         NextMemberOffset);
 
   if (isBSDLike(Kind))
     printNBits(Out, Kind, NumSyms * 2 * OffsetSize);
@@ -500,6 +538,19 @@ static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind,
 
   uint64_t Pos = MembersOffset;
   for (const MemberData &M : Members) {
+    if (isAIXBigArchive(Kind)) {
+      Expected<bool> Is64BitOrErr = is64BitSymbolicFile(M.Data);
+      // If there is an error, the error will have been emitted when
+      // 'computeMemberData' called the 'getSymbol' function, so don't need to
+      // handle it here.
+      if (!Is64BitOrErr)
+        cantFail(Is64BitOrErr.takeError());
+      if (*Is64BitOrErr != Is64Bit) {
+        Pos += M.Header.size() + M.Data.size() + M.Padding.size();
+        continue;
+      }
+    }
+
     for (unsigned StringOffset : M.Symbols) {
       if (isBSDLike(Kind))
         printNBits(Out, Kind, StringOffset);
@@ -581,29 +632,21 @@ static bool isECObject(object::SymbolicFile &Obj) {
 static Expected<std::vector<unsigned>>
 getSymbols(MemoryBufferRef Buf, uint16_t Index, raw_ostream &SymNames,
            SymMap *SymMap, bool &HasObject) {
-  std::vector<unsigned> Ret;
-
   // In the scenario when LLVMContext is populated SymbolicFile will contain a
   // reference to it, thus SymbolicFile should be destroyed first.
   LLVMContext Context;
-  std::unique_ptr<object::SymbolicFile> Obj;
 
-  const file_magic Type = identify_magic(Buf.getBuffer());
-  // Treat unsupported file types as having no symbols.
-  if (!object::SymbolicFile::isSymbolicFile(Type, &Context))
+  std::vector<unsigned> Ret;
+  Expected<std::unique_ptr<SymbolicFile>> ObjOrErr =
+      getSymbolicFile(Buf, Context);
+  if (!ObjOrErr)
+    return ObjOrErr.takeError();
+
+  // If the member is non-symbolic file, treat it as having no symbols.
+  if (!*ObjOrErr)
     return Ret;
-  if (Type == file_magic::bitcode) {
-    auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
-        Buf, file_magic::bitcode, &Context);
-    if (!ObjOrErr)
-      return ObjOrErr.takeError();
-    Obj = std::move(*ObjOrErr);
-  } else {
-    auto ObjOrErr = object::SymbolicFile::createSymbolicFile(Buf);
-    if (!ObjOrErr)
-      return ObjOrErr.takeError();
-    Obj = std::move(*ObjOrErr);
-  }
+
+  std::unique_ptr<object::SymbolicFile> Obj = std::move(*ObjOrErr);
 
   std::map<std::string, uint16_t> *Map = nullptr;
   if (SymMap)
@@ -853,12 +896,28 @@ static Error writeArchiveToStream(raw_ostream &Out,
   uint64_t LastMemberEndOffset = 0;
   uint64_t LastMemberHeaderOffset = 0;
   uint64_t NumSyms = 0;
+  uint64_t NumSyms32 = 0; // Store symbol number of 32-bit member files.
+
   for (const auto &M : Data) {
     // Record the start of the member's offset
     LastMemberHeaderOffset = LastMemberEndOffset;
     // Account for the size of each part associated with the member.
     LastMemberEndOffset += M.Header.size() + M.Data.size() + M.Padding.size();
     NumSyms += M.Symbols.size();
+
+    // AIX big archive files may contain two global symbol tables. The
+    // first global symbol table locates 32-bit file members that define global
+    // symbols; the second global symbol table does the same for 64-bit file
+    // members. As a big archive can have both 32-bit and 64-bit file members,
+    // we need to know the number of symbols in each symbol table individually.
+    if (isAIXBigArchive(Kind) && WriteSymtab) {
+      Expected<bool> Is64BitOrErr = is64BitSymbolicFile(M.Data);
+      if (Error E = Is64BitOrErr.takeError())
+        return E;
+
+      if (!*Is64BitOrErr)
+        NumSyms32 += M.Symbols.size();
+    }
   }
 
   std::optional<uint64_t> HeadersSize;
@@ -909,7 +968,7 @@ static Error writeArchiveToStream(raw_ostream &Out,
             Kind, Data.size(), StringTableSize, NumSyms, SymNamesBuf.size(),
             isCOFFArchive(Kind) ? &SymMap : nullptr);
       writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf,
-                       *HeadersSize);
+                       *HeadersSize, NumSyms);
 
       if (isCOFFArchive(Kind))
         writeSymbolMap(Out, Kind, Deterministic, Data, SymMap, *HeadersSize);
@@ -953,11 +1012,49 @@ static Error writeArchiveToStream(raw_ostream &Out,
                                20 * MemberOffsets.size() +
                                MemberTableNameStrTblSize;
 
+    SmallString<0> SymNamesBuf32;
+    SmallString<0> SymNamesBuf64;
+    raw_svector_ostream SymNames32(SymNamesBuf32);
+    raw_svector_ostream SymNames64(SymNamesBuf64);
+
+    if (WriteSymtab && NumSyms)
+      // Generate the symbol names for the members.
+      for (const NewArchiveMember &M : NewMembers) {
+        MemoryBufferRef Buf = M.Buf->getMemBufferRef();
+        Expected<bool> Is64BitOrErr = is64BitSymbolicFile(Buf.getBuffer());
+        if (!Is64BitOrErr)
+          return Is64BitOrErr.takeError();
+
+        bool HasObject;
+        Expected<std::vector<unsigned>> SymbolsOrErr =
+            getSymbols(Buf, 0, *Is64BitOrErr ? SymNames64 : SymNames32, nullptr,
+                       HasObject);
+        if (!SymbolsOrErr)
+          return SymbolsOrErr.takeError();
+      }
+
+    uint64_t MemberTableEndOffset =
+        LastMemberEndOffset +
+        alignTo(sizeof(object::BigArMemHdrType) + MemberTableSize, 2);
+
+    // In AIX OS, The 'GlobSymOffset' field in the fixed-length header contains
+    // the offset to the 32-bit global symbol table, and the 'GlobSym64Offset'
+    // contains the offset to the 64-bit global symbol table.
     uint64_t GlobalSymbolOffset =
-        (WriteSymtab && NumSyms > 0)
-            ? LastMemberEndOffset +
-                  alignTo(sizeof(object::BigArMemHdrType) + MemberTableSize, 2)
-            : 0;
+        (WriteSymtab && NumSyms32 > 0) ? MemberTableEndOffset : 0;
+
+    uint64_t GlobalSymbolOffset64 = 0;
+    uint64_t NumSyms64 = NumSyms - NumSyms32;
+    if (WriteSymtab && NumSyms64 > 0) {
+      if (GlobalSymbolOffset == 0)
+        GlobalSymbolOffset64 = MemberTableEndOffset;
+      else
+        // If there is a global symbol table for 32-bit members,
+        // the 64-bit global symbol table is after the 32-bit one.
+        GlobalSymbolOffset64 =
+            GlobalSymbolOffset + sizeof(object::BigArMemHdrType) +
+            (NumSyms32 + 1) * 8 + alignTo(SymNamesBuf32.size(), 2);
+    }
 
     // Fixed Sized Header.
     printWithSpacePadding(Out, NewMembers.size() ? LastMemberEndOffset : 0,
@@ -965,9 +1062,7 @@ static Error writeArchiveToStream(raw_ostream &Out,
     // If there are no file members in the archive, there will be no global
     // symbol table.
     printWithSpacePadding(Out, GlobalSymbolOffset, 20);
-    printWithSpacePadding(
-        Out, 0,
-        20); // Offset to 64 bits global symbol table - Not supported yet
+    printWithSpacePadding(Out, GlobalSymbolOffset64, 20);
     printWithSpacePadding(
         Out, NewMembers.size() ? sizeof(object::BigArchive::FixLenHdr) : 0,
         20); // Offset to first archive member
@@ -987,7 +1082,8 @@ static Error writeArchiveToStream(raw_ostream &Out,
       // Member table.
       printBigArchiveMemberHeader(Out, "", sys::toTimePoint(0), 0, 0, 0,
                                   MemberTableSize, LastMemberHeaderOffset,
-                                  GlobalSymbolOffset);
+                                  GlobalSymbolOffset ? GlobalSymbolOffset
+                                                     : GlobalSymbolOffset64);
       printWithSpacePadding(Out, MemberOffsets.size(), 20); // Number of members
       for (uint64_t MemberOffset : MemberOffsets)
         printWithSpacePadding(Out, MemberOffset,
@@ -999,9 +1095,25 @@ static Error writeArchiveToStream(raw_ostream &Out,
         Out << '\0'; // Name table must be tail padded to an even number of
                      // bytes.
 
-      if (WriteSymtab && NumSyms > 0)
-        writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf,
-                         *HeadersSize, LastMemberEndOffset);
+      if (WriteSymtab) {
+        // Write global symbol table for 32-bit file members.
+        if (GlobalSymbolOffset) {
+          writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf32,
+                           *HeadersSize, NumSyms32, LastMemberEndOffset,
+                           GlobalSymbolOffset64);
+          // Add padding between the symbol tables, if needed.
+          if (GlobalSymbolOffset64 && (SymNamesBuf32.size() % 2))
+            Out << '\0';
+        }
+
+        // Write global symbol table for 64-bit file members.
+        if (GlobalSymbolOffset64)
+          writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf64,
+                           *HeadersSize, NumSyms64,
+                           GlobalSymbolOffset ? GlobalSymbolOffset
+                                              : LastMemberEndOffset,
+                           0, true);
+      }
     }
   }
   Out.flush();

diff  --git a/llvm/test/Object/Inputs/bitcode-sym32.ll b/llvm/test/Object/Inputs/bitcode-sym32.ll
new file mode 100644
index 000000000000..d5a1bf9d4a49
--- /dev/null
+++ b/llvm/test/Object/Inputs/bitcode-sym32.ll
@@ -0,0 +1,14 @@
+target triple = "powerpcle-unknown-linux-gnu"
+
+ at C32 = dso_local global i32 5, align 4
+ at undef_var32 = external dso_local global i32, align 4
+
+define dso_local i32 @foo32(i32 %i) #0 {
+entry:
+  %i.addr = alloca i32, align 4
+  store i32 %i, ptr %i.addr, align 4
+  %0 = load i32, ptr %i.addr, align 4
+  %1 = load i32, ptr @undef_var32, align 4
+  %add = add nsw i32 %0, %1
+  ret i32 %add
+}

diff  --git a/llvm/test/Object/Inputs/bitcode-sym64.ll b/llvm/test/Object/Inputs/bitcode-sym64.ll
new file mode 100644
index 000000000000..a3c760757489
--- /dev/null
+++ b/llvm/test/Object/Inputs/bitcode-sym64.ll
@@ -0,0 +1,12 @@
+target triple = "powerpc64le-unknown-linux-gnu"
+
+ at C64 = dso_local global i32 5, align 4
+ at static_var64 = internal global i32 2, align 4
+
+define dso_local signext i32 @bar64() #0 {
+entry:
+  %0 = load i32, ptr @static_var64, align 4
+  %1 = load i32, ptr @C64, align 4
+  %add = add nsw i32 %0, %1
+  ret i32 %add
+}

diff  --git a/llvm/test/Object/bigarchive-malformed-global-symbol-table b/llvm/test/Object/bigarchive-malformed-global-symbol-table
new file mode 100644
index 000000000000..006fe4d19356
--- /dev/null
+++ b/llvm/test/Object/bigarchive-malformed-global-symbol-table
@@ -0,0 +1,49 @@
+## Test malformed global symbal table of big archive.
+
+# RUN: rm -rf %t && mkdir %t && cd %t
+# RUN: yaml2obj -DFLAG=0x1DF %s -o t32.o
+# RUN: yaml2obj -DFLAG=0x1F7 %s -o t64.o
+# RUN: llvm-ar q t.a t32.o t64.o
+
+# RUN: cp t.a t32_1.a
+# RUN: cp t.a t32_2.a
+# RUN: cp t.a t64_1.a
+# RUN: cp t.a t64_2.a
+
+## Truncate the file to end before the 32-bit global symbol table header ends.
+# RUN: %python -c "with open('t32_1.a', 'r+b') as input: input.truncate(856)"
+## Truncate the file to end before the 32-bit global symbol table ends.
+# RUN: %python -c "with open('t32_2.a', 'r+b') as input: input.truncate(995)"
+
+# RUN: not llvm-ar t t32_1.a 2>&1 | FileCheck -DFILE=t32_1.a --check-prefixes=HEADER32 %s
+# RUN: not llvm-ar t t32_2.a 2>&1 | FileCheck -DFILE=t32_2.a --check-prefixes=CONTENT32 %s
+
+# HEADER32:  error: unable to load '[[FILE]]': truncated or malformed archive (32-bit global symbol table header at offset 0x34e and size 0x72 goes past the end of file)
+# CONTENT32: error: unable to load '[[FILE]]': truncated or malformed archive (32-bit global symbol table content at offset 0x3c0 and size 0x2b goes past the end of file)
+
+## Truncate the file to end before the 64-bit global symbol table header ends.
+# RUN: %python -c "with open('t64_1.a', 'r+b') as input: input.truncate(1014)"
+## Truncate the file to end before the 64-bit global symbol table ends.
+# RUN: %python -c "with open('t64_2.a', 'r+b') as input: input.truncate(1096)"
+
+# RUN: not llvm-ar t t64_1.a 2>&1 | FileCheck -DFILE=t64_1.a --check-prefixes=HEADER64 %s
+# RUN: not llvm-ar t t64_2.a 2>&1 | FileCheck -DFILE=t64_2.a --check-prefixes=CONTENT64 %s
+
+# HEADER64:  error: unable to load '[[FILE]]': truncated or malformed archive (64-bit global symbol table header at offset 0x3ec and size 0x72 goes past the end of file)
+# CONTENT64: error: unable to load '[[FILE]]': truncated or malformed archive (64-bit global symbol table header at offset 0x3ec and size 0x72 goes past the end of file)
+
+--- !XCOFF
+FileHeader:
+  MagicNumber:       [[FLAG]]
+Sections:
+  - Name:            .data
+    Flags:           [ STYP_DATA ]
+Symbols:
+  - Name:            export_protected_var_[[FLAG]]
+    Section:         .data
+    Type:            0x4000
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_RW

diff  --git a/llvm/test/Object/bigarchive-malformed-header.test b/llvm/test/Object/bigarchive-malformed-header.test
new file mode 100644
index 000000000000..c2862b06d655
--- /dev/null
+++ b/llvm/test/Object/bigarchive-malformed-header.test
@@ -0,0 +1,13 @@
+## Test malformed big archive file in Fixed-Length header field.
+
+# RUN: echo    "<bigaf>" >  %t_mal_globalsym.a
+# RUN: echo -n "0                   1i28                0                   0                   0                   0                   " >> %t_mal_globalsym.a
+# RUN: not llvm-ar tv %t_mal_globalsym.a 2>&1 | FileCheck %s -DFILE=%t_mal_globalsym.a --check-prefixes=GLOBOFFSET
+
+# GLOBOFFSET: llvm-ar: error: unable to load '[[FILE]]': truncated or malformed archive (global symbol table offset of 32-bit members "1i28" is not a number)
+ 
+# RUN: echo    "<bigaf>" >  %t_mal_globalsym64.a
+# RUN: echo -n "0                   0                   1i28                0                   0                   0                   " >> %t_mal_globalsym64.a
+# RUN: not llvm-ar tv %t_mal_globalsym64.a 2>&1 | FileCheck %s -DFILE=%t_mal_globalsym64.a --check-prefixes=GLOB64OFFSET
+
+# GLOB64OFFSET: llvm-ar: error: unable to load '[[FILE]]': truncated or malformed archive (global symbol table offset of 64-bit members"1i28" is not a number)

diff  --git a/llvm/test/Object/bigarchive-symboltable.test b/llvm/test/Object/bigarchive-symboltable.test
new file mode 100644
index 000000000000..647f6da98498
--- /dev/null
+++ b/llvm/test/Object/bigarchive-symboltable.test
@@ -0,0 +1,98 @@
+## Test global symbal table of big archive.
+
+# RUN: rm -rf %t && mkdir %t && cd %t
+# RUN: yaml2obj --docnum=1 -DFLAG=0x1DF %s -o t32_1.o
+# RUN: yaml2obj --docnum=1 -DFLAG=0x1F7 %s -o t64_1.o
+# RUN: yaml2obj --docnum=2 -DFLAG=0x1DF %s -o t32_2.o
+# RUN: yaml2obj --docnum=2 -DFLAG=0x1F7 %s -o t64_2.o
+# RUN: llvm-as -o 32.bc %p/Inputs/bitcode-sym32.ll
+# RUN: llvm-as -o 64.bc %p/Inputs/bitcode-sym64.ll
+
+## Test printing a big archive which only has 32-bits symbol names
+# RUN: llvm-ar q t32.a t32_1.o t32_2.o 32.bc
+# RUN: llvm-nm --print-armap t32.a 2>&1 | FileCheck --check-prefixes=GLOB32,NOGL64 %s
+
+## Test printing a big archive which only has 64-bits symbol names
+# RUN: llvm-ar q t64.a t64_1.o t64_2.o 64.bc
+# RUN: llvm-nm --print-armap t64.a 2>&1 | FileCheck --check-prefixes=GLOB64,NOGL32 %s
+
+## Test printing all 32-bits symbol names first and then 64-bits
+# RUN: llvm-ar q t32_64_all.a t32_1.o t64_1.o t32_2.o t64_2.o 32.bc 64.bc
+
+# RUN: llvm-nm --print-armap t32_64_all.a 2>&1 | FileCheck --check-prefixes=GLOB32,GLOB64 %s
+
+#GLOB32:      var_0x1DF in t32_1.o
+#GLOB32-NEXT: array_0x1DF in t32_1.o
+#GLOB32-NEXT: func_0x1DF in t32_2.o
+#GLOB32-NEXT: bar_0x1DF in t32_2.o
+#GLOB32-NEXT: foo32 in 32.bc
+#GLOB32-NEXT: C32 in 32.bc 
+
+#NOGL32-NOT: var_0x1DF in t32_1.o
+#NOGL32-NOT:  array_0x1DF in t32_1.o
+#NOGL32-NOT:  func_0x1DF in t32_2.o
+#NOGL32-NOT:  bar_0x1DF in t32_2.o
+#NOGL32-NOT:  foo32 in 32.bc
+#NOGL32-NOT:  C32 in 32.bc
+
+#GLOB64:      var_0x1F7 in t64_1.o
+#GLOB64-NEXT: array_0x1F7 in t64_1.o
+#GLOB64-NEXT: func_0x1F7 in t64_2.o
+#GLOB64-NEXT: bar_0x1F7 in t64_2.o
+#GLOB64-NEXT: bar64 in 64.bc
+#GLOB64-NEXT: C64 in 64.bc
+
+#NOGL64-NOT: var_0x1F7 in t64_1.o
+#NOGL64-NOT: array_0x1F7 in t64_1.o
+#NOGL64-NOT: func_0x1F7 in t64_2.o
+#NOGL64-NOT: bar_0x1F7 in t64_2.o
+#NOGL64-NOT: bar64 in 64.bc
+#NOGL64-NOT: C64 in 64.bc
+
+--- !XCOFF
+FileHeader:
+  MagicNumber:       [[FLAG]]
+Sections:
+  - Name:            .data
+    Flags:           [ STYP_DATA ]
+Symbols:
+  - Name:            var_[[FLAG]]
+    Section:         .data
+    Type:            0x4000
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_RW
+  - Name:            array_[[FLAG]]
+    Section:         .data
+    Type:            0x4000
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_RW
+
+--- !XCOFF
+FileHeader:
+  MagicNumber:       [[FLAG]]
+Sections:
+  - Name:            .text
+    Flags:           [ STYP_DATA ]
+Symbols:
+  - Name:            func_[[FLAG]]
+    Section:         .text
+    Type:            0x4000
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_PR
+  - Name:            bar_[[FLAG]]
+    Section:         .text
+    Type:            0x4000
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_PR

diff  --git a/llvm/test/tools/llvm-ar/malformed-global-symbol-table-bigarchive.test b/llvm/test/tools/llvm-ar/malformed-global-symbol-table-bigarchive.test
deleted file mode 100644
index d4a8f377d4d9..000000000000
--- a/llvm/test/tools/llvm-ar/malformed-global-symbol-table-bigarchive.test
+++ /dev/null
@@ -1,33 +0,0 @@
-## Test malformed global symbal table of big archive.
-
-# RUN: rm -rf %t && mkdir %t && cd %t
-# RUN: yaml2obj %s -o t.o
-# RUN: llvm-ar q t.a t.o
-# RUN: cp t.a t2.a
-
-## Truncate the file to end before the global symbol table header ends.
-# RUN: %python -c "with open('t.a', 'r+b') as input: input.truncate(560)"
-## Truncate the file to end before the global symbol table ends.
-# RUN: %python -c "with open('t2.a', 'r+b') as input: input.truncate(656)"
-
-# RUN: not llvm-ar t t.a 2>&1 | FileCheck -DFILE=t.a %s
-# RUN: not llvm-ar t t2.a 2>&1 | FileCheck -DFILE=t2.a --check-prefixes=CHECK2 %s
-
-# CHECK:  error: unable to load '[[FILE]]': truncated or malformed archive (global symbol table header at offset 0x20e and size 0x72 goes past the end of file)
-# CHECK2: error: unable to load '[[FILE]]': truncated or malformed archive (global symbol table content at offset 0x280 and size 0x25 goes past the end of file)
-
---- !XCOFF
-FileHeader:
-  MagicNumber:       0x1DF
-Sections:
-  - Name:            .data
-    Flags:           [ STYP_DATA ]
-Symbols:
-  - Name:            export_protected_var
-    Section:         .data
-    Type:            0x4000
-    StorageClass:    C_EXT
-    AuxEntries:
-     - Type:                   AUX_CSECT
-       SymbolAlignmentAndType: 0x09
-       StorageMappingClass:    XMC_RW


        


More information about the llvm-commits mailing list