| 1 | //===- DbiStreamBuilder.cpp - PDB Dbi Stream Creation -----------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // The data structures defined in this file are based on the reference |
| 10 | // implementation which is available at |
| 11 | // https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.cpp |
| 12 | // |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | |
| 15 | #include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h" |
| 16 | #include "llvm/DebugInfo/CodeView/RecordName.h" |
| 17 | #include "llvm/DebugInfo/CodeView/RecordSerialization.h" |
| 18 | #include "llvm/DebugInfo/CodeView/SymbolRecord.h" |
| 19 | #include "llvm/DebugInfo/CodeView/SymbolSerializer.h" |
| 20 | #include "llvm/DebugInfo/MSF/MSFBuilder.h" |
| 21 | #include "llvm/DebugInfo/MSF/MSFCommon.h" |
| 22 | #include "llvm/DebugInfo/MSF/MappedBlockStream.h" |
| 23 | #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" |
| 24 | #include "llvm/DebugInfo/PDB/Native/Hash.h" |
| 25 | #include "llvm/DebugInfo/PDB/Native/RawTypes.h" |
| 26 | #include "llvm/Support/BinaryItemStream.h" |
| 27 | #include "llvm/Support/BinaryStreamWriter.h" |
| 28 | #include "llvm/Support/FormatVariadic.h" |
| 29 | #include "llvm/Support/Parallel.h" |
| 30 | #include "llvm/Support/TimeProfiler.h" |
| 31 | #include <algorithm> |
| 32 | #include <vector> |
| 33 | |
| 34 | using namespace llvm; |
| 35 | using namespace llvm::msf; |
| 36 | using namespace llvm::pdb; |
| 37 | using namespace llvm::codeview; |
| 38 | |
| 39 | // Helper class for building the public and global PDB hash table buckets. |
| 40 | struct llvm::pdb::GSIHashStreamBuilder { |
| 41 | // Sum of the size of all public or global records. |
| 42 | uint64_t RecordByteSize = 0; |
| 43 | |
| 44 | std::vector<PSHashRecord> HashRecords; |
| 45 | |
| 46 | // The hash bitmap has `ceil((IPHR_HASH + 1) / 32)` words in it. The |
| 47 | // reference implementation builds a hash table with IPHR_HASH buckets in it. |
| 48 | // The last bucket is used to link together free hash table cells in a linked |
| 49 | // list, but it is always empty in the compressed, on-disk format. However, |
| 50 | // the bitmap must have a bit for it. |
| 51 | std::array<support::ulittle32_t, (IPHR_HASH + 32) / 32> HashBitmap; |
| 52 | |
| 53 | std::vector<support::ulittle32_t> HashBuckets; |
| 54 | |
| 55 | uint32_t calculateSerializedLength() const; |
| 56 | Error commit(BinaryStreamWriter &Writer); |
| 57 | |
| 58 | void finalizePublicBuckets(); |
| 59 | void finalizeGlobalBuckets(uint32_t RecordZeroOffset); |
| 60 | |
| 61 | // Assign public and global symbol records into hash table buckets. |
| 62 | // Modifies the list of records to store the bucket index, but does not |
| 63 | // change the order. |
| 64 | void finalizeBuckets(uint32_t RecordZeroOffset, |
| 65 | MutableArrayRef<BulkPublic> Globals); |
| 66 | }; |
| 67 | |
| 68 | // DenseMapInfo implementation for deduplicating symbol records. |
| 69 | struct llvm::pdb::SymbolDenseMapInfo { |
| 70 | static unsigned getHashValue(const CVSymbol &Val) { |
| 71 | return xxh3_64bits(data: Val.RecordData); |
| 72 | } |
| 73 | static bool isEqual(const CVSymbol &LHS, const CVSymbol &RHS) { |
| 74 | return LHS.RecordData == RHS.RecordData; |
| 75 | } |
| 76 | }; |
| 77 | |
| 78 | namespace { |
| 79 | LLVM_PACKED_START |
| 80 | struct PublicSym32Layout { |
| 81 | RecordPrefix Prefix; |
| 82 | PublicSym32Header Pub; |
| 83 | // char Name[]; |
| 84 | }; |
| 85 | LLVM_PACKED_END |
| 86 | } // namespace |
| 87 | |
| 88 | // Calculate how much memory this public needs when serialized. |
| 89 | static uint32_t sizeOfPublic(const BulkPublic &Pub) { |
| 90 | uint32_t NameLen = Pub.NameLen; |
| 91 | NameLen = std::min(a: NameLen, |
| 92 | b: uint32_t(MaxRecordLength - sizeof(PublicSym32Layout) - 1)); |
| 93 | return alignTo(Value: sizeof(PublicSym32Layout) + NameLen + 1, Align: 4); |
| 94 | } |
| 95 | |
| 96 | static CVSymbol serializePublic(uint8_t *Mem, const BulkPublic &Pub) { |
| 97 | // Assume the caller has allocated sizeOfPublic bytes. |
| 98 | uint32_t NameLen = std::min( |
| 99 | a: Pub.NameLen, b: uint32_t(MaxRecordLength - sizeof(PublicSym32Layout) - 1)); |
| 100 | size_t Size = alignTo(Value: sizeof(PublicSym32Layout) + NameLen + 1, Align: 4); |
| 101 | assert(Size == sizeOfPublic(Pub)); |
| 102 | auto *FixedMem = reinterpret_cast<PublicSym32Layout *>(Mem); |
| 103 | FixedMem->Prefix.RecordKind = static_cast<uint16_t>(codeview::S_PUB32); |
| 104 | FixedMem->Prefix.RecordLen = static_cast<uint16_t>(Size - 2); |
| 105 | FixedMem->Pub.Flags = Pub.Flags; |
| 106 | FixedMem->Pub.Offset = Pub.Offset; |
| 107 | FixedMem->Pub.Segment = Pub.Segment; |
| 108 | char *NameMem = reinterpret_cast<char *>(FixedMem + 1); |
| 109 | memcpy(dest: NameMem, src: Pub.Name, n: NameLen); |
| 110 | // Zero the null terminator and remaining bytes. |
| 111 | memset(s: &NameMem[NameLen], c: 0, n: Size - sizeof(PublicSym32Layout) - NameLen); |
| 112 | return CVSymbol(ArrayRef(Mem, Size)); |
| 113 | } |
| 114 | |
| 115 | uint32_t GSIHashStreamBuilder::calculateSerializedLength() const { |
| 116 | uint32_t Size = sizeof(GSIHashHeader); |
| 117 | Size += HashRecords.size() * sizeof(PSHashRecord); |
| 118 | Size += HashBitmap.size() * sizeof(uint32_t); |
| 119 | Size += HashBuckets.size() * sizeof(uint32_t); |
| 120 | return Size; |
| 121 | } |
| 122 | |
| 123 | Error GSIHashStreamBuilder::commit(BinaryStreamWriter &Writer) { |
| 124 | GSIHashHeader ; |
| 125 | Header.VerSignature = GSIHashHeader::HdrSignature; |
| 126 | Header.VerHdr = GSIHashHeader::HdrVersion; |
| 127 | Header.HrSize = HashRecords.size() * sizeof(PSHashRecord); |
| 128 | Header.NumBuckets = HashBitmap.size() * 4 + HashBuckets.size() * 4; |
| 129 | |
| 130 | if (auto EC = Writer.writeObject(Obj: Header)) |
| 131 | return EC; |
| 132 | |
| 133 | if (auto EC = Writer.writeArray(Array: ArrayRef(HashRecords))) |
| 134 | return EC; |
| 135 | if (auto EC = Writer.writeArray(Array: ArrayRef(HashBitmap))) |
| 136 | return EC; |
| 137 | if (auto EC = Writer.writeArray(Array: ArrayRef(HashBuckets))) |
| 138 | return EC; |
| 139 | return Error::success(); |
| 140 | } |
| 141 | |
| 142 | static bool isAsciiString(StringRef S) { |
| 143 | return llvm::all_of(Range&: S, P: [](char C) { return unsigned(C) < 0x80; }); |
| 144 | } |
| 145 | |
| 146 | // See `caseInsensitiveComparePchPchCchCch` in gsi.cpp |
| 147 | static int gsiRecordCmp(StringRef S1, StringRef S2) { |
| 148 | size_t LS = S1.size(); |
| 149 | size_t RS = S2.size(); |
| 150 | // Shorter strings always compare less than longer strings. |
| 151 | if (LS != RS) |
| 152 | return (LS > RS) - (LS < RS); |
| 153 | |
| 154 | // If either string contains non ascii characters, memcmp them. |
| 155 | if (LLVM_UNLIKELY(!isAsciiString(S1) || !isAsciiString(S2))) |
| 156 | return memcmp(s1: S1.data(), s2: S2.data(), n: LS); |
| 157 | |
| 158 | // Both strings are ascii, perform a case-insensitive comparison. |
| 159 | return S1.compare_insensitive(RHS: S2); |
| 160 | } |
| 161 | |
| 162 | void GSIStreamBuilder::finalizePublicBuckets() { |
| 163 | PSH->finalizeBuckets(RecordZeroOffset: 0, Globals: Publics); |
| 164 | } |
| 165 | |
| 166 | void GSIStreamBuilder::finalizeGlobalBuckets(uint32_t RecordZeroOffset) { |
| 167 | // Build up a list of globals to be bucketed. Use the BulkPublic data |
| 168 | // structure for this purpose, even though these are global records, not |
| 169 | // public records. Most of the same fields are required: |
| 170 | // - Name |
| 171 | // - NameLen |
| 172 | // - SymOffset |
| 173 | // - BucketIdx |
| 174 | // The dead fields are Offset, Segment, and Flags. |
| 175 | std::vector<BulkPublic> Records; |
| 176 | Records.resize(new_size: Globals.size()); |
| 177 | uint32_t SymOffset = RecordZeroOffset; |
| 178 | for (size_t I = 0, E = Globals.size(); I < E; ++I) { |
| 179 | StringRef Name = getSymbolName(Sym: Globals[I]); |
| 180 | Records[I].Name = Name.data(); |
| 181 | Records[I].NameLen = Name.size(); |
| 182 | Records[I].SymOffset = SymOffset; |
| 183 | SymOffset += Globals[I].length(); |
| 184 | } |
| 185 | |
| 186 | GSH->finalizeBuckets(RecordZeroOffset, Globals: Records); |
| 187 | } |
| 188 | |
| 189 | void GSIHashStreamBuilder::finalizeBuckets( |
| 190 | uint32_t RecordZeroOffset, MutableArrayRef<BulkPublic> Records) { |
| 191 | // Hash every name in parallel. |
| 192 | parallelFor(Begin: 0, End: Records.size(), Fn: [&](size_t I) { |
| 193 | Records[I].setBucketIdx(hashStringV1(Str: Records[I].getName()) % IPHR_HASH); |
| 194 | }); |
| 195 | |
| 196 | // Count up the size of each bucket. Then, use an exclusive prefix sum to |
| 197 | // calculate the bucket start offsets. This is C++17 std::exclusive_scan, but |
| 198 | // we can't use it yet. |
| 199 | uint32_t BucketStarts[IPHR_HASH] = {0}; |
| 200 | for (const BulkPublic &P : Records) |
| 201 | ++BucketStarts[P.BucketIdx]; |
| 202 | uint32_t Sum = 0; |
| 203 | for (uint32_t &B : BucketStarts) { |
| 204 | uint32_t Size = B; |
| 205 | B = Sum; |
| 206 | Sum += Size; |
| 207 | } |
| 208 | |
| 209 | // Place globals into the hash table in bucket order. When placing a global, |
| 210 | // update the bucket start. Every hash table slot should be filled. Always use |
| 211 | // a refcount of one for now. |
| 212 | HashRecords.resize(new_size: Records.size()); |
| 213 | uint32_t BucketCursors[IPHR_HASH]; |
| 214 | memcpy(dest: BucketCursors, src: BucketStarts, n: sizeof(BucketCursors)); |
| 215 | for (int I = 0, E = Records.size(); I < E; ++I) { |
| 216 | uint32_t HashIdx = BucketCursors[Records[I].BucketIdx]++; |
| 217 | HashRecords[HashIdx].Off = I; |
| 218 | HashRecords[HashIdx].CRef = 1; |
| 219 | } |
| 220 | |
| 221 | // Within the buckets, sort each bucket by memcmp of the symbol's name. It's |
| 222 | // important that we use the same sorting algorithm as is used by the |
| 223 | // reference implementation to ensure that the search for a record within a |
| 224 | // bucket can properly early-out when it detects the record won't be found. |
| 225 | // The algorithm used here corresponds to the function |
| 226 | // caseInsensitiveComparePchPchCchCch in the reference implementation. |
| 227 | parallelFor(Begin: 0, End: IPHR_HASH, Fn: [&](size_t I) { |
| 228 | auto B = HashRecords.begin() + BucketStarts[I]; |
| 229 | auto E = HashRecords.begin() + BucketCursors[I]; |
| 230 | if (B == E) |
| 231 | return; |
| 232 | auto BucketCmp = [Records](const PSHashRecord &LHash, |
| 233 | const PSHashRecord &RHash) { |
| 234 | const BulkPublic &L = Records[uint32_t(LHash.Off)]; |
| 235 | const BulkPublic &R = Records[uint32_t(RHash.Off)]; |
| 236 | assert(L.BucketIdx == R.BucketIdx); |
| 237 | int Cmp = gsiRecordCmp(S1: L.getName(), S2: R.getName()); |
| 238 | if (Cmp != 0) |
| 239 | return Cmp < 0; |
| 240 | // This comparison is necessary to make the sorting stable in the presence |
| 241 | // of two static globals with the same name. The easiest way to observe |
| 242 | // this is with S_LDATA32 records. |
| 243 | return L.SymOffset < R.SymOffset; |
| 244 | }; |
| 245 | llvm::sort(Start: B, End: E, Comp: BucketCmp); |
| 246 | |
| 247 | // After we are done sorting, replace the global indices with the stream |
| 248 | // offsets of each global. Add one when writing symbol offsets to disk. |
| 249 | // See GSI1::fixSymRecs. |
| 250 | for (PSHashRecord &HRec : make_range(x: B, y: E)) |
| 251 | HRec.Off = Records[uint32_t(HRec.Off)].SymOffset + 1; |
| 252 | }); |
| 253 | |
| 254 | // For each non-empty bucket, push the bucket start offset into HashBuckets |
| 255 | // and set a bit in the hash bitmap. |
| 256 | for (uint32_t I = 0; I < HashBitmap.size(); ++I) { |
| 257 | uint32_t Word = 0; |
| 258 | for (uint32_t J = 0; J < 32; ++J) { |
| 259 | // Skip empty buckets. |
| 260 | uint32_t BucketIdx = I * 32 + J; |
| 261 | if (BucketIdx >= IPHR_HASH || |
| 262 | BucketStarts[BucketIdx] == BucketCursors[BucketIdx]) |
| 263 | continue; |
| 264 | Word |= (1U << J); |
| 265 | |
| 266 | // Calculate what the offset of the first hash record in the chain would |
| 267 | // be if it were inflated to contain 32-bit pointers. On a 32-bit system, |
| 268 | // each record would be 12 bytes. See HROffsetCalc in gsi.h. |
| 269 | const int SizeOfHROffsetCalc = 12; |
| 270 | ulittle32_t ChainStartOff = |
| 271 | ulittle32_t(BucketStarts[BucketIdx] * SizeOfHROffsetCalc); |
| 272 | HashBuckets.push_back(x: ChainStartOff); |
| 273 | } |
| 274 | HashBitmap[I] = Word; |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | GSIStreamBuilder::GSIStreamBuilder(msf::MSFBuilder &Msf) |
| 279 | : Msf(Msf), PSH(std::make_unique<GSIHashStreamBuilder>()), |
| 280 | GSH(std::make_unique<GSIHashStreamBuilder>()) {} |
| 281 | |
| 282 | GSIStreamBuilder::~GSIStreamBuilder() = default; |
| 283 | |
| 284 | uint32_t GSIStreamBuilder::calculatePublicsHashStreamSize() const { |
| 285 | uint32_t Size = 0; |
| 286 | Size += sizeof(PublicsStreamHeader); |
| 287 | Size += PSH->calculateSerializedLength(); |
| 288 | Size += Publics.size() * sizeof(uint32_t); // AddrMap |
| 289 | // FIXME: Add thunk map and section offsets for incremental linking. |
| 290 | |
| 291 | return Size; |
| 292 | } |
| 293 | |
| 294 | uint32_t GSIStreamBuilder::calculateGlobalsHashStreamSize() const { |
| 295 | return GSH->calculateSerializedLength(); |
| 296 | } |
| 297 | |
| 298 | Error GSIStreamBuilder::finalizeMsfLayout() { |
| 299 | // First we write public symbol records, then we write global symbol records. |
| 300 | finalizePublicBuckets(); |
| 301 | finalizeGlobalBuckets(RecordZeroOffset: PSH->RecordByteSize); |
| 302 | |
| 303 | Expected<uint32_t> Idx = Msf.addStream(Size: calculateGlobalsHashStreamSize()); |
| 304 | if (!Idx) |
| 305 | return Idx.takeError(); |
| 306 | GlobalsStreamIndex = *Idx; |
| 307 | |
| 308 | Idx = Msf.addStream(Size: calculatePublicsHashStreamSize()); |
| 309 | if (!Idx) |
| 310 | return Idx.takeError(); |
| 311 | PublicsStreamIndex = *Idx; |
| 312 | |
| 313 | uint64_t RecordBytes = PSH->RecordByteSize + GSH->RecordByteSize; |
| 314 | if (RecordBytes > UINT32_MAX) |
| 315 | return make_error<StringError>( |
| 316 | Args: formatv(Fmt: "the public symbols ({0} bytes) and global symbols ({1} bytes) " |
| 317 | "are too large to fit in a PDB file; " |
| 318 | "the maximum total is {2} bytes." , |
| 319 | Vals&: PSH->RecordByteSize, Vals&: GSH->RecordByteSize, UINT32_MAX), |
| 320 | Args: inconvertibleErrorCode()); |
| 321 | |
| 322 | Idx = Msf.addStream(Size: RecordBytes); |
| 323 | if (!Idx) |
| 324 | return Idx.takeError(); |
| 325 | RecordStreamIndex = *Idx; |
| 326 | return Error::success(); |
| 327 | } |
| 328 | |
| 329 | void GSIStreamBuilder::addPublicSymbols(std::vector<BulkPublic> &&PublicsIn) { |
| 330 | assert(Publics.empty() && PSH->RecordByteSize == 0 && |
| 331 | "publics can only be added once" ); |
| 332 | Publics = std::move(PublicsIn); |
| 333 | |
| 334 | // Sort the symbols by name. PDBs contain lots of symbols, so use parallelism. |
| 335 | parallelSort(R&: Publics, Comp: [](const BulkPublic &L, const BulkPublic &R) { |
| 336 | return L.getName() < R.getName(); |
| 337 | }); |
| 338 | |
| 339 | // Assign offsets and calculate the length of the public symbol records. |
| 340 | uint32_t SymOffset = 0; |
| 341 | for (BulkPublic &Pub : Publics) { |
| 342 | Pub.SymOffset = SymOffset; |
| 343 | SymOffset += sizeOfPublic(Pub); |
| 344 | } |
| 345 | |
| 346 | // Remember the length of the public stream records. |
| 347 | PSH->RecordByteSize = SymOffset; |
| 348 | } |
| 349 | |
| 350 | void GSIStreamBuilder::addGlobalSymbol(const ProcRefSym &Sym) { |
| 351 | serializeAndAddGlobal(Symbol: Sym); |
| 352 | } |
| 353 | |
| 354 | void GSIStreamBuilder::addGlobalSymbol(const DataSym &Sym) { |
| 355 | serializeAndAddGlobal(Symbol: Sym); |
| 356 | } |
| 357 | |
| 358 | void GSIStreamBuilder::addGlobalSymbol(const ConstantSym &Sym) { |
| 359 | serializeAndAddGlobal(Symbol: Sym); |
| 360 | } |
| 361 | |
| 362 | template <typename T> |
| 363 | void GSIStreamBuilder::serializeAndAddGlobal(const T &Symbol) { |
| 364 | T Copy(Symbol); |
| 365 | addGlobalSymbol(SymbolSerializer::writeOneSymbol(Copy, Msf.getAllocator(), |
| 366 | CodeViewContainer::Pdb)); |
| 367 | } |
| 368 | |
| 369 | void GSIStreamBuilder::addGlobalSymbol(const codeview::CVSymbol &Symbol) { |
| 370 | // Ignore duplicate typedefs and constants. |
| 371 | if (Symbol.kind() == S_UDT || Symbol.kind() == S_CONSTANT) { |
| 372 | auto Iter = GlobalsSeen.insert(V: Symbol); |
| 373 | if (!Iter.second) |
| 374 | return; |
| 375 | } |
| 376 | GSH->RecordByteSize += Symbol.length(); |
| 377 | Globals.push_back(x: Symbol); |
| 378 | } |
| 379 | |
| 380 | // Serialize each public and write it. |
| 381 | static Error writePublics(BinaryStreamWriter &Writer, |
| 382 | ArrayRef<BulkPublic> Publics) { |
| 383 | std::vector<uint8_t> Storage; |
| 384 | for (const BulkPublic &Pub : Publics) { |
| 385 | Storage.resize(new_size: sizeOfPublic(Pub)); |
| 386 | serializePublic(Mem: Storage.data(), Pub); |
| 387 | if (Error E = Writer.writeBytes(Buffer: Storage)) |
| 388 | return E; |
| 389 | } |
| 390 | return Error::success(); |
| 391 | } |
| 392 | |
| 393 | static Error writeRecords(BinaryStreamWriter &Writer, |
| 394 | ArrayRef<CVSymbol> Records) { |
| 395 | BinaryItemStream<CVSymbol> ItemStream(llvm::endianness::little); |
| 396 | ItemStream.setItems(Records); |
| 397 | BinaryStreamRef RecordsRef(ItemStream); |
| 398 | return Writer.writeStreamRef(Ref: RecordsRef); |
| 399 | } |
| 400 | |
| 401 | Error GSIStreamBuilder::commitSymbolRecordStream( |
| 402 | WritableBinaryStreamRef Stream) { |
| 403 | BinaryStreamWriter Writer(Stream); |
| 404 | |
| 405 | // Write public symbol records first, followed by global symbol records. This |
| 406 | // must match the order that we assume in finalizeMsfLayout when computing |
| 407 | // PSHZero and GSHZero. |
| 408 | if (auto EC = writePublics(Writer, Publics)) |
| 409 | return EC; |
| 410 | if (auto EC = writeRecords(Writer, Records: Globals)) |
| 411 | return EC; |
| 412 | |
| 413 | return Error::success(); |
| 414 | } |
| 415 | |
| 416 | static std::vector<support::ulittle32_t> |
| 417 | computeAddrMap(ArrayRef<BulkPublic> Publics) { |
| 418 | // Build a parallel vector of indices into the Publics vector, and sort it by |
| 419 | // address. |
| 420 | std::vector<ulittle32_t> PubAddrMap; |
| 421 | PubAddrMap.reserve(n: Publics.size()); |
| 422 | for (int I = 0, E = Publics.size(); I < E; ++I) |
| 423 | PubAddrMap.push_back(x: ulittle32_t(I)); |
| 424 | |
| 425 | auto AddrCmp = [Publics](const ulittle32_t &LIdx, const ulittle32_t &RIdx) { |
| 426 | const BulkPublic &L = Publics[LIdx]; |
| 427 | const BulkPublic &R = Publics[RIdx]; |
| 428 | if (L.Segment != R.Segment) |
| 429 | return L.Segment < R.Segment; |
| 430 | if (L.Offset != R.Offset) |
| 431 | return L.Offset < R.Offset; |
| 432 | // parallelSort is unstable, so we have to do name comparison to ensure |
| 433 | // that two names for the same location come out in a deterministic order. |
| 434 | return L.getName() < R.getName(); |
| 435 | }; |
| 436 | parallelSort(R&: PubAddrMap, Comp: AddrCmp); |
| 437 | |
| 438 | // Rewrite the public symbol indices into symbol offsets. |
| 439 | for (ulittle32_t &Entry : PubAddrMap) |
| 440 | Entry = Publics[Entry].SymOffset; |
| 441 | return PubAddrMap; |
| 442 | } |
| 443 | |
| 444 | Error GSIStreamBuilder::commitPublicsHashStream( |
| 445 | WritableBinaryStreamRef Stream) { |
| 446 | BinaryStreamWriter Writer(Stream); |
| 447 | PublicsStreamHeader ; |
| 448 | |
| 449 | // FIXME: Fill these in. They are for incremental linking. |
| 450 | Header.SymHash = PSH->calculateSerializedLength(); |
| 451 | Header.AddrMap = Publics.size() * 4; |
| 452 | Header.NumThunks = 0; |
| 453 | Header.SizeOfThunk = 0; |
| 454 | Header.ISectThunkTable = 0; |
| 455 | memset(s: Header.Padding, c: 0, n: sizeof(Header.Padding)); |
| 456 | Header.OffThunkTable = 0; |
| 457 | Header.NumSections = 0; |
| 458 | if (auto EC = Writer.writeObject(Obj: Header)) |
| 459 | return EC; |
| 460 | |
| 461 | if (auto EC = PSH->commit(Writer)) |
| 462 | return EC; |
| 463 | |
| 464 | std::vector<support::ulittle32_t> PubAddrMap = computeAddrMap(Publics); |
| 465 | assert(PubAddrMap.size() == Publics.size()); |
| 466 | if (auto EC = Writer.writeArray(Array: ArrayRef(PubAddrMap))) |
| 467 | return EC; |
| 468 | |
| 469 | return Error::success(); |
| 470 | } |
| 471 | |
| 472 | Error GSIStreamBuilder::commitGlobalsHashStream( |
| 473 | WritableBinaryStreamRef Stream) { |
| 474 | BinaryStreamWriter Writer(Stream); |
| 475 | return GSH->commit(Writer); |
| 476 | } |
| 477 | |
| 478 | Error GSIStreamBuilder::commit(const msf::MSFLayout &Layout, |
| 479 | WritableBinaryStreamRef Buffer) { |
| 480 | llvm::TimeTraceScope timeScope("Commit GSI stream" ); |
| 481 | auto GS = WritableMappedBlockStream::createIndexedStream( |
| 482 | Layout, MsfData: Buffer, StreamIndex: getGlobalsStreamIndex(), Allocator&: Msf.getAllocator()); |
| 483 | auto PS = WritableMappedBlockStream::createIndexedStream( |
| 484 | Layout, MsfData: Buffer, StreamIndex: getPublicsStreamIndex(), Allocator&: Msf.getAllocator()); |
| 485 | auto PRS = WritableMappedBlockStream::createIndexedStream( |
| 486 | Layout, MsfData: Buffer, StreamIndex: getRecordStreamIndex(), Allocator&: Msf.getAllocator()); |
| 487 | |
| 488 | if (auto EC = commitSymbolRecordStream(Stream: *PRS)) |
| 489 | return EC; |
| 490 | if (auto EC = commitGlobalsHashStream(Stream: *GS)) |
| 491 | return EC; |
| 492 | if (auto EC = commitPublicsHashStream(Stream: *PS)) |
| 493 | return EC; |
| 494 | return Error::success(); |
| 495 | } |
| 496 | |