diff --git a/llvm/include/llvm/Object/Archive.h b/llvm/include/llvm/Object/Archive.h --- a/llvm/include/llvm/Object/Archive.h +++ b/llvm/include/llvm/Object/Archive.h @@ -379,10 +379,10 @@ uint64_t getArchiveMagicLen() const; void setFirstRegular(const Child &C); -private: StringRef SymbolTable; StringRef StringTable; +private: StringRef FirstRegularData; uint16_t FirstRegularStartOfFile = -1; diff --git a/llvm/lib/Object/Archive.cpp b/llvm/lib/Object/Archive.cpp --- a/llvm/lib/Object/Archive.cpp +++ b/llvm/lib/Object/Archive.cpp @@ -965,14 +965,15 @@ Expected Archive::Symbol::getMember() const { const char *Buf = Parent->getSymbolTable().begin(); const char *Offsets = Buf; - if (Parent->kind() == K_GNU64 || Parent->kind() == K_DARWIN64) + if (Parent->kind() == K_GNU64 || Parent->kind() == K_DARWIN64 || + Parent->kind() == K_AIXBIG) Offsets += sizeof(uint64_t); else Offsets += sizeof(uint32_t); uint64_t Offset = 0; if (Parent->kind() == K_GNU) { Offset = read32be(Offsets + SymbolIndex * 4); - } else if (Parent->kind() == K_GNU64) { + } else if (Parent->kind() == K_GNU64 || Parent->kind() == K_AIXBIG) { Offset = read64be(Offsets + SymbolIndex * 8); } else if (Parent->kind() == K_BSD) { // The SymbolIndex is an index into the ranlib structs that start at @@ -1105,6 +1106,8 @@ // Skip the byte count of the string table. buf += sizeof(uint64_t); buf += ran_strx; + } else if (kind() == K_AIXBIG) { + buf = getStringTable().begin(); } else { uint32_t member_count = 0; uint32_t symbol_count = 0; @@ -1127,7 +1130,7 @@ const char *buf = getSymbolTable().begin(); if (kind() == K_GNU) return read32be(buf); - if (kind() == K_GNU64) + if (kind() == K_GNU64 || kind() == K_AIXBIG) return read64be(buf); if (kind() == K_BSD) return read32le(buf) / 8; @@ -1180,6 +1183,58 @@ Err = malformedError("malformed AIX big archive: last member offset \"" + RawOffset + "\" is not a number"); + // Calculate the global symbol table. + uint64_t GlobSymOffset = 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 (Err) + return; + + if (GlobSymOffset > 0) { + uint64_t BufferSize = Data.getBufferSize(); + 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 *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"); + 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"); + 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); + } + child_iterator I = child_begin(Err, false); if (Err) return; diff --git a/llvm/test/Object/archive-symtab.test b/llvm/test/Object/archive-symtab.test --- a/llvm/test/Object/archive-symtab.test +++ b/llvm/test/Object/archive-symtab.test @@ -65,6 +65,10 @@ # RUN: llvm-nm --print-armap %t.a | FileCheck %s # RUN: not grep '/SYM64/' %t.a +# RUN: rm -f %t.a +# RUN: llvm-ar rcsU --format=bigarchive %t.a %t.elf-x86-64 %t2.elf-x86-64 +# RUN: llvm-nm --print-armap %t.a | FileCheck %s + # CHECK: Archive map # CHECK-NEXT: main in {{.*}}.elf-x86-64 # CHECK-NEXT: foo in {{.*}}2.elf-x86-64 diff --git a/llvm/test/tools/llvm-ar/delete.test b/llvm/test/tools/llvm-ar/delete.test --- a/llvm/test/tools/llvm-ar/delete.test +++ b/llvm/test/tools/llvm-ar/delete.test @@ -1,4 +1,3 @@ -# XFAIL: system-aix ## Test the deletion of members and that symbols are removed from the symbol table. # RUN: yaml2obj %s -o %t-delete.o --docnum=1 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 new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-ar/malformed-global-symbol-table-bigarchive.test @@ -0,0 +1,33 @@ +## 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