diff options
Diffstat (limited to 'llvm/lib/Object/Archive.cpp')
| -rw-r--r-- | llvm/lib/Object/Archive.cpp | 298 |
1 files changed, 246 insertions, 52 deletions
diff --git a/llvm/lib/Object/Archive.cpp b/llvm/lib/Object/Archive.cpp index 2cf924123888..9920145a2f3c 100644 --- a/llvm/lib/Object/Archive.cpp +++ b/llvm/lib/Object/Archive.cpp @@ -18,14 +18,15 @@ #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" -#include "llvm/Support/Host.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/TargetParser/Host.h" #include <algorithm> #include <cassert> #include <cstddef> @@ -135,6 +136,13 @@ BigArchiveMemberHeader::BigArchiveMemberHeader(const Archive *Parent, return; ErrorAsOutParameter ErrAsOutParam(Err); + if (RawHeaderPtr + getSizeOf() >= Parent->getData().end()) { + if (Err) + *Err = malformedError("malformed AIX big archive: remaining buffer is " + "unable to contain next archive member"); + return; + } + if (Size < getSizeOf()) { Error SubErr = createMemberHeaderParseError(this, RawHeaderPtr, Size); if (Err) @@ -461,6 +469,7 @@ Archive::Child::Child(const Archive *Parent, const char *Start, Error *Err) : Parent(Parent) { if (!Start) { Header = nullptr; + StartOfFile = -1; return; } @@ -926,6 +935,34 @@ Archive::Archive(MemoryBufferRef Source, Error &Err) StringTable = BufOrErr.get(); if (Increment()) return; + + if (I == E) { + setFirstRegular(*C); + Err = Error::success(); + return; + } + + NameOrErr = C->getRawName(); + if (!NameOrErr) { + Err = NameOrErr.takeError(); + return; + } + Name = NameOrErr.get(); + } + + if (Name == "/<ECSYMBOLS>/") { + // ARM64EC-aware libraries contain an additional special member with + // an EC symbol map after the string table. Its format is similar to a + // regular symbol map, except it doesn't contain member offsets. Its indexes + // refer to member offsets from the regular symbol table instead. + Expected<StringRef> BufOrErr = C->getBuffer(); + if (!BufOrErr) { + Err = BufOrErr.takeError(); + return; + } + ECSymbolTable = BufOrErr.get(); + if (Increment()) + return; } setFirstRegular(*C); @@ -960,7 +997,17 @@ Archive::child_iterator Archive::child_end() const { return child_iterator::end(Child(nullptr, nullptr, nullptr)); } +bool Archive::Symbol::isECSymbol() const { + // Symbols use SymbolCount..SymbolCount+getNumberOfECSymbols() for EC symbol + // indexes. + uint32_t SymbolCount = Parent->getNumberOfSymbols(); + return SymbolCount <= SymbolIndex && + SymbolIndex < SymbolCount + Parent->getNumberOfECSymbols(); +} + StringRef Archive::Symbol::getName() const { + if (isECSymbol()) + return Parent->ECSymbolTable.begin() + StringIndex; return Parent->getSymbolTable().begin() + StringIndex; } @@ -999,15 +1046,24 @@ Expected<Archive::Child> Archive::Symbol::getMember() const { Buf += MemberCount * 4 + 4; uint32_t SymbolCount = read32le(Buf); - if (SymbolIndex >= SymbolCount) + uint16_t OffsetIndex; + if (SymbolIndex < SymbolCount) { + // Skip SymbolCount to get to the indices table. + const char *Indices = Buf + 4; + + // Get the index of the offset in the file member offset table for this + // symbol. + OffsetIndex = read16le(Indices + SymbolIndex * 2); + } else if (isECSymbol()) { + // Skip SymbolCount to get to the indices table. + const char *Indices = Parent->ECSymbolTable.begin() + 4; + + // Get the index of the offset in the file member offset table for this + // symbol. + OffsetIndex = read16le(Indices + (SymbolIndex - SymbolCount) * 2); + } else { return errorCodeToError(object_error::parse_failed); - - // Skip SymbolCount to get to the indices table. - const char *Indices = Buf + 4; - - // Get the index of the offset in the file member offset table for this - // symbol. - uint16_t OffsetIndex = read16le(Indices + SymbolIndex * 2); + } // Subtract 1 since OffsetIndex is 1 based. --OffsetIndex; @@ -1056,6 +1112,9 @@ Archive::Symbol Archive::Symbol::getNext() const { t.StringIndex -= CurRanStrx; t.StringIndex += NextRanStrx; } + } else if (t.isECSymbol()) { + // Go to one past next null. + t.StringIndex = Parent->ECSymbolTable.find('\0', t.StringIndex) + 1; } else { // Go to one past next null. t.StringIndex = Parent->getSymbolTable().find('\0', t.StringIndex) + 1; @@ -1126,6 +1185,51 @@ Archive::symbol_iterator Archive::symbol_end() const { return symbol_iterator(Symbol(this, getNumberOfSymbols(), 0)); } +Expected<iterator_range<Archive::symbol_iterator>> Archive::ec_symbols() const { + uint32_t Count = 0; + + // Validate EC symbol table. + if (!ECSymbolTable.empty()) { + if (ECSymbolTable.size() < sizeof(uint32_t)) + return malformedError("invalid EC symbols size (" + + Twine(ECSymbolTable.size()) + ")"); + if (SymbolTable.size() < sizeof(uint32_t)) + return malformedError("invalid symbols size (" + + Twine(ECSymbolTable.size()) + ")"); + + Count = read32le(ECSymbolTable.begin()); + size_t StringIndex = sizeof(uint32_t) + Count * sizeof(uint16_t); + if (ECSymbolTable.size() < StringIndex) + return malformedError("invalid EC symbols size. Size was " + + Twine(ECSymbolTable.size()) + ", but expected " + + Twine(StringIndex)); + + uint32_t MemberCount = read32le(SymbolTable.begin()); + const char *Indexes = ECSymbolTable.begin() + sizeof(uint32_t); + + for (uint32_t i = 0; i < Count; ++i) { + uint16_t Index = read16le(Indexes + i * sizeof(uint16_t)); + if (!Index) + return malformedError("invalid EC symbol index 0"); + if (Index > MemberCount) + return malformedError("invalid EC symbol index " + Twine(Index) + + " is larger than member count " + + Twine(MemberCount)); + + StringIndex = ECSymbolTable.find('\0', StringIndex); + if (StringIndex == StringRef::npos) + return malformedError("malformed EC symbol names: not null-terminated"); + ++StringIndex; + } + } + + uint32_t SymbolCount = getNumberOfSymbols(); + return make_range( + symbol_iterator(Symbol(this, SymbolCount, + sizeof(uint32_t) + Count * sizeof(uint16_t))), + symbol_iterator(Symbol(this, SymbolCount + Count, 0))); +} + uint32_t Archive::getNumberOfSymbols() const { if (!hasSymbolTable()) return 0; @@ -1144,6 +1248,12 @@ uint32_t Archive::getNumberOfSymbols() const { return read32le(buf); } +uint32_t Archive::getNumberOfECSymbols() const { + if (ECSymbolTable.size() < sizeof(uint32_t)) + return 0; + return read32le(ECSymbolTable.begin()); +} + Expected<std::optional<Archive::Child>> Archive::findSym(StringRef name) const { Archive::symbol_iterator bs = symbol_begin(); Archive::symbol_iterator es = symbol_end(); @@ -1167,11 +1277,78 @@ 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); StringRef Buffer = Data.getBuffer(); ArFixLenHdr = reinterpret_cast<const FixLenHdr *>(Buffer.data()); + uint64_t BufferSize = Data.getBufferSize(); + + if (BufferSize < sizeof(FixLenHdr)) { + Err = malformedError("malformed AIX big archive: incomplete fixed length " + "header, the archive is only" + + Twine(BufferSize) + " byte(s)"); + return; + } StringRef RawOffset = getFieldRawString(ArFixLenHdr->FirstChildOffset); if (RawOffset.getAsInteger(10, FirstChildOffset)) @@ -1185,56 +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 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 *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); |
