Zachary Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 1 | //===- 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 Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 11 | #include "llvm/DebugInfo/CodeView/SymbolRecord.h" |
| 12 | #include "llvm/DebugInfo/CodeView/SymbolSerializer.h" |
Zachary Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 13 | #include "llvm/DebugInfo/MSF/MSFBuilder.h" |
| 14 | #include "llvm/DebugInfo/MSF/MSFCommon.h" |
| 15 | #include "llvm/DebugInfo/MSF/MappedBlockStream.h" |
Reid Kleckner | 14d90fd | 2017-07-26 00:40:36 +0000 | [diff] [blame] | 16 | #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 17 | #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 Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 22 | |
Zachary Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 23 | using namespace llvm; |
| 24 | using namespace llvm::msf; |
| 25 | using namespace llvm::pdb; |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 26 | using namespace llvm::codeview; |
Zachary Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 27 | |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 28 | PublicsStreamBuilder::PublicsStreamBuilder(msf::MSFBuilder &Msf) |
| 29 | : Table(new GSIHashTableBuilder), Msf(Msf) {} |
Zachary Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 30 | |
| 31 | PublicsStreamBuilder::~PublicsStreamBuilder() {} |
| 32 | |
| 33 | uint32_t PublicsStreamBuilder::calculateSerializedLength() const { |
| 34 | uint32_t Size = 0; |
| 35 | Size += sizeof(PublicsStreamHeader); |
| 36 | Size += sizeof(GSIHashHeader); |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 37 | Size += Table->HashRecords.size() * sizeof(PSHashRecord); |
| 38 | Size += Table->HashBitmap.size() * sizeof(uint32_t); |
| 39 | Size += Table->HashBuckets.size() * sizeof(uint32_t); |
Zachary Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 40 | |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 41 | Size += Publics.size() * sizeof(uint32_t); // AddrMap |
| 42 | |
| 43 | // FIXME: Add thunk map and section offsets for incremental linking. |
| 44 | |
Zachary Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 45 | return Size; |
| 46 | } |
| 47 | |
| 48 | Error PublicsStreamBuilder::finalizeMsfLayout() { |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 49 | Table->addSymbols(Publics); |
| 50 | |
Zachary Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 51 | Expected<uint32_t> Idx = Msf.addStream(calculateSerializedLength()); |
| 52 | if (!Idx) |
| 53 | return Idx.takeError(); |
| 54 | StreamIdx = *Idx; |
| 55 | |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 56 | uint32_t PublicRecordBytes = 0; |
| 57 | for (auto &Pub : Publics) |
| 58 | PublicRecordBytes += Pub.length(); |
| 59 | |
| 60 | Expected<uint32_t> RecordIdx = Msf.addStream(PublicRecordBytes); |
Zachary Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 61 | if (!RecordIdx) |
| 62 | return RecordIdx.takeError(); |
| 63 | RecordStreamIdx = *RecordIdx; |
| 64 | return Error::success(); |
| 65 | } |
| 66 | |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 67 | void 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. |
| 74 | struct 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 | |
| 83 | bool 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 | |
| 99 | static 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. |
| 110 | static 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 | |
| 136 | Error PublicsStreamBuilder::commit(BinaryStreamWriter &PublicsWriter, |
| 137 | BinaryStreamWriter &RecWriter) { |
| 138 | assert(Table->HashRecords.size() == Publics.size()); |
| 139 | |
Zachary Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 140 | PublicsStreamHeader PSH; |
| 141 | GSIHashHeader GSH; |
| 142 | |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 143 | PSH.AddrMap = Publics.size() * 4; |
| 144 | |
| 145 | // FIXME: Fill these in. They are for incremental linking. |
Zachary Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 146 | PSH.NumThunks = 0; |
Zachary Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 147 | PSH.SizeOfThunk = 0; |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 148 | PSH.ISectThunkTable = 0; |
| 149 | PSH.OffThunkTable = 0; |
| 150 | PSH.NumSections = 0; |
Zachary Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 151 | |
| 152 | GSH.VerSignature = GSIHashHeader::HdrSignature; |
| 153 | GSH.VerHdr = GSIHashHeader::HdrVersion; |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 154 | 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 Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 158 | |
| 159 | if (auto EC = PublicsWriter.writeObject(PSH)) |
| 160 | return EC; |
| 161 | if (auto EC = PublicsWriter.writeObject(GSH)) |
| 162 | return EC; |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 163 | |
| 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 Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 169 | return EC; |
| 170 | |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 171 | std::vector<ulittle32_t> AddrMap = computeAddrMap(Publics); |
| 172 | if (auto EC = PublicsWriter.writeArray(makeArrayRef(AddrMap))) |
Zachary Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 173 | return EC; |
| 174 | |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 175 | 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 Turner | 7eaf1d9 | 2017-07-10 22:40:20 +0000 | [diff] [blame] | 181 | return Error::success(); |
| 182 | } |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 183 | |
| 184 | void 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 Kleckner | ef44329 | 2017-07-27 23:13:05 +0000 | [diff] [blame] | 204 | for (ulittle32_t &Word : HashBitmap) |
| 205 | Word = 0; |
Reid Kleckner | eacdf04 | 2017-07-27 18:25:59 +0000 | [diff] [blame] | 206 | 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 | } |