blob: 9b807c1653e75ee4b754f09d9a6325e3f082ad90 [file] [log] [blame]
Zachary Turner7eaf1d92017-07-10 22:40:20 +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/PublicsStreamBuilder.h"
Reid Klecknereacdf042017-07-27 18:25:59 +000011#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
12#include "llvm/DebugInfo/CodeView/SymbolSerializer.h"
Zachary Turner7eaf1d92017-07-10 22:40:20 +000013#include "llvm/DebugInfo/MSF/MSFBuilder.h"
14#include "llvm/DebugInfo/MSF/MSFCommon.h"
15#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
Reid Kleckner14d90fd2017-07-26 00:40:36 +000016#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
Reid Klecknereacdf042017-07-27 18:25:59 +000017#include "llvm/DebugInfo/PDB/Native/Hash.h"
18#include "llvm/Support/BinaryItemStream.h"
19#include "llvm/Support/BinaryStreamWriter.h"
20#include <algorithm>
21#include <vector>
Zachary Turner7eaf1d92017-07-10 22:40:20 +000022
Zachary Turner7eaf1d92017-07-10 22:40:20 +000023using namespace llvm;
24using namespace llvm::msf;
25using namespace llvm::pdb;
Reid Klecknereacdf042017-07-27 18:25:59 +000026using namespace llvm::codeview;
Zachary Turner7eaf1d92017-07-10 22:40:20 +000027
Reid Klecknereacdf042017-07-27 18:25:59 +000028PublicsStreamBuilder::PublicsStreamBuilder(msf::MSFBuilder &Msf)
29 : Table(new GSIHashTableBuilder), Msf(Msf) {}
Zachary Turner7eaf1d92017-07-10 22:40:20 +000030
31PublicsStreamBuilder::~PublicsStreamBuilder() {}
32
33uint32_t PublicsStreamBuilder::calculateSerializedLength() const {
34 uint32_t Size = 0;
35 Size += sizeof(PublicsStreamHeader);
36 Size += sizeof(GSIHashHeader);
Reid Klecknereacdf042017-07-27 18:25:59 +000037 Size += Table->HashRecords.size() * sizeof(PSHashRecord);
38 Size += Table->HashBitmap.size() * sizeof(uint32_t);
39 Size += Table->HashBuckets.size() * sizeof(uint32_t);
Zachary Turner7eaf1d92017-07-10 22:40:20 +000040
Reid Klecknereacdf042017-07-27 18:25:59 +000041 Size += Publics.size() * sizeof(uint32_t); // AddrMap
42
43 // FIXME: Add thunk map and section offsets for incremental linking.
44
Zachary Turner7eaf1d92017-07-10 22:40:20 +000045 return Size;
46}
47
48Error PublicsStreamBuilder::finalizeMsfLayout() {
Reid Klecknereacdf042017-07-27 18:25:59 +000049 Table->addSymbols(Publics);
50
Zachary Turner7eaf1d92017-07-10 22:40:20 +000051 Expected<uint32_t> Idx = Msf.addStream(calculateSerializedLength());
52 if (!Idx)
53 return Idx.takeError();
54 StreamIdx = *Idx;
55
Reid Klecknereacdf042017-07-27 18:25:59 +000056 uint32_t PublicRecordBytes = 0;
57 for (auto &Pub : Publics)
58 PublicRecordBytes += Pub.length();
59
60 Expected<uint32_t> RecordIdx = Msf.addStream(PublicRecordBytes);
Zachary Turner7eaf1d92017-07-10 22:40:20 +000061 if (!RecordIdx)
62 return RecordIdx.takeError();
63 RecordStreamIdx = *RecordIdx;
64 return Error::success();
65}
66
Reid Klecknereacdf042017-07-27 18:25:59 +000067void PublicsStreamBuilder::addPublicSymbol(const PublicSym32 &Pub) {
68 Publics.push_back(SymbolSerializer::writeOneSymbol(
69 const_cast<PublicSym32 &>(Pub), Msf.getAllocator(),
70 CodeViewContainer::Pdb));
71}
72
73// FIXME: Put this back in the header.
74struct PubSymLayout {
75 ulittle16_t reclen;
76 ulittle16_t reckind;
77 ulittle32_t flags;
78 ulittle32_t off;
79 ulittle16_t seg;
80 char name[1];
81};
82
83bool comparePubSymByAddrAndName(const CVSymbol *LS, const CVSymbol *RS) {
84 assert(LS->length() > sizeof(PubSymLayout) &&
85 RS->length() > sizeof(PubSymLayout));
86 auto *L = reinterpret_cast<const PubSymLayout *>(LS->data().data());
87 auto *R = reinterpret_cast<const PubSymLayout *>(RS->data().data());
88 if (L->seg < R->seg)
89 return true;
90 if (L->seg > R->seg)
91 return false;
92 if (L->off < R->off)
93 return true;
94 if (L->off > R->off)
95 return false;
96 return strcmp(L->name, R->name) < 0;
97}
98
99static StringRef getSymbolName(const CVSymbol &Sym) {
100 assert(Sym.kind() == S_PUB32 && "handle other kinds");
101 ArrayRef<uint8_t> NameBytes =
102 Sym.data().drop_front(offsetof(PubSymLayout, name));
103 return StringRef(reinterpret_cast<const char *>(NameBytes.data()),
104 NameBytes.size())
105 .trim('\0');
106}
107
108/// Compute the address map. The address map is an array of symbol offsets
109/// sorted so that it can be binary searched by address.
110static std::vector<ulittle32_t> computeAddrMap(ArrayRef<CVSymbol> Publics) {
111 // Make a vector of pointers to the symbols so we can sort it by address.
112 // Also gather the symbol offsets while we're at it.
113 std::vector<const CVSymbol *> PublicsByAddr;
114 std::vector<uint32_t> SymOffsets;
115 PublicsByAddr.reserve(Publics.size());
116 uint32_t SymOffset = 0;
117 for (const CVSymbol &Sym : Publics) {
118 PublicsByAddr.push_back(&Sym);
119 SymOffsets.push_back(SymOffset);
120 SymOffset += Sym.length();
121 }
122 std::stable_sort(PublicsByAddr.begin(), PublicsByAddr.end(),
123 comparePubSymByAddrAndName);
124
125 // Fill in the symbol offsets in the appropriate order.
126 std::vector<ulittle32_t> AddrMap;
127 AddrMap.reserve(Publics.size());
128 for (const CVSymbol *Sym : PublicsByAddr) {
129 ptrdiff_t Idx = std::distance(Publics.data(), Sym);
130 assert(Idx >= 0 && size_t(Idx) < Publics.size());
131 AddrMap.push_back(ulittle32_t(SymOffsets[Idx]));
132 }
133 return AddrMap;
134}
135
136Error PublicsStreamBuilder::commit(BinaryStreamWriter &PublicsWriter,
137 BinaryStreamWriter &RecWriter) {
138 assert(Table->HashRecords.size() == Publics.size());
139
Zachary Turner7eaf1d92017-07-10 22:40:20 +0000140 PublicsStreamHeader PSH;
141 GSIHashHeader GSH;
142
Reid Klecknereacdf042017-07-27 18:25:59 +0000143 PSH.AddrMap = Publics.size() * 4;
144
145 // FIXME: Fill these in. They are for incremental linking.
Zachary Turner7eaf1d92017-07-10 22:40:20 +0000146 PSH.NumThunks = 0;
Zachary Turner7eaf1d92017-07-10 22:40:20 +0000147 PSH.SizeOfThunk = 0;
Reid Klecknereacdf042017-07-27 18:25:59 +0000148 PSH.ISectThunkTable = 0;
149 PSH.OffThunkTable = 0;
150 PSH.NumSections = 0;
Zachary Turner7eaf1d92017-07-10 22:40:20 +0000151
152 GSH.VerSignature = GSIHashHeader::HdrSignature;
153 GSH.VerHdr = GSIHashHeader::HdrVersion;
Reid Klecknereacdf042017-07-27 18:25:59 +0000154 GSH.HrSize = Table->HashRecords.size() * sizeof(PSHashRecord);
155 GSH.NumBuckets = Table->HashBitmap.size() * 4 + Table->HashBuckets.size() * 4;
156
157 PSH.SymHash = sizeof(GSH) + GSH.HrSize + GSH.NumBuckets;
Zachary Turner7eaf1d92017-07-10 22:40:20 +0000158
159 if (auto EC = PublicsWriter.writeObject(PSH))
160 return EC;
161 if (auto EC = PublicsWriter.writeObject(GSH))
162 return EC;
Reid Klecknereacdf042017-07-27 18:25:59 +0000163
164 if (auto EC = PublicsWriter.writeArray(makeArrayRef(Table->HashRecords)))
165 return EC;
166 if (auto EC = PublicsWriter.writeArray(makeArrayRef(Table->HashBitmap)))
167 return EC;
168 if (auto EC = PublicsWriter.writeArray(makeArrayRef(Table->HashBuckets)))
Zachary Turner7eaf1d92017-07-10 22:40:20 +0000169 return EC;
170
Reid Klecknereacdf042017-07-27 18:25:59 +0000171 std::vector<ulittle32_t> AddrMap = computeAddrMap(Publics);
172 if (auto EC = PublicsWriter.writeArray(makeArrayRef(AddrMap)))
Zachary Turner7eaf1d92017-07-10 22:40:20 +0000173 return EC;
174
Reid Klecknereacdf042017-07-27 18:25:59 +0000175 BinaryItemStream<CVSymbol> Records(support::endianness::little);
176 Records.setItems(Publics);
177 BinaryStreamRef RecordsRef(Records);
178 if (auto EC = RecWriter.writeStreamRef(RecordsRef))
179 return EC;
180
Zachary Turner7eaf1d92017-07-10 22:40:20 +0000181 return Error::success();
182}
Reid Klecknereacdf042017-07-27 18:25:59 +0000183
184void GSIHashTableBuilder::addSymbols(ArrayRef<CVSymbol> Symbols) {
185 std::array<std::vector<PSHashRecord>, IPHR_HASH + 1> TmpBuckets;
186 uint32_t SymOffset = 0;
187 for (const CVSymbol &Sym : Symbols) {
188 PSHashRecord HR;
189 // Add one when writing symbol offsets to disk. See GSI1::fixSymRecs.
190 HR.Off = SymOffset + 1;
191 HR.CRef = 1; // Always use a refcount of 1.
192
193 // Hash the name to figure out which bucket this goes into.
194 StringRef Name = getSymbolName(Sym);
195 size_t BucketIdx = hashStringV1(Name) % IPHR_HASH;
196 TmpBuckets[BucketIdx].push_back(HR); // FIXME: Does order matter?
197
198 SymOffset += Sym.length();
199 }
200
201 // Compute the three tables: the hash records in bucket and chain order, the
202 // bucket presence bitmap, and the bucket chain start offsets.
203 HashRecords.reserve(Symbols.size());
Reid Kleckneref443292017-07-27 23:13:05 +0000204 for (ulittle32_t &Word : HashBitmap)
205 Word = 0;
Reid Klecknereacdf042017-07-27 18:25:59 +0000206 for (size_t BucketIdx = 0; BucketIdx < IPHR_HASH + 1; ++BucketIdx) {
207 auto &Bucket = TmpBuckets[BucketIdx];
208 if (Bucket.empty())
209 continue;
210 HashBitmap[BucketIdx / 32] |= 1U << (BucketIdx % 32);
211
212 // Calculate what the offset of the first hash record in the chain would be
213 // if it were inflated to contain 32-bit pointers. On a 32-bit system, each
214 // record would be 12 bytes. See HROffsetCalc in gsi.h.
215 const int SizeOfHROffsetCalc = 12;
216 ulittle32_t ChainStartOff =
217 ulittle32_t(HashRecords.size() * SizeOfHROffsetCalc);
218 HashBuckets.push_back(ChainStartOff);
219 for (const auto &HR : Bucket)
220 HashRecords.push_back(HR);
221 }
222}