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