1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-21 03:53:04 +02:00
llvm-mirror/lib/DebugInfo/PDB/Native/TpiStream.cpp

173 lines
6.0 KiB
C++
Raw Normal View History

//===- 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/CVTypeVisitor.h"
[CodeView] Decouple record deserialization from visitor dispatch. Until now, our use case for the visitor has been to take a stream of bytes representing a type stream, deserialize the records in sequence, and do something with them, where "something" is determined by how the user implements a particular set of callbacks on an abstract class. For actually writing PDBs, however, we want to do the reverse. We have some kind of description of the list of records in their in-memory format, and we want to process each one. Perhaps by serializing them to a byte stream, or perhaps by converting them from one description format (Yaml) to another (in-memory representation). This was difficult in the current model because deserialization and invoking the callbacks were tightly coupled. With this patch we change this so that TypeDeserializer is itself an implementation of the particular set of callbacks. This decouples deserialization from the iteration over a list of records and invocation of the callbacks. TypeDeserializer is initialized with another implementation of the callback interface, so that upon deserialization it can pass the deserialized record through to the next set of callbacks. In a sense this is like an implementation of the Decorator design pattern, where the Deserializer is a decorator. This will be useful for writing Pdbs from yaml, where we have a description of the type records in Yaml format. In this case, the visitor implementation would have each visitation callback method implemented in such a way as to extract the proper set of fields from the Yaml, and it could maintain state that builds up a list of these records. Finally at the end we can pass this information through to another set of callbacks which serializes them into a byte stream. Reviewed By: majnemer, ruiu, rnk Differential Revision: https://reviews.llvm.org/D23177 llvm-svn: 277871
2016-08-05 23:45:34 +02:00
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
#include "llvm/DebugInfo/MSF/BinaryStreamReader.h"
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.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/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(const PDBFile &File,
std::unique_ptr<MappedBlockStream> Stream)
: Pdb(File), Stream(std::move(Stream)) {}
TpiStream::~TpiStream() = default;
// Verifies that a given type record matches with a given hash value.
// Currently we only verify SRC_LINE records.
Error TpiStream::verifyHashValues() {
TpiHashVerifier Verifier(HashValues, Header->NumHashBuckets);
TypeDeserializer Deserializer;
TypeVisitorCallbackPipeline Pipeline;
Pipeline.addCallbackToPipeline(Deserializer);
Pipeline.addCallbackToPipeline(Verifier);
CVTypeVisitor Visitor(Pipeline);
return Visitor.visitTypeStream(TypeRecords);
}
Error TpiStream::reload() {
StreamReader 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.readArray(TypeRecords, Header->TypeRecordBytes))
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);
StreamReader HSR(*HS);
uint32_t NumHashValues =
Header->HashValueBuffer.Length / sizeof(ulittle32_t);
if (NumHashValues != NumTypeRecords())
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;
std::vector<ulittle32_t> HashValueList;
for (auto I : HashValues)
HashValueList.push_back(I);
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);
// TPI hash table is a parallel array for the type records.
// Verify that the hash values match with type records.
if (auto EC = verifyHashValues())
return EC;
}
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::NumTypeRecords() const {
return TypeIndexEnd() - TypeIndexBegin();
}
uint16_t TpiStream::getTypeHashStreamIndex() const {
return Header->HashStreamIndex;
}
uint16_t TpiStream::getTypeHashStreamAuxIndex() const {
return Header->HashAuxStreamIndex;
}
uint32_t TpiStream::NumHashBuckets() const { return Header->NumHashBuckets; }
uint32_t TpiStream::getHashKeySize() const { return Header->HashKeySize; }
FixedStreamArray<support::ulittle32_t> TpiStream::getHashValues() const {
return HashValues;
}
FixedStreamArray<TypeIndexOffset> TpiStream::getTypeIndexOffsets() const {
return TypeIndexOffsets;
}
HashTable &TpiStream::getHashAdjusters() { return HashAdjusters; }
CVTypeRange TpiStream::types(bool *HadError) const {
return make_range(TypeRecords.begin(HadError), TypeRecords.end());
}
Error TpiStream::commit() { return Error::success(); }