xref: /freebsd/contrib/llvm-project/llvm/lib/DebugInfo/PDB/Native/GlobalsStream.cpp (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
1 //===- GlobalsStream.cpp - PDB Index of Symbols by Name ---------*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
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
12 //
13 // When you are reading the reference source code, you'd find the
14 // information below useful.
15 //
16 //  - ppdb1->m_fMinimalDbgInfo seems to be always true.
17 //  - SMALLBUCKETS macro is defined.
18 //
19 //===----------------------------------------------------------------------===//
20 
21 #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
22 
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"
29 #include <algorithm>
30 
31 using namespace llvm;
32 using namespace llvm::msf;
33 using namespace llvm::pdb;
34 
35 GlobalsStream::GlobalsStream(std::unique_ptr<MappedBlockStream> Stream)
36     : Stream(std::move(Stream)) {}
37 
38 GlobalsStream::~GlobalsStream() = default;
39 
40 Error GlobalsStream::reload() {
41   BinaryStreamReader Reader(*Stream);
42   if (auto E = GlobalsTable.read(Reader))
43     return E;
44   return Error::success();
45 }
46 
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;
51 
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)
56     return Result;
57 
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];
64   } else {
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;
68   }
69 
70   EndRecordIndex /= 12;
71 
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)));
79     ++StartRecordIndex;
80   }
81   return Result;
82 }
83 
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.");
89 
90   return Error::success();
91 }
92 
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.");
98 
99   if (HashHdr->VerSignature != GSIHashHeader::HdrSignature)
100     return make_error<RawError>(
101         raw_error_code::feature_unsupported,
102         "GSIHashHeader signature (0xffffffff) not found.");
103 
104   return Error::success();
105 }
106 
107 static Error readGSIHashRecords(FixedStreamArray<PSHashRecord> &HashRecords,
108                                 const GSIHashHeader *HashHdr,
109                                 BinaryStreamReader &Reader) {
110   if (auto EC = checkHashHdrVersion(HashHdr))
111     return EC;
112 
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."));
123 
124   return Error::success();
125 }
126 
127 static Error
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))
134     return EC;
135 
136   // Before the actual hash buckets, there is a bitmap of length determined by
137   // IPHR_HASH.
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);
150     if (IsSet) {
151       ++NumBuckets1;
152       BucketMap[I] = CompressedBucketIdx++;
153     } else {
154       BucketMap[I] = -1;
155     }
156   }
157 
158   uint32_t NumBuckets = 0;
159   for (uint32_t B : HashBitmap)
160     NumBuckets += countPopulation(B);
161 
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."));
167 
168   return Error::success();
169 }
170 
171 Error GSIHashTable::read(BinaryStreamReader &Reader) {
172   if (auto EC = readGSIHashHeader(HashHdr, Reader))
173     return EC;
174   if (auto EC = readGSIHashRecords(HashRecords, HashHdr, Reader))
175     return EC;
176   if (HashHdr->HrSize > 0)
177     if (auto EC = readGSIHashBuckets(HashBuckets, HashBitmap, HashHdr,
178                                      BucketMap, Reader))
179       return EC;
180   return Error::success();
181 }
182