aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Object/Archive.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Object/Archive.cpp')
-rw-r--r--llvm/lib/Object/Archive.cpp298
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);