| //===- TpiStreamBuilder.cpp - -------------------------------------------===// | |
| // | |
| // 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/TpiStreamBuilder.h" | |
| #include "llvm/ADT/ArrayRef.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/DebugInfo/CodeView/TypeIndex.h" | |
| #include "llvm/DebugInfo/CodeView/TypeRecord.h" | |
| #include "llvm/DebugInfo/MSF/MSFBuilder.h" | |
| #include "llvm/DebugInfo/MSF/MappedBlockStream.h" | |
| #include "llvm/DebugInfo/PDB/Native/PDBFile.h" | |
| #include "llvm/DebugInfo/PDB/Native/RawError.h" | |
| #include "llvm/DebugInfo/PDB/Native/RawTypes.h" | |
| #include "llvm/Support/Allocator.h" | |
| #include "llvm/Support/BinaryByteStream.h" | |
| #include "llvm/Support/BinaryStreamArray.h" | |
| #include "llvm/Support/BinaryStreamReader.h" | |
| #include "llvm/Support/BinaryStreamWriter.h" | |
| #include "llvm/Support/Endian.h" | |
| #include "llvm/Support/Error.h" | |
| #include <algorithm> | |
| #include <cstdint> | |
| using namespace llvm; | |
| using namespace llvm::msf; | |
| using namespace llvm::pdb; | |
| using namespace llvm::support; | |
| TpiStreamBuilder::TpiStreamBuilder(MSFBuilder &Msf, uint32_t StreamIdx) | |
| : Msf(Msf), Allocator(Msf.getAllocator()), Header(nullptr), Idx(StreamIdx) { | |
| } | |
| TpiStreamBuilder::~TpiStreamBuilder() = default; | |
| void TpiStreamBuilder::setVersionHeader(PdbRaw_TpiVer Version) { | |
| VerHeader = Version; | |
| } | |
| void TpiStreamBuilder::addTypeRecord(ArrayRef<uint8_t> Record, | |
| Optional<uint32_t> Hash) { | |
| // If we just crossed an 8KB threshold, add a type index offset. | |
| size_t NewSize = TypeRecordBytes + Record.size(); | |
| constexpr size_t EightKB = 8 * 1024; | |
| if (NewSize / EightKB > TypeRecordBytes / EightKB || TypeRecords.empty()) { | |
| TypeIndexOffsets.push_back( | |
| {codeview::TypeIndex(codeview::TypeIndex::FirstNonSimpleIndex + | |
| TypeRecords.size()), | |
| ulittle32_t(TypeRecordBytes)}); | |
| } | |
| TypeRecordBytes = NewSize; | |
| TypeRecords.push_back(Record); | |
| if (Hash) | |
| TypeHashes.push_back(*Hash); | |
| } | |
| Error TpiStreamBuilder::finalize() { | |
| if (Header) | |
| return Error::success(); | |
| TpiStreamHeader *H = Allocator.Allocate<TpiStreamHeader>(); | |
| uint32_t Count = TypeRecords.size(); | |
| H->Version = VerHeader; | |
| H->HeaderSize = sizeof(TpiStreamHeader); | |
| H->TypeIndexBegin = codeview::TypeIndex::FirstNonSimpleIndex; | |
| H->TypeIndexEnd = H->TypeIndexBegin + Count; | |
| H->TypeRecordBytes = TypeRecordBytes; | |
| H->HashStreamIndex = HashStreamIndex; | |
| H->HashAuxStreamIndex = kInvalidStreamIndex; | |
| H->HashKeySize = sizeof(ulittle32_t); | |
| H->NumHashBuckets = MinTpiHashBuckets; | |
| // Recall that hash values go into a completely different stream identified by | |
| // the `HashStreamIndex` field of the `TpiStreamHeader`. Therefore, the data | |
| // begins at offset 0 of this independent stream. | |
| H->HashValueBuffer.Off = 0; | |
| H->HashValueBuffer.Length = calculateHashBufferSize(); | |
| // We never write any adjustments into our PDBs, so this is usually some | |
| // offset with zero length. | |
| H->HashAdjBuffer.Off = H->HashValueBuffer.Off + H->HashValueBuffer.Length; | |
| H->HashAdjBuffer.Length = 0; | |
| H->IndexOffsetBuffer.Off = H->HashAdjBuffer.Off + H->HashAdjBuffer.Length; | |
| H->IndexOffsetBuffer.Length = calculateIndexOffsetSize(); | |
| Header = H; | |
| return Error::success(); | |
| } | |
| uint32_t TpiStreamBuilder::calculateSerializedLength() { | |
| return sizeof(TpiStreamHeader) + TypeRecordBytes; | |
| } | |
| uint32_t TpiStreamBuilder::calculateHashBufferSize() const { | |
| assert((TypeRecords.size() == TypeHashes.size() || TypeHashes.empty()) && | |
| "either all or no type records should have hashes"); | |
| return TypeHashes.size() * sizeof(ulittle32_t); | |
| } | |
| uint32_t TpiStreamBuilder::calculateIndexOffsetSize() const { | |
| return TypeIndexOffsets.size() * sizeof(codeview::TypeIndexOffset); | |
| } | |
| Error TpiStreamBuilder::finalizeMsfLayout() { | |
| uint32_t Length = calculateSerializedLength(); | |
| if (auto EC = Msf.setStreamSize(Idx, Length)) | |
| return EC; | |
| uint32_t HashStreamSize = | |
| calculateHashBufferSize() + calculateIndexOffsetSize(); | |
| if (HashStreamSize == 0) | |
| return Error::success(); | |
| auto ExpectedIndex = Msf.addStream(HashStreamSize); | |
| if (!ExpectedIndex) | |
| return ExpectedIndex.takeError(); | |
| HashStreamIndex = *ExpectedIndex; | |
| if (!TypeHashes.empty()) { | |
| ulittle32_t *H = Allocator.Allocate<ulittle32_t>(TypeHashes.size()); | |
| MutableArrayRef<ulittle32_t> HashBuffer(H, TypeHashes.size()); | |
| for (uint32_t I = 0; I < TypeHashes.size(); ++I) { | |
| HashBuffer[I] = TypeHashes[I] % MinTpiHashBuckets; | |
| } | |
| ArrayRef<uint8_t> Bytes( | |
| reinterpret_cast<const uint8_t *>(HashBuffer.data()), | |
| calculateHashBufferSize()); | |
| HashValueStream = | |
| llvm::make_unique<BinaryByteStream>(Bytes, llvm::support::little); | |
| } | |
| return Error::success(); | |
| } | |
| Error TpiStreamBuilder::commit(const msf::MSFLayout &Layout, | |
| WritableBinaryStreamRef Buffer) { | |
| if (auto EC = finalize()) | |
| return EC; | |
| auto InfoS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer, | |
| Idx, Allocator); | |
| BinaryStreamWriter Writer(*InfoS); | |
| if (auto EC = Writer.writeObject(*Header)) | |
| return EC; | |
| for (auto Rec : TypeRecords) | |
| if (auto EC = Writer.writeBytes(Rec)) | |
| return EC; | |
| if (HashStreamIndex != kInvalidStreamIndex) { | |
| auto HVS = WritableMappedBlockStream::createIndexedStream( | |
| Layout, Buffer, HashStreamIndex, Allocator); | |
| BinaryStreamWriter HW(*HVS); | |
| if (HashValueStream) { | |
| if (auto EC = HW.writeStreamRef(*HashValueStream)) | |
| return EC; | |
| } | |
| for (auto &IndexOffset : TypeIndexOffsets) { | |
| if (auto EC = HW.writeObject(IndexOffset)) | |
| return EC; | |
| } | |
| } | |
| return Error::success(); | |
| } |