blob: eb7a0bbcc3d516cf8e8c9cab5d5c6440f764c50d [file] [log] [blame]
Zachary Turner946204c2017-08-09 04:23:25 +00001//===- DbiStreamBuilder.cpp - PDB Dbi Stream Creation -----------*- 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/GSIStreamBuilder.h"
11
12#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
13#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
14#include "llvm/DebugInfo/CodeView/SymbolSerializer.h"
15#include "llvm/DebugInfo/MSF/MSFBuilder.h"
16#include "llvm/DebugInfo/MSF/MSFCommon.h"
17#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
18#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
19#include "llvm/DebugInfo/PDB/Native/Hash.h"
20#include "llvm/Support/BinaryItemStream.h"
21#include "llvm/Support/BinaryStreamWriter.h"
22#include <algorithm>
23#include <vector>
24
25using namespace llvm;
26using namespace llvm::msf;
27using namespace llvm::pdb;
28using namespace llvm::codeview;
29
30static StringRef getSymbolName(const CVSymbol &Sym) {
31 assert(Sym.kind() == S_PUB32 && "handle other kinds");
32 PublicSym32 PSL =
33 cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(Sym));
34 return PSL.Name;
35}
36
37struct llvm::pdb::GSIHashStreamBuilder {
38 std::vector<CVSymbol> Records;
39 uint32_t StreamIndex;
40 std::vector<PSHashRecord> HashRecords;
41 std::array<support::ulittle32_t, (IPHR_HASH + 32) / 32> HashBitmap;
42 std::vector<support::ulittle32_t> HashBuckets;
43
44 uint32_t calculateSerializedLength() const;
45 uint32_t calculateRecordByteSize() const;
46 Error commit(BinaryStreamWriter &Writer);
47 void finalizeBuckets(uint32_t RecordZeroOffset);
48};
49
50uint32_t GSIHashStreamBuilder::calculateSerializedLength() const {
51 uint32_t Size = sizeof(GSIHashHeader);
52 Size += HashRecords.size() * sizeof(PSHashRecord);
53 Size += HashBitmap.size() * sizeof(uint32_t);
54 Size += HashBuckets.size() * sizeof(uint32_t);
55 return Size;
56}
57
58uint32_t GSIHashStreamBuilder::calculateRecordByteSize() const {
59 uint32_t Size = 0;
60 for (const auto &Sym : Records)
61 Size += Sym.length();
62 return Size;
63}
64
65Error GSIHashStreamBuilder::commit(BinaryStreamWriter &Writer) {
66 GSIHashHeader Header;
67 Header.VerSignature = GSIHashHeader::HdrSignature;
68 Header.VerHdr = GSIHashHeader::HdrVersion;
69 Header.HrSize = HashRecords.size() * sizeof(PSHashRecord);
70 Header.NumBuckets = HashBitmap.size() * 4 + HashBuckets.size() * 4;
71
72 if (auto EC = Writer.writeObject(Header))
73 return EC;
74
75 if (auto EC = Writer.writeArray(makeArrayRef(HashRecords)))
76 return EC;
77 if (auto EC = Writer.writeArray(makeArrayRef(HashBitmap)))
78 return EC;
79 if (auto EC = Writer.writeArray(makeArrayRef(HashBuckets)))
80 return EC;
81 return Error::success();
82}
83
84void GSIHashStreamBuilder::finalizeBuckets(uint32_t RecordZeroOffset) {
85 std::array<std::vector<PSHashRecord>, IPHR_HASH + 1> TmpBuckets;
86 uint32_t SymOffset = RecordZeroOffset;
87 for (const CVSymbol &Sym : Records) {
88 PSHashRecord HR;
89 // Add one when writing symbol offsets to disk. See GSI1::fixSymRecs.
90 HR.Off = SymOffset + 1;
91 HR.CRef = 1; // Always use a refcount of 1.
92
93 // Hash the name to figure out which bucket this goes into.
94 StringRef Name = getSymbolName(Sym);
95 size_t BucketIdx = hashStringV1(Name) % IPHR_HASH;
96 TmpBuckets[BucketIdx].push_back(HR); // FIXME: Does order matter?
97
98 SymOffset += Sym.length();
99 }
100
101 // Compute the three tables: the hash records in bucket and chain order, the
102 // bucket presence bitmap, and the bucket chain start offsets.
103 HashRecords.reserve(Records.size());
104 for (ulittle32_t &Word : HashBitmap)
105 Word = 0;
106 for (size_t BucketIdx = 0; BucketIdx < IPHR_HASH + 1; ++BucketIdx) {
107 auto &Bucket = TmpBuckets[BucketIdx];
108 if (Bucket.empty())
109 continue;
110 HashBitmap[BucketIdx / 32] |= 1U << (BucketIdx % 32);
111
112 // Calculate what the offset of the first hash record in the chain would
113 // be if it were inflated to contain 32-bit pointers. On a 32-bit system,
114 // each record would be 12 bytes. See HROffsetCalc in gsi.h.
115 const int SizeOfHROffsetCalc = 12;
116 ulittle32_t ChainStartOff =
117 ulittle32_t(HashRecords.size() * SizeOfHROffsetCalc);
118 HashBuckets.push_back(ChainStartOff);
119 for (const auto &HR : Bucket)
120 HashRecords.push_back(HR);
121 }
122}
123
124GSIStreamBuilder::GSIStreamBuilder(msf::MSFBuilder &Msf)
125 : Msf(Msf), PSH(llvm::make_unique<GSIHashStreamBuilder>()),
126 GSH(llvm::make_unique<GSIHashStreamBuilder>()) {}
127
128GSIStreamBuilder::~GSIStreamBuilder() {}
129
130uint32_t GSIStreamBuilder::calculatePublicsHashStreamSize() const {
131 uint32_t Size = 0;
132 Size += sizeof(PublicsStreamHeader);
133 Size += PSH->calculateSerializedLength();
134 Size += PSH->Records.size() * sizeof(uint32_t); // AddrMap
135 // FIXME: Add thunk map and section offsets for incremental linking.
136
137 return Size;
138}
139
140uint32_t GSIStreamBuilder::calculateGlobalsHashStreamSize() const {
141 return GSH->calculateSerializedLength();
142}
143
144Error GSIStreamBuilder::finalizeMsfLayout() {
145 // First we write public symbol records, then we write global symbol records.
146 uint32_t PSHZero = 0;
147 uint32_t GSHZero = PSH->calculateRecordByteSize();
148
149 PSH->finalizeBuckets(PSHZero);
150 GSH->finalizeBuckets(GSHZero);
151
152 Expected<uint32_t> Idx = Msf.addStream(calculatePublicsHashStreamSize());
153 if (!Idx)
154 return Idx.takeError();
155 PSH->StreamIndex = *Idx;
156
157 Idx = Msf.addStream(calculateGlobalsHashStreamSize());
158 if (!Idx)
159 return Idx.takeError();
160 GSH->StreamIndex = *Idx;
161
162 uint32_t RecordBytes =
163 GSH->calculateRecordByteSize() + PSH->calculateRecordByteSize();
164
165 Idx = Msf.addStream(RecordBytes);
166 if (!Idx)
167 return Idx.takeError();
168 RecordStreamIdx = *Idx;
169 return Error::success();
170}
171
172bool comparePubSymByAddrAndName(const CVSymbol *LS, const CVSymbol *RS) {
173 assert(LS->kind() == SymbolKind::S_PUB32);
174 assert(RS->kind() == SymbolKind::S_PUB32);
175
176 PublicSym32 PSL =
177 cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(*LS));
178 PublicSym32 PSR =
179 cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(*RS));
180
181 if (PSL.Segment != PSR.Segment)
182 return PSL.Segment < PSR.Segment;
183 if (PSL.Offset != PSR.Offset)
184 return PSL.Offset < PSR.Offset;
185
186 return PSL.Name < PSR.Name;
187}
188
189/// Compute the address map. The address map is an array of symbol offsets
190/// sorted so that it can be binary searched by address.
191static std::vector<ulittle32_t> computeAddrMap(ArrayRef<CVSymbol> Records) {
192 // Make a vector of pointers to the symbols so we can sort it by address.
193 // Also gather the symbol offsets while we're at it.
194 std::vector<const CVSymbol *> PublicsByAddr;
195 std::vector<uint32_t> SymOffsets;
196 PublicsByAddr.reserve(Records.size());
197 uint32_t SymOffset = 0;
198 for (const CVSymbol &Sym : Records) {
199 PublicsByAddr.push_back(&Sym);
200 SymOffsets.push_back(SymOffset);
201 SymOffset += Sym.length();
202 }
203 std::stable_sort(PublicsByAddr.begin(), PublicsByAddr.end(),
204 comparePubSymByAddrAndName);
205
206 // Fill in the symbol offsets in the appropriate order.
207 std::vector<ulittle32_t> AddrMap;
208 AddrMap.reserve(Records.size());
209 for (const CVSymbol *Sym : PublicsByAddr) {
210 ptrdiff_t Idx = std::distance(Records.data(), Sym);
211 assert(Idx >= 0 && size_t(Idx) < Records.size());
212 AddrMap.push_back(ulittle32_t(SymOffsets[Idx]));
213 }
214 return AddrMap;
215}
216
217uint32_t GSIStreamBuilder::getPublicsStreamIndex() const {
218 return PSH->StreamIndex;
219}
220
221uint32_t GSIStreamBuilder::getGlobalsStreamIndex() const {
222 return GSH->StreamIndex;
223}
224
225void GSIStreamBuilder::addPublicSymbol(const PublicSym32 &Pub) {
226 PublicSym32 Copy(Pub);
227 PSH->Records.push_back(SymbolSerializer::writeOneSymbol(
228 Copy, Msf.getAllocator(), CodeViewContainer::Pdb));
229}
230
231static Error writeRecords(BinaryStreamWriter &Writer,
232 ArrayRef<CVSymbol> Records) {
233 BinaryItemStream<CVSymbol> ItemStream(support::endianness::little);
234 ItemStream.setItems(Records);
235 BinaryStreamRef RecordsRef(ItemStream);
236 return Writer.writeStreamRef(RecordsRef);
237}
238
239Error GSIStreamBuilder::commitSymbolRecordStream(
240 WritableBinaryStreamRef Stream) {
241 BinaryStreamWriter Writer(Stream);
242
243 // Write public symbol records first, followed by global symbol records. This
244 // must match the order that we assume in finalizeMsfLayout when computing
245 // PSHZero and GSHZero.
246 if (auto EC = writeRecords(Writer, PSH->Records))
247 return EC;
248 if (auto EC = writeRecords(Writer, GSH->Records))
249 return EC;
250
251 return Error::success();
252}
253
254Error GSIStreamBuilder::commitPublicsHashStream(
255 WritableBinaryStreamRef Stream) {
256 // Skip the publics stream header so that we can write the GSH header first.
257 // Then seek back to the beginning and update the publics stream header with
258 // the byte offset after the GSH header.
259 BinaryStreamWriter Writer(Stream);
260 Writer.setOffset(sizeof(PublicsStreamHeader));
261
262 if (auto EC = PSH->commit(Writer))
263 return EC;
264 uint32_t OffsetAfterGSIHashes = Writer.getOffset();
265
266 Writer.setOffset(0);
267
268 // FIXME: Fill these in. They are for incremental linking.
269 PublicsStreamHeader Header;
270 Header.AddrMap = PSH->Records.size() * 4;
271
272 Header.NumThunks = 0;
273 Header.SizeOfThunk = 0;
274 Header.ISectThunkTable = 0;
275 Header.OffThunkTable = 0;
276 Header.NumSections = 0;
277 Header.SymHash = OffsetAfterGSIHashes;
278 if (auto EC = Writer.writeObject(Header))
279 return EC;
280
281 Writer.setOffset(OffsetAfterGSIHashes);
282
283 std::vector<ulittle32_t> AddrMap = computeAddrMap(PSH->Records);
284 if (auto EC = Writer.writeArray(makeArrayRef(AddrMap)))
285 return EC;
286
287 return Error::success();
288}
289
290Error GSIStreamBuilder::commitGlobalsHashStream(
291 WritableBinaryStreamRef Stream) {
292 BinaryStreamWriter Writer(Stream);
293 return GSH->commit(Writer);
294}
295
296Error GSIStreamBuilder::commit(const msf::MSFLayout &Layout,
297 WritableBinaryStreamRef Buffer) {
298 auto GS = WritableMappedBlockStream::createIndexedStream(
299 Layout, Buffer, getGlobalsStreamIndex(), Msf.getAllocator());
300 auto PS = WritableMappedBlockStream::createIndexedStream(
301 Layout, Buffer, getPublicsStreamIndex(), Msf.getAllocator());
302 auto PRS = WritableMappedBlockStream::createIndexedStream(
303 Layout, Buffer, getRecordStreamIdx(), Msf.getAllocator());
304
305 if (auto EC = commitSymbolRecordStream(*PRS))
306 return EC;
307 if (auto EC = commitGlobalsHashStream(*GS))
308 return EC;
309 if (auto EC = commitPublicsHashStream(*PS))
310 return EC;
311 return Error::success();
312}