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