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(Str: 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(Offset: Off); |
78 | if (codeview::getSymbolName(Sym: Record) == Name) |
79 | Result.push_back(x: std::make_pair(x&: Off, y: std::move(Record))); |
80 | ++StartRecordIndex; |
81 | } |
82 | return Result; |
83 | } |
84 | |
85 | static Error (const GSIHashHeader *HashHdr) { |
86 | if (HashHdr->VerHdr != GSIHashHeader::HdrVersion) |
87 | return make_error<RawError>( |
88 | Args: raw_error_code::feature_unsupported, |
89 | Args: "Encountered unsupported globals stream version." ); |
90 | |
91 | return Error::success(); |
92 | } |
93 | |
94 | static Error (const GSIHashHeader *&HashHdr, |
95 | BinaryStreamReader &Reader) { |
96 | if (Reader.readObject(Dest&: HashHdr)) |
97 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
98 | Args: "Stream does not contain a GSIHashHeader." ); |
99 | |
100 | if (HashHdr->VerSignature != GSIHashHeader::HdrSignature) |
101 | return make_error<RawError>( |
102 | Args: raw_error_code::feature_unsupported, |
103 | Args: "GSIHashHeader signature (0xffffffff) not found." ); |
104 | |
105 | return Error::success(); |
106 | } |
107 | |
108 | static Error (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>(Args: raw_error_code::corrupt_file, |
118 | Args: "Invalid HR array size." ); |
119 | uint32_t NumHashRecords = HashHdr->HrSize / sizeof(PSHashRecord); |
120 | if (auto EC = Reader.readArray(Array&: HashRecords, NumItems: NumHashRecords)) |
121 | return joinErrors(E1: std::move(EC), |
122 | E2: make_error<RawError>(Args: raw_error_code::corrupt_file, |
123 | Args: "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(Value: IPHR_HASH + 1, Align: 32); |
140 | uint32_t NumBitmapEntries = BitmapSizeInBits / 32; |
141 | if (auto EC = Reader.readArray(Array&: HashBitmap, NumItems: NumBitmapEntries)) |
142 | return joinErrors(E1: std::move(EC), |
143 | E2: make_error<RawError>(Args: raw_error_code::corrupt_file, |
144 | Args: "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 += llvm::popcount(Value: B); |
160 | |
161 | // Hash buckets follow. |
162 | if (auto EC = Reader.readArray(Array&: HashBuckets, NumItems: NumBuckets)) |
163 | return joinErrors(E1: std::move(EC), |
164 | E2: make_error<RawError>(Args: raw_error_code::corrupt_file, |
165 | Args: "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 | |