1 //===- GlobalsStream.cpp - PDB Index of Symbols by Name ---------*- C++ -*-===//
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-exception
7 //===----------------------------------------------------------------------===//
9 // The on-disk structores used in this file are based on the reference
10 // implementation which is available at
11 // https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h
13 // When you are reading the reference source code, you'd find the
14 // information below useful.
16 // - ppdb1->m_fMinimalDbgInfo seems to be always true.
17 // - SMALLBUCKETS macro is defined.
19 //===----------------------------------------------------------------------===//
21 #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
23 #include "llvm/DebugInfo/CodeView/RecordName.h"
24 #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
25 #include "llvm/DebugInfo/PDB/Native/Hash.h"
26 #include "llvm/DebugInfo/PDB/Native/RawError.h"
27 #include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
28 #include "llvm/Support/BinaryStreamReader.h"
29 #include "llvm/Support/Error.h"
33 using namespace llvm::msf
;
34 using namespace llvm::pdb
;
36 GlobalsStream::GlobalsStream(std::unique_ptr
<MappedBlockStream
> Stream
)
37 : Stream(std::move(Stream
)) {}
39 GlobalsStream::~GlobalsStream() = default;
41 Error
GlobalsStream::reload() {
42 BinaryStreamReader
Reader(*Stream
);
43 if (auto E
= GlobalsTable
.read(Reader
))
45 return Error::success();
48 std::vector
<std::pair
<uint32_t, codeview::CVSymbol
>>
49 GlobalsStream::findRecordsByName(StringRef Name
,
50 const SymbolStream
&Symbols
) const {
51 std::vector
<std::pair
<uint32_t, codeview::CVSymbol
>> Result
;
53 // Hash the name to figure out which bucket this goes into.
54 size_t ExpandedBucketIndex
= hashStringV1(Name
) % IPHR_HASH
;
55 int32_t CompressedBucketIndex
= GlobalsTable
.BucketMap
[ExpandedBucketIndex
];
56 if (CompressedBucketIndex
== -1)
59 uint32_t LastBucketIndex
= GlobalsTable
.HashBuckets
.size() - 1;
60 uint32_t StartRecordIndex
=
61 GlobalsTable
.HashBuckets
[CompressedBucketIndex
] / 12;
62 uint32_t EndRecordIndex
= 0;
63 if (LLVM_LIKELY(uint32_t(CompressedBucketIndex
) < LastBucketIndex
)) {
64 EndRecordIndex
= GlobalsTable
.HashBuckets
[CompressedBucketIndex
+ 1];
66 // If this is the last bucket, it consists of all hash records until the end
67 // of the HashRecords array.
68 EndRecordIndex
= GlobalsTable
.HashRecords
.size() * 12;
73 assert(EndRecordIndex
<= GlobalsTable
.HashRecords
.size());
74 while (StartRecordIndex
< EndRecordIndex
) {
75 PSHashRecord PSH
= GlobalsTable
.HashRecords
[StartRecordIndex
];
76 uint32_t Off
= PSH
.Off
- 1;
77 codeview::CVSymbol Record
= Symbols
.readRecord(Off
);
78 if (codeview::getSymbolName(Record
) == Name
)
79 Result
.push_back(std::make_pair(Off
, std::move(Record
)));
85 static Error
checkHashHdrVersion(const GSIHashHeader
*HashHdr
) {
86 if (HashHdr
->VerHdr
!= GSIHashHeader::HdrVersion
)
87 return make_error
<RawError
>(
88 raw_error_code::feature_unsupported
,
89 "Encountered unsupported globals stream version.");
91 return Error::success();
94 static Error
readGSIHashHeader(const GSIHashHeader
*&HashHdr
,
95 BinaryStreamReader
&Reader
) {
96 if (Reader
.readObject(HashHdr
))
97 return make_error
<RawError
>(raw_error_code::corrupt_file
,
98 "Stream does not contain a GSIHashHeader.");
100 if (HashHdr
->VerSignature
!= GSIHashHeader::HdrSignature
)
101 return make_error
<RawError
>(
102 raw_error_code::feature_unsupported
,
103 "GSIHashHeader signature (0xffffffff) not found.");
105 return Error::success();
108 static Error
readGSIHashRecords(FixedStreamArray
<PSHashRecord
> &HashRecords
,
109 const GSIHashHeader
*HashHdr
,
110 BinaryStreamReader
&Reader
) {
111 if (auto EC
= checkHashHdrVersion(HashHdr
))
114 // HashHdr->HrSize specifies the number of bytes of PSHashRecords we have.
115 // Verify that we can read them all.
116 if (HashHdr
->HrSize
% sizeof(PSHashRecord
))
117 return make_error
<RawError
>(raw_error_code::corrupt_file
,
118 "Invalid HR array size.");
119 uint32_t NumHashRecords
= HashHdr
->HrSize
/ sizeof(PSHashRecord
);
120 if (auto EC
= Reader
.readArray(HashRecords
, NumHashRecords
))
121 return joinErrors(std::move(EC
),
122 make_error
<RawError
>(raw_error_code::corrupt_file
,
123 "Error reading hash records."));
125 return Error::success();
129 readGSIHashBuckets(FixedStreamArray
<support::ulittle32_t
> &HashBuckets
,
130 FixedStreamArray
<support::ulittle32_t
> &HashBitmap
,
131 const GSIHashHeader
*HashHdr
,
132 MutableArrayRef
<int32_t> BucketMap
,
133 BinaryStreamReader
&Reader
) {
134 if (auto EC
= checkHashHdrVersion(HashHdr
))
137 // Before the actual hash buckets, there is a bitmap of length determined by
139 size_t BitmapSizeInBits
= alignTo(IPHR_HASH
+ 1, 32);
140 uint32_t NumBitmapEntries
= BitmapSizeInBits
/ 32;
141 if (auto EC
= Reader
.readArray(HashBitmap
, NumBitmapEntries
))
142 return joinErrors(std::move(EC
),
143 make_error
<RawError
>(raw_error_code::corrupt_file
,
144 "Could not read a bitmap."));
145 uint32_t CompressedBucketIdx
= 0;
146 for (uint32_t I
= 0; I
<= IPHR_HASH
; ++I
) {
147 uint8_t WordIdx
= I
/ 32;
148 uint8_t BitIdx
= I
% 32;
149 bool IsSet
= HashBitmap
[WordIdx
] & (1U << BitIdx
);
151 BucketMap
[I
] = CompressedBucketIdx
++;
157 uint32_t NumBuckets
= 0;
158 for (uint32_t B
: HashBitmap
)
159 NumBuckets
+= llvm::popcount(B
);
161 // Hash buckets follow.
162 if (auto EC
= Reader
.readArray(HashBuckets
, NumBuckets
))
163 return joinErrors(std::move(EC
),
164 make_error
<RawError
>(raw_error_code::corrupt_file
,
165 "Hash buckets corrupted."));
167 return Error::success();
170 Error
GSIHashTable::read(BinaryStreamReader
&Reader
) {
171 if (auto EC
= readGSIHashHeader(HashHdr
, Reader
))
173 if (auto EC
= readGSIHashRecords(HashRecords
, HashHdr
, Reader
))
175 if (HashHdr
->HrSize
> 0)
176 if (auto EC
= readGSIHashBuckets(HashBuckets
, HashBitmap
, HashHdr
,
179 return Error::success();