Path: blob/main/contrib/llvm-project/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp
35293 views
//===- DbiStreamBuilder.cpp - PDB Dbi Stream Creation -----------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"910#include "llvm/ADT/ArrayRef.h"11#include "llvm/BinaryFormat/COFF.h"12#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h"13#include "llvm/DebugInfo/MSF/MSFBuilder.h"14#include "llvm/DebugInfo/MSF/MappedBlockStream.h"15#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h"16#include "llvm/DebugInfo/PDB/Native/RawError.h"17#include "llvm/Object/COFF.h"18#include "llvm/Support/BinaryStreamWriter.h"19#include "llvm/Support/Parallel.h"20#include "llvm/Support/TimeProfiler.h"2122using namespace llvm;23using namespace llvm::codeview;24using namespace llvm::msf;25using namespace llvm::pdb;2627DbiStreamBuilder::DbiStreamBuilder(msf::MSFBuilder &Msf)28: Msf(Msf), Allocator(Msf.getAllocator()), Age(1), BuildNumber(0),29PdbDllVersion(0), PdbDllRbld(0), Flags(0), MachineType(PDB_Machine::x86),30Header(nullptr) {}3132DbiStreamBuilder::~DbiStreamBuilder() = default;3334void DbiStreamBuilder::setVersionHeader(PdbRaw_DbiVer V) { VerHeader = V; }3536void DbiStreamBuilder::setAge(uint32_t A) { Age = A; }3738void DbiStreamBuilder::setBuildNumber(uint16_t B) { BuildNumber = B; }3940void DbiStreamBuilder::setBuildNumber(uint8_t Major, uint8_t Minor) {41BuildNumber = (uint16_t(Major) << DbiBuildNo::BuildMajorShift) &42DbiBuildNo::BuildMajorMask;43BuildNumber |= (uint16_t(Minor) << DbiBuildNo::BuildMinorShift) &44DbiBuildNo::BuildMinorMask;45BuildNumber |= DbiBuildNo::NewVersionFormatMask;46}4748void DbiStreamBuilder::setPdbDllVersion(uint16_t V) { PdbDllVersion = V; }4950void DbiStreamBuilder::setPdbDllRbld(uint16_t R) { PdbDllRbld = R; }5152void DbiStreamBuilder::setFlags(uint16_t F) { Flags = F; }5354void DbiStreamBuilder::setMachineType(PDB_Machine M) { MachineType = M; }5556void DbiStreamBuilder::setMachineType(COFF::MachineTypes M) {57// These enums are mirrors of each other, so we can just cast the value.58MachineType = static_cast<pdb::PDB_Machine>(static_cast<unsigned>(M));59}6061void DbiStreamBuilder::setGlobalsStreamIndex(uint32_t Index) {62GlobalsStreamIndex = Index;63}6465void DbiStreamBuilder::setSymbolRecordStreamIndex(uint32_t Index) {66SymRecordStreamIndex = Index;67}6869void DbiStreamBuilder::setPublicsStreamIndex(uint32_t Index) {70PublicsStreamIndex = Index;71}7273void DbiStreamBuilder::addNewFpoData(const codeview::FrameData &FD) {74if (!NewFpoData)75NewFpoData.emplace(false);7677NewFpoData->addFrameData(FD);78}7980void DbiStreamBuilder::addOldFpoData(const object::FpoData &FD) {81OldFpoData.push_back(FD);82}8384Error DbiStreamBuilder::addDbgStream(pdb::DbgHeaderType Type,85ArrayRef<uint8_t> Data) {86assert(Type != DbgHeaderType::NewFPO &&87"NewFPO data should be written via addFrameData()!");8889DbgStreams[(int)Type] = DebugStream{};90DbgStreams[(int)Type]->Size = Data.size();91DbgStreams[(int)Type]->WriteFn = [Data](BinaryStreamWriter &Writer) {92return Writer.writeArray(Data);93};94return Error::success();95}9697uint32_t DbiStreamBuilder::addECName(StringRef Name) {98return ECNamesBuilder.insert(Name);99}100101uint32_t DbiStreamBuilder::calculateSerializedLength() const {102// For now we only support serializing the header.103return sizeof(DbiStreamHeader) + calculateFileInfoSubstreamSize() +104calculateModiSubstreamSize() + calculateSectionContribsStreamSize() +105calculateSectionMapStreamSize() + calculateDbgStreamsSize() +106ECNamesBuilder.calculateSerializedSize();107}108109Expected<DbiModuleDescriptorBuilder &>110DbiStreamBuilder::addModuleInfo(StringRef ModuleName) {111uint32_t Index = ModiList.size();112ModiList.push_back(113std::make_unique<DbiModuleDescriptorBuilder>(ModuleName, Index, Msf));114return *ModiList.back();115}116117Error DbiStreamBuilder::addModuleSourceFile(DbiModuleDescriptorBuilder &Module,118StringRef File) {119uint32_t Index = SourceFileNames.size();120SourceFileNames.insert(std::make_pair(File, Index));121Module.addSourceFile(File);122return Error::success();123}124125Expected<uint32_t> DbiStreamBuilder::getSourceFileNameIndex(StringRef File) {126auto NameIter = SourceFileNames.find(File);127if (NameIter == SourceFileNames.end())128return make_error<RawError>(raw_error_code::no_entry,129"The specified source file was not found");130return NameIter->getValue();131}132133uint32_t DbiStreamBuilder::calculateModiSubstreamSize() const {134uint32_t Size = 0;135for (const auto &M : ModiList)136Size += M->calculateSerializedLength();137return Size;138}139140uint32_t DbiStreamBuilder::calculateSectionContribsStreamSize() const {141if (SectionContribs.empty())142return 0;143return sizeof(enum PdbRaw_DbiSecContribVer) +144sizeof(SectionContribs[0]) * SectionContribs.size();145}146147uint32_t DbiStreamBuilder::calculateSectionMapStreamSize() const {148if (SectionMap.empty())149return 0;150return sizeof(SecMapHeader) + sizeof(SecMapEntry) * SectionMap.size();151}152153uint32_t DbiStreamBuilder::calculateNamesOffset() const {154uint32_t Offset = 0;155Offset += sizeof(ulittle16_t); // NumModules156Offset += sizeof(ulittle16_t); // NumSourceFiles157Offset += ModiList.size() * sizeof(ulittle16_t); // ModIndices158Offset += ModiList.size() * sizeof(ulittle16_t); // ModFileCounts159uint32_t NumFileInfos = 0;160for (const auto &M : ModiList)161NumFileInfos += M->source_files().size();162Offset += NumFileInfos * sizeof(ulittle32_t); // FileNameOffsets163return Offset;164}165166uint32_t DbiStreamBuilder::calculateFileInfoSubstreamSize() const {167uint32_t Size = calculateNamesOffset();168Size += calculateNamesBufferSize();169return alignTo(Size, sizeof(uint32_t));170}171172uint32_t DbiStreamBuilder::calculateNamesBufferSize() const {173uint32_t Size = 0;174for (const auto &F : SourceFileNames) {175Size += F.getKeyLength() + 1; // Names[I];176}177return Size;178}179180uint32_t DbiStreamBuilder::calculateDbgStreamsSize() const {181return DbgStreams.size() * sizeof(uint16_t);182}183184Error DbiStreamBuilder::generateFileInfoSubstream() {185uint32_t Size = calculateFileInfoSubstreamSize();186auto Data = Allocator.Allocate<uint8_t>(Size);187uint32_t NamesOffset = calculateNamesOffset();188189FileInfoBuffer = MutableBinaryByteStream(MutableArrayRef<uint8_t>(Data, Size),190llvm::endianness::little);191192WritableBinaryStreamRef MetadataBuffer =193WritableBinaryStreamRef(FileInfoBuffer).keep_front(NamesOffset);194BinaryStreamWriter MetadataWriter(MetadataBuffer);195196uint16_t ModiCount = std::min<uint32_t>(UINT16_MAX, ModiList.size());197uint16_t FileCount = std::min<uint32_t>(UINT16_MAX, SourceFileNames.size());198if (auto EC = MetadataWriter.writeInteger(ModiCount)) // NumModules199return EC;200if (auto EC = MetadataWriter.writeInteger(FileCount)) // NumSourceFiles201return EC;202for (uint16_t I = 0; I < ModiCount; ++I) {203if (auto EC = MetadataWriter.writeInteger(I)) // Mod Indices204return EC;205}206for (const auto &MI : ModiList) {207FileCount = static_cast<uint16_t>(MI->source_files().size());208if (auto EC = MetadataWriter.writeInteger(FileCount)) // Mod File Counts209return EC;210}211212// Before writing the FileNameOffsets array, write the NamesBuffer array.213// A side effect of this is that this will actually compute the various214// file name offsets, so we can then go back and write the FileNameOffsets215// array to the other substream.216NamesBuffer = WritableBinaryStreamRef(FileInfoBuffer).drop_front(NamesOffset);217BinaryStreamWriter NameBufferWriter(NamesBuffer);218for (auto &Name : SourceFileNames) {219Name.second = NameBufferWriter.getOffset();220if (auto EC = NameBufferWriter.writeCString(Name.getKey()))221return EC;222}223224for (const auto &MI : ModiList) {225for (StringRef Name : MI->source_files()) {226auto Result = SourceFileNames.find(Name);227if (Result == SourceFileNames.end())228return make_error<RawError>(raw_error_code::no_entry,229"The source file was not found.");230if (auto EC = MetadataWriter.writeInteger(Result->second))231return EC;232}233}234235if (auto EC = NameBufferWriter.padToAlignment(sizeof(uint32_t)))236return EC;237238if (NameBufferWriter.bytesRemaining() > 0)239return make_error<RawError>(raw_error_code::invalid_format,240"The names buffer contained unexpected data.");241242if (MetadataWriter.bytesRemaining() > sizeof(uint32_t))243return make_error<RawError>(244raw_error_code::invalid_format,245"The metadata buffer contained unexpected data.");246247return Error::success();248}249250Error DbiStreamBuilder::finalize() {251if (Header)252return Error::success();253254for (auto &MI : ModiList)255MI->finalize();256257if (auto EC = generateFileInfoSubstream())258return EC;259260DbiStreamHeader *H = Allocator.Allocate<DbiStreamHeader>();261::memset(H, 0, sizeof(DbiStreamHeader));262H->VersionHeader = *VerHeader;263H->VersionSignature = -1;264H->Age = Age;265H->BuildNumber = BuildNumber;266H->Flags = Flags;267H->PdbDllRbld = PdbDllRbld;268H->PdbDllVersion = PdbDllVersion;269H->MachineType = static_cast<uint16_t>(MachineType);270271H->ECSubstreamSize = ECNamesBuilder.calculateSerializedSize();272H->FileInfoSize = FileInfoBuffer.getLength();273H->ModiSubstreamSize = calculateModiSubstreamSize();274H->OptionalDbgHdrSize = DbgStreams.size() * sizeof(uint16_t);275H->SecContrSubstreamSize = calculateSectionContribsStreamSize();276H->SectionMapSize = calculateSectionMapStreamSize();277H->TypeServerSize = 0;278H->SymRecordStreamIndex = SymRecordStreamIndex;279H->PublicSymbolStreamIndex = PublicsStreamIndex;280H->MFCTypeServerIndex = 0; // Not sure what this is, but link.exe writes 0.281H->GlobalSymbolStreamIndex = GlobalsStreamIndex;282283Header = H;284return Error::success();285}286287Error DbiStreamBuilder::finalizeMsfLayout() {288if (NewFpoData) {289DbgStreams[(int)DbgHeaderType::NewFPO] = DebugStream{};290DbgStreams[(int)DbgHeaderType::NewFPO]->Size =291NewFpoData->calculateSerializedSize();292DbgStreams[(int)DbgHeaderType::NewFPO]->WriteFn =293[this](BinaryStreamWriter &Writer) {294return NewFpoData->commit(Writer);295};296}297298if (!OldFpoData.empty()) {299DbgStreams[(int)DbgHeaderType::FPO] = DebugStream{};300DbgStreams[(int)DbgHeaderType::FPO]->Size =301sizeof(object::FpoData) * OldFpoData.size();302DbgStreams[(int)DbgHeaderType::FPO]->WriteFn =303[this](BinaryStreamWriter &Writer) {304return Writer.writeArray(ArrayRef(OldFpoData));305};306}307308for (auto &S : DbgStreams) {309if (!S)310continue;311auto ExpectedIndex = Msf.addStream(S->Size);312if (!ExpectedIndex)313return ExpectedIndex.takeError();314S->StreamNumber = *ExpectedIndex;315}316317for (auto &MI : ModiList) {318if (auto EC = MI->finalizeMsfLayout())319return EC;320}321322uint32_t Length = calculateSerializedLength();323if (auto EC = Msf.setStreamSize(StreamDBI, Length))324return EC;325return Error::success();326}327328static uint16_t toSecMapFlags(uint32_t Flags) {329uint16_t Ret = 0;330if (Flags & COFF::IMAGE_SCN_MEM_READ)331Ret |= static_cast<uint16_t>(OMFSegDescFlags::Read);332if (Flags & COFF::IMAGE_SCN_MEM_WRITE)333Ret |= static_cast<uint16_t>(OMFSegDescFlags::Write);334if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE)335Ret |= static_cast<uint16_t>(OMFSegDescFlags::Execute);336if (!(Flags & COFF::IMAGE_SCN_MEM_16BIT))337Ret |= static_cast<uint16_t>(OMFSegDescFlags::AddressIs32Bit);338339// This seems always 1.340Ret |= static_cast<uint16_t>(OMFSegDescFlags::IsSelector);341342return Ret;343}344345// Populate the Section Map from COFF section headers.346//347// A Section Map seem to be a copy of a COFF section list in other format.348// I don't know why a PDB file contains both a COFF section header and349// a Section Map, but it seems it must be present in a PDB.350void DbiStreamBuilder::createSectionMap(351ArrayRef<llvm::object::coff_section> SecHdrs) {352int Idx = 0;353354auto Add = [&]() -> SecMapEntry & {355SectionMap.emplace_back();356auto &Entry = SectionMap.back();357memset(&Entry, 0, sizeof(Entry));358359Entry.Frame = Idx + 1;360361// We don't know the meaning of these fields yet.362Entry.SecName = UINT16_MAX;363Entry.ClassName = UINT16_MAX;364365return Entry;366};367368for (auto &Hdr : SecHdrs) {369auto &Entry = Add();370Entry.Flags = toSecMapFlags(Hdr.Characteristics);371Entry.SecByteLength = Hdr.VirtualSize;372++Idx;373}374375// The last entry is for absolute symbols.376auto &Entry = Add();377Entry.Flags = static_cast<uint16_t>(OMFSegDescFlags::AddressIs32Bit) |378static_cast<uint16_t>(OMFSegDescFlags::IsAbsoluteAddress);379Entry.SecByteLength = UINT32_MAX;380}381382Error DbiStreamBuilder::commit(const msf::MSFLayout &Layout,383WritableBinaryStreamRef MsfBuffer) {384llvm::TimeTraceScope timeScope("Commit DBI stream");385if (auto EC = finalize())386return EC;387388auto DbiS = WritableMappedBlockStream::createIndexedStream(389Layout, MsfBuffer, StreamDBI, Allocator);390391BinaryStreamWriter Writer(*DbiS);392if (auto EC = Writer.writeObject(*Header))393return EC;394395for (auto &M : ModiList) {396if (auto EC = M->commit(Writer))397return EC;398}399400// Commit symbol streams. This is a lot of data, so do it in parallel.401if (auto EC = parallelForEachError(402ModiList, [&](std::unique_ptr<DbiModuleDescriptorBuilder> &M) {403return M->commitSymbolStream(Layout, MsfBuffer);404}))405return EC;406407if (!SectionContribs.empty()) {408if (auto EC = Writer.writeEnum(DbiSecContribVer60))409return EC;410if (auto EC = Writer.writeArray(ArrayRef(SectionContribs)))411return EC;412}413414if (!SectionMap.empty()) {415ulittle16_t Size = static_cast<ulittle16_t>(SectionMap.size());416SecMapHeader SMHeader = {Size, Size};417if (auto EC = Writer.writeObject(SMHeader))418return EC;419if (auto EC = Writer.writeArray(ArrayRef(SectionMap)))420return EC;421}422423if (auto EC = Writer.writeStreamRef(FileInfoBuffer))424return EC;425426if (auto EC = ECNamesBuilder.commit(Writer))427return EC;428429for (auto &Stream : DbgStreams) {430uint16_t StreamNumber = kInvalidStreamIndex;431if (Stream)432StreamNumber = Stream->StreamNumber;433if (auto EC = Writer.writeInteger(StreamNumber))434return EC;435}436437for (auto &Stream : DbgStreams) {438if (!Stream)439continue;440assert(Stream->StreamNumber != kInvalidStreamIndex);441442auto WritableStream = WritableMappedBlockStream::createIndexedStream(443Layout, MsfBuffer, Stream->StreamNumber, Allocator);444BinaryStreamWriter DbgStreamWriter(*WritableStream);445446if (auto EC = Stream->WriteFn(DbgStreamWriter))447return EC;448}449450if (Writer.bytesRemaining() > 0)451return make_error<RawError>(raw_error_code::invalid_format,452"Unexpected bytes found in DBI Stream");453return Error::success();454}455456457