xref: /freebsd/contrib/llvm-project/llvm/lib/DebugInfo/PDB/Native/GlobalsStream.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
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