1 | //===- TpiStream.cpp - PDB Type Info (TPI) Stream 2 Access ----------------===// |
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 | #include "llvm/DebugInfo/PDB/Native/TpiStream.h" |
10 | |
11 | #include "llvm/ADT/iterator_range.h" |
12 | #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" |
13 | #include "llvm/DebugInfo/CodeView/RecordName.h" |
14 | #include "llvm/DebugInfo/CodeView/TypeRecord.h" |
15 | #include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" |
16 | #include "llvm/DebugInfo/MSF/MappedBlockStream.h" |
17 | #include "llvm/DebugInfo/PDB/Native/Hash.h" |
18 | #include "llvm/DebugInfo/PDB/Native/PDBFile.h" |
19 | #include "llvm/DebugInfo/PDB/Native/RawConstants.h" |
20 | #include "llvm/DebugInfo/PDB/Native/RawError.h" |
21 | #include "llvm/DebugInfo/PDB/Native/RawTypes.h" |
22 | #include "llvm/DebugInfo/PDB/Native/TpiHashing.h" |
23 | #include "llvm/Support/BinaryStreamReader.h" |
24 | #include "llvm/Support/Endian.h" |
25 | #include "llvm/Support/Error.h" |
26 | #include <cstdint> |
27 | #include <vector> |
28 | |
29 | using namespace llvm; |
30 | using namespace llvm::codeview; |
31 | using namespace llvm::support; |
32 | using namespace llvm::msf; |
33 | using namespace llvm::pdb; |
34 | |
35 | TpiStream::TpiStream(PDBFile &File, std::unique_ptr<MappedBlockStream> Stream) |
36 | : Pdb(File), Stream(std::move(Stream)) {} |
37 | |
38 | TpiStream::~TpiStream() = default; |
39 | |
40 | Error TpiStream::reload() { |
41 | BinaryStreamReader Reader(*Stream); |
42 | |
43 | if (Reader.bytesRemaining() < sizeof(TpiStreamHeader)) |
44 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
45 | Args: "TPI Stream does not contain a header." ); |
46 | |
47 | if (Reader.readObject(Dest&: Header)) |
48 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
49 | Args: "TPI Stream does not contain a header." ); |
50 | |
51 | if (Header->Version != PdbTpiV80) |
52 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
53 | Args: "Unsupported TPI Version." ); |
54 | |
55 | if (Header->HeaderSize != sizeof(TpiStreamHeader)) |
56 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
57 | Args: "Corrupt TPI Header size." ); |
58 | |
59 | if (Header->HashKeySize != sizeof(ulittle32_t)) |
60 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
61 | Args: "TPI Stream expected 4 byte hash key size." ); |
62 | |
63 | if (Header->NumHashBuckets < MinTpiHashBuckets || |
64 | Header->NumHashBuckets > MaxTpiHashBuckets) |
65 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
66 | Args: "TPI Stream Invalid number of hash buckets." ); |
67 | |
68 | // The actual type records themselves come from this stream |
69 | if (auto EC = |
70 | Reader.readSubstream(Ref&: TypeRecordsSubstream, Length: Header->TypeRecordBytes)) |
71 | return EC; |
72 | |
73 | BinaryStreamReader RecordReader(TypeRecordsSubstream.StreamData); |
74 | if (auto EC = |
75 | RecordReader.readArray(Array&: TypeRecords, Size: TypeRecordsSubstream.size())) |
76 | return EC; |
77 | |
78 | // Hash indices, hash values, etc come from the hash stream. |
79 | if (Header->HashStreamIndex != kInvalidStreamIndex) { |
80 | auto HS = Pdb.safelyCreateIndexedStream(StreamIndex: Header->HashStreamIndex); |
81 | if (!HS) { |
82 | consumeError(Err: HS.takeError()); |
83 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
84 | Args: "Invalid TPI hash stream index." ); |
85 | } |
86 | BinaryStreamReader HSR(**HS); |
87 | |
88 | // There should be a hash value for every type record, or no hashes at all. |
89 | uint32_t NumHashValues = |
90 | Header->HashValueBuffer.Length / sizeof(ulittle32_t); |
91 | if (NumHashValues != getNumTypeRecords() && NumHashValues != 0) |
92 | return make_error<RawError>( |
93 | Args: raw_error_code::corrupt_file, |
94 | Args: "TPI hash count does not match with the number of type records." ); |
95 | HSR.setOffset(Header->HashValueBuffer.Off); |
96 | if (auto EC = HSR.readArray(Array&: HashValues, NumItems: NumHashValues)) |
97 | return EC; |
98 | |
99 | HSR.setOffset(Header->IndexOffsetBuffer.Off); |
100 | uint32_t NumTypeIndexOffsets = |
101 | Header->IndexOffsetBuffer.Length / sizeof(TypeIndexOffset); |
102 | if (auto EC = HSR.readArray(Array&: TypeIndexOffsets, NumItems: NumTypeIndexOffsets)) |
103 | return EC; |
104 | |
105 | if (Header->HashAdjBuffer.Length > 0) { |
106 | HSR.setOffset(Header->HashAdjBuffer.Off); |
107 | if (auto EC = HashAdjusters.load(Stream&: HSR)) |
108 | return EC; |
109 | } |
110 | |
111 | HashStream = std::move(*HS); |
112 | } |
113 | |
114 | Types = std::make_unique<LazyRandomTypeCollection>( |
115 | args&: TypeRecords, args: getNumTypeRecords(), args: getTypeIndexOffsets()); |
116 | return Error::success(); |
117 | } |
118 | |
119 | PdbRaw_TpiVer TpiStream::getTpiVersion() const { |
120 | uint32_t Value = Header->Version; |
121 | return static_cast<PdbRaw_TpiVer>(Value); |
122 | } |
123 | |
124 | uint32_t TpiStream::TypeIndexBegin() const { return Header->TypeIndexBegin; } |
125 | |
126 | uint32_t TpiStream::TypeIndexEnd() const { return Header->TypeIndexEnd; } |
127 | |
128 | uint32_t TpiStream::getNumTypeRecords() const { |
129 | return TypeIndexEnd() - TypeIndexBegin(); |
130 | } |
131 | |
132 | uint16_t TpiStream::getTypeHashStreamIndex() const { |
133 | return Header->HashStreamIndex; |
134 | } |
135 | |
136 | uint16_t TpiStream::getTypeHashStreamAuxIndex() const { |
137 | return Header->HashAuxStreamIndex; |
138 | } |
139 | |
140 | uint32_t TpiStream::getNumHashBuckets() const { return Header->NumHashBuckets; } |
141 | uint32_t TpiStream::getHashKeySize() const { return Header->HashKeySize; } |
142 | |
143 | void TpiStream::buildHashMap() { |
144 | if (!HashMap.empty()) |
145 | return; |
146 | if (HashValues.empty()) |
147 | return; |
148 | |
149 | HashMap.resize(new_size: Header->NumHashBuckets); |
150 | |
151 | TypeIndex TIB{Header->TypeIndexBegin}; |
152 | TypeIndex TIE{Header->TypeIndexEnd}; |
153 | while (TIB < TIE) { |
154 | uint32_t HV = HashValues[TIB.toArrayIndex()]; |
155 | HashMap[HV].push_back(x: TIB++); |
156 | } |
157 | } |
158 | |
159 | std::vector<TypeIndex> TpiStream::findRecordsByName(StringRef Name) const { |
160 | if (!supportsTypeLookup()) |
161 | const_cast<TpiStream*>(this)->buildHashMap(); |
162 | |
163 | uint32_t Bucket = hashStringV1(Str: Name) % Header->NumHashBuckets; |
164 | if (Bucket > HashMap.size()) |
165 | return {}; |
166 | |
167 | std::vector<TypeIndex> Result; |
168 | for (TypeIndex TI : HashMap[Bucket]) { |
169 | std::string ThisName = computeTypeName(Types&: *Types, Index: TI); |
170 | if (ThisName == Name) |
171 | Result.push_back(x: TI); |
172 | } |
173 | return Result; |
174 | } |
175 | |
176 | bool TpiStream::supportsTypeLookup() const { return !HashMap.empty(); } |
177 | |
178 | Expected<TypeIndex> |
179 | TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const { |
180 | if (!supportsTypeLookup()) |
181 | const_cast<TpiStream*>(this)->buildHashMap(); |
182 | |
183 | CVType F = Types->getType(Index: ForwardRefTI); |
184 | if (!isUdtForwardRef(CVT: F)) |
185 | return ForwardRefTI; |
186 | |
187 | Expected<TagRecordHash> ForwardTRH = hashTagRecord(Type: F); |
188 | if (!ForwardTRH) |
189 | return ForwardTRH.takeError(); |
190 | |
191 | uint32_t BucketIdx = ForwardTRH->FullRecordHash % Header->NumHashBuckets; |
192 | |
193 | for (TypeIndex TI : HashMap[BucketIdx]) { |
194 | CVType CVT = Types->getType(Index: TI); |
195 | if (CVT.kind() != F.kind()) |
196 | continue; |
197 | |
198 | Expected<TagRecordHash> FullTRH = hashTagRecord(Type: CVT); |
199 | if (!FullTRH) |
200 | return FullTRH.takeError(); |
201 | if (ForwardTRH->FullRecordHash != FullTRH->FullRecordHash) |
202 | continue; |
203 | TagRecord &ForwardTR = ForwardTRH->getRecord(); |
204 | TagRecord &FullTR = FullTRH->getRecord(); |
205 | |
206 | if (!ForwardTR.hasUniqueName()) { |
207 | if (ForwardTR.getName() == FullTR.getName()) |
208 | return TI; |
209 | continue; |
210 | } |
211 | |
212 | if (!FullTR.hasUniqueName()) |
213 | continue; |
214 | if (ForwardTR.getUniqueName() == FullTR.getUniqueName()) |
215 | return TI; |
216 | } |
217 | return ForwardRefTI; |
218 | } |
219 | |
220 | codeview::CVType TpiStream::getType(codeview::TypeIndex Index) { |
221 | assert(!Index.isSimple()); |
222 | return Types->getType(Index); |
223 | } |
224 | |
225 | BinarySubstreamRef TpiStream::getTypeRecordsSubstream() const { |
226 | return TypeRecordsSubstream; |
227 | } |
228 | |
229 | FixedStreamArray<support::ulittle32_t> TpiStream::getHashValues() const { |
230 | return HashValues; |
231 | } |
232 | |
233 | FixedStreamArray<TypeIndexOffset> TpiStream::getTypeIndexOffsets() const { |
234 | return TypeIndexOffsets; |
235 | } |
236 | |
237 | HashTable<support::ulittle32_t> &TpiStream::getHashAdjusters() { |
238 | return HashAdjusters; |
239 | } |
240 | |
241 | CVTypeRange TpiStream::types(bool *HadError) const { |
242 | return make_range(x: TypeRecords.begin(HadError), y: TypeRecords.end()); |
243 | } |
244 | |
245 | Error TpiStream::commit() { return Error::success(); } |
246 | |