const FixLenHdr *ArFixLenHdr;
uint64_t FirstChildOffset = 0;
uint64_t LastChildOffset = 0;
+ std::string MergedGlobalSymtabBuf;
public:
BigArchive(MemoryBufferRef Source, Error &Err);
#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"
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);
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);
#endif
using namespace llvm;
+using namespace llvm::object;
struct SymMap {
bool UseECMap;
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);
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);
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);
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)
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;
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);
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,
// 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
// 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,
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();
--- /dev/null
+target triple = "powerpcle-unknown-linux-gnu"
+
+@C32 = dso_local global i32 5, align 4
+@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
+}
--- /dev/null
+target triple = "powerpc64le-unknown-linux-gnu"
+
+@C64 = dso_local global i32 5, align 4
+@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
+}
--- /dev/null
+## 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
--- /dev/null
+## 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)
--- /dev/null
+## 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
+++ /dev/null
-## 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