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/PDB/Native/Hash.h"
25 #include "llvm/DebugInfo/PDB/Native/RawError.h"
26 #include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
27 #include "llvm/Support/BinaryStreamReader.h"
28 #include "llvm/Support/Error.h"
32 using namespace llvm::msf
;
33 using namespace llvm::pdb
;
35 GlobalsStream::GlobalsStream(std::unique_ptr
<MappedBlockStream
> Stream
)
36 : Stream(std::move(Stream
)) {}
38 GlobalsStream::~GlobalsStream() = default;
40 Error
GlobalsStream::reload() {
41 BinaryStreamReader
Reader(*Stream
);
42 if (auto E
= GlobalsTable
.read(Reader
))
44 return Error::success();
47 std::vector
<std::pair
<uint32_t, codeview::CVSymbol
>>
48 GlobalsStream::findRecordsByName(StringRef Name
,
49 const SymbolStream
&Symbols
) const {
50 std::vector
<std::pair
<uint32_t, codeview::CVSymbol
>> Result
;
52 // Hash the name to figure out which bucket this goes into.
53 size_t ExpandedBucketIndex
= hashStringV1(Name
) % IPHR_HASH
;
54 int32_t CompressedBucketIndex
= GlobalsTable
.BucketMap
[ExpandedBucketIndex
];
55 if (CompressedBucketIndex
== -1)
58 uint32_t LastBucketIndex
= GlobalsTable
.HashBuckets
.size() - 1;
59 uint32_t StartRecordIndex
=
60 GlobalsTable
.HashBuckets
[CompressedBucketIndex
] / 12;
61 uint32_t EndRecordIndex
= 0;
62 if (LLVM_LIKELY(uint32_t(CompressedBucketIndex
) < LastBucketIndex
)) {
63 EndRecordIndex
= GlobalsTable
.HashBuckets
[CompressedBucketIndex
+ 1];
65 // If this is the last bucket, it consists of all hash records until the end
66 // of the HashRecords array.
67 EndRecordIndex
= GlobalsTable
.HashRecords
.size() * 12;
72 assert(EndRecordIndex
<= GlobalsTable
.HashRecords
.size());
73 while (StartRecordIndex
< EndRecordIndex
) {
74 PSHashRecord PSH
= GlobalsTable
.HashRecords
[StartRecordIndex
];
75 uint32_t Off
= PSH
.Off
- 1;
76 codeview::CVSymbol Record
= Symbols
.readRecord(Off
);
77 if (codeview::getSymbolName(Record
) == Name
)
78 Result
.push_back(std::make_pair(Off
, std::move(Record
)));
84 static Error
checkHashHdrVersion(const GSIHashHeader
*HashHdr
) {
85 if (HashHdr
->VerHdr
!= GSIHashHeader::HdrVersion
)
86 return make_error
<RawError
>(
87 raw_error_code::feature_unsupported
,
88 "Encountered unsupported globals stream version.");
90 return Error::success();
93 static Error
readGSIHashHeader(const GSIHashHeader
*&HashHdr
,
94 BinaryStreamReader
&Reader
) {
95 if (Reader
.readObject(HashHdr
))
96 return make_error
<RawError
>(raw_error_code::corrupt_file
,
97 "Stream does not contain a GSIHashHeader.");
99 if (HashHdr
->VerSignature
!= GSIHashHeader::HdrSignature
)
100 return make_error
<RawError
>(
101 raw_error_code::feature_unsupported
,
102 "GSIHashHeader signature (0xffffffff) not found.");
104 return Error::success();
107 static Error
readGSIHashRecords(FixedStreamArray
<PSHashRecord
> &HashRecords
,
108 const GSIHashHeader
*HashHdr
,
109 BinaryStreamReader
&Reader
) {
110 if (auto EC
= checkHashHdrVersion(HashHdr
))
113 // HashHdr->HrSize specifies the number of bytes of PSHashRecords we have.
114 // Verify that we can read them all.
115 if (HashHdr
->HrSize
% sizeof(PSHashRecord
))
116 return make_error
<RawError
>(raw_error_code::corrupt_file
,
117 "Invalid HR array size.");
118 uint32_t NumHashRecords
= HashHdr
->HrSize
/ sizeof(PSHashRecord
);
119 if (auto EC
= Reader
.readArray(HashRecords
, NumHashRecords
))
120 return joinErrors(std::move(EC
),
121 make_error
<RawError
>(raw_error_code::corrupt_file
,
122 "Error reading hash records."));
124 return Error::success();
128 readGSIHashBuckets(FixedStreamArray
<support::ulittle32_t
> &HashBuckets
,
129 FixedStreamArray
<support::ulittle32_t
> &HashBitmap
,
130 const GSIHashHeader
*HashHdr
,
131 MutableArrayRef
<int32_t> BucketMap
,
132 BinaryStreamReader
&Reader
) {
133 if (auto EC
= checkHashHdrVersion(HashHdr
))
136 // Before the actual hash buckets, there is a bitmap of length determined by
138 size_t BitmapSizeInBits
= alignTo(IPHR_HASH
+ 1, 32);
139 uint32_t NumBitmapEntries
= BitmapSizeInBits
/ 32;
140 if (auto EC
= Reader
.readArray(HashBitmap
, NumBitmapEntries
))
141 return joinErrors(std::move(EC
),
142 make_error
<RawError
>(raw_error_code::corrupt_file
,
143 "Could not read a bitmap."));
144 uint32_t NumBuckets1
= 0;
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
);
152 BucketMap
[I
] = CompressedBucketIdx
++;
158 uint32_t NumBuckets
= 0;
159 for (uint32_t B
: HashBitmap
)
160 NumBuckets
+= countPopulation(B
);
162 // Hash buckets follow.
163 if (auto EC
= Reader
.readArray(HashBuckets
, NumBuckets
))
164 return joinErrors(std::move(EC
),
165 make_error
<RawError
>(raw_error_code::corrupt_file
,
166 "Hash buckets corrupted."));
168 return Error::success();
171 Error
GSIHashTable::read(BinaryStreamReader
&Reader
) {
172 if (auto EC
= readGSIHashHeader(HashHdr
, Reader
))
174 if (auto EC
= readGSIHashRecords(HashRecords
, HashHdr
, Reader
))
176 if (HashHdr
->HrSize
> 0)
177 if (auto EC
= readGSIHashBuckets(HashBuckets
, HashBitmap
, HashHdr
,
180 return Error::success();