246 lines · cpp
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-exception6//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 29using namespace llvm;30using namespace llvm::codeview;31using namespace llvm::support;32using namespace llvm::msf;33using namespace llvm::pdb;34 35TpiStream::TpiStream(PDBFile &File, std::unique_ptr<MappedBlockStream> Stream)36 : Pdb(File), Stream(std::move(Stream)) {}37 38TpiStream::~TpiStream() = default;39 40Error TpiStream::reload() {41 BinaryStreamReader Reader(*Stream);42 43 if (Reader.bytesRemaining() < sizeof(TpiStreamHeader))44 return make_error<RawError>(raw_error_code::corrupt_file,45 "TPI Stream does not contain a header.");46 47 if (Reader.readObject(Header))48 return make_error<RawError>(raw_error_code::corrupt_file,49 "TPI Stream does not contain a header.");50 51 if (Header->Version != PdbTpiV80)52 return make_error<RawError>(raw_error_code::corrupt_file,53 "Unsupported TPI Version.");54 55 if (Header->HeaderSize != sizeof(TpiStreamHeader))56 return make_error<RawError>(raw_error_code::corrupt_file,57 "Corrupt TPI Header size.");58 59 if (Header->HashKeySize != sizeof(ulittle32_t))60 return make_error<RawError>(raw_error_code::corrupt_file,61 "TPI Stream expected 4 byte hash key size.");62 63 if (Header->NumHashBuckets < MinTpiHashBuckets ||64 Header->NumHashBuckets > MaxTpiHashBuckets)65 return make_error<RawError>(raw_error_code::corrupt_file,66 "TPI Stream Invalid number of hash buckets.");67 68 // The actual type records themselves come from this stream69 if (auto EC =70 Reader.readSubstream(TypeRecordsSubstream, Header->TypeRecordBytes))71 return EC;72 73 BinaryStreamReader RecordReader(TypeRecordsSubstream.StreamData);74 if (auto EC =75 RecordReader.readArray(TypeRecords, 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(Header->HashStreamIndex);81 if (!HS) {82 consumeError(HS.takeError());83 return make_error<RawError>(raw_error_code::corrupt_file,84 "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 raw_error_code::corrupt_file,94 "TPI hash count does not match with the number of type records.");95 HSR.setOffset(Header->HashValueBuffer.Off);96 if (auto EC = HSR.readArray(HashValues, 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(TypeIndexOffsets, NumTypeIndexOffsets))103 return EC;104 105 if (Header->HashAdjBuffer.Length > 0) {106 HSR.setOffset(Header->HashAdjBuffer.Off);107 if (auto EC = HashAdjusters.load(HSR))108 return EC;109 }110 111 HashStream = std::move(*HS);112 }113 114 Types = std::make_unique<LazyRandomTypeCollection>(115 TypeRecords, getNumTypeRecords(), getTypeIndexOffsets());116 return Error::success();117}118 119PdbRaw_TpiVer TpiStream::getTpiVersion() const {120 uint32_t Value = Header->Version;121 return static_cast<PdbRaw_TpiVer>(Value);122}123 124uint32_t TpiStream::TypeIndexBegin() const { return Header->TypeIndexBegin; }125 126uint32_t TpiStream::TypeIndexEnd() const { return Header->TypeIndexEnd; }127 128uint32_t TpiStream::getNumTypeRecords() const {129 return TypeIndexEnd() - TypeIndexBegin();130}131 132uint16_t TpiStream::getTypeHashStreamIndex() const {133 return Header->HashStreamIndex;134}135 136uint16_t TpiStream::getTypeHashStreamAuxIndex() const {137 return Header->HashAuxStreamIndex;138}139 140uint32_t TpiStream::getNumHashBuckets() const { return Header->NumHashBuckets; }141uint32_t TpiStream::getHashKeySize() const { return Header->HashKeySize; }142 143void TpiStream::buildHashMap() {144 if (!HashMap.empty())145 return;146 if (HashValues.empty())147 return;148 149 HashMap.resize(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(TIB++);156 }157}158 159std::vector<TypeIndex> TpiStream::findRecordsByName(StringRef Name) const {160 if (!supportsTypeLookup())161 const_cast<TpiStream*>(this)->buildHashMap();162 163 uint32_t Bucket = hashStringV1(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, TI);170 if (ThisName == Name)171 Result.push_back(TI);172 }173 return Result;174}175 176bool TpiStream::supportsTypeLookup() const { return !HashMap.empty(); }177 178Expected<TypeIndex>179TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const {180 if (!supportsTypeLookup())181 const_cast<TpiStream*>(this)->buildHashMap();182 183 CVType F = Types->getType(ForwardRefTI);184 if (!isUdtForwardRef(F))185 return ForwardRefTI;186 187 Expected<TagRecordHash> ForwardTRH = hashTagRecord(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(TI);195 if (CVT.kind() != F.kind())196 continue;197 198 Expected<TagRecordHash> FullTRH = hashTagRecord(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 220codeview::CVType TpiStream::getType(codeview::TypeIndex Index) {221 assert(!Index.isSimple());222 return Types->getType(Index);223}224 225BinarySubstreamRef TpiStream::getTypeRecordsSubstream() const {226 return TypeRecordsSubstream;227}228 229FixedStreamArray<support::ulittle32_t> TpiStream::getHashValues() const {230 return HashValues;231}232 233FixedStreamArray<TypeIndexOffset> TpiStream::getTypeIndexOffsets() const {234 return TypeIndexOffsets;235}236 237HashTable<support::ulittle32_t> &TpiStream::getHashAdjusters() {238 return HashAdjusters;239}240 241CVTypeRange TpiStream::types(bool *HadError) const {242 return make_range(TypeRecords.begin(HadError), TypeRecords.end());243}244 245Error TpiStream::commit() { return Error::success(); }246