1 //===- NamedStreamMap.cpp - PDB Named Stream 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/Native/NamedStreamMap.h"
11 
12 #include "llvm/ADT/SparseBitVector.h"
13 #include "llvm/ADT/StringMap.h"
14 #include "llvm/ADT/StringRef.h"
15 #include "llvm/ADT/iterator_range.h"
16 #include "llvm/DebugInfo/PDB/Native/HashTable.h"
17 #include "llvm/DebugInfo/PDB/Native/RawError.h"
18 #include "llvm/Support/BinaryStreamReader.h"
19 #include "llvm/Support/Error.h"
20 #include <algorithm>
21 #include <cstdint>
22 
23 using namespace llvm;
24 using namespace llvm::pdb;
25 
26 // FIXME: This shouldn't be necessary, but if we insert the strings in any
27 // other order, cvdump cannot read the generated name map.  This suggests that
28 // we may be using the wrong hash function.  A closer inspection of the cvdump
29 // source code may reveal something, but for now this at least makes us work,
30 // even if only by accident.
31 static constexpr const char *OrderedStreamNames[] = {"/LinkInfo", "/names",
32                                                      "/src/headerblock"};
33 
34 NamedStreamMap::NamedStreamMap() = default;
35 
36 Error NamedStreamMap::load(BinaryStreamReader &Stream) {
37   Mapping.clear();
38   FinalizedHashTable.clear();
39   FinalizedInfo.reset();
40 
41   uint32_t StringBufferSize;
42   if (auto EC = Stream.readInteger(StringBufferSize))
43     return joinErrors(std::move(EC),
44                       make_error<RawError>(raw_error_code::corrupt_file,
45                                            "Expected string buffer size"));
46 
47   BinaryStreamRef StringsBuffer;
48   if (auto EC = Stream.readStreamRef(StringsBuffer, StringBufferSize))
49     return EC;
50 
51   HashTable OffsetIndexMap;
52   if (auto EC = OffsetIndexMap.load(Stream))
53     return EC;
54 
55   uint32_t NameOffset;
56   uint32_t NameIndex;
57   for (const auto &Entry : OffsetIndexMap) {
58     std::tie(NameOffset, NameIndex) = Entry;
59 
60     // Compute the offset of the start of the string relative to the stream.
61     BinaryStreamReader NameReader(StringsBuffer);
62     NameReader.setOffset(NameOffset);
63     // Pump out our c-string from the stream.
64     StringRef Str;
65     if (auto EC = NameReader.readCString(Str))
66       return joinErrors(std::move(EC),
67                         make_error<RawError>(raw_error_code::corrupt_file,
68                                              "Expected name map name"));
69 
70     // Add this to a string-map from name to stream number.
71     Mapping.insert({Str, NameIndex});
72   }
73 
74   return Error::success();
75 }
76 
77 Error NamedStreamMap::commit(BinaryStreamWriter &Writer) const {
78   assert(FinalizedInfo.hasValue());
79 
80   // The first field is the number of bytes of string data.
81   if (auto EC = Writer.writeInteger(FinalizedInfo->StringDataBytes))
82     return EC;
83 
84   for (const auto &Name : OrderedStreamNames) {
85     auto Item = Mapping.find(Name);
86     assert(Item != Mapping.end());
87     if (auto EC = Writer.writeCString(Item->getKey()))
88       return EC;
89   }
90 
91   // And finally the Offset Index map.
92   if (auto EC = FinalizedHashTable.commit(Writer))
93     return EC;
94 
95   return Error::success();
96 }
97 
98 uint32_t NamedStreamMap::finalize() {
99   if (FinalizedInfo.hasValue())
100     return FinalizedInfo->SerializedLength;
101 
102   // Build the finalized hash table.
103   FinalizedHashTable.clear();
104   FinalizedInfo.emplace();
105 
106   for (const auto &Name : OrderedStreamNames) {
107     auto Item = Mapping.find(Name);
108     assert(Item != Mapping.end());
109     FinalizedHashTable.set(FinalizedInfo->StringDataBytes, Item->getValue());
110     FinalizedInfo->StringDataBytes += Item->getKeyLength() + 1;
111   }
112 
113   // Number of bytes of string data.
114   FinalizedInfo->SerializedLength += sizeof(support::ulittle32_t);
115   // Followed by that many actual bytes of string data.
116   FinalizedInfo->SerializedLength += FinalizedInfo->StringDataBytes;
117   // Followed by the mapping from Offset to Index.
118   FinalizedInfo->SerializedLength +=
119       FinalizedHashTable.calculateSerializedLength();
120   return FinalizedInfo->SerializedLength;
121 }
122 
123 iterator_range<StringMapConstIterator<uint32_t>>
124 NamedStreamMap::entries() const {
125   return make_range<StringMapConstIterator<uint32_t>>(Mapping.begin(),
126                                                       Mapping.end());
127 }
128 
129 uint32_t NamedStreamMap::size() const { return Mapping.size(); }
130 
131 bool NamedStreamMap::get(StringRef Stream, uint32_t &StreamNo) const {
132   auto Iter = Mapping.find(Stream);
133   if (Iter == Mapping.end())
134     return false;
135   StreamNo = Iter->second;
136   return true;
137 }
138 
139 void NamedStreamMap::set(StringRef Stream, uint32_t StreamNo) {
140   FinalizedInfo.reset();
141   Mapping[Stream] = StreamNo;
142 }
143 
144 void NamedStreamMap::remove(StringRef Stream) {
145   FinalizedInfo.reset();
146   Mapping.erase(Stream);
147 }
148