From 38d3c6cb9b9dc67a8a5460aa241824be3240b81e Mon Sep 17 00:00:00 2001 From: zhijian Date: Thu, 18 May 2023 10:54:14 -0400 Subject: [PATCH] [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 --- llvm/include/llvm/Object/Archive.h | 1 + llvm/lib/Object/Archive.cpp | 162 +++++++++++++----- llvm/lib/Object/ArchiveWriter.cpp | 190 ++++++++++++++++----- llvm/test/Object/Inputs/bitcode-sym32.ll | 14 ++ llvm/test/Object/Inputs/bitcode-sym64.ll | 12 ++ .../bigarchive-malformed-global-symbol-table | 49 ++++++ llvm/test/Object/bigarchive-malformed-header.test | 13 ++ llvm/test/Object/bigarchive-symboltable.test | 98 +++++++++++ .../malformed-global-symbol-table-bigarchive.test | 33 ---- 9 files changed, 458 insertions(+), 114 deletions(-) create mode 100644 llvm/test/Object/Inputs/bitcode-sym32.ll create mode 100644 llvm/test/Object/Inputs/bitcode-sym64.ll create mode 100644 llvm/test/Object/bigarchive-malformed-global-symbol-table create mode 100644 llvm/test/Object/bigarchive-malformed-header.test create mode 100644 llvm/test/Object/bigarchive-symboltable.test delete mode 100644 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 7722f85..27f5047 100644 --- a/llvm/include/llvm/Object/Archive.h +++ b/llvm/include/llvm/Object/Archive.h @@ -410,6 +410,7 @@ public: 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 081ff79..9920145 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(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 &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(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 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 16763a7..d79a5c6 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> +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 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> 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 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 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> getSymbols(MemoryBufferRef Buf, uint16_t Index, raw_ostream &SymNames, SymMap *SymMap, bool &HasObject) { - std::vector 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 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 Ret; + Expected> 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 Obj = std::move(*ObjOrErr); std::map *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 Is64BitOrErr = is64BitSymbolicFile(M.Data); + if (Error E = Is64BitOrErr.takeError()) + return E; + + if (!*Is64BitOrErr) + NumSyms32 += M.Symbols.size(); + } } std::optional 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 Is64BitOrErr = is64BitSymbolicFile(Buf.getBuffer()); + if (!Is64BitOrErr) + return Is64BitOrErr.takeError(); + + bool HasObject; + Expected> 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 0000000..d5a1bf9 --- /dev/null +++ b/llvm/test/Object/Inputs/bitcode-sym32.ll @@ -0,0 +1,14 @@ +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 +} diff --git a/llvm/test/Object/Inputs/bitcode-sym64.ll b/llvm/test/Object/Inputs/bitcode-sym64.ll new file mode 100644 index 0000000..a3c7607 --- /dev/null +++ b/llvm/test/Object/Inputs/bitcode-sym64.ll @@ -0,0 +1,12 @@ +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 +} 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 0000000..006fe4d --- /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 0000000..c2862b0 --- /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 "" > %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 "" > %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 0000000..647f6da --- /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 d4a8f37..0000000 --- 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 -- 2.7.4