1 //===- PDBFileBuilder.cpp - PDB File 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 #include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" 10 #include "llvm/ADT/SmallString.h" 11 #include "llvm/ADT/StringExtras.h" 12 #include "llvm/DebugInfo/CodeView/CodeView.h" 13 #include "llvm/DebugInfo/CodeView/GUID.h" 14 #include "llvm/DebugInfo/MSF/MSFBuilder.h" 15 #include "llvm/DebugInfo/MSF/MSFCommon.h" 16 #include "llvm/DebugInfo/MSF/MappedBlockStream.h" 17 #include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" 18 #include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h" 19 #include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" 20 #include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h" 21 #include "llvm/DebugInfo/PDB/Native/RawConstants.h" 22 #include "llvm/DebugInfo/PDB/Native/RawError.h" 23 #include "llvm/DebugInfo/PDB/Native/RawTypes.h" 24 #include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" 25 #include "llvm/Support/BinaryStreamWriter.h" 26 #include "llvm/Support/CRC.h" 27 #include "llvm/Support/Path.h" 28 #include "llvm/Support/TimeProfiler.h" 29 #include "llvm/Support/xxhash.h" 30 31 #include <ctime> 32 33 using namespace llvm; 34 using namespace llvm::codeview; 35 using namespace llvm::msf; 36 using namespace llvm::pdb; 37 using namespace llvm::support; 38 39 namespace llvm { 40 class WritableBinaryStream; 41 } 42 43 PDBFileBuilder::PDBFileBuilder(BumpPtrAllocator &Allocator) 44 : Allocator(Allocator), InjectedSourceHashTraits(Strings), 45 InjectedSourceTable(2) {} 46 47 PDBFileBuilder::~PDBFileBuilder() = default; 48 49 Error PDBFileBuilder::initialize(uint32_t BlockSize) { 50 auto ExpectedMsf = MSFBuilder::create(Allocator, BlockSize); 51 if (!ExpectedMsf) 52 return ExpectedMsf.takeError(); 53 Msf = std::make_unique<MSFBuilder>(std::move(*ExpectedMsf)); 54 return Error::success(); 55 } 56 57 MSFBuilder &PDBFileBuilder::getMsfBuilder() { return *Msf; } 58 59 InfoStreamBuilder &PDBFileBuilder::getInfoBuilder() { 60 if (!Info) 61 Info = std::make_unique<InfoStreamBuilder>(*Msf, NamedStreams); 62 return *Info; 63 } 64 65 DbiStreamBuilder &PDBFileBuilder::getDbiBuilder() { 66 if (!Dbi) 67 Dbi = std::make_unique<DbiStreamBuilder>(*Msf); 68 return *Dbi; 69 } 70 71 TpiStreamBuilder &PDBFileBuilder::getTpiBuilder() { 72 if (!Tpi) 73 Tpi = std::make_unique<TpiStreamBuilder>(*Msf, StreamTPI); 74 return *Tpi; 75 } 76 77 TpiStreamBuilder &PDBFileBuilder::getIpiBuilder() { 78 if (!Ipi) 79 Ipi = std::make_unique<TpiStreamBuilder>(*Msf, StreamIPI); 80 return *Ipi; 81 } 82 83 PDBStringTableBuilder &PDBFileBuilder::getStringTableBuilder() { 84 return Strings; 85 } 86 87 GSIStreamBuilder &PDBFileBuilder::getGsiBuilder() { 88 if (!Gsi) 89 Gsi = std::make_unique<GSIStreamBuilder>(*Msf); 90 return *Gsi; 91 } 92 93 Expected<uint32_t> PDBFileBuilder::allocateNamedStream(StringRef Name, 94 uint32_t Size) { 95 auto ExpectedStream = Msf->addStream(Size); 96 if (ExpectedStream) 97 NamedStreams.set(Name, *ExpectedStream); 98 return ExpectedStream; 99 } 100 101 Error PDBFileBuilder::addNamedStream(StringRef Name, StringRef Data) { 102 Expected<uint32_t> ExpectedIndex = allocateNamedStream(Name, Data.size()); 103 if (!ExpectedIndex) 104 return ExpectedIndex.takeError(); 105 assert(NamedStreamData.count(*ExpectedIndex) == 0); 106 NamedStreamData[*ExpectedIndex] = std::string(Data); 107 return Error::success(); 108 } 109 110 void PDBFileBuilder::addInjectedSource(StringRef Name, 111 std::unique_ptr<MemoryBuffer> Buffer) { 112 // Stream names must be exact matches, since they get looked up in a hash 113 // table and the hash value is dependent on the exact contents of the string. 114 // link.exe lowercases a path and converts / to \, so we must do the same. 115 SmallString<64> VName; 116 sys::path::native(Name.lower(), VName, sys::path::Style::windows_backslash); 117 118 uint32_t NI = getStringTableBuilder().insert(Name); 119 uint32_t VNI = getStringTableBuilder().insert(VName); 120 121 InjectedSourceDescriptor Desc; 122 Desc.Content = std::move(Buffer); 123 Desc.NameIndex = NI; 124 Desc.VNameIndex = VNI; 125 Desc.StreamName = "/src/files/"; 126 127 Desc.StreamName += VName; 128 129 InjectedSources.push_back(std::move(Desc)); 130 } 131 132 Error PDBFileBuilder::finalizeMsfLayout() { 133 llvm::TimeTraceScope timeScope("MSF layout"); 134 135 if (Ipi && Ipi->getRecordCount() > 0) { 136 // In theory newer PDBs always have an ID stream, but by saying that we're 137 // only going to *really* have an ID stream if there is at least one ID 138 // record, we leave open the opportunity to test older PDBs such as those 139 // that don't have an ID stream. 140 auto &Info = getInfoBuilder(); 141 Info.addFeature(PdbRaw_FeatureSig::VC140); 142 } 143 144 uint32_t StringsLen = Strings.calculateSerializedSize(); 145 146 Expected<uint32_t> SN = allocateNamedStream("/LinkInfo", 0); 147 if (!SN) 148 return SN.takeError(); 149 150 if (Gsi) { 151 if (auto EC = Gsi->finalizeMsfLayout()) 152 return EC; 153 if (Dbi) { 154 Dbi->setPublicsStreamIndex(Gsi->getPublicsStreamIndex()); 155 Dbi->setGlobalsStreamIndex(Gsi->getGlobalsStreamIndex()); 156 Dbi->setSymbolRecordStreamIndex(Gsi->getRecordStreamIndex()); 157 } 158 } 159 if (Tpi) { 160 if (auto EC = Tpi->finalizeMsfLayout()) 161 return EC; 162 } 163 if (Dbi) { 164 if (auto EC = Dbi->finalizeMsfLayout()) 165 return EC; 166 } 167 SN = allocateNamedStream("/names", StringsLen); 168 if (!SN) 169 return SN.takeError(); 170 171 if (Ipi) { 172 if (auto EC = Ipi->finalizeMsfLayout()) 173 return EC; 174 } 175 176 // Do this last, since it relies on the named stream map being complete, and 177 // that can be updated by previous steps in the finalization. 178 if (Info) { 179 if (auto EC = Info->finalizeMsfLayout()) 180 return EC; 181 } 182 183 if (!InjectedSources.empty()) { 184 for (const auto &IS : InjectedSources) { 185 JamCRC CRC(0); 186 CRC.update(arrayRefFromStringRef(IS.Content->getBuffer())); 187 188 SrcHeaderBlockEntry Entry; 189 ::memset(&Entry, 0, sizeof(SrcHeaderBlockEntry)); 190 Entry.Size = sizeof(SrcHeaderBlockEntry); 191 Entry.FileSize = IS.Content->getBufferSize(); 192 Entry.FileNI = IS.NameIndex; 193 Entry.VFileNI = IS.VNameIndex; 194 Entry.ObjNI = 1; 195 Entry.IsVirtual = 0; 196 Entry.Version = 197 static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne); 198 Entry.CRC = CRC.getCRC(); 199 StringRef VName = getStringTableBuilder().getStringForId(IS.VNameIndex); 200 InjectedSourceTable.set_as(VName, std::move(Entry), 201 InjectedSourceHashTraits); 202 } 203 204 uint32_t SrcHeaderBlockSize = 205 sizeof(SrcHeaderBlockHeader) + 206 InjectedSourceTable.calculateSerializedLength(); 207 SN = allocateNamedStream("/src/headerblock", SrcHeaderBlockSize); 208 if (!SN) 209 return SN.takeError(); 210 for (const auto &IS : InjectedSources) { 211 SN = allocateNamedStream(IS.StreamName, IS.Content->getBufferSize()); 212 if (!SN) 213 return SN.takeError(); 214 } 215 } 216 217 // Do this last, since it relies on the named stream map being complete, and 218 // that can be updated by previous steps in the finalization. 219 if (Info) { 220 if (auto EC = Info->finalizeMsfLayout()) 221 return EC; 222 } 223 224 return Error::success(); 225 } 226 227 Expected<uint32_t> PDBFileBuilder::getNamedStreamIndex(StringRef Name) const { 228 uint32_t SN = 0; 229 if (!NamedStreams.get(Name, SN)) 230 return llvm::make_error<pdb::RawError>(raw_error_code::no_stream); 231 return SN; 232 } 233 234 void PDBFileBuilder::commitSrcHeaderBlock(WritableBinaryStream &MsfBuffer, 235 const msf::MSFLayout &Layout) { 236 assert(!InjectedSourceTable.empty()); 237 238 uint32_t SN = cantFail(getNamedStreamIndex("/src/headerblock")); 239 auto Stream = WritableMappedBlockStream::createIndexedStream( 240 Layout, MsfBuffer, SN, Allocator); 241 BinaryStreamWriter Writer(*Stream); 242 243 SrcHeaderBlockHeader Header; 244 ::memset(&Header, 0, sizeof(Header)); 245 Header.Version = static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne); 246 Header.Size = Writer.bytesRemaining(); 247 248 cantFail(Writer.writeObject(Header)); 249 cantFail(InjectedSourceTable.commit(Writer)); 250 251 assert(Writer.bytesRemaining() == 0); 252 } 253 254 void PDBFileBuilder::commitInjectedSources(WritableBinaryStream &MsfBuffer, 255 const msf::MSFLayout &Layout) { 256 if (InjectedSourceTable.empty()) 257 return; 258 259 llvm::TimeTraceScope timeScope("Commit injected sources"); 260 commitSrcHeaderBlock(MsfBuffer, Layout); 261 262 for (const auto &IS : InjectedSources) { 263 uint32_t SN = cantFail(getNamedStreamIndex(IS.StreamName)); 264 265 auto SourceStream = WritableMappedBlockStream::createIndexedStream( 266 Layout, MsfBuffer, SN, Allocator); 267 BinaryStreamWriter SourceWriter(*SourceStream); 268 assert(SourceWriter.bytesRemaining() == IS.Content->getBufferSize()); 269 cantFail(SourceWriter.writeBytes( 270 arrayRefFromStringRef(IS.Content->getBuffer()))); 271 } 272 } 273 274 Error PDBFileBuilder::commit(StringRef Filename, codeview::GUID *Guid) { 275 assert(!Filename.empty()); 276 if (auto EC = finalizeMsfLayout()) 277 return EC; 278 279 MSFLayout Layout; 280 Expected<FileBufferByteStream> ExpectedMsfBuffer = 281 Msf->commit(Filename, Layout); 282 if (!ExpectedMsfBuffer) 283 return ExpectedMsfBuffer.takeError(); 284 FileBufferByteStream Buffer = std::move(*ExpectedMsfBuffer); 285 286 auto ExpectedSN = getNamedStreamIndex("/names"); 287 if (!ExpectedSN) 288 return ExpectedSN.takeError(); 289 290 auto NS = WritableMappedBlockStream::createIndexedStream( 291 Layout, Buffer, *ExpectedSN, Allocator); 292 BinaryStreamWriter NSWriter(*NS); 293 if (auto EC = Strings.commit(NSWriter)) 294 return EC; 295 296 { 297 llvm::TimeTraceScope timeScope("Named stream data"); 298 for (const auto &NSE : NamedStreamData) { 299 if (NSE.second.empty()) 300 continue; 301 302 auto NS = WritableMappedBlockStream::createIndexedStream( 303 Layout, Buffer, NSE.first, Allocator); 304 BinaryStreamWriter NSW(*NS); 305 if (auto EC = NSW.writeBytes(arrayRefFromStringRef(NSE.second))) 306 return EC; 307 } 308 } 309 310 if (Info) { 311 if (auto EC = Info->commit(Layout, Buffer)) 312 return EC; 313 } 314 315 if (Dbi) { 316 if (auto EC = Dbi->commit(Layout, Buffer)) 317 return EC; 318 } 319 320 if (Tpi) { 321 if (auto EC = Tpi->commit(Layout, Buffer)) 322 return EC; 323 } 324 325 if (Ipi) { 326 if (auto EC = Ipi->commit(Layout, Buffer)) 327 return EC; 328 } 329 330 if (Gsi) { 331 if (auto EC = Gsi->commit(Layout, Buffer)) 332 return EC; 333 } 334 335 auto InfoStreamBlocks = Layout.StreamMap[StreamPDB]; 336 assert(!InfoStreamBlocks.empty()); 337 uint64_t InfoStreamFileOffset = 338 blockToOffset(InfoStreamBlocks.front(), Layout.SB->BlockSize); 339 InfoStreamHeader *H = reinterpret_cast<InfoStreamHeader *>( 340 Buffer.getBufferStart() + InfoStreamFileOffset); 341 342 commitInjectedSources(Buffer, Layout); 343 344 // Set the build id at the very end, after every other byte of the PDB 345 // has been written. 346 if (Info->hashPDBContentsToGUID()) { 347 llvm::TimeTraceScope timeScope("Compute build ID"); 348 349 // Compute a hash of all sections of the output file. 350 uint64_t Digest = 351 xxh3_64bits({Buffer.getBufferStart(), Buffer.getBufferEnd()}); 352 353 H->Age = 1; 354 355 memcpy(H->Guid.Guid, &Digest, 8); 356 // xxhash only gives us 8 bytes, so put some fixed data in the other half. 357 memcpy(H->Guid.Guid + 8, "LLD PDB.", 8); 358 359 // Put the hash in the Signature field too. 360 H->Signature = static_cast<uint32_t>(Digest); 361 362 // Return GUID to caller. 363 memcpy(Guid, H->Guid.Guid, 16); 364 } else { 365 H->Age = Info->getAge(); 366 H->Guid = Info->getGuid(); 367 std::optional<uint32_t> Sig = Info->getSignature(); 368 H->Signature = Sig ? *Sig : time(nullptr); 369 } 370 371 return Buffer.commit(); 372 } 373