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 | |
31 | using namespace llvm; |
32 | using namespace llvm::msf; |
33 | using namespace llvm::pdb; |
34 | |
35 | GlobalsStream::GlobalsStream(std::unique_ptr<MappedBlockStream> Stream) |
36 | : Stream(std::move(Stream)) {} |
37 | |
38 | GlobalsStream::~GlobalsStream() = default; |
39 | |
40 | Error GlobalsStream::reload() { |
41 | BinaryStreamReader Reader(*Stream); |
42 | if (auto E = GlobalsTable.read(Reader)) |
43 | return E; |
44 | return Error::success(); |
45 | } |
46 | |
47 | std::vector<std::pair<uint32_t, codeview::CVSymbol>> |
48 | GlobalsStream::findRecordsByName(StringRef Name, |
49 | const SymbolStream &Symbols) const { |
50 | std::vector<std::pair<uint32_t, codeview::CVSymbol>> Result; |
51 | |
52 | // Hash the name to figure out which bucket this goes into. |
53 | size_t ExpandedBucketIndex = hashStringV1(Str: Name) % IPHR_HASH; |
54 | int32_t CompressedBucketIndex = GlobalsTable.BucketMap[ExpandedBucketIndex]; |
55 | if (CompressedBucketIndex == -1) |
56 | return Result; |
57 | |
58 | uint32_t LastBucketIndex = GlobalsTable.HashBuckets.size() - 1; |
59 | uint32_t StartRecordIndex = |
60 | GlobalsTable.HashBuckets[CompressedBucketIndex] / 12; |
61 | uint32_t EndRecordIndex = 0; |
62 | if (LLVM_LIKELY(uint32_t(CompressedBucketIndex) < LastBucketIndex)) { |
63 | EndRecordIndex = GlobalsTable.HashBuckets[CompressedBucketIndex + 1]; |
64 | } else { |
65 | // If this is the last bucket, it consists of all hash records until the end |
66 | // of the HashRecords array. |
67 | EndRecordIndex = GlobalsTable.HashRecords.size() * 12; |
68 | } |
69 | |
70 | EndRecordIndex /= 12; |
71 | |
72 | assert(EndRecordIndex <= GlobalsTable.HashRecords.size()); |
73 | while (StartRecordIndex < EndRecordIndex) { |
74 | PSHashRecord PSH = GlobalsTable.HashRecords[StartRecordIndex]; |
75 | uint32_t Off = PSH.Off - 1; |
76 | codeview::CVSymbol Record = Symbols.readRecord(Offset: Off); |
77 | if (codeview::getSymbolName(Sym: Record) == Name) |
78 | Result.push_back(x: std::make_pair(x&: Off, y: std::move(Record))); |
79 | ++StartRecordIndex; |
80 | } |
81 | return Result; |
82 | } |
83 | |
84 | static Error (const GSIHashHeader *HashHdr) { |
85 | if (HashHdr->VerHdr != GSIHashHeader::HdrVersion) |
86 | return make_error<RawError>( |
87 | Args: raw_error_code::feature_unsupported, |
88 | Args: "Encountered unsupported globals stream version." ); |
89 | |
90 | return Error::success(); |
91 | } |
92 | |
93 | static Error (const GSIHashHeader *&HashHdr, |
94 | BinaryStreamReader &Reader) { |
95 | if (Reader.readObject(Dest&: HashHdr)) |
96 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
97 | Args: "Stream does not contain a GSIHashHeader." ); |
98 | |
99 | if (HashHdr->VerSignature != GSIHashHeader::HdrSignature) |
100 | return make_error<RawError>( |
101 | Args: raw_error_code::feature_unsupported, |
102 | Args: "GSIHashHeader signature (0xffffffff) not found." ); |
103 | |
104 | return Error::success(); |
105 | } |
106 | |
107 | static Error (FixedStreamArray<PSHashRecord> &HashRecords, |
108 | const GSIHashHeader *HashHdr, |
109 | BinaryStreamReader &Reader) { |
110 | if (auto EC = checkHashHdrVersion(HashHdr)) |
111 | return EC; |
112 | |
113 | // HashHdr->HrSize specifies the number of bytes of PSHashRecords we have. |
114 | // Verify that we can read them all. |
115 | if (HashHdr->HrSize % sizeof(PSHashRecord)) |
116 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
117 | Args: "Invalid HR array size." ); |
118 | uint32_t NumHashRecords = HashHdr->HrSize / sizeof(PSHashRecord); |
119 | if (auto EC = Reader.readArray(Array&: HashRecords, NumItems: NumHashRecords)) |
120 | return joinErrors(E1: std::move(EC), |
121 | E2: make_error<RawError>(Args: raw_error_code::corrupt_file, |
122 | Args: "Error reading hash records." )); |
123 | |
124 | return Error::success(); |
125 | } |
126 | |
127 | static Error |
128 | readGSIHashBuckets(FixedStreamArray<support::ulittle32_t> &HashBuckets, |
129 | FixedStreamArray<support::ulittle32_t> &HashBitmap, |
130 | const GSIHashHeader *HashHdr, |
131 | MutableArrayRef<int32_t> BucketMap, |
132 | BinaryStreamReader &Reader) { |
133 | if (auto EC = checkHashHdrVersion(HashHdr)) |
134 | return EC; |
135 | |
136 | // Before the actual hash buckets, there is a bitmap of length determined by |
137 | // IPHR_HASH. |
138 | size_t BitmapSizeInBits = alignTo(Value: IPHR_HASH + 1, Align: 32); |
139 | uint32_t NumBitmapEntries = BitmapSizeInBits / 32; |
140 | if (auto EC = Reader.readArray(Array&: HashBitmap, NumItems: NumBitmapEntries)) |
141 | return joinErrors(E1: std::move(EC), |
142 | E2: make_error<RawError>(Args: raw_error_code::corrupt_file, |
143 | Args: "Could not read a bitmap." )); |
144 | uint32_t CompressedBucketIdx = 0; |
145 | for (uint32_t I = 0; I <= IPHR_HASH; ++I) { |
146 | uint8_t WordIdx = I / 32; |
147 | uint8_t BitIdx = I % 32; |
148 | bool IsSet = HashBitmap[WordIdx] & (1U << BitIdx); |
149 | if (IsSet) { |
150 | BucketMap[I] = CompressedBucketIdx++; |
151 | } else { |
152 | BucketMap[I] = -1; |
153 | } |
154 | } |
155 | |
156 | uint32_t NumBuckets = 0; |
157 | for (uint32_t B : HashBitmap) |
158 | NumBuckets += llvm::popcount(Value: B); |
159 | |
160 | // Hash buckets follow. |
161 | if (auto EC = Reader.readArray(Array&: HashBuckets, NumItems: NumBuckets)) |
162 | return joinErrors(E1: std::move(EC), |
163 | E2: make_error<RawError>(Args: raw_error_code::corrupt_file, |
164 | Args: "Hash buckets corrupted." )); |
165 | |
166 | return Error::success(); |
167 | } |
168 | |
169 | Error GSIHashTable::read(BinaryStreamReader &Reader) { |
170 | if (auto EC = readGSIHashHeader(HashHdr, Reader)) |
171 | return EC; |
172 | if (auto EC = readGSIHashRecords(HashRecords, HashHdr, Reader)) |
173 | return EC; |
174 | if (HashHdr->HrSize > 0) |
175 | if (auto EC = readGSIHashBuckets(HashBuckets, HashBitmap, HashHdr, |
176 | BucketMap, Reader)) |
177 | return EC; |
178 | return Error::success(); |
179 | } |
180 | |