mirror of
https://github.com/RPCS3/llvm.git
synced 2026-08-25 11:51:41 -04:00
b17277efb8
Some records point to an LF_CLASS, LF_UNION, LF_STRUCTURE, or LF_ENUM
which is a forward reference and doesn't contain complete debug
information. In these cases, we'd like to be able to quickly locate the
full record. The TPI stream stores an array of pre-computed record hash
values, one for each type record. If we pre-process this on startup, we
can build a mapping from hash value -> {list of possible matching type
indices}. Since hashes of full records are only based on the name and or
unique name and not the full record contents, we can then use forward
ref record to compute the hash of what *would* be the full record by
just hashing the name, use this to get the list of possible matches, and
iterate those looking for a match on name or unique name.
llvm-pdbutil is updated to resolve forward references for the purposes
of testing (plus it's just useful).
Differential Revision: https://reviews.llvm.org/D52283
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@342656 91177308-0d34-0410-b5e6-96231b3b80d8
254 lines
8.1 KiB
C++
254 lines
8.1 KiB
C++
//===- TpiStream.cpp - PDB Type Info (TPI) Stream 2 Access ----------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
|
|
|
|
#include "llvm/ADT/iterator_range.h"
|
|
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
|
|
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
|
#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
|
|
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
|
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
|
|
#include "llvm/DebugInfo/PDB/Native/Hash.h"
|
|
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
|
#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
|
|
#include "llvm/DebugInfo/PDB/Native/RawError.h"
|
|
#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
|
|
#include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
|
|
#include "llvm/Support/BinaryStreamReader.h"
|
|
#include "llvm/Support/Endian.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::codeview;
|
|
using namespace llvm::support;
|
|
using namespace llvm::msf;
|
|
using namespace llvm::pdb;
|
|
|
|
TpiStream::TpiStream(PDBFile &File, std::unique_ptr<MappedBlockStream> Stream)
|
|
: Pdb(File), Stream(std::move(Stream)) {}
|
|
|
|
TpiStream::~TpiStream() = default;
|
|
|
|
Error TpiStream::reload() {
|
|
BinaryStreamReader Reader(*Stream);
|
|
|
|
if (Reader.bytesRemaining() < sizeof(TpiStreamHeader))
|
|
return make_error<RawError>(raw_error_code::corrupt_file,
|
|
"TPI Stream does not contain a header.");
|
|
|
|
if (Reader.readObject(Header))
|
|
return make_error<RawError>(raw_error_code::corrupt_file,
|
|
"TPI Stream does not contain a header.");
|
|
|
|
if (Header->Version != PdbTpiV80)
|
|
return make_error<RawError>(raw_error_code::corrupt_file,
|
|
"Unsupported TPI Version.");
|
|
|
|
if (Header->HeaderSize != sizeof(TpiStreamHeader))
|
|
return make_error<RawError>(raw_error_code::corrupt_file,
|
|
"Corrupt TPI Header size.");
|
|
|
|
if (Header->HashKeySize != sizeof(ulittle32_t))
|
|
return make_error<RawError>(raw_error_code::corrupt_file,
|
|
"TPI Stream expected 4 byte hash key size.");
|
|
|
|
if (Header->NumHashBuckets < MinTpiHashBuckets ||
|
|
Header->NumHashBuckets > MaxTpiHashBuckets)
|
|
return make_error<RawError>(raw_error_code::corrupt_file,
|
|
"TPI Stream Invalid number of hash buckets.");
|
|
|
|
// The actual type records themselves come from this stream
|
|
if (auto EC =
|
|
Reader.readSubstream(TypeRecordsSubstream, Header->TypeRecordBytes))
|
|
return EC;
|
|
|
|
BinaryStreamReader RecordReader(TypeRecordsSubstream.StreamData);
|
|
if (auto EC =
|
|
RecordReader.readArray(TypeRecords, TypeRecordsSubstream.size()))
|
|
return EC;
|
|
|
|
// Hash indices, hash values, etc come from the hash stream.
|
|
if (Header->HashStreamIndex != kInvalidStreamIndex) {
|
|
if (Header->HashStreamIndex >= Pdb.getNumStreams())
|
|
return make_error<RawError>(raw_error_code::corrupt_file,
|
|
"Invalid TPI hash stream index.");
|
|
|
|
auto HS = MappedBlockStream::createIndexedStream(
|
|
Pdb.getMsfLayout(), Pdb.getMsfBuffer(), Header->HashStreamIndex,
|
|
Pdb.getAllocator());
|
|
BinaryStreamReader HSR(*HS);
|
|
|
|
// There should be a hash value for every type record, or no hashes at all.
|
|
uint32_t NumHashValues =
|
|
Header->HashValueBuffer.Length / sizeof(ulittle32_t);
|
|
if (NumHashValues != getNumTypeRecords() && NumHashValues != 0)
|
|
return make_error<RawError>(
|
|
raw_error_code::corrupt_file,
|
|
"TPI hash count does not match with the number of type records.");
|
|
HSR.setOffset(Header->HashValueBuffer.Off);
|
|
if (auto EC = HSR.readArray(HashValues, NumHashValues))
|
|
return EC;
|
|
|
|
HSR.setOffset(Header->IndexOffsetBuffer.Off);
|
|
uint32_t NumTypeIndexOffsets =
|
|
Header->IndexOffsetBuffer.Length / sizeof(TypeIndexOffset);
|
|
if (auto EC = HSR.readArray(TypeIndexOffsets, NumTypeIndexOffsets))
|
|
return EC;
|
|
|
|
if (Header->HashAdjBuffer.Length > 0) {
|
|
HSR.setOffset(Header->HashAdjBuffer.Off);
|
|
if (auto EC = HashAdjusters.load(HSR))
|
|
return EC;
|
|
}
|
|
|
|
HashStream = std::move(HS);
|
|
}
|
|
|
|
Types = llvm::make_unique<LazyRandomTypeCollection>(
|
|
TypeRecords, getNumTypeRecords(), getTypeIndexOffsets());
|
|
return Error::success();
|
|
}
|
|
|
|
PdbRaw_TpiVer TpiStream::getTpiVersion() const {
|
|
uint32_t Value = Header->Version;
|
|
return static_cast<PdbRaw_TpiVer>(Value);
|
|
}
|
|
|
|
uint32_t TpiStream::TypeIndexBegin() const { return Header->TypeIndexBegin; }
|
|
|
|
uint32_t TpiStream::TypeIndexEnd() const { return Header->TypeIndexEnd; }
|
|
|
|
uint32_t TpiStream::getNumTypeRecords() const {
|
|
return TypeIndexEnd() - TypeIndexBegin();
|
|
}
|
|
|
|
uint16_t TpiStream::getTypeHashStreamIndex() const {
|
|
return Header->HashStreamIndex;
|
|
}
|
|
|
|
uint16_t TpiStream::getTypeHashStreamAuxIndex() const {
|
|
return Header->HashAuxStreamIndex;
|
|
}
|
|
|
|
uint32_t TpiStream::getNumHashBuckets() const { return Header->NumHashBuckets; }
|
|
uint32_t TpiStream::getHashKeySize() const { return Header->HashKeySize; }
|
|
|
|
void TpiStream::buildHashMap() {
|
|
if (!HashMap.empty())
|
|
return;
|
|
if (HashValues.empty())
|
|
return;
|
|
|
|
HashMap.resize(Header->NumHashBuckets);
|
|
|
|
TypeIndex TIB{Header->TypeIndexBegin};
|
|
TypeIndex TIE{Header->TypeIndexEnd};
|
|
while (TIB < TIE) {
|
|
uint32_t HV = HashValues[TIB.toArrayIndex()];
|
|
HashMap[HV].push_back(TIB++);
|
|
}
|
|
}
|
|
|
|
bool TpiStream::supportsTypeLookup() const { return !HashMap.empty(); }
|
|
|
|
template <typename RecordT> static ClassOptions getUdtOptions(CVType CVT) {
|
|
RecordT Record;
|
|
if (auto EC = TypeDeserializer::deserializeAs<RecordT>(CVT, Record)) {
|
|
consumeError(std::move(EC));
|
|
return ClassOptions::None;
|
|
}
|
|
return Record.getOptions();
|
|
}
|
|
|
|
static bool isUdtForwardRef(CVType CVT) {
|
|
ClassOptions UdtOptions = ClassOptions::None;
|
|
switch (CVT.kind()) {
|
|
case LF_STRUCTURE:
|
|
case LF_CLASS:
|
|
case LF_INTERFACE:
|
|
UdtOptions = getUdtOptions<ClassRecord>(std::move(CVT));
|
|
break;
|
|
case LF_ENUM:
|
|
UdtOptions = getUdtOptions<EnumRecord>(std::move(CVT));
|
|
break;
|
|
case LF_UNION:
|
|
UdtOptions = getUdtOptions<UnionRecord>(std::move(CVT));
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return (UdtOptions & ClassOptions::ForwardReference) != ClassOptions::None;
|
|
}
|
|
|
|
Expected<TypeIndex>
|
|
TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const {
|
|
CVType F = Types->getType(ForwardRefTI);
|
|
if (!isUdtForwardRef(F))
|
|
return ForwardRefTI;
|
|
|
|
Expected<TagRecordHash> ForwardTRH = hashTagRecord(F);
|
|
if (!ForwardTRH)
|
|
return ForwardTRH.takeError();
|
|
|
|
TagRecordHash Copy = std::move(*ForwardTRH);
|
|
uint32_t BucketIdx = ForwardTRH->FullRecordHash % Header->NumHashBuckets;
|
|
|
|
for (TypeIndex TI : HashMap[BucketIdx]) {
|
|
CVType CVT = Types->getType(TI);
|
|
if (CVT.kind() != F.kind())
|
|
continue;
|
|
|
|
Expected<TagRecordHash> FullTRH = hashTagRecord(CVT);
|
|
if (!FullTRH)
|
|
return FullTRH.takeError();
|
|
if (ForwardTRH->FullRecordHash != FullTRH->FullRecordHash)
|
|
continue;
|
|
TagRecord &ForwardTR = ForwardTRH->getRecord();
|
|
TagRecord &FullTR = FullTRH->getRecord();
|
|
|
|
if (!ForwardTR.hasUniqueName()) {
|
|
if (ForwardTR.getName() == FullTR.getName())
|
|
return TI;
|
|
continue;
|
|
}
|
|
|
|
if (!FullTR.hasUniqueName())
|
|
continue;
|
|
if (ForwardTR.getUniqueName() == FullTR.getUniqueName())
|
|
return TI;
|
|
}
|
|
return ForwardRefTI;
|
|
}
|
|
|
|
BinarySubstreamRef TpiStream::getTypeRecordsSubstream() const {
|
|
return TypeRecordsSubstream;
|
|
}
|
|
|
|
FixedStreamArray<support::ulittle32_t> TpiStream::getHashValues() const {
|
|
return HashValues;
|
|
}
|
|
|
|
FixedStreamArray<TypeIndexOffset> TpiStream::getTypeIndexOffsets() const {
|
|
return TypeIndexOffsets;
|
|
}
|
|
|
|
HashTable<support::ulittle32_t> &TpiStream::getHashAdjusters() {
|
|
return HashAdjusters;
|
|
}
|
|
|
|
CVTypeRange TpiStream::types(bool *HadError) const {
|
|
return make_range(TypeRecords.begin(HadError), TypeRecords.end());
|
|
}
|
|
|
|
Error TpiStream::commit() { return Error::success(); }
|