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