//===- DbiStreamBuilder.cpp - PDB Dbi Stream Creation -----------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/DebugInfo/MSF/MSFBuilder.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h" #include "llvm/DebugInfo/PDB/Native/DbiStream.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" #include "llvm/Object/COFF.h" #include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Support/COFF.h" using namespace llvm; using namespace llvm::codeview; using namespace llvm::msf; using namespace llvm::pdb; DbiStreamBuilder::DbiStreamBuilder(msf::MSFBuilder &Msf) : Msf(Msf), Allocator(Msf.getAllocator()), Age(1), BuildNumber(0), PdbDllVersion(0), PdbDllRbld(0), Flags(0), MachineType(PDB_Machine::x86), Header(nullptr), DbgStreams((int)DbgHeaderType::Max) {} DbiStreamBuilder::~DbiStreamBuilder() {} void DbiStreamBuilder::setVersionHeader(PdbRaw_DbiVer V) { VerHeader = V; } void DbiStreamBuilder::setAge(uint32_t A) { Age = A; } void DbiStreamBuilder::setBuildNumber(uint16_t B) { BuildNumber = B; } void DbiStreamBuilder::setPdbDllVersion(uint16_t V) { PdbDllVersion = V; } void DbiStreamBuilder::setPdbDllRbld(uint16_t R) { PdbDllRbld = R; } void DbiStreamBuilder::setFlags(uint16_t F) { Flags = F; } void DbiStreamBuilder::setMachineType(PDB_Machine M) { MachineType = M; } void DbiStreamBuilder::setSectionContribs(ArrayRef Arr) { SectionContribs = Arr; } void DbiStreamBuilder::setSectionMap(ArrayRef SecMap) { SectionMap = SecMap; } Error DbiStreamBuilder::addDbgStream(pdb::DbgHeaderType Type, ArrayRef Data) { if (DbgStreams[(int)Type].StreamNumber) return make_error(raw_error_code::duplicate_entry, "The specified stream type already exists"); auto ExpectedIndex = Msf.addStream(Data.size()); if (!ExpectedIndex) return ExpectedIndex.takeError(); uint32_t Index = std::move(*ExpectedIndex); DbgStreams[(int)Type].Data = Data; DbgStreams[(int)Type].StreamNumber = Index; return Error::success(); } uint32_t DbiStreamBuilder::calculateSerializedLength() const { // For now we only support serializing the header. return sizeof(DbiStreamHeader) + calculateFileInfoSubstreamSize() + calculateModiSubstreamSize() + calculateSectionContribsStreamSize() + calculateSectionMapStreamSize() + calculateDbgStreamsSize(); } Expected DbiStreamBuilder::addModuleInfo(StringRef ModuleName) { uint32_t Index = ModiList.size(); auto MIB = llvm::make_unique(ModuleName, Index, Msf); auto M = MIB.get(); auto Result = ModiMap.insert(std::make_pair(ModuleName, std::move(MIB))); if (!Result.second) return make_error(raw_error_code::duplicate_entry, "The specified module already exists"); ModiList.push_back(M); return *M; } Error DbiStreamBuilder::addModuleSourceFile(StringRef Module, StringRef File) { auto ModIter = ModiMap.find(Module); if (ModIter == ModiMap.end()) return make_error(raw_error_code::no_entry, "The specified module was not found"); uint32_t Index = SourceFileNames.size(); SourceFileNames.insert(std::make_pair(File, Index)); auto &ModEntry = *ModIter; ModEntry.second->addSourceFile(File); return Error::success(); } Expected DbiStreamBuilder::getSourceFileNameIndex(StringRef File) { auto NameIter = SourceFileNames.find(File); if (NameIter == SourceFileNames.end()) return make_error(raw_error_code::no_entry, "The specified source file was not found"); return NameIter->getValue(); } uint32_t DbiStreamBuilder::calculateModiSubstreamSize() const { uint32_t Size = 0; for (const auto &M : ModiList) Size += M->calculateSerializedLength(); return Size; } uint32_t DbiStreamBuilder::calculateSectionContribsStreamSize() const { if (SectionContribs.empty()) return 0; return sizeof(enum PdbRaw_DbiSecContribVer) + sizeof(SectionContribs[0]) * SectionContribs.size(); } uint32_t DbiStreamBuilder::calculateSectionMapStreamSize() const { if (SectionMap.empty()) return 0; return sizeof(SecMapHeader) + sizeof(SecMapEntry) * SectionMap.size(); } uint32_t DbiStreamBuilder::calculateNamesOffset() const { uint32_t Offset = 0; Offset += sizeof(ulittle16_t); // NumModules Offset += sizeof(ulittle16_t); // NumSourceFiles Offset += ModiList.size() * sizeof(ulittle16_t); // ModIndices Offset += ModiList.size() * sizeof(ulittle16_t); // ModFileCounts uint32_t NumFileInfos = 0; for (const auto &M : ModiList) NumFileInfos += M->source_files().size(); Offset += NumFileInfos * sizeof(ulittle32_t); // FileNameOffsets return Offset; } uint32_t DbiStreamBuilder::calculateFileInfoSubstreamSize() const { uint32_t Size = calculateNamesOffset(); Size += calculateNamesBufferSize(); return alignTo(Size, sizeof(uint32_t)); } uint32_t DbiStreamBuilder::calculateNamesBufferSize() const { uint32_t Size = 0; for (const auto &F : SourceFileNames) { Size += F.getKeyLength() + 1; // Names[I]; } return Size; } uint32_t DbiStreamBuilder::calculateDbgStreamsSize() const { return DbgStreams.size() * sizeof(uint16_t); } Error DbiStreamBuilder::generateFileInfoSubstream() { uint32_t Size = calculateFileInfoSubstreamSize(); auto Data = Allocator.Allocate(Size); uint32_t NamesOffset = calculateNamesOffset(); FileInfoBuffer = MutableBinaryByteStream(MutableArrayRef(Data, Size), llvm::support::little); WritableBinaryStreamRef MetadataBuffer = WritableBinaryStreamRef(FileInfoBuffer).keep_front(NamesOffset); BinaryStreamWriter MetadataWriter(MetadataBuffer); uint16_t ModiCount = std::min(UINT16_MAX, ModiList.size()); uint16_t FileCount = std::min(UINT16_MAX, SourceFileNames.size()); if (auto EC = MetadataWriter.writeInteger(ModiCount)) // NumModules return EC; if (auto EC = MetadataWriter.writeInteger(FileCount)) // NumSourceFiles return EC; for (uint16_t I = 0; I < ModiCount; ++I) { if (auto EC = MetadataWriter.writeInteger(I)) // Mod Indices return EC; } for (const auto &MI : ModiList) { FileCount = static_cast(MI->source_files().size()); if (auto EC = MetadataWriter.writeInteger(FileCount)) // Mod File Counts return EC; } // Before writing the FileNameOffsets array, write the NamesBuffer array. // A side effect of this is that this will actually compute the various // file name offsets, so we can then go back and write the FileNameOffsets // array to the other substream. NamesBuffer = WritableBinaryStreamRef(FileInfoBuffer).drop_front(NamesOffset); BinaryStreamWriter NameBufferWriter(NamesBuffer); for (auto &Name : SourceFileNames) { Name.second = NameBufferWriter.getOffset(); if (auto EC = NameBufferWriter.writeCString(Name.getKey())) return EC; } for (const auto &MI : ModiList) { for (StringRef Name : MI->source_files()) { auto Result = SourceFileNames.find(Name); if (Result == SourceFileNames.end()) return make_error(raw_error_code::no_entry, "The source file was not found."); if (auto EC = MetadataWriter.writeInteger(Result->second)) return EC; } } if (auto EC = NameBufferWriter.padToAlignment(sizeof(uint32_t))) return EC; if (NameBufferWriter.bytesRemaining() > 0) return make_error(raw_error_code::invalid_format, "The names buffer contained unexpected data."); if (MetadataWriter.bytesRemaining() > sizeof(uint32_t)) return make_error( raw_error_code::invalid_format, "The metadata buffer contained unexpected data."); return Error::success(); } Error DbiStreamBuilder::finalize() { if (Header) return Error::success(); for (auto &MI : ModiList) MI->finalize(); if (auto EC = generateFileInfoSubstream()) return EC; DbiStreamHeader *H = Allocator.Allocate(); H->VersionHeader = *VerHeader; H->VersionSignature = -1; H->Age = Age; H->BuildNumber = BuildNumber; H->Flags = Flags; H->PdbDllRbld = PdbDllRbld; H->PdbDllVersion = PdbDllVersion; H->MachineType = static_cast(MachineType); H->ECSubstreamSize = 0; H->FileInfoSize = FileInfoBuffer.getLength(); H->ModiSubstreamSize = calculateModiSubstreamSize(); H->OptionalDbgHdrSize = DbgStreams.size() * sizeof(uint16_t); H->SecContrSubstreamSize = calculateSectionContribsStreamSize(); H->SectionMapSize = calculateSectionMapStreamSize(); H->TypeServerSize = 0; H->SymRecordStreamIndex = kInvalidStreamIndex; H->PublicSymbolStreamIndex = kInvalidStreamIndex; H->MFCTypeServerIndex = kInvalidStreamIndex; H->GlobalSymbolStreamIndex = kInvalidStreamIndex; Header = H; return Error::success(); } Error DbiStreamBuilder::finalizeMsfLayout() { for (auto &MI : ModiList) { if (auto EC = MI->finalizeMsfLayout()) return EC; } uint32_t Length = calculateSerializedLength(); if (auto EC = Msf.setStreamSize(StreamDBI, Length)) return EC; return Error::success(); } static uint16_t toSecMapFlags(uint32_t Flags) { uint16_t Ret = 0; if (Flags & COFF::IMAGE_SCN_MEM_READ) Ret |= static_cast(OMFSegDescFlags::Read); if (Flags & COFF::IMAGE_SCN_MEM_WRITE) Ret |= static_cast(OMFSegDescFlags::Write); if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE) Ret |= static_cast(OMFSegDescFlags::Execute); if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE) Ret |= static_cast(OMFSegDescFlags::Execute); if (!(Flags & COFF::IMAGE_SCN_MEM_16BIT)) Ret |= static_cast(OMFSegDescFlags::AddressIs32Bit); // This seems always 1. Ret |= static_cast(OMFSegDescFlags::IsSelector); return Ret; } // A utility function to create Section Contributions // for a given input sections. std::vector DbiStreamBuilder::createSectionContribs( ArrayRef SecHdrs) { std::vector Ret; // Create a SectionContrib for each input section. for (auto &Sec : SecHdrs) { Ret.emplace_back(); auto &Entry = Ret.back(); memset(&Entry, 0, sizeof(Entry)); Entry.Off = Sec.PointerToRawData; Entry.Size = Sec.SizeOfRawData; Entry.Characteristics = Sec.Characteristics; } return Ret; } // A utility function to create a Section Map for a given list of COFF sections. // // A Section Map seem to be a copy of a COFF section list in other format. // I don't know why a PDB file contains both a COFF section header and // a Section Map, but it seems it must be present in a PDB. std::vector DbiStreamBuilder::createSectionMap( ArrayRef SecHdrs) { std::vector Ret; int Idx = 0; auto Add = [&]() -> SecMapEntry & { Ret.emplace_back(); auto &Entry = Ret.back(); memset(&Entry, 0, sizeof(Entry)); Entry.Frame = Idx + 1; // We don't know the meaning of these fields yet. Entry.SecName = UINT16_MAX; Entry.ClassName = UINT16_MAX; return Entry; }; for (auto &Hdr : SecHdrs) { auto &Entry = Add(); Entry.Flags = toSecMapFlags(Hdr.Characteristics); Entry.SecByteLength = Hdr.VirtualSize; ++Idx; } // The last entry is for absolute symbols. auto &Entry = Add(); Entry.Flags = static_cast(OMFSegDescFlags::AddressIs32Bit) | static_cast(OMFSegDescFlags::IsAbsoluteAddress); Entry.SecByteLength = UINT32_MAX; return Ret; } Error DbiStreamBuilder::commit(const msf::MSFLayout &Layout, WritableBinaryStreamRef MsfBuffer) { if (auto EC = finalize()) return EC; auto DbiS = WritableMappedBlockStream::createIndexedStream(Layout, MsfBuffer, StreamDBI); BinaryStreamWriter Writer(*DbiS); if (auto EC = Writer.writeObject(*Header)) return EC; for (auto &M : ModiList) { if (auto EC = M->commit(Writer, Layout, MsfBuffer)) return EC; } if (!SectionContribs.empty()) { if (auto EC = Writer.writeEnum(DbiSecContribVer60)) return EC; if (auto EC = Writer.writeArray(SectionContribs)) return EC; } if (!SectionMap.empty()) { ulittle16_t Size = static_cast(SectionMap.size()); SecMapHeader SMHeader = {Size, Size}; if (auto EC = Writer.writeObject(SMHeader)) return EC; if (auto EC = Writer.writeArray(SectionMap)) return EC; } if (auto EC = Writer.writeStreamRef(FileInfoBuffer)) return EC; for (auto &Stream : DbgStreams) if (auto EC = Writer.writeInteger(Stream.StreamNumber)) return EC; for (auto &Stream : DbgStreams) { if (Stream.StreamNumber == kInvalidStreamIndex) continue; auto WritableStream = WritableMappedBlockStream::createIndexedStream( Layout, MsfBuffer, Stream.StreamNumber); BinaryStreamWriter DbgStreamWriter(*WritableStream); if (auto EC = DbgStreamWriter.writeArray(Stream.Data)) return EC; } if (Writer.bytesRemaining() > 0) return make_error(raw_error_code::invalid_format, "Unexpected bytes found in DBI Stream"); return Error::success(); }