1//===- NameMap.cpp - PDB Name Map -------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "llvm/DebugInfo/PDB/Raw/NameMap.h"
11#include "llvm/ADT/SparseBitVector.h"
12#include "llvm/DebugInfo/CodeView/StreamReader.h"
13#include "llvm/DebugInfo/CodeView/StreamWriter.h"
14#include "llvm/DebugInfo/PDB/Raw/RawError.h"
15
16using namespace llvm;
17using namespace llvm::codeview;
18using namespace llvm::pdb;
19
20NameMap::NameMap() {}
21
22Error NameMap::load(codeview::StreamReader &Stream) {
23
24  // This is some sort of weird string-set/hash table encoded in the stream.
25  // It starts with the number of bytes in the table.
26  uint32_t NumberOfBytes;
27  if (auto EC = Stream.readInteger(NumberOfBytes))
28    return joinErrors(std::move(EC),
29                      make_error<RawError>(raw_error_code::corrupt_file,
30                                           "Expected name map length"));
31  if (Stream.bytesRemaining() < NumberOfBytes)
32    return make_error<RawError>(raw_error_code::corrupt_file,
33                                "Invalid name map length");
34
35  // Following that field is the starting offset of strings in the name table.
36  uint32_t StringsOffset = Stream.getOffset();
37  Stream.setOffset(StringsOffset + NumberOfBytes);
38
39  // This appears to be equivalent to the total number of strings *actually*
40  // in the name table.
41  uint32_t HashSize;
42  if (auto EC = Stream.readInteger(HashSize))
43    return joinErrors(std::move(EC),
44                      make_error<RawError>(raw_error_code::corrupt_file,
45                                           "Expected name map hash size"));
46
47  // This appears to be an upper bound on the number of strings in the name
48  // table.
49  uint32_t MaxNumberOfStrings;
50  if (auto EC = Stream.readInteger(MaxNumberOfStrings))
51    return joinErrors(std::move(EC),
52                      make_error<RawError>(raw_error_code::corrupt_file,
53                                           "Expected name map max strings"));
54
55  if (MaxNumberOfStrings > (UINT32_MAX / sizeof(uint32_t)))
56    return make_error<RawError>(raw_error_code::corrupt_file,
57                                "Implausible number of strings");
58
59  const uint32_t MaxNumberOfWords = UINT32_MAX / (sizeof(uint32_t) * 8);
60
61  // This appears to be a hash table which uses bitfields to determine whether
62  // or not a bucket is 'present'.
63  uint32_t NumPresentWords;
64  if (auto EC = Stream.readInteger(NumPresentWords))
65    return joinErrors(std::move(EC),
66                      make_error<RawError>(raw_error_code::corrupt_file,
67                                           "Expected name map num words"));
68
69  if (NumPresentWords > MaxNumberOfWords)
70    return make_error<RawError>(raw_error_code::corrupt_file,
71                                "Number of present words is too large");
72
73  SparseBitVector<> Present;
74  for (uint32_t I = 0; I != NumPresentWords; ++I) {
75    uint32_t Word;
76    if (auto EC = Stream.readInteger(Word))
77      return joinErrors(std::move(EC),
78                        make_error<RawError>(raw_error_code::corrupt_file,
79                                             "Expected name map word"));
80    for (unsigned Idx = 0; Idx < 32; ++Idx)
81      if (Word & (1U << Idx))
82        Present.set((I * 32) + Idx);
83  }
84
85  // This appears to be a hash table which uses bitfields to determine whether
86  // or not a bucket is 'deleted'.
87  uint32_t NumDeletedWords;
88  if (auto EC = Stream.readInteger(NumDeletedWords))
89    return joinErrors(
90        std::move(EC),
91        make_error<RawError>(raw_error_code::corrupt_file,
92                             "Expected name map num deleted words"));
93
94  if (NumDeletedWords > MaxNumberOfWords)
95    return make_error<RawError>(raw_error_code::corrupt_file,
96                                "Number of deleted words is too large");
97
98  SparseBitVector<> Deleted;
99  for (uint32_t I = 0; I != NumDeletedWords; ++I) {
100    uint32_t Word;
101    if (auto EC = Stream.readInteger(Word))
102      return joinErrors(std::move(EC),
103                        make_error<RawError>(raw_error_code::corrupt_file,
104                                             "Expected name map word"));
105    for (unsigned Idx = 0; Idx < 32; ++Idx)
106      if (Word & (1U << Idx))
107        Deleted.set((I * 32) + Idx);
108  }
109
110  for (unsigned I : Present) {
111    // For all present entries, dump out their mapping.
112    (void)I;
113
114    // This appears to be an offset relative to the start of the strings.
115    // It tells us where the null-terminated string begins.
116    uint32_t NameOffset;
117    if (auto EC = Stream.readInteger(NameOffset))
118      return joinErrors(std::move(EC),
119                        make_error<RawError>(raw_error_code::corrupt_file,
120                                             "Expected name map name offset"));
121
122    // This appears to be a stream number into the stream directory.
123    uint32_t NameIndex;
124    if (auto EC = Stream.readInteger(NameIndex))
125      return joinErrors(std::move(EC),
126                        make_error<RawError>(raw_error_code::corrupt_file,
127                                             "Expected name map name index"));
128
129    // Compute the offset of the start of the string relative to the stream.
130    uint32_t StringOffset = StringsOffset + NameOffset;
131    uint32_t OldOffset = Stream.getOffset();
132    // Pump out our c-string from the stream.
133    StringRef Str;
134    Stream.setOffset(StringOffset);
135    if (auto EC = Stream.readZeroString(Str))
136      return joinErrors(std::move(EC),
137                        make_error<RawError>(raw_error_code::corrupt_file,
138                                             "Expected name map name"));
139
140    Stream.setOffset(OldOffset);
141    // Add this to a string-map from name to stream number.
142    Mapping.insert({Str, NameIndex});
143  }
144
145  return Error::success();
146}
147
148Error NameMap::commit(codeview::StreamWriter &Writer) {
149  if (auto EC = Writer.writeInteger(0U)) // Number of bytes in table
150    return EC;
151
152  if (auto EC = Writer.writeInteger(0U)) // Hash Size
153    return EC;
154
155  if (auto EC = Writer.writeInteger(0U)) // Max Number of Strings
156    return EC;
157
158  if (auto EC = Writer.writeInteger(0U)) // Num Present Words
159    return EC;
160
161  if (auto EC = Writer.writeInteger(0U)) // Num Deleted Words
162    return EC;
163  return Error::success();
164}
165
166iterator_range<StringMapConstIterator<uint32_t>> NameMap::entries() const {
167  return llvm::make_range<StringMapConstIterator<uint32_t>>(Mapping.begin(),
168                                                            Mapping.end());
169}
170
171bool NameMap::tryGetValue(StringRef Name, uint32_t &Value) const {
172  auto Iter = Mapping.find(Name);
173  if (Iter == Mapping.end())
174    return false;
175  Value = Iter->second;
176  return true;
177}
178