1 //===- PDBStringTable.cpp - PDB String Table -----------------------*- C++
2 //-*-===//
3 //
4 //                     The LLVM Compiler Infrastructure
5 //
6 // This file is distributed under the University of Illinois Open Source
7 // License. See LICENSE.TXT for details.
8 //
9 //===----------------------------------------------------------------------===//
10 
11 #include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"
12 
13 #include "llvm/ADT/ArrayRef.h"
14 #include "llvm/DebugInfo/PDB/Native/Hash.h"
15 #include "llvm/DebugInfo/PDB/Native/RawError.h"
16 #include "llvm/DebugInfo/PDB/Native/RawTypes.h"
17 #include "llvm/Support/BinaryStreamReader.h"
18 #include "llvm/Support/Endian.h"
19 
20 using namespace llvm;
21 using namespace llvm::support;
22 using namespace llvm::pdb;
23 
24 PDBStringTable::PDBStringTable() {}
25 
26 Error PDBStringTable::load(BinaryStreamReader &Stream) {
27   ByteSize = Stream.getLength();
28 
29   const PDBStringTableHeader *H;
30   if (auto EC = Stream.readObject(H))
31     return EC;
32 
33   if (H->Signature != PDBStringTableSignature)
34     return make_error<RawError>(raw_error_code::corrupt_file,
35                                 "Invalid hash table signature");
36   if (H->HashVersion != 1 && H->HashVersion != 2)
37     return make_error<RawError>(raw_error_code::corrupt_file,
38                                 "Unsupported hash version");
39 
40   Signature = H->Signature;
41   HashVersion = H->HashVersion;
42   if (auto EC = Stream.readStreamRef(NamesBuffer, H->ByteSize))
43     return joinErrors(std::move(EC),
44                       make_error<RawError>(raw_error_code::corrupt_file,
45                                            "Invalid hash table byte length"));
46 
47   const support::ulittle32_t *HashCount;
48   if (auto EC = Stream.readObject(HashCount))
49     return EC;
50 
51   if (auto EC = Stream.readArray(IDs, *HashCount))
52     return joinErrors(std::move(EC),
53                       make_error<RawError>(raw_error_code::corrupt_file,
54                                            "Could not read bucket array"));
55 
56   if (Stream.bytesRemaining() < sizeof(support::ulittle32_t))
57     return make_error<RawError>(raw_error_code::corrupt_file,
58                                 "Missing name count");
59 
60   if (auto EC = Stream.readInteger(NameCount))
61     return EC;
62 
63   if (Stream.bytesRemaining() > 0)
64     return make_error<RawError>(raw_error_code::stream_too_long,
65                                 "Unexpected bytes found in string table");
66 
67   return Error::success();
68 }
69 
70 uint32_t PDBStringTable::getByteSize() const { return ByteSize; }
71 
72 StringRef PDBStringTable::getStringForID(uint32_t ID) const {
73   if (ID == IDs[0])
74     return StringRef();
75 
76   // NamesBuffer is a buffer of null terminated strings back to back.  ID is
77   // the starting offset of the string we're looking for.  So just seek into
78   // the desired offset and a read a null terminated stream from that offset.
79   StringRef Result;
80   BinaryStreamReader NameReader(NamesBuffer);
81   NameReader.setOffset(ID);
82   if (auto EC = NameReader.readCString(Result))
83     consumeError(std::move(EC));
84   return Result;
85 }
86 
87 uint32_t PDBStringTable::getIDForString(StringRef Str) const {
88   uint32_t Hash = (HashVersion == 1) ? hashStringV1(Str) : hashStringV2(Str);
89   size_t Count = IDs.size();
90   uint32_t Start = Hash % Count;
91   for (size_t I = 0; I < Count; ++I) {
92     // The hash is just a starting point for the search, but if it
93     // doesn't work we should find the string no matter what, because
94     // we iterate the entire array.
95     uint32_t Index = (Start + I) % Count;
96 
97     uint32_t ID = IDs[Index];
98     StringRef S = getStringForID(ID);
99     if (S == Str)
100       return ID;
101   }
102   // IDs[0] contains the ID of the "invalid" entry.
103   return IDs[0];
104 }
105 
106 FixedStreamArray<support::ulittle32_t> PDBStringTable::name_ids() const {
107   return IDs;
108 }
109