1//===- PDBStringTable.cpp - PDB String Table ---------------------*- 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#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"
10
11#include "llvm/ADT/ArrayRef.h"
12#include "llvm/DebugInfo/PDB/Native/Hash.h"
13#include "llvm/DebugInfo/PDB/Native/RawError.h"
14#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
15#include "llvm/Support/BinaryStreamReader.h"
16#include "llvm/Support/Endian.h"
17
18using namespace llvm;
19using namespace llvm::support;
20using namespace llvm::pdb;
21
22uint32_t PDBStringTable::getByteSize() const { return Header->ByteSize; }
23uint32_t PDBStringTable::getNameCount() const { return NameCount; }
24uint32_t PDBStringTable::getHashVersion() const { return Header->HashVersion; }
25uint32_t PDBStringTable::getSignature() const { return Header->Signature; }
26
27Error PDBStringTable::readHeader(BinaryStreamReader &Reader) {
28  if (auto EC = Reader.readObject(Header))
29    return EC;
30
31  if (Header->Signature != PDBStringTableSignature)
32    return make_error<RawError>(raw_error_code::corrupt_file,
33                                "Invalid hash table signature");
34  if (Header->HashVersion != 1 && Header->HashVersion != 2)
35    return make_error<RawError>(raw_error_code::corrupt_file,
36                                "Unsupported hash version");
37
38  assert(Reader.bytesRemaining() == 0);
39  return Error::success();
40}
41
42Error PDBStringTable::readStrings(BinaryStreamReader &Reader) {
43  BinaryStreamRef Stream;
44  if (auto EC = Reader.readStreamRef(Stream))
45    return EC;
46
47  if (auto EC = Strings.initialize(Stream)) {
48    return joinErrors(std::move(EC),
49                      make_error<RawError>(raw_error_code::corrupt_file,
50                                           "Invalid hash table byte length"));
51  }
52
53  assert(Reader.bytesRemaining() == 0);
54  return Error::success();
55}
56
57const codeview::DebugStringTableSubsectionRef &
58PDBStringTable::getStringTable() const {
59  return Strings;
60}
61
62Error PDBStringTable::readHashTable(BinaryStreamReader &Reader) {
63  const support::ulittle32_t *HashCount;
64  if (auto EC = Reader.readObject(HashCount))
65    return EC;
66
67  if (auto EC = Reader.readArray(IDs, *HashCount)) {
68    return joinErrors(std::move(EC),
69                      make_error<RawError>(raw_error_code::corrupt_file,
70                                           "Could not read bucket array"));
71  }
72
73  return Error::success();
74}
75
76Error PDBStringTable::readEpilogue(BinaryStreamReader &Reader) {
77  if (auto EC = Reader.readInteger(NameCount))
78    return EC;
79
80  assert(Reader.bytesRemaining() == 0);
81  return Error::success();
82}
83
84Error PDBStringTable::reload(BinaryStreamReader &Reader) {
85
86  BinaryStreamReader SectionReader;
87
88  std::tie(SectionReader, Reader) = Reader.split(sizeof(PDBStringTableHeader));
89  if (auto EC = readHeader(SectionReader))
90    return EC;
91
92  std::tie(SectionReader, Reader) = Reader.split(Header->ByteSize);
93  if (auto EC = readStrings(SectionReader))
94    return EC;
95
96  // We don't know how long the hash table is until we parse it, so let the
97  // function responsible for doing that figure it out.
98  if (auto EC = readHashTable(Reader))
99    return EC;
100
101  std::tie(SectionReader, Reader) = Reader.split(sizeof(uint32_t));
102  if (auto EC = readEpilogue(SectionReader))
103    return EC;
104
105  assert(Reader.bytesRemaining() == 0);
106  return Error::success();
107}
108
109Expected<StringRef> PDBStringTable::getStringForID(uint32_t ID) const {
110  return Strings.getString(ID);
111}
112
113Expected<uint32_t> PDBStringTable::getIDForString(StringRef Str) const {
114  uint32_t Hash =
115      (Header->HashVersion == 1) ? hashStringV1(Str) : hashStringV2(Str);
116  size_t Count = IDs.size();
117  uint32_t Start = Hash % Count;
118  for (size_t I = 0; I < Count; ++I) {
119    // The hash is just a starting point for the search, but if it
120    // doesn't work we should find the string no matter what, because
121    // we iterate the entire array.
122    uint32_t Index = (Start + I) % Count;
123
124    // If we find 0, it means the item isn't in the hash table.
125    uint32_t ID = IDs[Index];
126    if (ID == 0)
127      return make_error<RawError>(raw_error_code::no_entry);
128    auto ExpectedStr = getStringForID(ID);
129    if (!ExpectedStr)
130      return ExpectedStr.takeError();
131
132    if (*ExpectedStr == Str)
133      return ID;
134  }
135  return make_error<RawError>(raw_error_code::no_entry);
136}
137
138FixedStreamArray<support::ulittle32_t> PDBStringTable::name_ids() const {
139  return IDs;
140}
141