1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
|
//===- PDBStringTableBuilder.cpp - PDB String Table -------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/DebugInfo/PDB/Native/Hash.h"
#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
#include "llvm/Support/BinaryStreamWriter.h"
#include "llvm/Support/Endian.h"
#include <map>
using namespace llvm;
using namespace llvm::msf;
using namespace llvm::support;
using namespace llvm::support::endian;
using namespace llvm::pdb;
StringTableHashTraits::StringTableHashTraits(PDBStringTableBuilder &Table)
: Table(&Table) {}
uint32_t StringTableHashTraits::hashLookupKey(StringRef S) const {
// The reference implementation doesn't include code for /src/headerblock
// handling, but it can only read natvis entries lld's PDB files if
// this hash function truncates the hash to 16 bit.
// PDB/include/misc.h in the reference implementation has a hashSz() function
// that returns an unsigned short, that seems what's being used for
// /src/headerblock.
return static_cast<uint16_t>(Table->getIdForString(S));
}
StringRef StringTableHashTraits::storageKeyToLookupKey(uint32_t Offset) const {
return Table->getStringForId(Offset);
}
uint32_t StringTableHashTraits::lookupKeyToStorageKey(StringRef S) {
return Table->insert(S);
}
uint32_t PDBStringTableBuilder::insert(StringRef S) {
return Strings.insert(S);
}
uint32_t PDBStringTableBuilder::getIdForString(StringRef S) const {
return Strings.getIdForString(S);
}
StringRef PDBStringTableBuilder::getStringForId(uint32_t Id) const {
return Strings.getStringForId(Id);
}
static uint32_t computeBucketCount(uint32_t NumStrings) {
// This is a precomputed list of Buckets given the specified number of
// strings. Matching the reference algorithm exactly is not strictly
// necessary for correctness, but it helps when comparing LLD's PDBs with
// Microsoft's PDBs so as to eliminate superfluous differences.
// The reference implementation does (in nmt.h, NMT::grow()):
// unsigned StringCount = 0;
// unsigned BucketCount = 1;
// fn insert() {
// ++StringCount;
// if (BucketCount * 3 / 4 < StringCount)
// BucketCount = BucketCount * 3 / 2 + 1;
// }
// This list contains all StringCount, BucketCount pairs where BucketCount was
// just incremented. It ends before the first BucketCount entry where
// BucketCount * 3 would overflow a 32-bit unsigned int.
static std::map<uint32_t, uint32_t> StringsToBuckets = {
{0, 1},
{1, 2},
{2, 4},
{4, 7},
{6, 11},
{9, 17},
{13, 26},
{20, 40},
{31, 61},
{46, 92},
{70, 139},
{105, 209},
{157, 314},
{236, 472},
{355, 709},
{532, 1064},
{799, 1597},
{1198, 2396},
{1798, 3595},
{2697, 5393},
{4045, 8090},
{6068, 12136},
{9103, 18205},
{13654, 27308},
{20482, 40963},
{30723, 61445},
{46084, 92168},
{69127, 138253},
{103690, 207380},
{155536, 311071},
{233304, 466607},
{349956, 699911},
{524934, 1049867},
{787401, 1574801},
{1181101, 2362202},
{1771652, 3543304},
{2657479, 5314957},
{3986218, 7972436},
{5979328, 11958655},
{8968992, 17937983},
{13453488, 26906975},
{20180232, 40360463},
{30270348, 60540695},
{45405522, 90811043},
{68108283, 136216565},
{102162424, 204324848},
{153243637, 306487273},
{229865455, 459730910},
{344798183, 689596366},
{517197275, 1034394550},
{775795913, 1551591826},
{1163693870, 2327387740}};
auto Entry = StringsToBuckets.lower_bound(NumStrings);
assert(Entry != StringsToBuckets.end());
return Entry->second;
}
uint32_t PDBStringTableBuilder::calculateHashTableSize() const {
uint32_t Size = sizeof(uint32_t); // Hash table begins with 4-byte size field.
Size += sizeof(uint32_t) * computeBucketCount(Strings.size());
return Size;
}
uint32_t PDBStringTableBuilder::calculateSerializedSize() const {
uint32_t Size = 0;
Size += sizeof(PDBStringTableHeader);
Size += Strings.calculateSerializedSize();
Size += calculateHashTableSize();
Size += sizeof(uint32_t); // The /names stream ends with the string count.
return Size;
}
void PDBStringTableBuilder::setStrings(
const codeview::DebugStringTableSubsection &Strings) {
this->Strings = Strings;
}
Error PDBStringTableBuilder::writeHeader(BinaryStreamWriter &Writer) const {
// Write a header
PDBStringTableHeader H;
H.Signature = PDBStringTableSignature;
H.HashVersion = 1;
H.ByteSize = Strings.calculateSerializedSize();
if (auto EC = Writer.writeObject(H))
return EC;
assert(Writer.bytesRemaining() == 0);
return Error::success();
}
Error PDBStringTableBuilder::writeStrings(BinaryStreamWriter &Writer) const {
if (auto EC = Strings.commit(Writer))
return EC;
assert(Writer.bytesRemaining() == 0);
return Error::success();
}
Error PDBStringTableBuilder::writeHashTable(BinaryStreamWriter &Writer) const {
// Write a hash table.
uint32_t BucketCount = computeBucketCount(Strings.size());
if (auto EC = Writer.writeInteger(BucketCount))
return EC;
std::vector<ulittle32_t> Buckets(BucketCount);
for (auto &Pair : Strings) {
StringRef S = Pair.getKey();
uint32_t Offset = Pair.getValue();
uint32_t Hash = hashStringV1(S);
for (uint32_t I = 0; I != BucketCount; ++I) {
uint32_t Slot = (Hash + I) % BucketCount;
if (Buckets[Slot] != 0)
continue;
Buckets[Slot] = Offset;
break;
}
}
if (auto EC = Writer.writeArray(ArrayRef<ulittle32_t>(Buckets)))
return EC;
assert(Writer.bytesRemaining() == 0);
return Error::success();
}
Error PDBStringTableBuilder::writeEpilogue(BinaryStreamWriter &Writer) const {
if (auto EC = Writer.writeInteger<uint32_t>(Strings.size()))
return EC;
assert(Writer.bytesRemaining() == 0);
return Error::success();
}
Error PDBStringTableBuilder::commit(BinaryStreamWriter &Writer) const {
BinaryStreamWriter SectionWriter;
std::tie(SectionWriter, Writer) = Writer.split(sizeof(PDBStringTableHeader));
if (auto EC = writeHeader(SectionWriter))
return EC;
std::tie(SectionWriter, Writer) =
Writer.split(Strings.calculateSerializedSize());
if (auto EC = writeStrings(SectionWriter))
return EC;
std::tie(SectionWriter, Writer) = Writer.split(calculateHashTableSize());
if (auto EC = writeHashTable(SectionWriter))
return EC;
std::tie(SectionWriter, Writer) = Writer.split(sizeof(uint32_t));
if (auto EC = writeEpilogue(SectionWriter))
return EC;
return Error::success();
}
|