1 //===- GlobalsStream.cpp - PDB Index of Symbols by Name ---------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // The on-disk structores used in this file are based on the reference 10 // implementation which is available at 11 // https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h 12 // 13 // When you are reading the reference source code, you'd find the 14 // information below useful. 15 // 16 // - ppdb1->m_fMinimalDbgInfo seems to be always true. 17 // - SMALLBUCKETS macro is defined. 18 // 19 //===----------------------------------------------------------------------===// 20 21 #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" 22 23 #include "llvm/DebugInfo/CodeView/RecordName.h" 24 #include "llvm/DebugInfo/MSF/MappedBlockStream.h" 25 #include "llvm/DebugInfo/PDB/Native/Hash.h" 26 #include "llvm/DebugInfo/PDB/Native/RawError.h" 27 #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" 28 #include "llvm/Support/BinaryStreamReader.h" 29 #include "llvm/Support/Error.h" 30 #include <algorithm> 31 32 using namespace llvm; 33 using namespace llvm::msf; 34 using namespace llvm::pdb; 35 36 GlobalsStream::GlobalsStream(std::unique_ptr<MappedBlockStream> Stream) 37 : Stream(std::move(Stream)) {} 38 39 GlobalsStream::~GlobalsStream() = default; 40 41 Error GlobalsStream::reload() { 42 BinaryStreamReader Reader(*Stream); 43 if (auto E = GlobalsTable.read(Reader)) 44 return E; 45 return Error::success(); 46 } 47 48 std::vector<std::pair<uint32_t, codeview::CVSymbol>> 49 GlobalsStream::findRecordsByName(StringRef Name, 50 const SymbolStream &Symbols) const { 51 std::vector<std::pair<uint32_t, codeview::CVSymbol>> Result; 52 53 // Hash the name to figure out which bucket this goes into. 54 size_t ExpandedBucketIndex = hashStringV1(Name) % IPHR_HASH; 55 int32_t CompressedBucketIndex = GlobalsTable.BucketMap[ExpandedBucketIndex]; 56 if (CompressedBucketIndex == -1) 57 return Result; 58 59 uint32_t LastBucketIndex = GlobalsTable.HashBuckets.size() - 1; 60 uint32_t StartRecordIndex = 61 GlobalsTable.HashBuckets[CompressedBucketIndex] / 12; 62 uint32_t EndRecordIndex = 0; 63 if (LLVM_LIKELY(uint32_t(CompressedBucketIndex) < LastBucketIndex)) { 64 EndRecordIndex = GlobalsTable.HashBuckets[CompressedBucketIndex + 1]; 65 } else { 66 // If this is the last bucket, it consists of all hash records until the end 67 // of the HashRecords array. 68 EndRecordIndex = GlobalsTable.HashRecords.size() * 12; 69 } 70 71 EndRecordIndex /= 12; 72 73 assert(EndRecordIndex <= GlobalsTable.HashRecords.size()); 74 while (StartRecordIndex < EndRecordIndex) { 75 PSHashRecord PSH = GlobalsTable.HashRecords[StartRecordIndex]; 76 uint32_t Off = PSH.Off - 1; 77 codeview::CVSymbol Record = Symbols.readRecord(Off); 78 if (codeview::getSymbolName(Record) == Name) 79 Result.push_back(std::make_pair(Off, std::move(Record))); 80 ++StartRecordIndex; 81 } 82 return Result; 83 } 84 85 static Error checkHashHdrVersion(const GSIHashHeader *HashHdr) { 86 if (HashHdr->VerHdr != GSIHashHeader::HdrVersion) 87 return make_error<RawError>( 88 raw_error_code::feature_unsupported, 89 "Encountered unsupported globals stream version."); 90 91 return Error::success(); 92 } 93 94 static Error readGSIHashHeader(const GSIHashHeader *&HashHdr, 95 BinaryStreamReader &Reader) { 96 if (Reader.readObject(HashHdr)) 97 return make_error<RawError>(raw_error_code::corrupt_file, 98 "Stream does not contain a GSIHashHeader."); 99 100 if (HashHdr->VerSignature != GSIHashHeader::HdrSignature) 101 return make_error<RawError>( 102 raw_error_code::feature_unsupported, 103 "GSIHashHeader signature (0xffffffff) not found."); 104 105 return Error::success(); 106 } 107 108 static Error readGSIHashRecords(FixedStreamArray<PSHashRecord> &HashRecords, 109 const GSIHashHeader *HashHdr, 110 BinaryStreamReader &Reader) { 111 if (auto EC = checkHashHdrVersion(HashHdr)) 112 return EC; 113 114 // HashHdr->HrSize specifies the number of bytes of PSHashRecords we have. 115 // Verify that we can read them all. 116 if (HashHdr->HrSize % sizeof(PSHashRecord)) 117 return make_error<RawError>(raw_error_code::corrupt_file, 118 "Invalid HR array size."); 119 uint32_t NumHashRecords = HashHdr->HrSize / sizeof(PSHashRecord); 120 if (auto EC = Reader.readArray(HashRecords, NumHashRecords)) 121 return joinErrors(std::move(EC), 122 make_error<RawError>(raw_error_code::corrupt_file, 123 "Error reading hash records.")); 124 125 return Error::success(); 126 } 127 128 static Error 129 readGSIHashBuckets(FixedStreamArray<support::ulittle32_t> &HashBuckets, 130 FixedStreamArray<support::ulittle32_t> &HashBitmap, 131 const GSIHashHeader *HashHdr, 132 MutableArrayRef<int32_t> BucketMap, 133 BinaryStreamReader &Reader) { 134 if (auto EC = checkHashHdrVersion(HashHdr)) 135 return EC; 136 137 // Before the actual hash buckets, there is a bitmap of length determined by 138 // IPHR_HASH. 139 size_t BitmapSizeInBits = alignTo(IPHR_HASH + 1, 32); 140 uint32_t NumBitmapEntries = BitmapSizeInBits / 32; 141 if (auto EC = Reader.readArray(HashBitmap, NumBitmapEntries)) 142 return joinErrors(std::move(EC), 143 make_error<RawError>(raw_error_code::corrupt_file, 144 "Could not read a bitmap.")); 145 uint32_t CompressedBucketIdx = 0; 146 for (uint32_t I = 0; I <= IPHR_HASH; ++I) { 147 uint8_t WordIdx = I / 32; 148 uint8_t BitIdx = I % 32; 149 bool IsSet = HashBitmap[WordIdx] & (1U << BitIdx); 150 if (IsSet) { 151 BucketMap[I] = CompressedBucketIdx++; 152 } else { 153 BucketMap[I] = -1; 154 } 155 } 156 157 uint32_t NumBuckets = 0; 158 for (uint32_t B : HashBitmap) 159 NumBuckets += countPopulation(B); 160 161 // Hash buckets follow. 162 if (auto EC = Reader.readArray(HashBuckets, NumBuckets)) 163 return joinErrors(std::move(EC), 164 make_error<RawError>(raw_error_code::corrupt_file, 165 "Hash buckets corrupted.")); 166 167 return Error::success(); 168 } 169 170 Error GSIHashTable::read(BinaryStreamReader &Reader) { 171 if (auto EC = readGSIHashHeader(HashHdr, Reader)) 172 return EC; 173 if (auto EC = readGSIHashRecords(HashRecords, HashHdr, Reader)) 174 return EC; 175 if (HashHdr->HrSize > 0) 176 if (auto EC = readGSIHashBuckets(HashBuckets, HashBitmap, HashHdr, 177 BucketMap, Reader)) 178 return EC; 179 return Error::success(); 180 } 181