xref: /freebsd/contrib/llvm-project/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp (revision 5b27928474e6a4103d65b347544705c40c9618fd)
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