10b57cec5SDimitry Andric //===- GlobalsStream.cpp - PDB Index of Symbols by Name ---------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // The on-disk structores used in this file are based on the reference
100b57cec5SDimitry Andric // implementation which is available at
110b57cec5SDimitry Andric // https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h
120b57cec5SDimitry Andric //
130b57cec5SDimitry Andric // When you are reading the reference source code, you'd find the
140b57cec5SDimitry Andric // information below useful.
150b57cec5SDimitry Andric //
160b57cec5SDimitry Andric // - ppdb1->m_fMinimalDbgInfo seems to be always true.
170b57cec5SDimitry Andric // - SMALLBUCKETS macro is defined.
180b57cec5SDimitry Andric //
190b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
200b57cec5SDimitry Andric
210b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
220b57cec5SDimitry Andric
230b57cec5SDimitry Andric #include "llvm/DebugInfo/CodeView/RecordName.h"
2481ad6265SDimitry Andric #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
250b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/Hash.h"
260b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/RawError.h"
270b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
280b57cec5SDimitry Andric #include "llvm/Support/BinaryStreamReader.h"
290b57cec5SDimitry Andric #include "llvm/Support/Error.h"
300b57cec5SDimitry Andric #include <algorithm>
310b57cec5SDimitry Andric
320b57cec5SDimitry Andric using namespace llvm;
330b57cec5SDimitry Andric using namespace llvm::msf;
340b57cec5SDimitry Andric using namespace llvm::pdb;
350b57cec5SDimitry Andric
GlobalsStream(std::unique_ptr<MappedBlockStream> Stream)360b57cec5SDimitry Andric GlobalsStream::GlobalsStream(std::unique_ptr<MappedBlockStream> Stream)
370b57cec5SDimitry Andric : Stream(std::move(Stream)) {}
380b57cec5SDimitry Andric
390b57cec5SDimitry Andric GlobalsStream::~GlobalsStream() = default;
400b57cec5SDimitry Andric
reload()410b57cec5SDimitry Andric Error GlobalsStream::reload() {
420b57cec5SDimitry Andric BinaryStreamReader Reader(*Stream);
430b57cec5SDimitry Andric if (auto E = GlobalsTable.read(Reader))
440b57cec5SDimitry Andric return E;
450b57cec5SDimitry Andric return Error::success();
460b57cec5SDimitry Andric }
470b57cec5SDimitry Andric
480b57cec5SDimitry Andric std::vector<std::pair<uint32_t, codeview::CVSymbol>>
findRecordsByName(StringRef Name,const SymbolStream & Symbols) const490b57cec5SDimitry Andric GlobalsStream::findRecordsByName(StringRef Name,
500b57cec5SDimitry Andric const SymbolStream &Symbols) const {
510b57cec5SDimitry Andric std::vector<std::pair<uint32_t, codeview::CVSymbol>> Result;
520b57cec5SDimitry Andric
530b57cec5SDimitry Andric // Hash the name to figure out which bucket this goes into.
540b57cec5SDimitry Andric size_t ExpandedBucketIndex = hashStringV1(Name) % IPHR_HASH;
550b57cec5SDimitry Andric int32_t CompressedBucketIndex = GlobalsTable.BucketMap[ExpandedBucketIndex];
560b57cec5SDimitry Andric if (CompressedBucketIndex == -1)
570b57cec5SDimitry Andric return Result;
580b57cec5SDimitry Andric
590b57cec5SDimitry Andric uint32_t LastBucketIndex = GlobalsTable.HashBuckets.size() - 1;
600b57cec5SDimitry Andric uint32_t StartRecordIndex =
610b57cec5SDimitry Andric GlobalsTable.HashBuckets[CompressedBucketIndex] / 12;
620b57cec5SDimitry Andric uint32_t EndRecordIndex = 0;
630b57cec5SDimitry Andric if (LLVM_LIKELY(uint32_t(CompressedBucketIndex) < LastBucketIndex)) {
640b57cec5SDimitry Andric EndRecordIndex = GlobalsTable.HashBuckets[CompressedBucketIndex + 1];
650b57cec5SDimitry Andric } else {
660b57cec5SDimitry Andric // If this is the last bucket, it consists of all hash records until the end
670b57cec5SDimitry Andric // of the HashRecords array.
680b57cec5SDimitry Andric EndRecordIndex = GlobalsTable.HashRecords.size() * 12;
690b57cec5SDimitry Andric }
700b57cec5SDimitry Andric
710b57cec5SDimitry Andric EndRecordIndex /= 12;
720b57cec5SDimitry Andric
730b57cec5SDimitry Andric assert(EndRecordIndex <= GlobalsTable.HashRecords.size());
740b57cec5SDimitry Andric while (StartRecordIndex < EndRecordIndex) {
750b57cec5SDimitry Andric PSHashRecord PSH = GlobalsTable.HashRecords[StartRecordIndex];
760b57cec5SDimitry Andric uint32_t Off = PSH.Off - 1;
770b57cec5SDimitry Andric codeview::CVSymbol Record = Symbols.readRecord(Off);
780b57cec5SDimitry Andric if (codeview::getSymbolName(Record) == Name)
790b57cec5SDimitry Andric Result.push_back(std::make_pair(Off, std::move(Record)));
800b57cec5SDimitry Andric ++StartRecordIndex;
810b57cec5SDimitry Andric }
820b57cec5SDimitry Andric return Result;
830b57cec5SDimitry Andric }
840b57cec5SDimitry Andric
checkHashHdrVersion(const GSIHashHeader * HashHdr)850b57cec5SDimitry Andric static Error checkHashHdrVersion(const GSIHashHeader *HashHdr) {
860b57cec5SDimitry Andric if (HashHdr->VerHdr != GSIHashHeader::HdrVersion)
870b57cec5SDimitry Andric return make_error<RawError>(
880b57cec5SDimitry Andric raw_error_code::feature_unsupported,
890b57cec5SDimitry Andric "Encountered unsupported globals stream version.");
900b57cec5SDimitry Andric
910b57cec5SDimitry Andric return Error::success();
920b57cec5SDimitry Andric }
930b57cec5SDimitry Andric
readGSIHashHeader(const GSIHashHeader * & HashHdr,BinaryStreamReader & Reader)940b57cec5SDimitry Andric static Error readGSIHashHeader(const GSIHashHeader *&HashHdr,
950b57cec5SDimitry Andric BinaryStreamReader &Reader) {
960b57cec5SDimitry Andric if (Reader.readObject(HashHdr))
970b57cec5SDimitry Andric return make_error<RawError>(raw_error_code::corrupt_file,
980b57cec5SDimitry Andric "Stream does not contain a GSIHashHeader.");
990b57cec5SDimitry Andric
1000b57cec5SDimitry Andric if (HashHdr->VerSignature != GSIHashHeader::HdrSignature)
1010b57cec5SDimitry Andric return make_error<RawError>(
1020b57cec5SDimitry Andric raw_error_code::feature_unsupported,
1030b57cec5SDimitry Andric "GSIHashHeader signature (0xffffffff) not found.");
1040b57cec5SDimitry Andric
1050b57cec5SDimitry Andric return Error::success();
1060b57cec5SDimitry Andric }
1070b57cec5SDimitry Andric
readGSIHashRecords(FixedStreamArray<PSHashRecord> & HashRecords,const GSIHashHeader * HashHdr,BinaryStreamReader & Reader)1080b57cec5SDimitry Andric static Error readGSIHashRecords(FixedStreamArray<PSHashRecord> &HashRecords,
1090b57cec5SDimitry Andric const GSIHashHeader *HashHdr,
1100b57cec5SDimitry Andric BinaryStreamReader &Reader) {
1110b57cec5SDimitry Andric if (auto EC = checkHashHdrVersion(HashHdr))
1120b57cec5SDimitry Andric return EC;
1130b57cec5SDimitry Andric
1140b57cec5SDimitry Andric // HashHdr->HrSize specifies the number of bytes of PSHashRecords we have.
1150b57cec5SDimitry Andric // Verify that we can read them all.
1160b57cec5SDimitry Andric if (HashHdr->HrSize % sizeof(PSHashRecord))
1170b57cec5SDimitry Andric return make_error<RawError>(raw_error_code::corrupt_file,
1180b57cec5SDimitry Andric "Invalid HR array size.");
1190b57cec5SDimitry Andric uint32_t NumHashRecords = HashHdr->HrSize / sizeof(PSHashRecord);
1200b57cec5SDimitry Andric if (auto EC = Reader.readArray(HashRecords, NumHashRecords))
1210b57cec5SDimitry Andric return joinErrors(std::move(EC),
1220b57cec5SDimitry Andric make_error<RawError>(raw_error_code::corrupt_file,
1230b57cec5SDimitry Andric "Error reading hash records."));
1240b57cec5SDimitry Andric
1250b57cec5SDimitry Andric return Error::success();
1260b57cec5SDimitry Andric }
1270b57cec5SDimitry Andric
1280b57cec5SDimitry Andric static Error
readGSIHashBuckets(FixedStreamArray<support::ulittle32_t> & HashBuckets,FixedStreamArray<support::ulittle32_t> & HashBitmap,const GSIHashHeader * HashHdr,MutableArrayRef<int32_t> BucketMap,BinaryStreamReader & Reader)1290b57cec5SDimitry Andric readGSIHashBuckets(FixedStreamArray<support::ulittle32_t> &HashBuckets,
1300b57cec5SDimitry Andric FixedStreamArray<support::ulittle32_t> &HashBitmap,
1310b57cec5SDimitry Andric const GSIHashHeader *HashHdr,
1320b57cec5SDimitry Andric MutableArrayRef<int32_t> BucketMap,
1330b57cec5SDimitry Andric BinaryStreamReader &Reader) {
1340b57cec5SDimitry Andric if (auto EC = checkHashHdrVersion(HashHdr))
1350b57cec5SDimitry Andric return EC;
1360b57cec5SDimitry Andric
1370b57cec5SDimitry Andric // Before the actual hash buckets, there is a bitmap of length determined by
1380b57cec5SDimitry Andric // IPHR_HASH.
1390b57cec5SDimitry Andric size_t BitmapSizeInBits = alignTo(IPHR_HASH + 1, 32);
1400b57cec5SDimitry Andric uint32_t NumBitmapEntries = BitmapSizeInBits / 32;
1410b57cec5SDimitry Andric if (auto EC = Reader.readArray(HashBitmap, NumBitmapEntries))
1420b57cec5SDimitry Andric return joinErrors(std::move(EC),
1430b57cec5SDimitry Andric make_error<RawError>(raw_error_code::corrupt_file,
1440b57cec5SDimitry Andric "Could not read a bitmap."));
1450b57cec5SDimitry Andric uint32_t CompressedBucketIdx = 0;
1460b57cec5SDimitry Andric for (uint32_t I = 0; I <= IPHR_HASH; ++I) {
1470b57cec5SDimitry Andric uint8_t WordIdx = I / 32;
1480b57cec5SDimitry Andric uint8_t BitIdx = I % 32;
1490b57cec5SDimitry Andric bool IsSet = HashBitmap[WordIdx] & (1U << BitIdx);
1500b57cec5SDimitry Andric if (IsSet) {
1510b57cec5SDimitry Andric BucketMap[I] = CompressedBucketIdx++;
1520b57cec5SDimitry Andric } else {
1530b57cec5SDimitry Andric BucketMap[I] = -1;
1540b57cec5SDimitry Andric }
1550b57cec5SDimitry Andric }
1560b57cec5SDimitry Andric
1570b57cec5SDimitry Andric uint32_t NumBuckets = 0;
1580b57cec5SDimitry Andric for (uint32_t B : HashBitmap)
159*bdd1243dSDimitry Andric NumBuckets += llvm::popcount(B);
1600b57cec5SDimitry Andric
1610b57cec5SDimitry Andric // Hash buckets follow.
1620b57cec5SDimitry Andric if (auto EC = Reader.readArray(HashBuckets, NumBuckets))
1630b57cec5SDimitry Andric return joinErrors(std::move(EC),
1640b57cec5SDimitry Andric make_error<RawError>(raw_error_code::corrupt_file,
1650b57cec5SDimitry Andric "Hash buckets corrupted."));
1660b57cec5SDimitry Andric
1670b57cec5SDimitry Andric return Error::success();
1680b57cec5SDimitry Andric }
1690b57cec5SDimitry Andric
read(BinaryStreamReader & Reader)1700b57cec5SDimitry Andric Error GSIHashTable::read(BinaryStreamReader &Reader) {
1710b57cec5SDimitry Andric if (auto EC = readGSIHashHeader(HashHdr, Reader))
1720b57cec5SDimitry Andric return EC;
1730b57cec5SDimitry Andric if (auto EC = readGSIHashRecords(HashRecords, HashHdr, Reader))
1740b57cec5SDimitry Andric return EC;
1750b57cec5SDimitry Andric if (HashHdr->HrSize > 0)
1760b57cec5SDimitry Andric if (auto EC = readGSIHashBuckets(HashBuckets, HashBitmap, HashHdr,
1770b57cec5SDimitry Andric BucketMap, Reader))
1780b57cec5SDimitry Andric return EC;
1790b57cec5SDimitry Andric return Error::success();
1800b57cec5SDimitry Andric }
181