456 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"10 11#include "llvm/ADT/ArrayRef.h"12#include "llvm/BinaryFormat/COFF.h"13#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h"14#include "llvm/DebugInfo/MSF/MSFBuilder.h"15#include "llvm/DebugInfo/MSF/MappedBlockStream.h"16#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h"17#include "llvm/DebugInfo/PDB/Native/RawError.h"18#include "llvm/Object/COFF.h"19#include "llvm/Support/BinaryStreamWriter.h"20#include "llvm/Support/Parallel.h"21#include "llvm/Support/TimeProfiler.h"22 23using namespace llvm;24using namespace llvm::codeview;25using namespace llvm::msf;26using namespace llvm::pdb;27 28DbiStreamBuilder::DbiStreamBuilder(msf::MSFBuilder &Msf)29 : Msf(Msf), Allocator(Msf.getAllocator()), Age(1), BuildNumber(0),30 PdbDllVersion(0), PdbDllRbld(0), Flags(0), MachineType(PDB_Machine::x86),31 Header(nullptr) {}32 33DbiStreamBuilder::~DbiStreamBuilder() = default;34 35void DbiStreamBuilder::setVersionHeader(PdbRaw_DbiVer V) { VerHeader = V; }36 37void DbiStreamBuilder::setAge(uint32_t A) { Age = A; }38 39void DbiStreamBuilder::setBuildNumber(uint16_t B) { BuildNumber = B; }40 41void DbiStreamBuilder::setBuildNumber(uint8_t Major, uint8_t Minor) {42 BuildNumber = (uint16_t(Major) << DbiBuildNo::BuildMajorShift) &43 DbiBuildNo::BuildMajorMask;44 BuildNumber |= (uint16_t(Minor) << DbiBuildNo::BuildMinorShift) &45 DbiBuildNo::BuildMinorMask;46 BuildNumber |= DbiBuildNo::NewVersionFormatMask;47}48 49void DbiStreamBuilder::setPdbDllVersion(uint16_t V) { PdbDllVersion = V; }50 51void DbiStreamBuilder::setPdbDllRbld(uint16_t R) { PdbDllRbld = R; }52 53void DbiStreamBuilder::setFlags(uint16_t F) { Flags = F; }54 55void DbiStreamBuilder::setMachineType(PDB_Machine M) { MachineType = M; }56 57void DbiStreamBuilder::setMachineType(COFF::MachineTypes M) {58 // These enums are mirrors of each other, so we can just cast the value.59 MachineType = static_cast<pdb::PDB_Machine>(static_cast<unsigned>(M));60}61 62void DbiStreamBuilder::setGlobalsStreamIndex(uint32_t Index) {63 GlobalsStreamIndex = Index;64}65 66void DbiStreamBuilder::setSymbolRecordStreamIndex(uint32_t Index) {67 SymRecordStreamIndex = Index;68}69 70void DbiStreamBuilder::setPublicsStreamIndex(uint32_t Index) {71 PublicsStreamIndex = Index;72}73 74void DbiStreamBuilder::addNewFpoData(const codeview::FrameData &FD) {75 if (!NewFpoData)76 NewFpoData.emplace(false);77 78 NewFpoData->addFrameData(FD);79}80 81void DbiStreamBuilder::addOldFpoData(const object::FpoData &FD) {82 OldFpoData.push_back(FD);83}84 85Error DbiStreamBuilder::addDbgStream(pdb::DbgHeaderType Type,86 ArrayRef<uint8_t> Data) {87 assert(Type != DbgHeaderType::NewFPO &&88 "NewFPO data should be written via addFrameData()!");89 90 DbgStreams[(int)Type] = DebugStream{};91 DbgStreams[(int)Type]->Size = Data.size();92 DbgStreams[(int)Type]->WriteFn = [Data](BinaryStreamWriter &Writer) {93 return Writer.writeArray(Data);94 };95 return Error::success();96}97 98uint32_t DbiStreamBuilder::addECName(StringRef Name) {99 return ECNamesBuilder.insert(Name);100}101 102uint32_t DbiStreamBuilder::calculateSerializedLength() const {103 // For now we only support serializing the header.104 return sizeof(DbiStreamHeader) + calculateFileInfoSubstreamSize() +105 calculateModiSubstreamSize() + calculateSectionContribsStreamSize() +106 calculateSectionMapStreamSize() + calculateDbgStreamsSize() +107 ECNamesBuilder.calculateSerializedSize();108}109 110Expected<DbiModuleDescriptorBuilder &>111DbiStreamBuilder::addModuleInfo(StringRef ModuleName) {112 uint32_t Index = ModiList.size();113 ModiList.push_back(114 std::make_unique<DbiModuleDescriptorBuilder>(ModuleName, Index, Msf));115 return *ModiList.back();116}117 118Error DbiStreamBuilder::addModuleSourceFile(DbiModuleDescriptorBuilder &Module,119 StringRef File) {120 uint32_t Index = SourceFileNames.size();121 SourceFileNames.insert(std::make_pair(File, Index));122 Module.addSourceFile(File);123 return Error::success();124}125 126Expected<uint32_t> DbiStreamBuilder::getSourceFileNameIndex(StringRef File) {127 auto NameIter = SourceFileNames.find(File);128 if (NameIter == SourceFileNames.end())129 return make_error<RawError>(raw_error_code::no_entry,130 "The specified source file was not found");131 return NameIter->getValue();132}133 134uint32_t DbiStreamBuilder::calculateModiSubstreamSize() const {135 uint32_t Size = 0;136 for (const auto &M : ModiList)137 Size += M->calculateSerializedLength();138 return Size;139}140 141uint32_t DbiStreamBuilder::calculateSectionContribsStreamSize() const {142 if (SectionContribs.empty())143 return 0;144 return sizeof(enum PdbRaw_DbiSecContribVer) +145 sizeof(SectionContribs[0]) * SectionContribs.size();146}147 148uint32_t DbiStreamBuilder::calculateSectionMapStreamSize() const {149 if (SectionMap.empty())150 return 0;151 return sizeof(SecMapHeader) + sizeof(SecMapEntry) * SectionMap.size();152}153 154uint32_t DbiStreamBuilder::calculateNamesOffset() const {155 uint32_t Offset = 0;156 Offset += sizeof(ulittle16_t); // NumModules157 Offset += sizeof(ulittle16_t); // NumSourceFiles158 Offset += ModiList.size() * sizeof(ulittle16_t); // ModIndices159 Offset += ModiList.size() * sizeof(ulittle16_t); // ModFileCounts160 uint32_t NumFileInfos = 0;161 for (const auto &M : ModiList)162 NumFileInfos += M->source_files().size();163 Offset += NumFileInfos * sizeof(ulittle32_t); // FileNameOffsets164 return Offset;165}166 167uint32_t DbiStreamBuilder::calculateFileInfoSubstreamSize() const {168 uint32_t Size = calculateNamesOffset();169 Size += calculateNamesBufferSize();170 return alignTo(Size, sizeof(uint32_t));171}172 173uint32_t DbiStreamBuilder::calculateNamesBufferSize() const {174 uint32_t Size = 0;175 for (const auto &F : SourceFileNames) {176 Size += F.getKeyLength() + 1; // Names[I];177 }178 return Size;179}180 181uint32_t DbiStreamBuilder::calculateDbgStreamsSize() const {182 return DbgStreams.size() * sizeof(uint16_t);183}184 185Error DbiStreamBuilder::generateFileInfoSubstream() {186 uint32_t Size = calculateFileInfoSubstreamSize();187 auto Data = Allocator.Allocate<uint8_t>(Size);188 uint32_t NamesOffset = calculateNamesOffset();189 190 FileInfoBuffer = MutableBinaryByteStream(MutableArrayRef<uint8_t>(Data, Size),191 llvm::endianness::little);192 193 WritableBinaryStreamRef MetadataBuffer =194 WritableBinaryStreamRef(FileInfoBuffer).keep_front(NamesOffset);195 BinaryStreamWriter MetadataWriter(MetadataBuffer);196 197 uint16_t ModiCount = std::min<uint32_t>(UINT16_MAX, ModiList.size());198 uint16_t FileCount = std::min<uint32_t>(UINT16_MAX, SourceFileNames.size());199 if (auto EC = MetadataWriter.writeInteger(ModiCount)) // NumModules200 return EC;201 if (auto EC = MetadataWriter.writeInteger(FileCount)) // NumSourceFiles202 return EC;203 for (uint16_t I = 0; I < ModiCount; ++I) {204 if (auto EC = MetadataWriter.writeInteger(I)) // Mod Indices205 return EC;206 }207 for (const auto &MI : ModiList) {208 FileCount = static_cast<uint16_t>(MI->source_files().size());209 if (auto EC = MetadataWriter.writeInteger(FileCount)) // Mod File Counts210 return EC;211 }212 213 // Before writing the FileNameOffsets array, write the NamesBuffer array.214 // A side effect of this is that this will actually compute the various215 // file name offsets, so we can then go back and write the FileNameOffsets216 // array to the other substream.217 NamesBuffer = WritableBinaryStreamRef(FileInfoBuffer).drop_front(NamesOffset);218 BinaryStreamWriter NameBufferWriter(NamesBuffer);219 for (auto &Name : SourceFileNames) {220 Name.second = NameBufferWriter.getOffset();221 if (auto EC = NameBufferWriter.writeCString(Name.getKey()))222 return EC;223 }224 225 for (const auto &MI : ModiList) {226 for (StringRef Name : MI->source_files()) {227 auto Result = SourceFileNames.find(Name);228 if (Result == SourceFileNames.end())229 return make_error<RawError>(raw_error_code::no_entry,230 "The source file was not found.");231 if (auto EC = MetadataWriter.writeInteger(Result->second))232 return EC;233 }234 }235 236 if (auto EC = NameBufferWriter.padToAlignment(sizeof(uint32_t)))237 return EC;238 239 if (NameBufferWriter.bytesRemaining() > 0)240 return make_error<RawError>(raw_error_code::invalid_format,241 "The names buffer contained unexpected data.");242 243 if (MetadataWriter.bytesRemaining() > sizeof(uint32_t))244 return make_error<RawError>(245 raw_error_code::invalid_format,246 "The metadata buffer contained unexpected data.");247 248 return Error::success();249}250 251Error DbiStreamBuilder::finalize() {252 if (Header)253 return Error::success();254 255 for (auto &MI : ModiList)256 MI->finalize();257 258 if (auto EC = generateFileInfoSubstream())259 return EC;260 261 DbiStreamHeader *H = Allocator.Allocate<DbiStreamHeader>();262 ::memset(H, 0, sizeof(DbiStreamHeader));263 H->VersionHeader = *VerHeader;264 H->VersionSignature = -1;265 H->Age = Age;266 H->BuildNumber = BuildNumber;267 H->Flags = Flags;268 H->PdbDllRbld = PdbDllRbld;269 H->PdbDllVersion = PdbDllVersion;270 H->MachineType = static_cast<uint16_t>(MachineType);271 272 H->ECSubstreamSize = ECNamesBuilder.calculateSerializedSize();273 H->FileInfoSize = FileInfoBuffer.getLength();274 H->ModiSubstreamSize = calculateModiSubstreamSize();275 H->OptionalDbgHdrSize = DbgStreams.size() * sizeof(uint16_t);276 H->SecContrSubstreamSize = calculateSectionContribsStreamSize();277 H->SectionMapSize = calculateSectionMapStreamSize();278 H->TypeServerSize = 0;279 H->SymRecordStreamIndex = SymRecordStreamIndex;280 H->PublicSymbolStreamIndex = PublicsStreamIndex;281 H->MFCTypeServerIndex = 0; // Not sure what this is, but link.exe writes 0.282 H->GlobalSymbolStreamIndex = GlobalsStreamIndex;283 284 Header = H;285 return Error::success();286}287 288Error DbiStreamBuilder::finalizeMsfLayout() {289 if (NewFpoData) {290 DbgStreams[(int)DbgHeaderType::NewFPO] = DebugStream{};291 DbgStreams[(int)DbgHeaderType::NewFPO]->Size =292 NewFpoData->calculateSerializedSize();293 DbgStreams[(int)DbgHeaderType::NewFPO]->WriteFn =294 [this](BinaryStreamWriter &Writer) {295 return NewFpoData->commit(Writer);296 };297 }298 299 if (!OldFpoData.empty()) {300 DbgStreams[(int)DbgHeaderType::FPO] = DebugStream{};301 DbgStreams[(int)DbgHeaderType::FPO]->Size =302 sizeof(object::FpoData) * OldFpoData.size();303 DbgStreams[(int)DbgHeaderType::FPO]->WriteFn =304 [this](BinaryStreamWriter &Writer) {305 return Writer.writeArray(ArrayRef(OldFpoData));306 };307 }308 309 for (auto &S : DbgStreams) {310 if (!S)311 continue;312 auto ExpectedIndex = Msf.addStream(S->Size);313 if (!ExpectedIndex)314 return ExpectedIndex.takeError();315 S->StreamNumber = *ExpectedIndex;316 }317 318 for (auto &MI : ModiList) {319 if (auto EC = MI->finalizeMsfLayout())320 return EC;321 }322 323 uint32_t Length = calculateSerializedLength();324 if (auto EC = Msf.setStreamSize(StreamDBI, Length))325 return EC;326 return Error::success();327}328 329static uint16_t toSecMapFlags(uint32_t Flags) {330 uint16_t Ret = 0;331 if (Flags & COFF::IMAGE_SCN_MEM_READ)332 Ret |= static_cast<uint16_t>(OMFSegDescFlags::Read);333 if (Flags & COFF::IMAGE_SCN_MEM_WRITE)334 Ret |= static_cast<uint16_t>(OMFSegDescFlags::Write);335 if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE)336 Ret |= static_cast<uint16_t>(OMFSegDescFlags::Execute);337 if (!(Flags & COFF::IMAGE_SCN_MEM_16BIT))338 Ret |= static_cast<uint16_t>(OMFSegDescFlags::AddressIs32Bit);339 340 // This seems always 1.341 Ret |= static_cast<uint16_t>(OMFSegDescFlags::IsSelector);342 343 return Ret;344}345 346// Populate the Section Map from COFF section headers.347//348// A Section Map seem to be a copy of a COFF section list in other format.349// I don't know why a PDB file contains both a COFF section header and350// a Section Map, but it seems it must be present in a PDB.351void DbiStreamBuilder::createSectionMap(352 ArrayRef<llvm::object::coff_section> SecHdrs) {353 int Idx = 0;354 355 auto Add = [&]() -> SecMapEntry & {356 SectionMap.emplace_back();357 auto &Entry = SectionMap.back();358 memset(&Entry, 0, sizeof(Entry));359 360 Entry.Frame = Idx + 1;361 362 // We don't know the meaning of these fields yet.363 Entry.SecName = UINT16_MAX;364 Entry.ClassName = UINT16_MAX;365 366 return Entry;367 };368 369 for (auto &Hdr : SecHdrs) {370 auto &Entry = Add();371 Entry.Flags = toSecMapFlags(Hdr.Characteristics);372 Entry.SecByteLength = Hdr.VirtualSize;373 ++Idx;374 }375 376 // The last entry is for absolute symbols.377 auto &Entry = Add();378 Entry.Flags = static_cast<uint16_t>(OMFSegDescFlags::AddressIs32Bit) |379 static_cast<uint16_t>(OMFSegDescFlags::IsAbsoluteAddress);380 Entry.SecByteLength = UINT32_MAX;381}382 383Error DbiStreamBuilder::commit(const msf::MSFLayout &Layout,384 WritableBinaryStreamRef MsfBuffer) {385 llvm::TimeTraceScope timeScope("Commit DBI stream");386 if (auto EC = finalize())387 return EC;388 389 auto DbiS = WritableMappedBlockStream::createIndexedStream(390 Layout, MsfBuffer, StreamDBI, Allocator);391 392 BinaryStreamWriter Writer(*DbiS);393 if (auto EC = Writer.writeObject(*Header))394 return EC;395 396 for (auto &M : ModiList) {397 if (auto EC = M->commit(Writer))398 return EC;399 }400 401 // Commit symbol streams. This is a lot of data, so do it in parallel.402 if (auto EC = parallelForEachError(403 ModiList, [&](std::unique_ptr<DbiModuleDescriptorBuilder> &M) {404 return M->commitSymbolStream(Layout, MsfBuffer);405 }))406 return EC;407 408 if (!SectionContribs.empty()) {409 if (auto EC = Writer.writeEnum(DbiSecContribVer60))410 return EC;411 if (auto EC = Writer.writeArray(ArrayRef(SectionContribs)))412 return EC;413 }414 415 if (!SectionMap.empty()) {416 ulittle16_t Size = static_cast<ulittle16_t>(SectionMap.size());417 SecMapHeader SMHeader = {Size, Size};418 if (auto EC = Writer.writeObject(SMHeader))419 return EC;420 if (auto EC = Writer.writeArray(ArrayRef(SectionMap)))421 return EC;422 }423 424 if (auto EC = Writer.writeStreamRef(FileInfoBuffer))425 return EC;426 427 if (auto EC = ECNamesBuilder.commit(Writer))428 return EC;429 430 for (auto &Stream : DbgStreams) {431 uint16_t StreamNumber = kInvalidStreamIndex;432 if (Stream)433 StreamNumber = Stream->StreamNumber;434 if (auto EC = Writer.writeInteger(StreamNumber))435 return EC;436 }437 438 for (auto &Stream : DbgStreams) {439 if (!Stream)440 continue;441 assert(Stream->StreamNumber != kInvalidStreamIndex);442 443 auto WritableStream = WritableMappedBlockStream::createIndexedStream(444 Layout, MsfBuffer, Stream->StreamNumber, Allocator);445 BinaryStreamWriter DbgStreamWriter(*WritableStream);446 447 if (auto EC = Stream->WriteFn(DbgStreamWriter))448 return EC;449 }450 451 if (Writer.bytesRemaining() > 0)452 return make_error<RawError>(raw_error_code::invalid_format,453 "Unexpected bytes found in DBI Stream");454 return Error::success();455}456