aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/llvm14/lib/DebugInfo/PDB/Native/GlobalsStream.cpp
blob: f27d60f468156420a853aab1a04002f0d34b03bd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
//===- GlobalsStream.cpp - PDB Index of Symbols by Name ---------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// The on-disk structores used in this file are based on the reference
// implementation which is available at
// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h
//
// When you are reading the reference source code, you'd find the
// information below useful.
//
//  - ppdb1->m_fMinimalDbgInfo seems to be always true.
//  - SMALLBUCKETS macro is defined.
//
//===----------------------------------------------------------------------===//

#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"

#include "llvm/DebugInfo/CodeView/RecordName.h"
#include "llvm/DebugInfo/PDB/Native/Hash.h"
#include "llvm/DebugInfo/PDB/Native/RawError.h"
#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
#include "llvm/Support/BinaryStreamReader.h"
#include "llvm/Support/Error.h"
#include <algorithm>

using namespace llvm;
using namespace llvm::msf;
using namespace llvm::pdb;

GlobalsStream::GlobalsStream(std::unique_ptr<MappedBlockStream> Stream)
    : Stream(std::move(Stream)) {}

GlobalsStream::~GlobalsStream() = default;

Error GlobalsStream::reload() {
  BinaryStreamReader Reader(*Stream);
  if (auto E = GlobalsTable.read(Reader))
    return E;
  return Error::success();
}

std::vector<std::pair<uint32_t, codeview::CVSymbol>>
GlobalsStream::findRecordsByName(StringRef Name,
                                 const SymbolStream &Symbols) const {
  std::vector<std::pair<uint32_t, codeview::CVSymbol>> Result;

  // Hash the name to figure out which bucket this goes into.
  size_t ExpandedBucketIndex = hashStringV1(Name) % IPHR_HASH;
  int32_t CompressedBucketIndex = GlobalsTable.BucketMap[ExpandedBucketIndex];
  if (CompressedBucketIndex == -1)
    return Result;

  uint32_t LastBucketIndex = GlobalsTable.HashBuckets.size() - 1;
  uint32_t StartRecordIndex =
      GlobalsTable.HashBuckets[CompressedBucketIndex] / 12;
  uint32_t EndRecordIndex = 0;
  if (LLVM_LIKELY(uint32_t(CompressedBucketIndex) < LastBucketIndex)) {
    EndRecordIndex = GlobalsTable.HashBuckets[CompressedBucketIndex + 1];
  } else {
    // If this is the last bucket, it consists of all hash records until the end
    // of the HashRecords array.
    EndRecordIndex = GlobalsTable.HashRecords.size() * 12;
  }

  EndRecordIndex /= 12;

  assert(EndRecordIndex <= GlobalsTable.HashRecords.size());
  while (StartRecordIndex < EndRecordIndex) {
    PSHashRecord PSH = GlobalsTable.HashRecords[StartRecordIndex];
    uint32_t Off = PSH.Off - 1;
    codeview::CVSymbol Record = Symbols.readRecord(Off);
    if (codeview::getSymbolName(Record) == Name)
      Result.push_back(std::make_pair(Off, std::move(Record)));
    ++StartRecordIndex;
  }
  return Result;
}

static Error checkHashHdrVersion(const GSIHashHeader *HashHdr) {
  if (HashHdr->VerHdr != GSIHashHeader::HdrVersion)
    return make_error<RawError>(
        raw_error_code::feature_unsupported,
        "Encountered unsupported globals stream version.");

  return Error::success();
}

static Error readGSIHashHeader(const GSIHashHeader *&HashHdr,
                               BinaryStreamReader &Reader) {
  if (Reader.readObject(HashHdr))
    return make_error<RawError>(raw_error_code::corrupt_file,
                                "Stream does not contain a GSIHashHeader.");

  if (HashHdr->VerSignature != GSIHashHeader::HdrSignature)
    return make_error<RawError>(
        raw_error_code::feature_unsupported,
        "GSIHashHeader signature (0xffffffff) not found.");

  return Error::success();
}

static Error readGSIHashRecords(FixedStreamArray<PSHashRecord> &HashRecords,
                                const GSIHashHeader *HashHdr,
                                BinaryStreamReader &Reader) {
  if (auto EC = checkHashHdrVersion(HashHdr))
    return EC;

  // HashHdr->HrSize specifies the number of bytes of PSHashRecords we have.
  // Verify that we can read them all.
  if (HashHdr->HrSize % sizeof(PSHashRecord))
    return make_error<RawError>(raw_error_code::corrupt_file,
                                "Invalid HR array size.");
  uint32_t NumHashRecords = HashHdr->HrSize / sizeof(PSHashRecord);
  if (auto EC = Reader.readArray(HashRecords, NumHashRecords))
    return joinErrors(std::move(EC),
                      make_error<RawError>(raw_error_code::corrupt_file,
                                           "Error reading hash records."));

  return Error::success();
}

static Error
readGSIHashBuckets(FixedStreamArray<support::ulittle32_t> &HashBuckets,
                   FixedStreamArray<support::ulittle32_t> &HashBitmap,
                   const GSIHashHeader *HashHdr,
                   MutableArrayRef<int32_t> BucketMap,
                   BinaryStreamReader &Reader) {
  if (auto EC = checkHashHdrVersion(HashHdr))
    return EC;

  // Before the actual hash buckets, there is a bitmap of length determined by
  // IPHR_HASH.
  size_t BitmapSizeInBits = alignTo(IPHR_HASH + 1, 32);
  uint32_t NumBitmapEntries = BitmapSizeInBits / 32;
  if (auto EC = Reader.readArray(HashBitmap, NumBitmapEntries))
    return joinErrors(std::move(EC),
                      make_error<RawError>(raw_error_code::corrupt_file,
                                           "Could not read a bitmap."));
  uint32_t NumBuckets1 = 0;
  uint32_t CompressedBucketIdx = 0;
  for (uint32_t I = 0; I <= IPHR_HASH; ++I) {
    uint8_t WordIdx = I / 32;
    uint8_t BitIdx = I % 32;
    bool IsSet = HashBitmap[WordIdx] & (1U << BitIdx);
    if (IsSet) {
      ++NumBuckets1;
      BucketMap[I] = CompressedBucketIdx++;
    } else {
      BucketMap[I] = -1;
    }
  }

  uint32_t NumBuckets = 0;
  for (uint32_t B : HashBitmap)
    NumBuckets += countPopulation(B);

  // Hash buckets follow.
  if (auto EC = Reader.readArray(HashBuckets, NumBuckets))
    return joinErrors(std::move(EC),
                      make_error<RawError>(raw_error_code::corrupt_file,
                                           "Hash buckets corrupted."));

  return Error::success();
}

Error GSIHashTable::read(BinaryStreamReader &Reader) {
  if (auto EC = readGSIHashHeader(HashHdr, Reader))
    return EC;
  if (auto EC = readGSIHashRecords(HashRecords, HashHdr, Reader))
    return EC;
  if (HashHdr->HrSize > 0)
    if (auto EC = readGSIHashBuckets(HashBuckets, HashBitmap, HashHdr,
                                     BucketMap, Reader))
      return EC;
  return Error::success();
}