10b57cec5SDimitry Andric //===- TpiStream.cpp - PDB Type Info (TPI) Stream 2 Access ----------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
90b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
100b57cec5SDimitry Andric
110b57cec5SDimitry Andric #include "llvm/ADT/iterator_range.h"
120b57cec5SDimitry Andric #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
130b57cec5SDimitry Andric #include "llvm/DebugInfo/CodeView/RecordName.h"
140b57cec5SDimitry Andric #include "llvm/DebugInfo/CodeView/TypeRecord.h"
150b57cec5SDimitry Andric #include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
160b57cec5SDimitry Andric #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
170b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/Hash.h"
180b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
190b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/RawConstants.h"
200b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/RawError.h"
210b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/RawTypes.h"
220b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
230b57cec5SDimitry Andric #include "llvm/Support/BinaryStreamReader.h"
240b57cec5SDimitry Andric #include "llvm/Support/Endian.h"
250b57cec5SDimitry Andric #include "llvm/Support/Error.h"
260b57cec5SDimitry Andric #include <algorithm>
270b57cec5SDimitry Andric #include <cstdint>
280b57cec5SDimitry Andric #include <vector>
290b57cec5SDimitry Andric
300b57cec5SDimitry Andric using namespace llvm;
310b57cec5SDimitry Andric using namespace llvm::codeview;
320b57cec5SDimitry Andric using namespace llvm::support;
330b57cec5SDimitry Andric using namespace llvm::msf;
340b57cec5SDimitry Andric using namespace llvm::pdb;
350b57cec5SDimitry Andric
TpiStream(PDBFile & File,std::unique_ptr<MappedBlockStream> Stream)360b57cec5SDimitry Andric TpiStream::TpiStream(PDBFile &File, std::unique_ptr<MappedBlockStream> Stream)
370b57cec5SDimitry Andric : Pdb(File), Stream(std::move(Stream)) {}
380b57cec5SDimitry Andric
390b57cec5SDimitry Andric TpiStream::~TpiStream() = default;
400b57cec5SDimitry Andric
reload()410b57cec5SDimitry Andric Error TpiStream::reload() {
420b57cec5SDimitry Andric BinaryStreamReader Reader(*Stream);
430b57cec5SDimitry Andric
440b57cec5SDimitry Andric if (Reader.bytesRemaining() < sizeof(TpiStreamHeader))
450b57cec5SDimitry Andric return make_error<RawError>(raw_error_code::corrupt_file,
460b57cec5SDimitry Andric "TPI Stream does not contain a header.");
470b57cec5SDimitry Andric
480b57cec5SDimitry Andric if (Reader.readObject(Header))
490b57cec5SDimitry Andric return make_error<RawError>(raw_error_code::corrupt_file,
500b57cec5SDimitry Andric "TPI Stream does not contain a header.");
510b57cec5SDimitry Andric
520b57cec5SDimitry Andric if (Header->Version != PdbTpiV80)
530b57cec5SDimitry Andric return make_error<RawError>(raw_error_code::corrupt_file,
540b57cec5SDimitry Andric "Unsupported TPI Version.");
550b57cec5SDimitry Andric
560b57cec5SDimitry Andric if (Header->HeaderSize != sizeof(TpiStreamHeader))
570b57cec5SDimitry Andric return make_error<RawError>(raw_error_code::corrupt_file,
580b57cec5SDimitry Andric "Corrupt TPI Header size.");
590b57cec5SDimitry Andric
600b57cec5SDimitry Andric if (Header->HashKeySize != sizeof(ulittle32_t))
610b57cec5SDimitry Andric return make_error<RawError>(raw_error_code::corrupt_file,
620b57cec5SDimitry Andric "TPI Stream expected 4 byte hash key size.");
630b57cec5SDimitry Andric
640b57cec5SDimitry Andric if (Header->NumHashBuckets < MinTpiHashBuckets ||
650b57cec5SDimitry Andric Header->NumHashBuckets > MaxTpiHashBuckets)
660b57cec5SDimitry Andric return make_error<RawError>(raw_error_code::corrupt_file,
670b57cec5SDimitry Andric "TPI Stream Invalid number of hash buckets.");
680b57cec5SDimitry Andric
690b57cec5SDimitry Andric // The actual type records themselves come from this stream
700b57cec5SDimitry Andric if (auto EC =
710b57cec5SDimitry Andric Reader.readSubstream(TypeRecordsSubstream, Header->TypeRecordBytes))
720b57cec5SDimitry Andric return EC;
730b57cec5SDimitry Andric
740b57cec5SDimitry Andric BinaryStreamReader RecordReader(TypeRecordsSubstream.StreamData);
750b57cec5SDimitry Andric if (auto EC =
760b57cec5SDimitry Andric RecordReader.readArray(TypeRecords, TypeRecordsSubstream.size()))
770b57cec5SDimitry Andric return EC;
780b57cec5SDimitry Andric
790b57cec5SDimitry Andric // Hash indices, hash values, etc come from the hash stream.
800b57cec5SDimitry Andric if (Header->HashStreamIndex != kInvalidStreamIndex) {
810b57cec5SDimitry Andric auto HS = Pdb.safelyCreateIndexedStream(Header->HashStreamIndex);
820b57cec5SDimitry Andric if (!HS) {
830b57cec5SDimitry Andric consumeError(HS.takeError());
840b57cec5SDimitry Andric return make_error<RawError>(raw_error_code::corrupt_file,
850b57cec5SDimitry Andric "Invalid TPI hash stream index.");
860b57cec5SDimitry Andric }
870b57cec5SDimitry Andric BinaryStreamReader HSR(**HS);
880b57cec5SDimitry Andric
890b57cec5SDimitry Andric // There should be a hash value for every type record, or no hashes at all.
900b57cec5SDimitry Andric uint32_t NumHashValues =
910b57cec5SDimitry Andric Header->HashValueBuffer.Length / sizeof(ulittle32_t);
920b57cec5SDimitry Andric if (NumHashValues != getNumTypeRecords() && NumHashValues != 0)
930b57cec5SDimitry Andric return make_error<RawError>(
940b57cec5SDimitry Andric raw_error_code::corrupt_file,
950b57cec5SDimitry Andric "TPI hash count does not match with the number of type records.");
960b57cec5SDimitry Andric HSR.setOffset(Header->HashValueBuffer.Off);
970b57cec5SDimitry Andric if (auto EC = HSR.readArray(HashValues, NumHashValues))
980b57cec5SDimitry Andric return EC;
990b57cec5SDimitry Andric
1000b57cec5SDimitry Andric HSR.setOffset(Header->IndexOffsetBuffer.Off);
1010b57cec5SDimitry Andric uint32_t NumTypeIndexOffsets =
1020b57cec5SDimitry Andric Header->IndexOffsetBuffer.Length / sizeof(TypeIndexOffset);
1030b57cec5SDimitry Andric if (auto EC = HSR.readArray(TypeIndexOffsets, NumTypeIndexOffsets))
1040b57cec5SDimitry Andric return EC;
1050b57cec5SDimitry Andric
1060b57cec5SDimitry Andric if (Header->HashAdjBuffer.Length > 0) {
1070b57cec5SDimitry Andric HSR.setOffset(Header->HashAdjBuffer.Off);
1080b57cec5SDimitry Andric if (auto EC = HashAdjusters.load(HSR))
1090b57cec5SDimitry Andric return EC;
1100b57cec5SDimitry Andric }
1110b57cec5SDimitry Andric
1120b57cec5SDimitry Andric HashStream = std::move(*HS);
1130b57cec5SDimitry Andric }
1140b57cec5SDimitry Andric
115*8bcb0991SDimitry Andric Types = std::make_unique<LazyRandomTypeCollection>(
1160b57cec5SDimitry Andric TypeRecords, getNumTypeRecords(), getTypeIndexOffsets());
1170b57cec5SDimitry Andric return Error::success();
1180b57cec5SDimitry Andric }
1190b57cec5SDimitry Andric
getTpiVersion() const1200b57cec5SDimitry Andric PdbRaw_TpiVer TpiStream::getTpiVersion() const {
1210b57cec5SDimitry Andric uint32_t Value = Header->Version;
1220b57cec5SDimitry Andric return static_cast<PdbRaw_TpiVer>(Value);
1230b57cec5SDimitry Andric }
1240b57cec5SDimitry Andric
TypeIndexBegin() const1250b57cec5SDimitry Andric uint32_t TpiStream::TypeIndexBegin() const { return Header->TypeIndexBegin; }
1260b57cec5SDimitry Andric
TypeIndexEnd() const1270b57cec5SDimitry Andric uint32_t TpiStream::TypeIndexEnd() const { return Header->TypeIndexEnd; }
1280b57cec5SDimitry Andric
getNumTypeRecords() const1290b57cec5SDimitry Andric uint32_t TpiStream::getNumTypeRecords() const {
1300b57cec5SDimitry Andric return TypeIndexEnd() - TypeIndexBegin();
1310b57cec5SDimitry Andric }
1320b57cec5SDimitry Andric
getTypeHashStreamIndex() const1330b57cec5SDimitry Andric uint16_t TpiStream::getTypeHashStreamIndex() const {
1340b57cec5SDimitry Andric return Header->HashStreamIndex;
1350b57cec5SDimitry Andric }
1360b57cec5SDimitry Andric
getTypeHashStreamAuxIndex() const1370b57cec5SDimitry Andric uint16_t TpiStream::getTypeHashStreamAuxIndex() const {
1380b57cec5SDimitry Andric return Header->HashAuxStreamIndex;
1390b57cec5SDimitry Andric }
1400b57cec5SDimitry Andric
getNumHashBuckets() const1410b57cec5SDimitry Andric uint32_t TpiStream::getNumHashBuckets() const { return Header->NumHashBuckets; }
getHashKeySize() const1420b57cec5SDimitry Andric uint32_t TpiStream::getHashKeySize() const { return Header->HashKeySize; }
1430b57cec5SDimitry Andric
buildHashMap()1440b57cec5SDimitry Andric void TpiStream::buildHashMap() {
1450b57cec5SDimitry Andric if (!HashMap.empty())
1460b57cec5SDimitry Andric return;
1470b57cec5SDimitry Andric if (HashValues.empty())
1480b57cec5SDimitry Andric return;
1490b57cec5SDimitry Andric
1500b57cec5SDimitry Andric HashMap.resize(Header->NumHashBuckets);
1510b57cec5SDimitry Andric
1520b57cec5SDimitry Andric TypeIndex TIB{Header->TypeIndexBegin};
1530b57cec5SDimitry Andric TypeIndex TIE{Header->TypeIndexEnd};
1540b57cec5SDimitry Andric while (TIB < TIE) {
1550b57cec5SDimitry Andric uint32_t HV = HashValues[TIB.toArrayIndex()];
1560b57cec5SDimitry Andric HashMap[HV].push_back(TIB++);
1570b57cec5SDimitry Andric }
1580b57cec5SDimitry Andric }
1590b57cec5SDimitry Andric
findRecordsByName(StringRef Name) const1600b57cec5SDimitry Andric std::vector<TypeIndex> TpiStream::findRecordsByName(StringRef Name) const {
1610b57cec5SDimitry Andric if (!supportsTypeLookup())
1620b57cec5SDimitry Andric const_cast<TpiStream*>(this)->buildHashMap();
1630b57cec5SDimitry Andric
1640b57cec5SDimitry Andric uint32_t Bucket = hashStringV1(Name) % Header->NumHashBuckets;
1650b57cec5SDimitry Andric if (Bucket > HashMap.size())
1660b57cec5SDimitry Andric return {};
1670b57cec5SDimitry Andric
1680b57cec5SDimitry Andric std::vector<TypeIndex> Result;
1690b57cec5SDimitry Andric for (TypeIndex TI : HashMap[Bucket]) {
1700b57cec5SDimitry Andric std::string ThisName = computeTypeName(*Types, TI);
1710b57cec5SDimitry Andric if (ThisName == Name)
1720b57cec5SDimitry Andric Result.push_back(TI);
1730b57cec5SDimitry Andric }
1740b57cec5SDimitry Andric return Result;
1750b57cec5SDimitry Andric }
1760b57cec5SDimitry Andric
supportsTypeLookup() const1770b57cec5SDimitry Andric bool TpiStream::supportsTypeLookup() const { return !HashMap.empty(); }
1780b57cec5SDimitry Andric
1790b57cec5SDimitry Andric Expected<TypeIndex>
findFullDeclForForwardRef(TypeIndex ForwardRefTI) const1800b57cec5SDimitry Andric TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const {
1810b57cec5SDimitry Andric if (!supportsTypeLookup())
1820b57cec5SDimitry Andric const_cast<TpiStream*>(this)->buildHashMap();
1830b57cec5SDimitry Andric
1840b57cec5SDimitry Andric CVType F = Types->getType(ForwardRefTI);
1850b57cec5SDimitry Andric if (!isUdtForwardRef(F))
1860b57cec5SDimitry Andric return ForwardRefTI;
1870b57cec5SDimitry Andric
1880b57cec5SDimitry Andric Expected<TagRecordHash> ForwardTRH = hashTagRecord(F);
1890b57cec5SDimitry Andric if (!ForwardTRH)
1900b57cec5SDimitry Andric return ForwardTRH.takeError();
1910b57cec5SDimitry Andric
1920b57cec5SDimitry Andric uint32_t BucketIdx = ForwardTRH->FullRecordHash % Header->NumHashBuckets;
1930b57cec5SDimitry Andric
1940b57cec5SDimitry Andric for (TypeIndex TI : HashMap[BucketIdx]) {
1950b57cec5SDimitry Andric CVType CVT = Types->getType(TI);
1960b57cec5SDimitry Andric if (CVT.kind() != F.kind())
1970b57cec5SDimitry Andric continue;
1980b57cec5SDimitry Andric
1990b57cec5SDimitry Andric Expected<TagRecordHash> FullTRH = hashTagRecord(CVT);
2000b57cec5SDimitry Andric if (!FullTRH)
2010b57cec5SDimitry Andric return FullTRH.takeError();
2020b57cec5SDimitry Andric if (ForwardTRH->FullRecordHash != FullTRH->FullRecordHash)
2030b57cec5SDimitry Andric continue;
2040b57cec5SDimitry Andric TagRecord &ForwardTR = ForwardTRH->getRecord();
2050b57cec5SDimitry Andric TagRecord &FullTR = FullTRH->getRecord();
2060b57cec5SDimitry Andric
2070b57cec5SDimitry Andric if (!ForwardTR.hasUniqueName()) {
2080b57cec5SDimitry Andric if (ForwardTR.getName() == FullTR.getName())
2090b57cec5SDimitry Andric return TI;
2100b57cec5SDimitry Andric continue;
2110b57cec5SDimitry Andric }
2120b57cec5SDimitry Andric
2130b57cec5SDimitry Andric if (!FullTR.hasUniqueName())
2140b57cec5SDimitry Andric continue;
2150b57cec5SDimitry Andric if (ForwardTR.getUniqueName() == FullTR.getUniqueName())
2160b57cec5SDimitry Andric return TI;
2170b57cec5SDimitry Andric }
2180b57cec5SDimitry Andric return ForwardRefTI;
2190b57cec5SDimitry Andric }
2200b57cec5SDimitry Andric
getType(codeview::TypeIndex Index)2210b57cec5SDimitry Andric codeview::CVType TpiStream::getType(codeview::TypeIndex Index) {
2220b57cec5SDimitry Andric assert(!Index.isSimple());
2230b57cec5SDimitry Andric return Types->getType(Index);
2240b57cec5SDimitry Andric }
2250b57cec5SDimitry Andric
getTypeRecordsSubstream() const2260b57cec5SDimitry Andric BinarySubstreamRef TpiStream::getTypeRecordsSubstream() const {
2270b57cec5SDimitry Andric return TypeRecordsSubstream;
2280b57cec5SDimitry Andric }
2290b57cec5SDimitry Andric
getHashValues() const2300b57cec5SDimitry Andric FixedStreamArray<support::ulittle32_t> TpiStream::getHashValues() const {
2310b57cec5SDimitry Andric return HashValues;
2320b57cec5SDimitry Andric }
2330b57cec5SDimitry Andric
getTypeIndexOffsets() const2340b57cec5SDimitry Andric FixedStreamArray<TypeIndexOffset> TpiStream::getTypeIndexOffsets() const {
2350b57cec5SDimitry Andric return TypeIndexOffsets;
2360b57cec5SDimitry Andric }
2370b57cec5SDimitry Andric
getHashAdjusters()2380b57cec5SDimitry Andric HashTable<support::ulittle32_t> &TpiStream::getHashAdjusters() {
2390b57cec5SDimitry Andric return HashAdjusters;
2400b57cec5SDimitry Andric }
2410b57cec5SDimitry Andric
types(bool * HadError) const2420b57cec5SDimitry Andric CVTypeRange TpiStream::types(bool *HadError) const {
2430b57cec5SDimitry Andric return make_range(TypeRecords.begin(HadError), TypeRecords.end());
2440b57cec5SDimitry Andric }
2450b57cec5SDimitry Andric
commit()2460b57cec5SDimitry Andric Error TpiStream::commit() { return Error::success(); }
247