16b6b8c4fSAdrian McCarthy //===- TpiStream.cpp - PDB Type Info (TPI) Stream 2 Access ----------------===//
26b6b8c4fSAdrian McCarthy //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66b6b8c4fSAdrian McCarthy //
76b6b8c4fSAdrian McCarthy //===----------------------------------------------------------------------===//
86b6b8c4fSAdrian McCarthy 
96b6b8c4fSAdrian McCarthy #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
10d4136e94SZachary Turner 
116b6b8c4fSAdrian McCarthy #include "llvm/ADT/iterator_range.h"
12d4136e94SZachary Turner #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
13b96181c2SZachary Turner #include "llvm/DebugInfo/CodeView/RecordName.h"
146b6b8c4fSAdrian McCarthy #include "llvm/DebugInfo/CodeView/TypeRecord.h"
15355ffb00SZachary Turner #include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
166b6b8c4fSAdrian McCarthy #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
17cfa1d499SZachary Turner #include "llvm/DebugInfo/PDB/Native/Hash.h"
186b6b8c4fSAdrian McCarthy #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
196b6b8c4fSAdrian McCarthy #include "llvm/DebugInfo/PDB/Native/RawConstants.h"
206b6b8c4fSAdrian McCarthy #include "llvm/DebugInfo/PDB/Native/RawError.h"
216b6b8c4fSAdrian McCarthy #include "llvm/DebugInfo/PDB/Native/RawTypes.h"
226b6b8c4fSAdrian McCarthy #include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
23d9dc2829SZachary Turner #include "llvm/Support/BinaryStreamReader.h"
246b6b8c4fSAdrian McCarthy #include "llvm/Support/Endian.h"
256b6b8c4fSAdrian McCarthy #include "llvm/Support/Error.h"
266b6b8c4fSAdrian McCarthy #include <algorithm>
276b6b8c4fSAdrian McCarthy #include <cstdint>
286b6b8c4fSAdrian McCarthy #include <vector>
296b6b8c4fSAdrian McCarthy 
306b6b8c4fSAdrian McCarthy using namespace llvm;
316b6b8c4fSAdrian McCarthy using namespace llvm::codeview;
326b6b8c4fSAdrian McCarthy using namespace llvm::support;
336b6b8c4fSAdrian McCarthy using namespace llvm::msf;
346b6b8c4fSAdrian McCarthy using namespace llvm::pdb;
356b6b8c4fSAdrian McCarthy 
TpiStream(PDBFile & File,std::unique_ptr<MappedBlockStream> Stream)365b74ff33SZachary Turner TpiStream::TpiStream(PDBFile &File, std::unique_ptr<MappedBlockStream> Stream)
376b6b8c4fSAdrian McCarthy     : Pdb(File), Stream(std::move(Stream)) {}
386b6b8c4fSAdrian McCarthy 
396b6b8c4fSAdrian McCarthy TpiStream::~TpiStream() = default;
406b6b8c4fSAdrian McCarthy 
reload()416b6b8c4fSAdrian McCarthy Error TpiStream::reload() {
42120faca4SZachary Turner   BinaryStreamReader Reader(*Stream);
436b6b8c4fSAdrian McCarthy 
446b6b8c4fSAdrian McCarthy   if (Reader.bytesRemaining() < sizeof(TpiStreamHeader))
456b6b8c4fSAdrian McCarthy     return make_error<RawError>(raw_error_code::corrupt_file,
466b6b8c4fSAdrian McCarthy                                 "TPI Stream does not contain a header.");
476b6b8c4fSAdrian McCarthy 
486b6b8c4fSAdrian McCarthy   if (Reader.readObject(Header))
496b6b8c4fSAdrian McCarthy     return make_error<RawError>(raw_error_code::corrupt_file,
506b6b8c4fSAdrian McCarthy                                 "TPI Stream does not contain a header.");
516b6b8c4fSAdrian McCarthy 
526b6b8c4fSAdrian McCarthy   if (Header->Version != PdbTpiV80)
536b6b8c4fSAdrian McCarthy     return make_error<RawError>(raw_error_code::corrupt_file,
546b6b8c4fSAdrian McCarthy                                 "Unsupported TPI Version.");
556b6b8c4fSAdrian McCarthy 
566b6b8c4fSAdrian McCarthy   if (Header->HeaderSize != sizeof(TpiStreamHeader))
576b6b8c4fSAdrian McCarthy     return make_error<RawError>(raw_error_code::corrupt_file,
586b6b8c4fSAdrian McCarthy                                 "Corrupt TPI Header size.");
596b6b8c4fSAdrian McCarthy 
606b6b8c4fSAdrian McCarthy   if (Header->HashKeySize != sizeof(ulittle32_t))
616b6b8c4fSAdrian McCarthy     return make_error<RawError>(raw_error_code::corrupt_file,
626b6b8c4fSAdrian McCarthy                                 "TPI Stream expected 4 byte hash key size.");
636b6b8c4fSAdrian McCarthy 
646b6b8c4fSAdrian McCarthy   if (Header->NumHashBuckets < MinTpiHashBuckets ||
656b6b8c4fSAdrian McCarthy       Header->NumHashBuckets > MaxTpiHashBuckets)
666b6b8c4fSAdrian McCarthy     return make_error<RawError>(raw_error_code::corrupt_file,
676b6b8c4fSAdrian McCarthy                                 "TPI Stream Invalid number of hash buckets.");
686b6b8c4fSAdrian McCarthy 
696b6b8c4fSAdrian McCarthy   // The actual type records themselves come from this stream
70c2f5b4bfSZachary Turner   if (auto EC =
71c2f5b4bfSZachary Turner           Reader.readSubstream(TypeRecordsSubstream, Header->TypeRecordBytes))
72c2f5b4bfSZachary Turner     return EC;
73c2f5b4bfSZachary Turner 
74c2f5b4bfSZachary Turner   BinaryStreamReader RecordReader(TypeRecordsSubstream.StreamData);
75c2f5b4bfSZachary Turner   if (auto EC =
76c2f5b4bfSZachary Turner           RecordReader.readArray(TypeRecords, TypeRecordsSubstream.size()))
776b6b8c4fSAdrian McCarthy     return EC;
786b6b8c4fSAdrian McCarthy 
796b6b8c4fSAdrian McCarthy   // Hash indices, hash values, etc come from the hash stream.
806b6b8c4fSAdrian McCarthy   if (Header->HashStreamIndex != kInvalidStreamIndex) {
8113f7ddffSNico Weber     auto HS = Pdb.safelyCreateIndexedStream(Header->HashStreamIndex);
8213f7ddffSNico Weber     if (!HS) {
8313f7ddffSNico Weber       consumeError(HS.takeError());
846b6b8c4fSAdrian McCarthy       return make_error<RawError>(raw_error_code::corrupt_file,
856b6b8c4fSAdrian McCarthy                                   "Invalid TPI hash stream index.");
8613f7ddffSNico Weber     }
8713f7ddffSNico Weber     BinaryStreamReader HSR(**HS);
886b6b8c4fSAdrian McCarthy 
896e545ffcSReid Kleckner     // There should be a hash value for every type record, or no hashes at all.
906b6b8c4fSAdrian McCarthy     uint32_t NumHashValues =
916b6b8c4fSAdrian McCarthy         Header->HashValueBuffer.Length / sizeof(ulittle32_t);
92bedc85fbSZachary Turner     if (NumHashValues != getNumTypeRecords() && NumHashValues != 0)
936b6b8c4fSAdrian McCarthy       return make_error<RawError>(
946b6b8c4fSAdrian McCarthy           raw_error_code::corrupt_file,
956b6b8c4fSAdrian McCarthy           "TPI hash count does not match with the number of type records.");
966b6b8c4fSAdrian McCarthy     HSR.setOffset(Header->HashValueBuffer.Off);
976b6b8c4fSAdrian McCarthy     if (auto EC = HSR.readArray(HashValues, NumHashValues))
986b6b8c4fSAdrian McCarthy       return EC;
996b6b8c4fSAdrian McCarthy 
1006b6b8c4fSAdrian McCarthy     HSR.setOffset(Header->IndexOffsetBuffer.Off);
1016b6b8c4fSAdrian McCarthy     uint32_t NumTypeIndexOffsets =
1026b6b8c4fSAdrian McCarthy         Header->IndexOffsetBuffer.Length / sizeof(TypeIndexOffset);
1036b6b8c4fSAdrian McCarthy     if (auto EC = HSR.readArray(TypeIndexOffsets, NumTypeIndexOffsets))
1046b6b8c4fSAdrian McCarthy       return EC;
1056b6b8c4fSAdrian McCarthy 
1066b6b8c4fSAdrian McCarthy     if (Header->HashAdjBuffer.Length > 0) {
1076b6b8c4fSAdrian McCarthy       HSR.setOffset(Header->HashAdjBuffer.Off);
1086b6b8c4fSAdrian McCarthy       if (auto EC = HashAdjusters.load(HSR))
1096b6b8c4fSAdrian McCarthy         return EC;
1106b6b8c4fSAdrian McCarthy     }
1116b6b8c4fSAdrian McCarthy 
11213f7ddffSNico Weber     HashStream = std::move(*HS);
1136b6b8c4fSAdrian McCarthy   }
1146b6b8c4fSAdrian McCarthy 
115*0eaee545SJonas Devlieghere   Types = std::make_unique<LazyRandomTypeCollection>(
116d4136e94SZachary Turner       TypeRecords, getNumTypeRecords(), getTypeIndexOffsets());
1176b6b8c4fSAdrian McCarthy   return Error::success();
1186b6b8c4fSAdrian McCarthy }
1196b6b8c4fSAdrian McCarthy 
getTpiVersion() const1206b6b8c4fSAdrian McCarthy PdbRaw_TpiVer TpiStream::getTpiVersion() const {
1216b6b8c4fSAdrian McCarthy   uint32_t Value = Header->Version;
1226b6b8c4fSAdrian McCarthy   return static_cast<PdbRaw_TpiVer>(Value);
1236b6b8c4fSAdrian McCarthy }
1246b6b8c4fSAdrian McCarthy 
TypeIndexBegin() const1256b6b8c4fSAdrian McCarthy uint32_t TpiStream::TypeIndexBegin() const { return Header->TypeIndexBegin; }
1266b6b8c4fSAdrian McCarthy 
TypeIndexEnd() const1276b6b8c4fSAdrian McCarthy uint32_t TpiStream::TypeIndexEnd() const { return Header->TypeIndexEnd; }
1286b6b8c4fSAdrian McCarthy 
getNumTypeRecords() const129bedc85fbSZachary Turner uint32_t TpiStream::getNumTypeRecords() const {
1306b6b8c4fSAdrian McCarthy   return TypeIndexEnd() - TypeIndexBegin();
1316b6b8c4fSAdrian McCarthy }
1326b6b8c4fSAdrian McCarthy 
getTypeHashStreamIndex() const1336b6b8c4fSAdrian McCarthy uint16_t TpiStream::getTypeHashStreamIndex() const {
1346b6b8c4fSAdrian McCarthy   return Header->HashStreamIndex;
1356b6b8c4fSAdrian McCarthy }
1366b6b8c4fSAdrian McCarthy 
getTypeHashStreamAuxIndex() const1376b6b8c4fSAdrian McCarthy uint16_t TpiStream::getTypeHashStreamAuxIndex() const {
1386b6b8c4fSAdrian McCarthy   return Header->HashAuxStreamIndex;
1396b6b8c4fSAdrian McCarthy }
1406b6b8c4fSAdrian McCarthy 
getNumHashBuckets() const141bedc85fbSZachary Turner uint32_t TpiStream::getNumHashBuckets() const { return Header->NumHashBuckets; }
getHashKeySize() const1426b6b8c4fSAdrian McCarthy uint32_t TpiStream::getHashKeySize() const { return Header->HashKeySize; }
1436b6b8c4fSAdrian McCarthy 
buildHashMap()144cfa1d499SZachary Turner void TpiStream::buildHashMap() {
145cfa1d499SZachary Turner   if (!HashMap.empty())
146cfa1d499SZachary Turner     return;
147cfa1d499SZachary Turner   if (HashValues.empty())
148cfa1d499SZachary Turner     return;
149cfa1d499SZachary Turner 
150cfa1d499SZachary Turner   HashMap.resize(Header->NumHashBuckets);
151cfa1d499SZachary Turner 
152cfa1d499SZachary Turner   TypeIndex TIB{Header->TypeIndexBegin};
153cfa1d499SZachary Turner   TypeIndex TIE{Header->TypeIndexEnd};
154cfa1d499SZachary Turner   while (TIB < TIE) {
155cfa1d499SZachary Turner     uint32_t HV = HashValues[TIB.toArrayIndex()];
156cfa1d499SZachary Turner     HashMap[HV].push_back(TIB++);
157cfa1d499SZachary Turner   }
158cfa1d499SZachary Turner }
159cfa1d499SZachary Turner 
findRecordsByName(StringRef Name) const160b96181c2SZachary Turner std::vector<TypeIndex> TpiStream::findRecordsByName(StringRef Name) const {
1611e0cce79SZachary Turner   if (!supportsTypeLookup())
1621e0cce79SZachary Turner     const_cast<TpiStream*>(this)->buildHashMap();
1631e0cce79SZachary Turner 
164b96181c2SZachary Turner   uint32_t Bucket = hashStringV1(Name) % Header->NumHashBuckets;
165b96181c2SZachary Turner   if (Bucket > HashMap.size())
166b96181c2SZachary Turner     return {};
167b96181c2SZachary Turner 
168b96181c2SZachary Turner   std::vector<TypeIndex> Result;
169b96181c2SZachary Turner   for (TypeIndex TI : HashMap[Bucket]) {
170b96181c2SZachary Turner     std::string ThisName = computeTypeName(*Types, TI);
171b96181c2SZachary Turner     if (ThisName == Name)
172b96181c2SZachary Turner       Result.push_back(TI);
173b96181c2SZachary Turner   }
174b96181c2SZachary Turner   return Result;
175b96181c2SZachary Turner }
176b96181c2SZachary Turner 
supportsTypeLookup() const177cfa1d499SZachary Turner bool TpiStream::supportsTypeLookup() const { return !HashMap.empty(); }
178cfa1d499SZachary Turner 
179cfa1d499SZachary Turner Expected<TypeIndex>
findFullDeclForForwardRef(TypeIndex ForwardRefTI) const180cfa1d499SZachary Turner TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const {
1811e0cce79SZachary Turner   if (!supportsTypeLookup())
1821e0cce79SZachary Turner     const_cast<TpiStream*>(this)->buildHashMap();
1831e0cce79SZachary Turner 
184cfa1d499SZachary Turner   CVType F = Types->getType(ForwardRefTI);
185cfa1d499SZachary Turner   if (!isUdtForwardRef(F))
186cfa1d499SZachary Turner     return ForwardRefTI;
187cfa1d499SZachary Turner 
188cfa1d499SZachary Turner   Expected<TagRecordHash> ForwardTRH = hashTagRecord(F);
189cfa1d499SZachary Turner   if (!ForwardTRH)
190cfa1d499SZachary Turner     return ForwardTRH.takeError();
191cfa1d499SZachary Turner 
192cfa1d499SZachary Turner   uint32_t BucketIdx = ForwardTRH->FullRecordHash % Header->NumHashBuckets;
193cfa1d499SZachary Turner 
194cfa1d499SZachary Turner   for (TypeIndex TI : HashMap[BucketIdx]) {
195cfa1d499SZachary Turner     CVType CVT = Types->getType(TI);
196cfa1d499SZachary Turner     if (CVT.kind() != F.kind())
197cfa1d499SZachary Turner       continue;
198cfa1d499SZachary Turner 
199cfa1d499SZachary Turner     Expected<TagRecordHash> FullTRH = hashTagRecord(CVT);
200cfa1d499SZachary Turner     if (!FullTRH)
201cfa1d499SZachary Turner       return FullTRH.takeError();
202cfa1d499SZachary Turner     if (ForwardTRH->FullRecordHash != FullTRH->FullRecordHash)
203cfa1d499SZachary Turner       continue;
204cfa1d499SZachary Turner     TagRecord &ForwardTR = ForwardTRH->getRecord();
205cfa1d499SZachary Turner     TagRecord &FullTR = FullTRH->getRecord();
206cfa1d499SZachary Turner 
207cfa1d499SZachary Turner     if (!ForwardTR.hasUniqueName()) {
208cfa1d499SZachary Turner       if (ForwardTR.getName() == FullTR.getName())
209cfa1d499SZachary Turner         return TI;
210cfa1d499SZachary Turner       continue;
211cfa1d499SZachary Turner     }
212cfa1d499SZachary Turner 
213cfa1d499SZachary Turner     if (!FullTR.hasUniqueName())
214cfa1d499SZachary Turner       continue;
215cfa1d499SZachary Turner     if (ForwardTR.getUniqueName() == FullTR.getUniqueName())
216cfa1d499SZachary Turner       return TI;
217cfa1d499SZachary Turner   }
218cfa1d499SZachary Turner   return ForwardRefTI;
219cfa1d499SZachary Turner }
220cfa1d499SZachary Turner 
getType(codeview::TypeIndex Index)221b96181c2SZachary Turner codeview::CVType TpiStream::getType(codeview::TypeIndex Index) {
22203a24052SZachary Turner   assert(!Index.isSimple());
223b96181c2SZachary Turner   return Types->getType(Index);
224b96181c2SZachary Turner }
225b96181c2SZachary Turner 
getTypeRecordsSubstream() const226c2f5b4bfSZachary Turner BinarySubstreamRef TpiStream::getTypeRecordsSubstream() const {
227c2f5b4bfSZachary Turner   return TypeRecordsSubstream;
228c2f5b4bfSZachary Turner }
229c2f5b4bfSZachary Turner 
getHashValues() const2306b6b8c4fSAdrian McCarthy FixedStreamArray<support::ulittle32_t> TpiStream::getHashValues() const {
2316b6b8c4fSAdrian McCarthy   return HashValues;
2326b6b8c4fSAdrian McCarthy }
2336b6b8c4fSAdrian McCarthy 
getTypeIndexOffsets() const2346b6b8c4fSAdrian McCarthy FixedStreamArray<TypeIndexOffset> TpiStream::getTypeIndexOffsets() const {
2356b6b8c4fSAdrian McCarthy   return TypeIndexOffsets;
2366b6b8c4fSAdrian McCarthy }
2376b6b8c4fSAdrian McCarthy 
getHashAdjusters()238ebf03f6cSZachary Turner HashTable<support::ulittle32_t> &TpiStream::getHashAdjusters() {
239ebf03f6cSZachary Turner   return HashAdjusters;
240ebf03f6cSZachary Turner }
2416b6b8c4fSAdrian McCarthy 
types(bool * HadError) const2427b327d05SZachary Turner CVTypeRange TpiStream::types(bool *HadError) const {
2436b6b8c4fSAdrian McCarthy   return make_range(TypeRecords.begin(HadError), TypeRecords.end());
2446b6b8c4fSAdrian McCarthy }
2456b6b8c4fSAdrian McCarthy 
commit()2466b6b8c4fSAdrian McCarthy Error TpiStream::commit() { return Error::success(); }
247