diff options
Diffstat (limited to 'llvm/lib/DebugInfo/PDB/Native')
43 files changed, 7683 insertions, 0 deletions
| diff --git a/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp new file mode 100644 index 0000000000000..5095efcdee3cf --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp @@ -0,0 +1,93 @@ +//===- DbiModuleDescriptor.cpp - PDB module information -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MathExtras.h" +#include <cstdint> + +using namespace llvm; +using namespace llvm::pdb; +using namespace llvm::support; + +DbiModuleDescriptor::DbiModuleDescriptor() = default; + +DbiModuleDescriptor::DbiModuleDescriptor(const DbiModuleDescriptor &Info) = +    default; + +DbiModuleDescriptor::~DbiModuleDescriptor() = default; + +Error DbiModuleDescriptor::initialize(BinaryStreamRef Stream, +                                      DbiModuleDescriptor &Info) { +  BinaryStreamReader Reader(Stream); +  if (auto EC = Reader.readObject(Info.Layout)) +    return EC; + +  if (auto EC = Reader.readCString(Info.ModuleName)) +    return EC; + +  if (auto EC = Reader.readCString(Info.ObjFileName)) +    return EC; +  return Error::success(); +} + +bool DbiModuleDescriptor::hasECInfo() const { +  return (Layout->Flags & ModInfoFlags::HasECFlagMask) != 0; +} + +uint16_t DbiModuleDescriptor::getTypeServerIndex() const { +  return (Layout->Flags & ModInfoFlags::TypeServerIndexMask) >> +         ModInfoFlags::TypeServerIndexShift; +} + +const SectionContrib &DbiModuleDescriptor::getSectionContrib() const { +  return Layout->SC; +} + +uint16_t DbiModuleDescriptor::getModuleStreamIndex() const { +  return Layout->ModDiStream; +} + +uint32_t DbiModuleDescriptor::getSymbolDebugInfoByteSize() const { +  return Layout->SymBytes; +} + +uint32_t DbiModuleDescriptor::getC11LineInfoByteSize() const { +  return Layout->C11Bytes; +} + +uint32_t DbiModuleDescriptor::getC13LineInfoByteSize() const { +  return Layout->C13Bytes; +} + +uint32_t DbiModuleDescriptor::getNumberOfFiles() const { +  return Layout->NumFiles; +} + +uint32_t DbiModuleDescriptor::getSourceFileNameIndex() const { +  return Layout->SrcFileNameNI; +} + +uint32_t DbiModuleDescriptor::getPdbFilePathNameIndex() const { +  return Layout->PdbFilePathNI; +} + +StringRef DbiModuleDescriptor::getModuleName() const { return ModuleName; } + +StringRef DbiModuleDescriptor::getObjFileName() const { return ObjFileName; } + +uint32_t DbiModuleDescriptor::getRecordLength() const { +  uint32_t M = ModuleName.str().size() + 1; +  uint32_t O = ObjFileName.str().size() + 1; +  uint32_t Size = sizeof(ModuleInfoHeader) + M + O; +  Size = alignTo(Size, 4); +  return Size; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp new file mode 100644 index 0000000000000..419734771ccd5 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp @@ -0,0 +1,191 @@ +//===- DbiModuleDescriptorBuilder.cpp - PDB Mod Info Creation ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/BinaryStreamWriter.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +static uint32_t calculateDiSymbolStreamSize(uint32_t SymbolByteSize, +                                            uint32_t C13Size) { +  uint32_t Size = sizeof(uint32_t);   // Signature +  Size += alignTo(SymbolByteSize, 4); // Symbol Data +  Size += 0;                          // TODO: Layout.C11Bytes +  Size += C13Size;                    // C13 Debug Info Size +  Size += sizeof(uint32_t);           // GlobalRefs substream size (always 0) +  Size += 0;                          // GlobalRefs substream bytes +  return Size; +} + +DbiModuleDescriptorBuilder::DbiModuleDescriptorBuilder(StringRef ModuleName, +                                                       uint32_t ModIndex, +                                                       msf::MSFBuilder &Msf) +    : MSF(Msf), ModuleName(ModuleName) { +  ::memset(&Layout, 0, sizeof(Layout)); +  Layout.Mod = ModIndex; +} + +DbiModuleDescriptorBuilder::~DbiModuleDescriptorBuilder() {} + +uint16_t DbiModuleDescriptorBuilder::getStreamIndex() const { +  return Layout.ModDiStream; +} + +void DbiModuleDescriptorBuilder::setObjFileName(StringRef Name) { +  ObjFileName = Name; +} + +void DbiModuleDescriptorBuilder::setPdbFilePathNI(uint32_t NI) { +  PdbFilePathNI = NI; +} + +void DbiModuleDescriptorBuilder::setFirstSectionContrib( +    const SectionContrib &SC) { +  Layout.SC = SC; +} + +void DbiModuleDescriptorBuilder::addSymbol(CVSymbol Symbol) { +  // Defer to the bulk API. It does the same thing. +  addSymbolsInBulk(Symbol.data()); +} + +void DbiModuleDescriptorBuilder::addSymbolsInBulk( +    ArrayRef<uint8_t> BulkSymbols) { +  // Do nothing for empty runs of symbols. +  if (BulkSymbols.empty()) +    return; + +  Symbols.push_back(BulkSymbols); +  // Symbols written to a PDB file are required to be 4 byte aligned. The same +  // is not true of object files. +  assert(BulkSymbols.size() % alignOf(CodeViewContainer::Pdb) == 0 && +         "Invalid Symbol alignment!"); +  SymbolByteSize += BulkSymbols.size(); +} + +void DbiModuleDescriptorBuilder::addSourceFile(StringRef Path) { +  SourceFiles.push_back(Path); +} + +uint32_t DbiModuleDescriptorBuilder::calculateC13DebugInfoSize() const { +  uint32_t Result = 0; +  for (const auto &Builder : C13Builders) { +    assert(Builder && "Empty C13 Fragment Builder!"); +    Result += Builder->calculateSerializedLength(); +  } +  return Result; +} + +uint32_t DbiModuleDescriptorBuilder::calculateSerializedLength() const { +  uint32_t L = sizeof(Layout); +  uint32_t M = ModuleName.size() + 1; +  uint32_t O = ObjFileName.size() + 1; +  return alignTo(L + M + O, sizeof(uint32_t)); +} + +void DbiModuleDescriptorBuilder::finalize() { +  Layout.FileNameOffs = 0; // TODO: Fix this +  Layout.Flags = 0;        // TODO: Fix this +  Layout.C11Bytes = 0; +  Layout.C13Bytes = calculateC13DebugInfoSize(); +  (void)Layout.Mod;         // Set in constructor +  (void)Layout.ModDiStream; // Set in finalizeMsfLayout +  Layout.NumFiles = SourceFiles.size(); +  Layout.PdbFilePathNI = PdbFilePathNI; +  Layout.SrcFileNameNI = 0; + +  // This value includes both the signature field as well as the record bytes +  // from the symbol stream. +  Layout.SymBytes = +      Layout.ModDiStream == kInvalidStreamIndex ? 0 : getNextSymbolOffset(); +} + +Error DbiModuleDescriptorBuilder::finalizeMsfLayout() { +  this->Layout.ModDiStream = kInvalidStreamIndex; +  uint32_t C13Size = calculateC13DebugInfoSize(); +  if (!C13Size && !SymbolByteSize) +    return Error::success(); +  auto ExpectedSN = +      MSF.addStream(calculateDiSymbolStreamSize(SymbolByteSize, C13Size)); +  if (!ExpectedSN) +    return ExpectedSN.takeError(); +  Layout.ModDiStream = *ExpectedSN; +  return Error::success(); +} + +Error DbiModuleDescriptorBuilder::commit(BinaryStreamWriter &ModiWriter, +                                         const msf::MSFLayout &MsfLayout, +                                         WritableBinaryStreamRef MsfBuffer) { +  // We write the Modi record to the `ModiWriter`, but we additionally write its +  // symbol stream to a brand new stream. +  if (auto EC = ModiWriter.writeObject(Layout)) +    return EC; +  if (auto EC = ModiWriter.writeCString(ModuleName)) +    return EC; +  if (auto EC = ModiWriter.writeCString(ObjFileName)) +    return EC; +  if (auto EC = ModiWriter.padToAlignment(sizeof(uint32_t))) +    return EC; + +  if (Layout.ModDiStream != kInvalidStreamIndex) { +    auto NS = WritableMappedBlockStream::createIndexedStream( +        MsfLayout, MsfBuffer, Layout.ModDiStream, MSF.getAllocator()); +    WritableBinaryStreamRef Ref(*NS); +    BinaryStreamWriter SymbolWriter(Ref); +    // Write the symbols. +    if (auto EC = +            SymbolWriter.writeInteger<uint32_t>(COFF::DEBUG_SECTION_MAGIC)) +      return EC; +    for (ArrayRef<uint8_t> Syms : Symbols) { +      if (auto EC = SymbolWriter.writeBytes(Syms)) +        return EC; +    } +    assert(SymbolWriter.getOffset() % alignOf(CodeViewContainer::Pdb) == 0 && +           "Invalid debug section alignment!"); +    // TODO: Write C11 Line data +    for (const auto &Builder : C13Builders) { +      assert(Builder && "Empty C13 Fragment Builder!"); +      if (auto EC = Builder->commit(SymbolWriter)) +        return EC; +    } + +    // TODO: Figure out what GlobalRefs substream actually is and populate it. +    if (auto EC = SymbolWriter.writeInteger<uint32_t>(0)) +      return EC; +    if (SymbolWriter.bytesRemaining() > 0) +      return make_error<RawError>(raw_error_code::stream_too_long); +  } +  return Error::success(); +} + +void DbiModuleDescriptorBuilder::addDebugSubsection( +    std::shared_ptr<DebugSubsection> Subsection) { +  assert(Subsection); +  C13Builders.push_back(std::make_unique<DebugSubsectionRecordBuilder>( +      std::move(Subsection), CodeViewContainer::Pdb)); +} + +void DbiModuleDescriptorBuilder::addDebugSubsection( +    const DebugSubsectionRecord &SubsectionContents) { +  C13Builders.push_back(std::make_unique<DebugSubsectionRecordBuilder>( +      SubsectionContents, CodeViewContainer::Pdb)); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp new file mode 100644 index 0000000000000..5cf014e881cde --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp @@ -0,0 +1,279 @@ +//===- DbiModuleList.cpp - PDB module information list --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/DbiModuleList.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstdint> + +using namespace llvm; +using namespace llvm::pdb; + +DbiModuleSourceFilesIterator::DbiModuleSourceFilesIterator( +    const DbiModuleList &Modules, uint32_t Modi, uint16_t Filei) +    : Modules(&Modules), Modi(Modi), Filei(Filei) { +  setValue(); +} + +bool DbiModuleSourceFilesIterator:: +operator==(const DbiModuleSourceFilesIterator &R) const { +  // incompatible iterators are never equal +  if (!isCompatible(R)) +    return false; + +  // If they're compatible, and they're both ends, then they're equal. +  if (isEnd() && R.isEnd()) +    return true; + +  // If one is an end and the other is not, they're not equal. +  if (isEnd() != R.isEnd()) +    return false; + +  // Now we know: +  // - They're compatible +  // - They're not *both* end iterators +  // - Their endness is the same. +  // Thus, they're compatible iterators pointing to a valid file on the same +  // module.  All we need to check are the file indices. +  assert(Modules == R.Modules); +  assert(Modi == R.Modi); +  assert(!isEnd()); +  assert(!R.isEnd()); + +  return (Filei == R.Filei); +} + +bool DbiModuleSourceFilesIterator:: +operator<(const DbiModuleSourceFilesIterator &R) const { +  assert(isCompatible(R)); + +  // It's not sufficient to compare the file indices, because default +  // constructed iterators could be equal to iterators with valid indices.  To +  // account for this, early-out if they're equal. +  if (*this == R) +    return false; + +  return Filei < R.Filei; +} + +std::ptrdiff_t DbiModuleSourceFilesIterator:: +operator-(const DbiModuleSourceFilesIterator &R) const { +  assert(isCompatible(R)); +  assert(!(*this < R)); + +  // If they're both end iterators, the distance is 0. +  if (isEnd() && R.isEnd()) +    return 0; + +  assert(!R.isEnd()); + +  // At this point, R cannot be end, but *this can, which means that *this +  // might be a universal end iterator with none of its fields set.  So in that +  // case have to rely on R as the authority to figure out how many files there +  // are to compute the distance. +  uint32_t Thisi = Filei; +  if (isEnd()) { +    uint32_t RealModi = R.Modi; +    Thisi = R.Modules->getSourceFileCount(RealModi); +  } + +  assert(Thisi >= R.Filei); +  return Thisi - R.Filei; +} + +DbiModuleSourceFilesIterator &DbiModuleSourceFilesIterator:: +operator+=(std::ptrdiff_t N) { +  assert(!isEnd()); + +  Filei += N; +  assert(Filei <= Modules->getSourceFileCount(Modi)); +  setValue(); +  return *this; +} + +DbiModuleSourceFilesIterator &DbiModuleSourceFilesIterator:: +operator-=(std::ptrdiff_t N) { +  // Note that we can subtract from an end iterator, but not a universal end +  // iterator. +  assert(!isUniversalEnd()); + +  assert(N <= Filei); + +  Filei -= N; +  return *this; +} + +void DbiModuleSourceFilesIterator::setValue() { +  if (isEnd()) { +    ThisValue = ""; +    return; +  } + +  uint32_t Off = Modules->ModuleInitialFileIndex[Modi] + Filei; +  auto ExpectedValue = Modules->getFileName(Off); +  if (!ExpectedValue) { +    consumeError(ExpectedValue.takeError()); +    Filei = Modules->getSourceFileCount(Modi); +  } else +    ThisValue = *ExpectedValue; +} + +bool DbiModuleSourceFilesIterator::isEnd() const { +  if (isUniversalEnd()) +    return true; + +  assert(Modules); +  assert(Modi <= Modules->getModuleCount()); +  assert(Filei <= Modules->getSourceFileCount(Modi)); + +  if (Modi == Modules->getModuleCount()) +    return true; +  if (Filei == Modules->getSourceFileCount(Modi)) +    return true; +  return false; +} + +bool DbiModuleSourceFilesIterator::isUniversalEnd() const { return !Modules; } + +bool DbiModuleSourceFilesIterator::isCompatible( +    const DbiModuleSourceFilesIterator &R) const { +  // Universal iterators are compatible with any other iterator. +  if (isUniversalEnd() || R.isUniversalEnd()) +    return true; + +  // At this point, neither iterator is a universal end iterator, although one +  // or both might be non-universal end iterators.  Regardless, the module index +  // is valid, so they are compatible if and only if they refer to the same +  // module. +  return Modi == R.Modi; +} + +Error DbiModuleList::initialize(BinaryStreamRef ModInfo, +                                BinaryStreamRef FileInfo) { +  if (auto EC = initializeModInfo(ModInfo)) +    return EC; +  if (auto EC = initializeFileInfo(FileInfo)) +    return EC; + +  return Error::success(); +} + +Error DbiModuleList::initializeModInfo(BinaryStreamRef ModInfo) { +  ModInfoSubstream = ModInfo; + +  if (ModInfo.getLength() == 0) +    return Error::success(); + +  BinaryStreamReader Reader(ModInfo); + +  if (auto EC = Reader.readArray(Descriptors, ModInfo.getLength())) +    return EC; + +  return Error::success(); +} + +Error DbiModuleList::initializeFileInfo(BinaryStreamRef FileInfo) { +  FileInfoSubstream = FileInfo; + +  if (FileInfo.getLength() == 0) +    return Error::success(); + +  BinaryStreamReader FISR(FileInfo); +  if (auto EC = FISR.readObject(FileInfoHeader)) +    return EC; + +  // First is an array of `NumModules` module indices.  This does not seem to be +  // used for anything meaningful, so we ignore it. +  FixedStreamArray<support::ulittle16_t> ModuleIndices; +  if (auto EC = FISR.readArray(ModuleIndices, FileInfoHeader->NumModules)) +    return EC; +  if (auto EC = FISR.readArray(ModFileCountArray, FileInfoHeader->NumModules)) +    return EC; + +  // Compute the real number of source files.  We can't trust the value in +  // `FileInfoHeader->NumSourceFiles` because it is a unit16, and the sum of all +  // source file counts might be larger than a unit16.  So we compute the real +  // count by summing up the individual counts. +  uint32_t NumSourceFiles = 0; +  for (auto Count : ModFileCountArray) +    NumSourceFiles += Count; + +  // In the reference implementation, this array is where the pointer documented +  // at the definition of ModuleInfoHeader::FileNameOffs points to.  Note that +  // although the field in ModuleInfoHeader is ignored this array is not, as it +  // is the authority on where each filename begins in the names buffer. +  if (auto EC = FISR.readArray(FileNameOffsets, NumSourceFiles)) +    return EC; + +  if (auto EC = FISR.readStreamRef(NamesBuffer)) +    return EC; + +  auto DescriptorIter = Descriptors.begin(); +  uint32_t NextFileIndex = 0; +  ModuleInitialFileIndex.resize(FileInfoHeader->NumModules); +  ModuleDescriptorOffsets.resize(FileInfoHeader->NumModules); +  for (size_t I = 0; I < FileInfoHeader->NumModules; ++I) { +    assert(DescriptorIter != Descriptors.end()); +    ModuleInitialFileIndex[I] = NextFileIndex; +    ModuleDescriptorOffsets[I] = DescriptorIter.offset(); + +    NextFileIndex += ModFileCountArray[I]; +    ++DescriptorIter; +  } + +  assert(DescriptorIter == Descriptors.end()); +  assert(NextFileIndex == NumSourceFiles); + +  return Error::success(); +} + +uint32_t DbiModuleList::getModuleCount() const { +  return FileInfoHeader->NumModules; +} + +uint32_t DbiModuleList::getSourceFileCount() const { +  return FileNameOffsets.size(); +} + +uint16_t DbiModuleList::getSourceFileCount(uint32_t Modi) const { +  return ModFileCountArray[Modi]; +} + +DbiModuleDescriptor DbiModuleList::getModuleDescriptor(uint32_t Modi) const { +  assert(Modi < getModuleCount()); +  uint32_t Offset = ModuleDescriptorOffsets[Modi]; +  auto Iter = Descriptors.at(Offset); +  assert(Iter != Descriptors.end()); +  return *Iter; +} + +iterator_range<DbiModuleSourceFilesIterator> +DbiModuleList::source_files(uint32_t Modi) const { +  return make_range<DbiModuleSourceFilesIterator>( +      DbiModuleSourceFilesIterator(*this, Modi, 0), +      DbiModuleSourceFilesIterator()); +} + +Expected<StringRef> DbiModuleList::getFileName(uint32_t Index) const { +  BinaryStreamReader Names(NamesBuffer); +  if (Index >= getSourceFileCount()) +    return make_error<RawError>(raw_error_code::index_out_of_bounds); + +  uint32_t FileOffset = FileNameOffsets[Index]; +  Names.setOffset(FileOffset); +  StringRef Name; +  if (auto EC = Names.readCString(Name)) +    return std::move(EC); +  return Name; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/DbiStream.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiStream.cpp new file mode 100644 index 0000000000000..4eb16804171df --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/DbiStream.cpp @@ -0,0 +1,383 @@ +//===- DbiStream.cpp - PDB Dbi Stream (Stream 3) Access -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/BinaryStreamArray.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cstddef> +#include <cstdint> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; +using namespace llvm::support; + +template <typename ContribType> +static Error loadSectionContribs(FixedStreamArray<ContribType> &Output, +                                 BinaryStreamReader &Reader) { +  if (Reader.bytesRemaining() % sizeof(ContribType) != 0) +    return make_error<RawError>( +        raw_error_code::corrupt_file, +        "Invalid number of bytes of section contributions"); + +  uint32_t Count = Reader.bytesRemaining() / sizeof(ContribType); +  if (auto EC = Reader.readArray(Output, Count)) +    return EC; +  return Error::success(); +} + +DbiStream::DbiStream(std::unique_ptr<BinaryStream> Stream) +    : Stream(std::move(Stream)), Header(nullptr) {} + +DbiStream::~DbiStream() = default; + +Error DbiStream::reload(PDBFile *Pdb) { +  BinaryStreamReader Reader(*Stream); + +  if (Stream->getLength() < sizeof(DbiStreamHeader)) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "DBI Stream does not contain a header."); +  if (auto EC = Reader.readObject(Header)) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "DBI Stream does not contain a header."); + +  if (Header->VersionSignature != -1) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Invalid DBI version signature."); + +  // Require at least version 7, which should be present in all PDBs +  // produced in the last decade and allows us to avoid having to +  // special case all kinds of complicated arcane formats. +  if (Header->VersionHeader < PdbDbiV70) +    return make_error<RawError>(raw_error_code::feature_unsupported, +                                "Unsupported DBI version."); + +  if (Stream->getLength() != +      sizeof(DbiStreamHeader) + Header->ModiSubstreamSize + +          Header->SecContrSubstreamSize + Header->SectionMapSize + +          Header->FileInfoSize + Header->TypeServerSize + +          Header->OptionalDbgHdrSize + Header->ECSubstreamSize) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "DBI Length does not equal sum of substreams."); + +  // Only certain substreams are guaranteed to be aligned.  Validate +  // them here. +  if (Header->ModiSubstreamSize % sizeof(uint32_t) != 0) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "DBI MODI substream not aligned."); +  if (Header->SecContrSubstreamSize % sizeof(uint32_t) != 0) +    return make_error<RawError>( +        raw_error_code::corrupt_file, +        "DBI section contribution substream not aligned."); +  if (Header->SectionMapSize % sizeof(uint32_t) != 0) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "DBI section map substream not aligned."); +  if (Header->FileInfoSize % sizeof(uint32_t) != 0) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "DBI file info substream not aligned."); +  if (Header->TypeServerSize % sizeof(uint32_t) != 0) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "DBI type server substream not aligned."); + +  if (auto EC = Reader.readSubstream(ModiSubstream, Header->ModiSubstreamSize)) +    return EC; + +  if (auto EC = Reader.readSubstream(SecContrSubstream, +                                     Header->SecContrSubstreamSize)) +    return EC; +  if (auto EC = Reader.readSubstream(SecMapSubstream, Header->SectionMapSize)) +    return EC; +  if (auto EC = Reader.readSubstream(FileInfoSubstream, Header->FileInfoSize)) +    return EC; +  if (auto EC = +          Reader.readSubstream(TypeServerMapSubstream, Header->TypeServerSize)) +    return EC; +  if (auto EC = Reader.readSubstream(ECSubstream, Header->ECSubstreamSize)) +    return EC; +  if (auto EC = Reader.readArray( +          DbgStreams, Header->OptionalDbgHdrSize / sizeof(ulittle16_t))) +    return EC; + +  if (auto EC = Modules.initialize(ModiSubstream.StreamData, +                                   FileInfoSubstream.StreamData)) +    return EC; + +  if (auto EC = initializeSectionContributionData()) +    return EC; +  if (auto EC = initializeSectionHeadersData(Pdb)) +    return EC; +  if (auto EC = initializeSectionMapData()) +    return EC; +  if (auto EC = initializeOldFpoRecords(Pdb)) +    return EC; +  if (auto EC = initializeNewFpoRecords(Pdb)) +     return EC; + +  if (Reader.bytesRemaining() > 0) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Found unexpected bytes in DBI Stream."); + +  if (!ECSubstream.empty()) { +    BinaryStreamReader ECReader(ECSubstream.StreamData); +    if (auto EC = ECNames.reload(ECReader)) +      return EC; +  } + +  return Error::success(); +} + +PdbRaw_DbiVer DbiStream::getDbiVersion() const { +  uint32_t Value = Header->VersionHeader; +  return static_cast<PdbRaw_DbiVer>(Value); +} + +uint32_t DbiStream::getAge() const { return Header->Age; } + +uint16_t DbiStream::getPublicSymbolStreamIndex() const { +  return Header->PublicSymbolStreamIndex; +} + +uint16_t DbiStream::getGlobalSymbolStreamIndex() const { +  return Header->GlobalSymbolStreamIndex; +} + +uint16_t DbiStream::getFlags() const { return Header->Flags; } + +bool DbiStream::isIncrementallyLinked() const { +  return (Header->Flags & DbiFlags::FlagIncrementalMask) != 0; +} + +bool DbiStream::hasCTypes() const { +  return (Header->Flags & DbiFlags::FlagHasCTypesMask) != 0; +} + +bool DbiStream::isStripped() const { +  return (Header->Flags & DbiFlags::FlagStrippedMask) != 0; +} + +uint16_t DbiStream::getBuildNumber() const { return Header->BuildNumber; } + +uint16_t DbiStream::getBuildMajorVersion() const { +  return (Header->BuildNumber & DbiBuildNo::BuildMajorMask) >> +         DbiBuildNo::BuildMajorShift; +} + +uint16_t DbiStream::getBuildMinorVersion() const { +  return (Header->BuildNumber & DbiBuildNo::BuildMinorMask) >> +         DbiBuildNo::BuildMinorShift; +} + +uint16_t DbiStream::getPdbDllRbld() const { return Header->PdbDllRbld; } + +uint32_t DbiStream::getPdbDllVersion() const { return Header->PdbDllVersion; } + +uint32_t DbiStream::getSymRecordStreamIndex() const { +  return Header->SymRecordStreamIndex; +} + +PDB_Machine DbiStream::getMachineType() const { +  uint16_t Machine = Header->MachineType; +  return static_cast<PDB_Machine>(Machine); +} + +FixedStreamArray<object::coff_section> DbiStream::getSectionHeaders() const { +  return SectionHeaders; +} + +bool DbiStream::hasOldFpoRecords() const { return OldFpoStream != nullptr; } + +FixedStreamArray<object::FpoData> DbiStream::getOldFpoRecords() const { +  return OldFpoRecords; +} + +bool DbiStream::hasNewFpoRecords() const { return NewFpoStream != nullptr; } + +const DebugFrameDataSubsectionRef &DbiStream::getNewFpoRecords() const { +  return NewFpoRecords; +} + +const DbiModuleList &DbiStream::modules() const { return Modules; } + +FixedStreamArray<SecMapEntry> DbiStream::getSectionMap() const { +  return SectionMap; +} + +void DbiStream::visitSectionContributions( +    ISectionContribVisitor &Visitor) const { +  if (!SectionContribs.empty()) { +    assert(SectionContribVersion == DbiSecContribVer60); +    for (auto &SC : SectionContribs) +      Visitor.visit(SC); +  } else if (!SectionContribs2.empty()) { +    assert(SectionContribVersion == DbiSecContribV2); +    for (auto &SC : SectionContribs2) +      Visitor.visit(SC); +  } +} + +Expected<StringRef> DbiStream::getECName(uint32_t NI) const { +  return ECNames.getStringForID(NI); +} + +Error DbiStream::initializeSectionContributionData() { +  if (SecContrSubstream.empty()) +    return Error::success(); + +  BinaryStreamReader SCReader(SecContrSubstream.StreamData); +  if (auto EC = SCReader.readEnum(SectionContribVersion)) +    return EC; + +  if (SectionContribVersion == DbiSecContribVer60) +    return loadSectionContribs<SectionContrib>(SectionContribs, SCReader); +  if (SectionContribVersion == DbiSecContribV2) +    return loadSectionContribs<SectionContrib2>(SectionContribs2, SCReader); + +  return make_error<RawError>(raw_error_code::feature_unsupported, +                              "Unsupported DBI Section Contribution version"); +} + +// Initializes this->SectionHeaders. +Error DbiStream::initializeSectionHeadersData(PDBFile *Pdb) { +  Expected<std::unique_ptr<msf::MappedBlockStream>> ExpectedStream = +      createIndexedStreamForHeaderType(Pdb, DbgHeaderType::SectionHdr); +  if (auto EC = ExpectedStream.takeError()) +    return EC; + +  auto &SHS = *ExpectedStream; +  if (!SHS) +    return Error::success(); + +  size_t StreamLen = SHS->getLength(); +  if (StreamLen % sizeof(object::coff_section)) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Corrupted section header stream."); + +  size_t NumSections = StreamLen / sizeof(object::coff_section); +  BinaryStreamReader Reader(*SHS); +  if (auto EC = Reader.readArray(SectionHeaders, NumSections)) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Could not read a bitmap."); + +  SectionHeaderStream = std::move(SHS); +  return Error::success(); +} + +// Initializes this->Fpos. +Error DbiStream::initializeOldFpoRecords(PDBFile *Pdb) { +  Expected<std::unique_ptr<msf::MappedBlockStream>> ExpectedStream = +      createIndexedStreamForHeaderType(Pdb, DbgHeaderType::FPO); +  if (auto EC = ExpectedStream.takeError()) +    return EC; + +  auto &FS = *ExpectedStream; +  if (!FS) +    return Error::success(); + +  size_t StreamLen = FS->getLength(); +  if (StreamLen % sizeof(object::FpoData)) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Corrupted Old FPO stream."); + +  size_t NumRecords = StreamLen / sizeof(object::FpoData); +  BinaryStreamReader Reader(*FS); +  if (auto EC = Reader.readArray(OldFpoRecords, NumRecords)) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Corrupted Old FPO stream."); +  OldFpoStream = std::move(FS); +  return Error::success(); +} + +Error DbiStream::initializeNewFpoRecords(PDBFile *Pdb) { +  Expected<std::unique_ptr<msf::MappedBlockStream>> ExpectedStream = +      createIndexedStreamForHeaderType(Pdb, DbgHeaderType::NewFPO); +  if (auto EC = ExpectedStream.takeError()) +    return EC; + +  auto &FS = *ExpectedStream; +  if (!FS) +    return Error::success(); + +  if (auto EC = NewFpoRecords.initialize(*FS)) +    return EC; + +  NewFpoStream = std::move(FS); +  return Error::success(); +} + +Expected<std::unique_ptr<msf::MappedBlockStream>> +DbiStream::createIndexedStreamForHeaderType(PDBFile *Pdb, +                                            DbgHeaderType Type) const { +  if (!Pdb) +    return nullptr; + +  if (DbgStreams.empty()) +    return nullptr; + +  uint32_t StreamNum = getDebugStreamIndex(Type); + +  // This means there is no such stream. +  if (StreamNum == kInvalidStreamIndex) +    return nullptr; + +  return Pdb->safelyCreateIndexedStream(StreamNum); +} + +BinarySubstreamRef DbiStream::getSectionContributionData() const { +  return SecContrSubstream; +} + +BinarySubstreamRef DbiStream::getSecMapSubstreamData() const { +  return SecMapSubstream; +} + +BinarySubstreamRef DbiStream::getModiSubstreamData() const { +  return ModiSubstream; +} + +BinarySubstreamRef DbiStream::getFileInfoSubstreamData() const { +  return FileInfoSubstream; +} + +BinarySubstreamRef DbiStream::getTypeServerMapSubstreamData() const { +  return TypeServerMapSubstream; +} + +BinarySubstreamRef DbiStream::getECSubstreamData() const { return ECSubstream; } + +Error DbiStream::initializeSectionMapData() { +  if (SecMapSubstream.empty()) +    return Error::success(); + +  BinaryStreamReader SMReader(SecMapSubstream.StreamData); +  const SecMapHeader *Header; +  if (auto EC = SMReader.readObject(Header)) +    return EC; +  if (auto EC = SMReader.readArray(SectionMap, Header->SecCount)) +    return EC; +  return Error::success(); +} + +uint32_t DbiStream::getDebugStreamIndex(DbgHeaderType Type) const { +  uint16_t T = static_cast<uint16_t>(Type); +  if (T >= DbgStreams.size()) +    return kInvalidStreamIndex; +  return DbgStreams[T]; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp new file mode 100644 index 0000000000000..0e00c2f7ff98c --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp @@ -0,0 +1,455 @@ +//===- DbiStreamBuilder.cpp - PDB Dbi Stream Creation -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.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" + +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) {} + +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::setBuildNumber(uint8_t Major, uint8_t Minor) { +  BuildNumber = (uint16_t(Major) << DbiBuildNo::BuildMajorShift) & +                DbiBuildNo::BuildMajorMask; +  BuildNumber |= (uint16_t(Minor) << DbiBuildNo::BuildMinorShift) & +                 DbiBuildNo::BuildMinorMask; +  BuildNumber |= DbiBuildNo::NewVersionFormatMask; +} + +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::setMachineType(COFF::MachineTypes M) { +  // These enums are mirrors of each other, so we can just cast the value. +  MachineType = static_cast<pdb::PDB_Machine>(static_cast<unsigned>(M)); +} + +void DbiStreamBuilder::setSectionMap(ArrayRef<SecMapEntry> SecMap) { +  SectionMap = SecMap; +} + +void DbiStreamBuilder::setGlobalsStreamIndex(uint32_t Index) { +  GlobalsStreamIndex = Index; +} + +void DbiStreamBuilder::setSymbolRecordStreamIndex(uint32_t Index) { +  SymRecordStreamIndex = Index; +} + +void DbiStreamBuilder::setPublicsStreamIndex(uint32_t Index) { +  PublicsStreamIndex = Index; +} + +void DbiStreamBuilder::addNewFpoData(const codeview::FrameData &FD) { +  if (!NewFpoData.hasValue()) +    NewFpoData.emplace(false); + +  NewFpoData->addFrameData(FD); +} + +void DbiStreamBuilder::addOldFpoData(const object::FpoData &FD) { +  OldFpoData.push_back(FD); +} + +Error DbiStreamBuilder::addDbgStream(pdb::DbgHeaderType Type, +                                     ArrayRef<uint8_t> Data) { +  assert(Type != DbgHeaderType::NewFPO && +         "NewFPO data should be written via addFrameData()!"); + +  DbgStreams[(int)Type].emplace(); +  DbgStreams[(int)Type]->Size = Data.size(); +  DbgStreams[(int)Type]->WriteFn = [Data](BinaryStreamWriter &Writer) { +    return Writer.writeArray(Data); +  }; +  return Error::success(); +} + +uint32_t DbiStreamBuilder::addECName(StringRef Name) { +  return ECNamesBuilder.insert(Name); +} + +uint32_t DbiStreamBuilder::calculateSerializedLength() const { +  // For now we only support serializing the header. +  return sizeof(DbiStreamHeader) + calculateFileInfoSubstreamSize() + +         calculateModiSubstreamSize() + calculateSectionContribsStreamSize() + +         calculateSectionMapStreamSize() + calculateDbgStreamsSize() + +         ECNamesBuilder.calculateSerializedSize(); +} + +Expected<DbiModuleDescriptorBuilder &> +DbiStreamBuilder::addModuleInfo(StringRef ModuleName) { +  uint32_t Index = ModiList.size(); +  ModiList.push_back( +      std::make_unique<DbiModuleDescriptorBuilder>(ModuleName, Index, Msf)); +  return *ModiList.back(); +} + +Error DbiStreamBuilder::addModuleSourceFile(DbiModuleDescriptorBuilder &Module, +                                            StringRef File) { +  uint32_t Index = SourceFileNames.size(); +  SourceFileNames.insert(std::make_pair(File, Index)); +  Module.addSourceFile(File); +  return Error::success(); +} + +Expected<uint32_t> DbiStreamBuilder::getSourceFileNameIndex(StringRef File) { +  auto NameIter = SourceFileNames.find(File); +  if (NameIter == SourceFileNames.end()) +    return make_error<RawError>(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<uint8_t>(Size); +  uint32_t NamesOffset = calculateNamesOffset(); + +  FileInfoBuffer = MutableBinaryByteStream(MutableArrayRef<uint8_t>(Data, Size), +                                           llvm::support::little); + +  WritableBinaryStreamRef MetadataBuffer = +      WritableBinaryStreamRef(FileInfoBuffer).keep_front(NamesOffset); +  BinaryStreamWriter MetadataWriter(MetadataBuffer); + +  uint16_t ModiCount = std::min<uint32_t>(UINT16_MAX, ModiList.size()); +  uint16_t FileCount = std::min<uint32_t>(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<uint16_t>(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<RawError>(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<RawError>(raw_error_code::invalid_format, +                                "The names buffer contained unexpected data."); + +  if (MetadataWriter.bytesRemaining() > sizeof(uint32_t)) +    return make_error<RawError>( +        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<DbiStreamHeader>(); +  ::memset(H, 0, sizeof(DbiStreamHeader)); +  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<uint16_t>(MachineType); + +  H->ECSubstreamSize = ECNamesBuilder.calculateSerializedSize(); +  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 = SymRecordStreamIndex; +  H->PublicSymbolStreamIndex = PublicsStreamIndex; +  H->MFCTypeServerIndex = 0; // Not sure what this is, but link.exe writes 0. +  H->GlobalSymbolStreamIndex = GlobalsStreamIndex; + +  Header = H; +  return Error::success(); +} + +Error DbiStreamBuilder::finalizeMsfLayout() { +  if (NewFpoData.hasValue()) { +    DbgStreams[(int)DbgHeaderType::NewFPO].emplace(); +    DbgStreams[(int)DbgHeaderType::NewFPO]->Size = +        NewFpoData->calculateSerializedSize(); +    DbgStreams[(int)DbgHeaderType::NewFPO]->WriteFn = +        [this](BinaryStreamWriter &Writer) { +          return NewFpoData->commit(Writer); +        }; +  } + +  if (!OldFpoData.empty()) { +    DbgStreams[(int)DbgHeaderType::FPO].emplace(); +    DbgStreams[(int)DbgHeaderType::FPO]->Size = +        sizeof(object::FpoData) * OldFpoData.size(); +    DbgStreams[(int)DbgHeaderType::FPO]->WriteFn = +        [this](BinaryStreamWriter &Writer) { +          return Writer.writeArray(makeArrayRef(OldFpoData)); +        }; +  } + +  for (auto &S : DbgStreams) { +    if (!S.hasValue()) +      continue; +    auto ExpectedIndex = Msf.addStream(S->Size); +    if (!ExpectedIndex) +      return ExpectedIndex.takeError(); +    S->StreamNumber = *ExpectedIndex; +  } + +  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<uint16_t>(OMFSegDescFlags::Read); +  if (Flags & COFF::IMAGE_SCN_MEM_WRITE) +    Ret |= static_cast<uint16_t>(OMFSegDescFlags::Write); +  if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE) +    Ret |= static_cast<uint16_t>(OMFSegDescFlags::Execute); +  if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE) +    Ret |= static_cast<uint16_t>(OMFSegDescFlags::Execute); +  if (!(Flags & COFF::IMAGE_SCN_MEM_16BIT)) +    Ret |= static_cast<uint16_t>(OMFSegDescFlags::AddressIs32Bit); + +  // This seems always 1. +  Ret |= static_cast<uint16_t>(OMFSegDescFlags::IsSelector); + +  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<SecMapEntry> DbiStreamBuilder::createSectionMap( +    ArrayRef<llvm::object::coff_section> SecHdrs) { +  std::vector<SecMapEntry> 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<uint16_t>(OMFSegDescFlags::AddressIs32Bit) | +                static_cast<uint16_t>(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, Allocator); + +  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(makeArrayRef(SectionContribs))) +      return EC; +  } + +  if (!SectionMap.empty()) { +    ulittle16_t Size = static_cast<ulittle16_t>(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; + +  if (auto EC = ECNamesBuilder.commit(Writer)) +    return EC; + +  for (auto &Stream : DbgStreams) { +    uint16_t StreamNumber = kInvalidStreamIndex; +    if (Stream.hasValue()) +      StreamNumber = Stream->StreamNumber; +    if (auto EC = Writer.writeInteger(StreamNumber)) +      return EC; +  } + +  for (auto &Stream : DbgStreams) { +    if (!Stream.hasValue()) +      continue; +    assert(Stream->StreamNumber != kInvalidStreamIndex); + +    auto WritableStream = WritableMappedBlockStream::createIndexedStream( +        Layout, MsfBuffer, Stream->StreamNumber, Allocator); +    BinaryStreamWriter DbgStreamWriter(*WritableStream); + +    if (auto EC = Stream->WriteFn(DbgStreamWriter)) +      return EC; +  } + +  if (Writer.bytesRemaining() > 0) +    return make_error<RawError>(raw_error_code::invalid_format, +                                "Unexpected bytes found in DBI Stream"); +  return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/EnumTables.cpp b/llvm/lib/DebugInfo/PDB/Native/EnumTables.cpp new file mode 100644 index 0000000000000..f5125393695bc --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/EnumTables.cpp @@ -0,0 +1,37 @@ +//===- EnumTables.cpp - Enum to string conversion tables --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/EnumTables.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" + +using namespace llvm; +using namespace llvm::pdb; + +#define PDB_ENUM_CLASS_ENT(enum_class, enum)                                   \ +  { #enum, std::underlying_type < enum_class > ::type(enum_class::enum) } + +#define PDB_ENUM_ENT(ns, enum)                                                 \ +  { #enum, ns::enum } + +static const EnumEntry<uint16_t> OMFSegMapDescFlagNames[] = { +    PDB_ENUM_CLASS_ENT(OMFSegDescFlags, Read), +    PDB_ENUM_CLASS_ENT(OMFSegDescFlags, Write), +    PDB_ENUM_CLASS_ENT(OMFSegDescFlags, Execute), +    PDB_ENUM_CLASS_ENT(OMFSegDescFlags, AddressIs32Bit), +    PDB_ENUM_CLASS_ENT(OMFSegDescFlags, IsSelector), +    PDB_ENUM_CLASS_ENT(OMFSegDescFlags, IsAbsoluteAddress), +    PDB_ENUM_CLASS_ENT(OMFSegDescFlags, IsGroup), +}; + +namespace llvm { +namespace pdb { +ArrayRef<EnumEntry<uint16_t>> getOMFSegMapDescFlagNames() { +  return makeArrayRef(OMFSegMapDescFlagNames); +} +} +}
\ No newline at end of file diff --git a/llvm/lib/DebugInfo/PDB/Native/GSIStreamBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/GSIStreamBuilder.cpp new file mode 100644 index 0000000000000..432f1e9b24d3a --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/GSIStreamBuilder.cpp @@ -0,0 +1,378 @@ +//===- DbiStreamBuilder.cpp - PDB Dbi Stream Creation -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h" + +#include "llvm/ADT/DenseSet.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/SymbolSerializer.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/Support/BinaryItemStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/xxhash.h" +#include <algorithm> +#include <vector> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; +using namespace llvm::codeview; + +struct llvm::pdb::GSIHashStreamBuilder { +  struct SymbolDenseMapInfo { +    static inline CVSymbol getEmptyKey() { +      static CVSymbol Empty; +      return Empty; +    } +    static inline CVSymbol getTombstoneKey() { +      static CVSymbol Tombstone( +          DenseMapInfo<ArrayRef<uint8_t>>::getTombstoneKey()); +      return Tombstone; +    } +    static unsigned getHashValue(const CVSymbol &Val) { +      return xxHash64(Val.RecordData); +    } +    static bool isEqual(const CVSymbol &LHS, const CVSymbol &RHS) { +      return LHS.RecordData == RHS.RecordData; +    } +  }; + +  std::vector<CVSymbol> Records; +  uint32_t StreamIndex; +  llvm::DenseSet<CVSymbol, SymbolDenseMapInfo> SymbolHashes; +  std::vector<PSHashRecord> HashRecords; +  std::array<support::ulittle32_t, (IPHR_HASH + 32) / 32> HashBitmap; +  std::vector<support::ulittle32_t> HashBuckets; + +  uint32_t calculateSerializedLength() const; +  uint32_t calculateRecordByteSize() const; +  Error commit(BinaryStreamWriter &Writer); +  void finalizeBuckets(uint32_t RecordZeroOffset); + +  template <typename T> void addSymbol(const T &Symbol, MSFBuilder &Msf) { +    T Copy(Symbol); +    addSymbol(SymbolSerializer::writeOneSymbol(Copy, Msf.getAllocator(), +                                               CodeViewContainer::Pdb)); +  } +  void addSymbol(const CVSymbol &Symbol) { +    if (Symbol.kind() == S_UDT || Symbol.kind() == S_CONSTANT) { +      auto Iter = SymbolHashes.insert(Symbol); +      if (!Iter.second) +        return; +    } + +    Records.push_back(Symbol); +  } +}; + +uint32_t GSIHashStreamBuilder::calculateSerializedLength() const { +  uint32_t Size = sizeof(GSIHashHeader); +  Size += HashRecords.size() * sizeof(PSHashRecord); +  Size += HashBitmap.size() * sizeof(uint32_t); +  Size += HashBuckets.size() * sizeof(uint32_t); +  return Size; +} + +uint32_t GSIHashStreamBuilder::calculateRecordByteSize() const { +  uint32_t Size = 0; +  for (const auto &Sym : Records) +    Size += Sym.length(); +  return Size; +} + +Error GSIHashStreamBuilder::commit(BinaryStreamWriter &Writer) { +  GSIHashHeader Header; +  Header.VerSignature = GSIHashHeader::HdrSignature; +  Header.VerHdr = GSIHashHeader::HdrVersion; +  Header.HrSize = HashRecords.size() * sizeof(PSHashRecord); +  Header.NumBuckets = HashBitmap.size() * 4 + HashBuckets.size() * 4; + +  if (auto EC = Writer.writeObject(Header)) +    return EC; + +  if (auto EC = Writer.writeArray(makeArrayRef(HashRecords))) +    return EC; +  if (auto EC = Writer.writeArray(makeArrayRef(HashBitmap))) +    return EC; +  if (auto EC = Writer.writeArray(makeArrayRef(HashBuckets))) +    return EC; +  return Error::success(); +} + +static bool isAsciiString(StringRef S) { +  return llvm::all_of(S, [](char C) { return unsigned(C) < 0x80; }); +} + +// See `caseInsensitiveComparePchPchCchCch` in gsi.cpp +static bool gsiRecordLess(StringRef S1, StringRef S2) { +  size_t LS = S1.size(); +  size_t RS = S2.size(); +  // Shorter strings always compare less than longer strings. +  if (LS != RS) +    return LS < RS; + +  // If either string contains non ascii characters, memcmp them. +  if (LLVM_UNLIKELY(!isAsciiString(S1) || !isAsciiString(S2))) +    return memcmp(S1.data(), S2.data(), LS) < 0; + +  // Both strings are ascii, perform a case-insenstive comparison. +  return S1.compare_lower(S2.data()) < 0; +} + +void GSIHashStreamBuilder::finalizeBuckets(uint32_t RecordZeroOffset) { +  std::array<std::vector<std::pair<StringRef, PSHashRecord>>, IPHR_HASH + 1> +      TmpBuckets; +  uint32_t SymOffset = RecordZeroOffset; +  for (const CVSymbol &Sym : Records) { +    PSHashRecord HR; +    // Add one when writing symbol offsets to disk. See GSI1::fixSymRecs. +    HR.Off = SymOffset + 1; +    HR.CRef = 1; // Always use a refcount of 1. + +    // Hash the name to figure out which bucket this goes into. +    StringRef Name = getSymbolName(Sym); +    size_t BucketIdx = hashStringV1(Name) % IPHR_HASH; +    TmpBuckets[BucketIdx].push_back(std::make_pair(Name, HR)); +    SymOffset += Sym.length(); +  } + +  // Compute the three tables: the hash records in bucket and chain order, the +  // bucket presence bitmap, and the bucket chain start offsets. +  HashRecords.reserve(Records.size()); +  for (ulittle32_t &Word : HashBitmap) +    Word = 0; +  for (size_t BucketIdx = 0; BucketIdx < IPHR_HASH + 1; ++BucketIdx) { +    auto &Bucket = TmpBuckets[BucketIdx]; +    if (Bucket.empty()) +      continue; +    HashBitmap[BucketIdx / 32] |= 1U << (BucketIdx % 32); + +    // Calculate what the offset of the first hash record in the chain would +    // be if it were inflated to contain 32-bit pointers. On a 32-bit system, +    // each record would be 12 bytes. See HROffsetCalc in gsi.h. +    const int SizeOfHROffsetCalc = 12; +    ulittle32_t ChainStartOff = +        ulittle32_t(HashRecords.size() * SizeOfHROffsetCalc); +    HashBuckets.push_back(ChainStartOff); + +    // Sort each bucket by memcmp of the symbol's name.  It's important that +    // we use the same sorting algorithm as is used by the reference +    // implementation to ensure that the search for a record within a bucket +    // can properly early-out when it detects the record won't be found.  The +    // algorithm used here corredsponds to the function +    // caseInsensitiveComparePchPchCchCch in the reference implementation. +    llvm::sort(Bucket, [](const std::pair<StringRef, PSHashRecord> &Left, +                          const std::pair<StringRef, PSHashRecord> &Right) { +      return gsiRecordLess(Left.first, Right.first); +    }); + +    for (const auto &Entry : Bucket) +      HashRecords.push_back(Entry.second); +  } +} + +GSIStreamBuilder::GSIStreamBuilder(msf::MSFBuilder &Msf) +    : Msf(Msf), PSH(std::make_unique<GSIHashStreamBuilder>()), +      GSH(std::make_unique<GSIHashStreamBuilder>()) {} + +GSIStreamBuilder::~GSIStreamBuilder() {} + +uint32_t GSIStreamBuilder::calculatePublicsHashStreamSize() const { +  uint32_t Size = 0; +  Size += sizeof(PublicsStreamHeader); +  Size += PSH->calculateSerializedLength(); +  Size += PSH->Records.size() * sizeof(uint32_t); // AddrMap +  // FIXME: Add thunk map and section offsets for incremental linking. + +  return Size; +} + +uint32_t GSIStreamBuilder::calculateGlobalsHashStreamSize() const { +  return GSH->calculateSerializedLength(); +} + +Error GSIStreamBuilder::finalizeMsfLayout() { +  // First we write public symbol records, then we write global symbol records. +  uint32_t PSHZero = 0; +  uint32_t GSHZero = PSH->calculateRecordByteSize(); + +  PSH->finalizeBuckets(PSHZero); +  GSH->finalizeBuckets(GSHZero); + +  Expected<uint32_t> Idx = Msf.addStream(calculateGlobalsHashStreamSize()); +  if (!Idx) +    return Idx.takeError(); +  GSH->StreamIndex = *Idx; +  Idx = Msf.addStream(calculatePublicsHashStreamSize()); +  if (!Idx) +    return Idx.takeError(); +  PSH->StreamIndex = *Idx; + +  uint32_t RecordBytes = +      GSH->calculateRecordByteSize() + PSH->calculateRecordByteSize(); + +  Idx = Msf.addStream(RecordBytes); +  if (!Idx) +    return Idx.takeError(); +  RecordStreamIdx = *Idx; +  return Error::success(); +} + +static bool comparePubSymByAddrAndName( +    const std::pair<const CVSymbol *, const PublicSym32 *> &LS, +    const std::pair<const CVSymbol *, const PublicSym32 *> &RS) { +  if (LS.second->Segment != RS.second->Segment) +    return LS.second->Segment < RS.second->Segment; +  if (LS.second->Offset != RS.second->Offset) +    return LS.second->Offset < RS.second->Offset; + +  return LS.second->Name < RS.second->Name; +} + +/// Compute the address map. The address map is an array of symbol offsets +/// sorted so that it can be binary searched by address. +static std::vector<ulittle32_t> computeAddrMap(ArrayRef<CVSymbol> Records) { +  // Make a vector of pointers to the symbols so we can sort it by address. +  // Also gather the symbol offsets while we're at it. + +  std::vector<PublicSym32> DeserializedPublics; +  std::vector<std::pair<const CVSymbol *, const PublicSym32 *>> PublicsByAddr; +  std::vector<uint32_t> SymOffsets; +  DeserializedPublics.reserve(Records.size()); +  PublicsByAddr.reserve(Records.size()); +  SymOffsets.reserve(Records.size()); + +  uint32_t SymOffset = 0; +  for (const CVSymbol &Sym : Records) { +    assert(Sym.kind() == SymbolKind::S_PUB32); +    DeserializedPublics.push_back( +        cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(Sym))); +    PublicsByAddr.emplace_back(&Sym, &DeserializedPublics.back()); +    SymOffsets.push_back(SymOffset); +    SymOffset += Sym.length(); +  } +  llvm::stable_sort(PublicsByAddr, comparePubSymByAddrAndName); + +  // Fill in the symbol offsets in the appropriate order. +  std::vector<ulittle32_t> AddrMap; +  AddrMap.reserve(Records.size()); +  for (auto &Sym : PublicsByAddr) { +    ptrdiff_t Idx = std::distance(Records.data(), Sym.first); +    assert(Idx >= 0 && size_t(Idx) < Records.size()); +    AddrMap.push_back(ulittle32_t(SymOffsets[Idx])); +  } +  return AddrMap; +} + +uint32_t GSIStreamBuilder::getPublicsStreamIndex() const { +  return PSH->StreamIndex; +} + +uint32_t GSIStreamBuilder::getGlobalsStreamIndex() const { +  return GSH->StreamIndex; +} + +void GSIStreamBuilder::addPublicSymbol(const PublicSym32 &Pub) { +  PSH->addSymbol(Pub, Msf); +} + +void GSIStreamBuilder::addGlobalSymbol(const ProcRefSym &Sym) { +  GSH->addSymbol(Sym, Msf); +} + +void GSIStreamBuilder::addGlobalSymbol(const DataSym &Sym) { +  GSH->addSymbol(Sym, Msf); +} + +void GSIStreamBuilder::addGlobalSymbol(const ConstantSym &Sym) { +  GSH->addSymbol(Sym, Msf); +} + +void GSIStreamBuilder::addGlobalSymbol(const codeview::CVSymbol &Sym) { +  GSH->addSymbol(Sym); +} + +static Error writeRecords(BinaryStreamWriter &Writer, +                          ArrayRef<CVSymbol> Records) { +  BinaryItemStream<CVSymbol> ItemStream(support::endianness::little); +  ItemStream.setItems(Records); +  BinaryStreamRef RecordsRef(ItemStream); +  return Writer.writeStreamRef(RecordsRef); +} + +Error GSIStreamBuilder::commitSymbolRecordStream( +    WritableBinaryStreamRef Stream) { +  BinaryStreamWriter Writer(Stream); + +  // Write public symbol records first, followed by global symbol records.  This +  // must match the order that we assume in finalizeMsfLayout when computing +  // PSHZero and GSHZero. +  if (auto EC = writeRecords(Writer, PSH->Records)) +    return EC; +  if (auto EC = writeRecords(Writer, GSH->Records)) +    return EC; + +  return Error::success(); +} + +Error GSIStreamBuilder::commitPublicsHashStream( +    WritableBinaryStreamRef Stream) { +  BinaryStreamWriter Writer(Stream); +  PublicsStreamHeader Header; + +  // FIXME: Fill these in. They are for incremental linking. +  Header.SymHash = PSH->calculateSerializedLength(); +  Header.AddrMap = PSH->Records.size() * 4; +  Header.NumThunks = 0; +  Header.SizeOfThunk = 0; +  Header.ISectThunkTable = 0; +  memset(Header.Padding, 0, sizeof(Header.Padding)); +  Header.OffThunkTable = 0; +  Header.NumSections = 0; +  if (auto EC = Writer.writeObject(Header)) +    return EC; + +  if (auto EC = PSH->commit(Writer)) +    return EC; + +  std::vector<ulittle32_t> AddrMap = computeAddrMap(PSH->Records); +  if (auto EC = Writer.writeArray(makeArrayRef(AddrMap))) +    return EC; + +  return Error::success(); +} + +Error GSIStreamBuilder::commitGlobalsHashStream( +    WritableBinaryStreamRef Stream) { +  BinaryStreamWriter Writer(Stream); +  return GSH->commit(Writer); +} + +Error GSIStreamBuilder::commit(const msf::MSFLayout &Layout, +                               WritableBinaryStreamRef Buffer) { +  auto GS = WritableMappedBlockStream::createIndexedStream( +      Layout, Buffer, getGlobalsStreamIndex(), Msf.getAllocator()); +  auto PS = WritableMappedBlockStream::createIndexedStream( +      Layout, Buffer, getPublicsStreamIndex(), Msf.getAllocator()); +  auto PRS = WritableMappedBlockStream::createIndexedStream( +      Layout, Buffer, getRecordStreamIdx(), Msf.getAllocator()); + +  if (auto EC = commitSymbolRecordStream(*PRS)) +    return EC; +  if (auto EC = commitGlobalsHashStream(*GS)) +    return EC; +  if (auto EC = commitPublicsHashStream(*PS)) +    return EC; +  return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/GlobalsStream.cpp b/llvm/lib/DebugInfo/PDB/Native/GlobalsStream.cpp new file mode 100644 index 0000000000000..f27d60f468156 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/GlobalsStream.cpp @@ -0,0 +1,181 @@ +//===- GlobalsStream.cpp - PDB Index of Symbols by Name ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// The on-disk structores used in this file are based on the reference +// implementation which is available at +// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h +// +// When you are reading the reference source code, you'd find the +// information below useful. +// +//  - ppdb1->m_fMinimalDbgInfo seems to be always true. +//  - SMALLBUCKETS macro is defined. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" + +#include "llvm/DebugInfo/CodeView/RecordName.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Error.h" +#include <algorithm> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; + +GlobalsStream::GlobalsStream(std::unique_ptr<MappedBlockStream> Stream) +    : Stream(std::move(Stream)) {} + +GlobalsStream::~GlobalsStream() = default; + +Error GlobalsStream::reload() { +  BinaryStreamReader Reader(*Stream); +  if (auto E = GlobalsTable.read(Reader)) +    return E; +  return Error::success(); +} + +std::vector<std::pair<uint32_t, codeview::CVSymbol>> +GlobalsStream::findRecordsByName(StringRef Name, +                                 const SymbolStream &Symbols) const { +  std::vector<std::pair<uint32_t, codeview::CVSymbol>> Result; + +  // Hash the name to figure out which bucket this goes into. +  size_t ExpandedBucketIndex = hashStringV1(Name) % IPHR_HASH; +  int32_t CompressedBucketIndex = GlobalsTable.BucketMap[ExpandedBucketIndex]; +  if (CompressedBucketIndex == -1) +    return Result; + +  uint32_t LastBucketIndex = GlobalsTable.HashBuckets.size() - 1; +  uint32_t StartRecordIndex = +      GlobalsTable.HashBuckets[CompressedBucketIndex] / 12; +  uint32_t EndRecordIndex = 0; +  if (LLVM_LIKELY(uint32_t(CompressedBucketIndex) < LastBucketIndex)) { +    EndRecordIndex = GlobalsTable.HashBuckets[CompressedBucketIndex + 1]; +  } else { +    // If this is the last bucket, it consists of all hash records until the end +    // of the HashRecords array. +    EndRecordIndex = GlobalsTable.HashRecords.size() * 12; +  } + +  EndRecordIndex /= 12; + +  assert(EndRecordIndex <= GlobalsTable.HashRecords.size()); +  while (StartRecordIndex < EndRecordIndex) { +    PSHashRecord PSH = GlobalsTable.HashRecords[StartRecordIndex]; +    uint32_t Off = PSH.Off - 1; +    codeview::CVSymbol Record = Symbols.readRecord(Off); +    if (codeview::getSymbolName(Record) == Name) +      Result.push_back(std::make_pair(Off, std::move(Record))); +    ++StartRecordIndex; +  } +  return Result; +} + +static Error checkHashHdrVersion(const GSIHashHeader *HashHdr) { +  if (HashHdr->VerHdr != GSIHashHeader::HdrVersion) +    return make_error<RawError>( +        raw_error_code::feature_unsupported, +        "Encountered unsupported globals stream version."); + +  return Error::success(); +} + +static Error readGSIHashHeader(const GSIHashHeader *&HashHdr, +                               BinaryStreamReader &Reader) { +  if (Reader.readObject(HashHdr)) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Stream does not contain a GSIHashHeader."); + +  if (HashHdr->VerSignature != GSIHashHeader::HdrSignature) +    return make_error<RawError>( +        raw_error_code::feature_unsupported, +        "GSIHashHeader signature (0xffffffff) not found."); + +  return Error::success(); +} + +static Error readGSIHashRecords(FixedStreamArray<PSHashRecord> &HashRecords, +                                const GSIHashHeader *HashHdr, +                                BinaryStreamReader &Reader) { +  if (auto EC = checkHashHdrVersion(HashHdr)) +    return EC; + +  // HashHdr->HrSize specifies the number of bytes of PSHashRecords we have. +  // Verify that we can read them all. +  if (HashHdr->HrSize % sizeof(PSHashRecord)) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Invalid HR array size."); +  uint32_t NumHashRecords = HashHdr->HrSize / sizeof(PSHashRecord); +  if (auto EC = Reader.readArray(HashRecords, NumHashRecords)) +    return joinErrors(std::move(EC), +                      make_error<RawError>(raw_error_code::corrupt_file, +                                           "Error reading hash records.")); + +  return Error::success(); +} + +static Error +readGSIHashBuckets(FixedStreamArray<support::ulittle32_t> &HashBuckets, +                   FixedStreamArray<support::ulittle32_t> &HashBitmap, +                   const GSIHashHeader *HashHdr, +                   MutableArrayRef<int32_t> BucketMap, +                   BinaryStreamReader &Reader) { +  if (auto EC = checkHashHdrVersion(HashHdr)) +    return EC; + +  // Before the actual hash buckets, there is a bitmap of length determined by +  // IPHR_HASH. +  size_t BitmapSizeInBits = alignTo(IPHR_HASH + 1, 32); +  uint32_t NumBitmapEntries = BitmapSizeInBits / 32; +  if (auto EC = Reader.readArray(HashBitmap, NumBitmapEntries)) +    return joinErrors(std::move(EC), +                      make_error<RawError>(raw_error_code::corrupt_file, +                                           "Could not read a bitmap.")); +  uint32_t NumBuckets1 = 0; +  uint32_t CompressedBucketIdx = 0; +  for (uint32_t I = 0; I <= IPHR_HASH; ++I) { +    uint8_t WordIdx = I / 32; +    uint8_t BitIdx = I % 32; +    bool IsSet = HashBitmap[WordIdx] & (1U << BitIdx); +    if (IsSet) { +      ++NumBuckets1; +      BucketMap[I] = CompressedBucketIdx++; +    } else { +      BucketMap[I] = -1; +    } +  } + +  uint32_t NumBuckets = 0; +  for (uint32_t B : HashBitmap) +    NumBuckets += countPopulation(B); + +  // Hash buckets follow. +  if (auto EC = Reader.readArray(HashBuckets, NumBuckets)) +    return joinErrors(std::move(EC), +                      make_error<RawError>(raw_error_code::corrupt_file, +                                           "Hash buckets corrupted.")); + +  return Error::success(); +} + +Error GSIHashTable::read(BinaryStreamReader &Reader) { +  if (auto EC = readGSIHashHeader(HashHdr, Reader)) +    return EC; +  if (auto EC = readGSIHashRecords(HashRecords, HashHdr, Reader)) +    return EC; +  if (HashHdr->HrSize > 0) +    if (auto EC = readGSIHashBuckets(HashBuckets, HashBitmap, HashHdr, +                                     BucketMap, Reader)) +      return EC; +  return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/Hash.cpp b/llvm/lib/DebugInfo/PDB/Native/Hash.cpp new file mode 100644 index 0000000000000..7fb6b4bd5d31e --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/Hash.cpp @@ -0,0 +1,84 @@ +//===- Hash.cpp - PDB Hash Functions --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/Endian.h" +#include <cstdint> + +using namespace llvm; +using namespace llvm::support; + +// Corresponds to `Hasher::lhashPbCb` in PDB/include/misc.h. +// Used for name hash table and TPI/IPI hashes. +uint32_t pdb::hashStringV1(StringRef Str) { +  uint32_t Result = 0; +  uint32_t Size = Str.size(); + +  ArrayRef<ulittle32_t> Longs(reinterpret_cast<const ulittle32_t *>(Str.data()), +                              Size / 4); + +  for (auto Value : Longs) +    Result ^= Value; + +  const uint8_t *Remainder = reinterpret_cast<const uint8_t *>(Longs.end()); +  uint32_t RemainderSize = Size % 4; + +  // Maximum of 3 bytes left.  Hash a 2 byte word if possible, then hash the +  // possibly remaining 1 byte. +  if (RemainderSize >= 2) { +    uint16_t Value = *reinterpret_cast<const ulittle16_t *>(Remainder); +    Result ^= static_cast<uint32_t>(Value); +    Remainder += 2; +    RemainderSize -= 2; +  } + +  // hash possible odd byte +  if (RemainderSize == 1) { +    Result ^= *(Remainder++); +  } + +  const uint32_t toLowerMask = 0x20202020; +  Result |= toLowerMask; +  Result ^= (Result >> 11); + +  return Result ^ (Result >> 16); +} + +// Corresponds to `HasherV2::HashULONG` in PDB/include/misc.h. +// Used for name hash table. +uint32_t pdb::hashStringV2(StringRef Str) { +  uint32_t Hash = 0xb170a1bf; + +  ArrayRef<char> Buffer(Str.begin(), Str.end()); + +  ArrayRef<ulittle32_t> Items( +      reinterpret_cast<const ulittle32_t *>(Buffer.data()), +      Buffer.size() / sizeof(ulittle32_t)); +  for (ulittle32_t Item : Items) { +    Hash += Item; +    Hash += (Hash << 10); +    Hash ^= (Hash >> 6); +  } +  Buffer = Buffer.slice(Items.size() * sizeof(ulittle32_t)); +  for (uint8_t Item : Buffer) { +    Hash += Item; +    Hash += (Hash << 10); +    Hash ^= (Hash >> 6); +  } + +  return Hash * 1664525U + 1013904223U; +} + +// Corresponds to `SigForPbCb` in langapi/shared/crc32.h. +uint32_t pdb::hashBufferV8(ArrayRef<uint8_t> Buf) { +  JamCRC JC(/*Init=*/0U); +  JC.update(Buf); +  return JC.getCRC(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/HashTable.cpp b/llvm/lib/DebugInfo/PDB/Native/HashTable.cpp new file mode 100644 index 0000000000000..dfdcdf1f4eafe --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/HashTable.cpp @@ -0,0 +1,71 @@ +//===- HashTable.cpp - PDB Hash Table -------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/HashTable.h" +#include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MathExtras.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +Error llvm::pdb::readSparseBitVector(BinaryStreamReader &Stream, +                                     SparseBitVector<> &V) { +  uint32_t NumWords; +  if (auto EC = Stream.readInteger(NumWords)) +    return joinErrors( +        std::move(EC), +        make_error<RawError>(raw_error_code::corrupt_file, +                             "Expected hash table number of words")); + +  for (uint32_t I = 0; I != NumWords; ++I) { +    uint32_t Word; +    if (auto EC = Stream.readInteger(Word)) +      return joinErrors(std::move(EC), +                        make_error<RawError>(raw_error_code::corrupt_file, +                                             "Expected hash table word")); +    for (unsigned Idx = 0; Idx < 32; ++Idx) +      if (Word & (1U << Idx)) +        V.set((I * 32) + Idx); +  } +  return Error::success(); +} + +Error llvm::pdb::writeSparseBitVector(BinaryStreamWriter &Writer, +                                      SparseBitVector<> &Vec) { +  constexpr int BitsPerWord = 8 * sizeof(uint32_t); + +  int ReqBits = Vec.find_last() + 1; +  uint32_t ReqWords = alignTo(ReqBits, BitsPerWord) / BitsPerWord; +  if (auto EC = Writer.writeInteger(ReqWords)) +    return joinErrors( +        std::move(EC), +        make_error<RawError>(raw_error_code::corrupt_file, +                             "Could not write linear map number of words")); + +  uint32_t Idx = 0; +  for (uint32_t I = 0; I != ReqWords; ++I) { +    uint32_t Word = 0; +    for (uint32_t WordIdx = 0; WordIdx < 32; ++WordIdx, ++Idx) { +      if (Vec.test(Idx)) +        Word |= (1 << WordIdx); +    } +    if (auto EC = Writer.writeInteger(Word)) +      return joinErrors(std::move(EC), make_error<RawError>( +                                           raw_error_code::corrupt_file, +                                           "Could not write linear map word")); +  } +  return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/InfoStream.cpp b/llvm/lib/DebugInfo/PDB/Native/InfoStream.cpp new file mode 100644 index 0000000000000..f41bb32d69af8 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/InfoStream.cpp @@ -0,0 +1,131 @@ +//===- InfoStream.cpp - PDB Info Stream (Stream 1) Access -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamReader.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +InfoStream::InfoStream(std::unique_ptr<BinaryStream> Stream) +    : Stream(std::move(Stream)), Header(nullptr) {} + +Error InfoStream::reload() { +  BinaryStreamReader Reader(*Stream); + +  if (auto EC = Reader.readObject(Header)) +    return joinErrors( +        std::move(EC), +        make_error<RawError>(raw_error_code::corrupt_file, +                             "PDB Stream does not contain a header.")); + +  switch (Header->Version) { +  case PdbImplVC70: +  case PdbImplVC80: +  case PdbImplVC110: +  case PdbImplVC140: +    break; +  default: +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Unsupported PDB stream version."); +  } + +  uint32_t Offset = Reader.getOffset(); +  if (auto EC = NamedStreams.load(Reader)) +    return EC; +  uint32_t NewOffset = Reader.getOffset(); +  NamedStreamMapByteSize = NewOffset - Offset; + +  Reader.setOffset(Offset); +  if (auto EC = Reader.readSubstream(SubNamedStreams, NamedStreamMapByteSize)) +    return EC; + +  bool Stop = false; +  while (!Stop && !Reader.empty()) { +    PdbRaw_FeatureSig Sig; +    if (auto EC = Reader.readEnum(Sig)) +      return EC; +    // Since this value comes from a file, it's possible we have some strange +    // value which doesn't correspond to any value.  We don't want to warn on +    // -Wcovered-switch-default in this case, so switch on the integral value +    // instead of the enumeration value. +    switch (uint32_t(Sig)) { +    case uint32_t(PdbRaw_FeatureSig::VC110): +      // No other flags for VC110 PDB. +      Stop = true; +      LLVM_FALLTHROUGH; +    case uint32_t(PdbRaw_FeatureSig::VC140): +      Features |= PdbFeatureContainsIdStream; +      break; +    case uint32_t(PdbRaw_FeatureSig::NoTypeMerge): +      Features |= PdbFeatureNoTypeMerging; +      break; +    case uint32_t(PdbRaw_FeatureSig::MinimalDebugInfo): +      Features |= PdbFeatureMinimalDebugInfo; +      break; +    default: +      continue; +    } +    FeatureSignatures.push_back(Sig); +  } +  return Error::success(); +} + +uint32_t InfoStream::getStreamSize() const { return Stream->getLength(); } + +Expected<uint32_t> InfoStream::getNamedStreamIndex(llvm::StringRef Name) const { +  uint32_t Result; +  if (!NamedStreams.get(Name, Result)) +    return make_error<RawError>(raw_error_code::no_stream); +  return Result; +} + +StringMap<uint32_t> InfoStream::named_streams() const { +  return NamedStreams.entries(); +} + +bool InfoStream::containsIdStream() const { +  return !!(Features & PdbFeatureContainsIdStream); +} + +PdbRaw_ImplVer InfoStream::getVersion() const { +  return static_cast<PdbRaw_ImplVer>(uint32_t(Header->Version)); +} + +uint32_t InfoStream::getSignature() const { +  return uint32_t(Header->Signature); +} + +uint32_t InfoStream::getAge() const { return uint32_t(Header->Age); } + +GUID InfoStream::getGuid() const { return Header->Guid; } + +uint32_t InfoStream::getNamedStreamMapByteSize() const { +  return NamedStreamMapByteSize; +} + +PdbRaw_Features InfoStream::getFeatures() const { return Features; } + +ArrayRef<PdbRaw_FeatureSig> InfoStream::getFeatureSignatures() const { +  return FeatureSignatures; +} + +const NamedStreamMap &InfoStream::getNamedStreams() const { +  return NamedStreams; +} + +BinarySubstreamRef InfoStream::getNamedStreamsBuffer() const { +  return SubNamedStreams; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp new file mode 100644 index 0000000000000..42daa7cae7997 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp @@ -0,0 +1,82 @@ +//===- InfoStreamBuilder.cpp - PDB Info Stream Creation ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" + +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" +#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamWriter.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +InfoStreamBuilder::InfoStreamBuilder(msf::MSFBuilder &Msf, +                                     NamedStreamMap &NamedStreams) +    : Msf(Msf), Ver(PdbRaw_ImplVer::PdbImplVC70), Age(0), +      NamedStreams(NamedStreams) { +  ::memset(&Guid, 0, sizeof(Guid)); +} + +void InfoStreamBuilder::setVersion(PdbRaw_ImplVer V) { Ver = V; } + +void InfoStreamBuilder::addFeature(PdbRaw_FeatureSig Sig) { +  Features.push_back(Sig); +} + +void InfoStreamBuilder::setHashPDBContentsToGUID(bool B) { +  HashPDBContentsToGUID = B; +} + +void InfoStreamBuilder::setAge(uint32_t A) { Age = A; } + +void InfoStreamBuilder::setSignature(uint32_t S) { Signature = S; } + +void InfoStreamBuilder::setGuid(GUID G) { Guid = G; } + + +Error InfoStreamBuilder::finalizeMsfLayout() { +  uint32_t Length = sizeof(InfoStreamHeader) + +                    NamedStreams.calculateSerializedLength() + +                    (Features.size() + 1) * sizeof(uint32_t); +  if (auto EC = Msf.setStreamSize(StreamPDB, Length)) +    return EC; +  return Error::success(); +} + +Error InfoStreamBuilder::commit(const msf::MSFLayout &Layout, +                                WritableBinaryStreamRef Buffer) const { +  auto InfoS = WritableMappedBlockStream::createIndexedStream( +      Layout, Buffer, StreamPDB, Msf.getAllocator()); +  BinaryStreamWriter Writer(*InfoS); + +  InfoStreamHeader H; +  // Leave the build id fields 0 so they can be set as the last step before +  // committing the file to disk. +  ::memset(&H, 0, sizeof(H)); +  H.Version = Ver; +  if (auto EC = Writer.writeObject(H)) +    return EC; + +  if (auto EC = NamedStreams.commit(Writer)) +    return EC; +  if (auto EC = Writer.writeInteger(0)) +    return EC; +  for (auto E : Features) { +    if (auto EC = Writer.writeEnum(E)) +      return EC; +  } +  assert(Writer.bytesRemaining() == 0); +  return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/InjectedSourceStream.cpp b/llvm/lib/DebugInfo/PDB/Native/InjectedSourceStream.cpp new file mode 100644 index 0000000000000..3f4101db7b93e --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/InjectedSourceStream.cpp @@ -0,0 +1,65 @@ +//===- InjectedSourceStream.cpp - PDB Headerblock Stream Access -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/InjectedSourceStream.h" + +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; +using namespace llvm::pdb; + +InjectedSourceStream::InjectedSourceStream( +    std::unique_ptr<MappedBlockStream> Stream) +    : Stream(std::move(Stream)) {} + +Error InjectedSourceStream::reload(const PDBStringTable &Strings) { +  BinaryStreamReader Reader(*Stream); + +  if (auto EC = Reader.readObject(Header)) +    return EC; + +  if (Header->Version != +      static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne)) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Invalid headerblock header version"); + +  if (auto EC = InjectedSourceTable.load(Reader)) +    return EC; + +  for (const auto& Entry : *this) { +    if (Entry.second.Size != sizeof(SrcHeaderBlockEntry)) +      return make_error<RawError>(raw_error_code::corrupt_file, +                                  "Invalid headerbock entry size"); +    if (Entry.second.Version != +        static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne)) +      return make_error<RawError>(raw_error_code::corrupt_file, +                                  "Invalid headerbock entry version"); + +    // Check that all name references are valid. +    auto Name = Strings.getStringForID(Entry.second.FileNI); +    if (!Name) +      return Name.takeError(); +    auto ObjName = Strings.getStringForID(Entry.second.ObjNI); +    if (!ObjName) +      return ObjName.takeError(); +    auto VName = Strings.getStringForID(Entry.second.VFileNI); +    if (!VName) +      return VName.takeError(); +  } + +  assert(Reader.bytesRemaining() == 0); +  return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/ModuleDebugStream.cpp b/llvm/lib/DebugInfo/PDB/Native/ModuleDebugStream.cpp new file mode 100644 index 0000000000000..1445f0bd9e1be --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/ModuleDebugStream.cpp @@ -0,0 +1,144 @@ +//===- ModuleDebugStream.cpp - PDB Module Info Stream Access --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamRef.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cstdint> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +ModuleDebugStreamRef::ModuleDebugStreamRef( +    const DbiModuleDescriptor &Module, +    std::unique_ptr<MappedBlockStream> Stream) +    : Mod(Module), Stream(std::move(Stream)) {} + +ModuleDebugStreamRef::~ModuleDebugStreamRef() = default; + +Error ModuleDebugStreamRef::reload() { +  BinaryStreamReader Reader(*Stream); + +  if (Mod.getModuleStreamIndex() != llvm::pdb::kInvalidStreamIndex) { +    if (Error E = reloadSerialize(Reader)) +      return E; +  } +  if (Reader.bytesRemaining() > 0) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Unexpected bytes in module stream."); +  return Error::success(); +} + +Error ModuleDebugStreamRef::reloadSerialize(BinaryStreamReader &Reader) { +  uint32_t SymbolSize = Mod.getSymbolDebugInfoByteSize(); +  uint32_t C11Size = Mod.getC11LineInfoByteSize(); +  uint32_t C13Size = Mod.getC13LineInfoByteSize(); + +  if (C11Size > 0 && C13Size > 0) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Module has both C11 and C13 line info"); + +  BinaryStreamRef S; + +  if (auto EC = Reader.readInteger(Signature)) +    return EC; +  Reader.setOffset(0); +  if (auto EC = Reader.readSubstream(SymbolsSubstream, SymbolSize)) +    return EC; +  if (auto EC = Reader.readSubstream(C11LinesSubstream, C11Size)) +    return EC; +  if (auto EC = Reader.readSubstream(C13LinesSubstream, C13Size)) +    return EC; + +  BinaryStreamReader SymbolReader(SymbolsSubstream.StreamData); +  if (auto EC = SymbolReader.readArray( +          SymbolArray, SymbolReader.bytesRemaining(), sizeof(uint32_t))) +    return EC; + +  BinaryStreamReader SubsectionsReader(C13LinesSubstream.StreamData); +  if (auto EC = SubsectionsReader.readArray(Subsections, +                                            SubsectionsReader.bytesRemaining())) +    return EC; + +  uint32_t GlobalRefsSize; +  if (auto EC = Reader.readInteger(GlobalRefsSize)) +    return EC; +  if (auto EC = Reader.readSubstream(GlobalRefsSubstream, GlobalRefsSize)) +    return EC; +  return Error::success(); +} + +const codeview::CVSymbolArray +ModuleDebugStreamRef::getSymbolArrayForScope(uint32_t ScopeBegin) const { +  return limitSymbolArrayToScope(SymbolArray, ScopeBegin); +} + +BinarySubstreamRef ModuleDebugStreamRef::getSymbolsSubstream() const { +  return SymbolsSubstream; +} + +BinarySubstreamRef ModuleDebugStreamRef::getC11LinesSubstream() const { +  return C11LinesSubstream; +} + +BinarySubstreamRef ModuleDebugStreamRef::getC13LinesSubstream() const { +  return C13LinesSubstream; +} + +BinarySubstreamRef ModuleDebugStreamRef::getGlobalRefsSubstream() const { +  return GlobalRefsSubstream; +} + +iterator_range<codeview::CVSymbolArray::Iterator> +ModuleDebugStreamRef::symbols(bool *HadError) const { +  return make_range(SymbolArray.begin(HadError), SymbolArray.end()); +} + +CVSymbol ModuleDebugStreamRef::readSymbolAtOffset(uint32_t Offset) const { +  auto Iter = SymbolArray.at(Offset); +  assert(Iter != SymbolArray.end()); +  return *Iter; +} + +iterator_range<ModuleDebugStreamRef::DebugSubsectionIterator> +ModuleDebugStreamRef::subsections() const { +  return make_range(Subsections.begin(), Subsections.end()); +} + +bool ModuleDebugStreamRef::hasDebugSubsections() const { +  return !C13LinesSubstream.empty(); +} + +Error ModuleDebugStreamRef::commit() { return Error::success(); } + +Expected<codeview::DebugChecksumsSubsectionRef> +ModuleDebugStreamRef::findChecksumsSubsection() const { +  codeview::DebugChecksumsSubsectionRef Result; +  for (const auto &SS : subsections()) { +    if (SS.kind() != DebugSubsectionKind::FileChecksums) +      continue; + +    if (auto EC = Result.initialize(SS.getRecordData())) +      return std::move(EC); +    return Result; +  } +  return Result; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp b/llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp new file mode 100644 index 0000000000000..4a88391494cd2 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp @@ -0,0 +1,126 @@ +//===- NamedStreamMap.cpp - PDB Named Stream Map --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/HashTable.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamRef.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <tuple> + +using namespace llvm; +using namespace llvm::pdb; + +NamedStreamMapTraits::NamedStreamMapTraits(NamedStreamMap &NS) : NS(&NS) {} + +uint16_t NamedStreamMapTraits::hashLookupKey(StringRef S) const { +  // In the reference implementation, this uses +  // HASH Hasher<ULONG*, USHORT*>::hashPbCb(PB pb, size_t cb, ULONG ulMod). +  // Here, the type HASH is a typedef of unsigned short. +  // ** It is not a bug that we truncate the result of hashStringV1, in fact +  //    it is a bug if we do not! ** +  // See NMTNI::hash() in the reference implementation. +  return static_cast<uint16_t>(hashStringV1(S)); +} + +StringRef NamedStreamMapTraits::storageKeyToLookupKey(uint32_t Offset) const { +  return NS->getString(Offset); +} + +uint32_t NamedStreamMapTraits::lookupKeyToStorageKey(StringRef S) { +  return NS->appendStringData(S); +} + +NamedStreamMap::NamedStreamMap() : HashTraits(*this), OffsetIndexMap(1) {} + +Error NamedStreamMap::load(BinaryStreamReader &Stream) { +  uint32_t StringBufferSize; +  if (auto EC = Stream.readInteger(StringBufferSize)) +    return joinErrors(std::move(EC), +                      make_error<RawError>(raw_error_code::corrupt_file, +                                           "Expected string buffer size")); + +  StringRef Buffer; +  if (auto EC = Stream.readFixedString(Buffer, StringBufferSize)) +    return EC; +  NamesBuffer.assign(Buffer.begin(), Buffer.end()); + +  return OffsetIndexMap.load(Stream); +} + +Error NamedStreamMap::commit(BinaryStreamWriter &Writer) const { +  // The first field is the number of bytes of string data. +  if (auto EC = Writer.writeInteger<uint32_t>(NamesBuffer.size())) +    return EC; + +  // Then the actual string data. +  StringRef Data(NamesBuffer.data(), NamesBuffer.size()); +  if (auto EC = Writer.writeFixedString(Data)) +    return EC; + +  // And finally the Offset Index map. +  if (auto EC = OffsetIndexMap.commit(Writer)) +    return EC; + +  return Error::success(); +} + +uint32_t NamedStreamMap::calculateSerializedLength() const { +  return sizeof(uint32_t)                              // String data size +         + NamesBuffer.size()                          // String data +         + OffsetIndexMap.calculateSerializedLength(); // Offset Index Map +} + +uint32_t NamedStreamMap::size() const { return OffsetIndexMap.size(); } + +StringRef NamedStreamMap::getString(uint32_t Offset) const { +  assert(NamesBuffer.size() > Offset); +  return StringRef(NamesBuffer.data() + Offset); +} + +uint32_t NamedStreamMap::hashString(uint32_t Offset) const { +  return hashStringV1(getString(Offset)); +} + +bool NamedStreamMap::get(StringRef Stream, uint32_t &StreamNo) const { +  auto Iter = OffsetIndexMap.find_as(Stream, HashTraits); +  if (Iter == OffsetIndexMap.end()) +    return false; +  StreamNo = (*Iter).second; +  return true; +} + +StringMap<uint32_t> NamedStreamMap::entries() const { +  StringMap<uint32_t> Result; +  for (const auto &Entry : OffsetIndexMap) { +    StringRef Stream(NamesBuffer.data() + Entry.first); +    Result.try_emplace(Stream, Entry.second); +  } +  return Result; +} + +uint32_t NamedStreamMap::appendStringData(StringRef S) { +  uint32_t Offset = NamesBuffer.size(); +  NamesBuffer.insert(NamesBuffer.end(), S.begin(), S.end()); +  NamesBuffer.push_back('\0'); +  return Offset; +} + +void NamedStreamMap::set(StringRef Stream, uint32_t StreamNo) { +  OffsetIndexMap.set_as(Stream, support::ulittle32_t(StreamNo), HashTraits); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeCompilandSymbol.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeCompilandSymbol.cpp new file mode 100644 index 0000000000000..39ae84acba202 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeCompilandSymbol.cpp @@ -0,0 +1,60 @@ +//===- NativeCompilandSymbol.cpp - Native impl for compilands ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" + +#include "llvm/ADT/STLExtras.h" + +namespace llvm { +namespace pdb { + +NativeCompilandSymbol::NativeCompilandSymbol(NativeSession &Session, +                                             SymIndexId SymbolId, +                                             DbiModuleDescriptor MI) +    : NativeRawSymbol(Session, PDB_SymType::Compiland, SymbolId), Module(MI) {} + +PDB_SymType NativeCompilandSymbol::getSymTag() const { +  return PDB_SymType::Compiland; +} + +void NativeCompilandSymbol::dump(raw_ostream &OS, int Indent, +                                 PdbSymbolIdField ShowIdFields, +                                 PdbSymbolIdField RecurseIdFields) const { +  NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + +  dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session, +                    PdbSymbolIdField::LexicalParent, ShowIdFields, +                    RecurseIdFields); +  dumpSymbolField(OS, "libraryName", getLibraryName(), Indent); +  dumpSymbolField(OS, "name", getName(), Indent); +  dumpSymbolField(OS, "editAndContinueEnabled", isEditAndContinueEnabled(), +                  Indent); +} + +bool NativeCompilandSymbol::isEditAndContinueEnabled() const { +  return Module.hasECInfo(); +} + +SymIndexId NativeCompilandSymbol::getLexicalParentId() const { return 0; } + +// The usage of getObjFileName for getLibraryName and getModuleName for getName +// may seem backwards, but it is consistent with DIA, which is what this API +// was modeled after.  We may rename these methods later to try to eliminate +// this potential confusion. + +std::string NativeCompilandSymbol::getLibraryName() const { +  return Module.getObjFileName(); +} + +std::string NativeCompilandSymbol::getName() const { +  return Module.getModuleName(); +} + +} // namespace pdb +} // namespace llvm diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeEnumGlobals.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeEnumGlobals.cpp new file mode 100644 index 0000000000000..54646867bc5ff --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeEnumGlobals.cpp @@ -0,0 +1,54 @@ +//==- NativeEnumGlobals.cpp - Native Global Enumerator impl ------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeEnumGlobals.h" + +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeEnumGlobals::NativeEnumGlobals(NativeSession &PDBSession, +                                     std::vector<codeview::SymbolKind> Kinds) +    : Index(0), Session(PDBSession) { +  GlobalsStream &GS = cantFail(Session.getPDBFile().getPDBGlobalsStream()); +  SymbolStream &SS = cantFail(Session.getPDBFile().getPDBSymbolStream()); +  for (uint32_t Off : GS.getGlobalsTable()) { +    CVSymbol S = SS.readRecord(Off); +    if (!llvm::is_contained(Kinds, S.kind())) +      continue; +    MatchOffsets.push_back(Off); +  } +} + +uint32_t NativeEnumGlobals::getChildCount() const { +  return static_cast<uint32_t>(MatchOffsets.size()); +} + +std::unique_ptr<PDBSymbol> +NativeEnumGlobals::getChildAtIndex(uint32_t N) const { +  if (N >= MatchOffsets.size()) +    return nullptr; + +  SymIndexId Id = +      Session.getSymbolCache().getOrCreateGlobalSymbolByOffset(MatchOffsets[N]); +  return Session.getSymbolCache().getSymbolById(Id); +} + +std::unique_ptr<PDBSymbol> NativeEnumGlobals::getNext() { +  return getChildAtIndex(Index++); +} + +void NativeEnumGlobals::reset() { Index = 0; } diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeEnumInjectedSources.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeEnumInjectedSources.cpp new file mode 100644 index 0000000000000..2f6a5bc3d5744 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeEnumInjectedSources.cpp @@ -0,0 +1,121 @@ +//==- NativeEnumInjectedSources.cpp - Native Injected Source Enumerator --*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeEnumInjectedSources.h" + +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" + +namespace llvm { +namespace pdb { + +namespace { + +Expected<std::string> readStreamData(BinaryStream &Stream, uint32_t Limit) { +  uint32_t Offset = 0, DataLength = std::min(Limit, Stream.getLength()); +  std::string Result; +  Result.reserve(DataLength); +  while (Offset < DataLength) { +    ArrayRef<uint8_t> Data; +    if (auto E = Stream.readLongestContiguousChunk(Offset, Data)) +      return std::move(E); +    Data = Data.take_front(DataLength - Offset); +    Offset += Data.size(); +    Result += toStringRef(Data); +  } +  return Result; +} + +class NativeInjectedSource final : public IPDBInjectedSource { +  const SrcHeaderBlockEntry &Entry; +  const PDBStringTable &Strings; +  PDBFile &File; + +public: +  NativeInjectedSource(const SrcHeaderBlockEntry &Entry, +                       PDBFile &File, const PDBStringTable &Strings) +      : Entry(Entry), Strings(Strings), File(File) {} + +  uint32_t getCrc32() const override { return Entry.CRC; } +  uint64_t getCodeByteSize() const override { return Entry.FileSize; } + +  std::string getFileName() const override { +    StringRef Ret = cantFail(Strings.getStringForID(Entry.FileNI), +                             "InjectedSourceStream should have rejected this"); +    return Ret; +  } + +  std::string getObjectFileName() const override { +    StringRef Ret = cantFail(Strings.getStringForID(Entry.ObjNI), +                             "InjectedSourceStream should have rejected this"); +    return Ret; +  } + +  std::string getVirtualFileName() const override { +    StringRef Ret = cantFail(Strings.getStringForID(Entry.VFileNI), +                             "InjectedSourceStream should have rejected this"); +    return Ret; +  } + +  uint32_t getCompression() const override { return Entry.Compression; } + +  std::string getCode() const override { +    // Get name of stream storing the data. +    StringRef VName = +        cantFail(Strings.getStringForID(Entry.VFileNI), +                 "InjectedSourceStream should have rejected this"); +    std::string StreamName = ("/src/files/" + VName).str(); + +    // Find stream with that name and read its data. +    // FIXME: Consider validating (or even loading) all this in +    // InjectedSourceStream so that no error can happen here. +    auto ExpectedFileStream = File.safelyCreateNamedStream(StreamName); +    if (!ExpectedFileStream) { +      consumeError(ExpectedFileStream.takeError()); +      return "(failed to open data stream)"; +    } + +    auto Data = readStreamData(**ExpectedFileStream, Entry.FileSize); +    if (!Data) { +      consumeError(Data.takeError()); +      return "(failed to read data)"; +    } +    return *Data; +  } +}; + +} // namespace + +NativeEnumInjectedSources::NativeEnumInjectedSources( +    PDBFile &File, const InjectedSourceStream &IJS, +    const PDBStringTable &Strings) +    : File(File), Stream(IJS), Strings(Strings), Cur(Stream.begin()) {} + +uint32_t NativeEnumInjectedSources::getChildCount() const { +  return static_cast<uint32_t>(Stream.size()); +} + +std::unique_ptr<IPDBInjectedSource> +NativeEnumInjectedSources::getChildAtIndex(uint32_t N) const { +  if (N >= getChildCount()) +    return nullptr; +  return std::make_unique<NativeInjectedSource>(std::next(Stream.begin(), N)->second, +                                           File, Strings); +} + +std::unique_ptr<IPDBInjectedSource> NativeEnumInjectedSources::getNext() { +  if (Cur == Stream.end()) +    return nullptr; +  return std::make_unique<NativeInjectedSource>((Cur++)->second, File, Strings); +} + +void NativeEnumInjectedSources::reset() { Cur = Stream.begin(); } + +} +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeEnumModules.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeEnumModules.cpp new file mode 100644 index 0000000000000..c6621924b5160 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeEnumModules.cpp @@ -0,0 +1,43 @@ +//==- NativeEnumModules.cpp - Native Symbol Enumerator impl ------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeEnumModules.h" + +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeExeSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" + +namespace llvm { +namespace pdb { + +NativeEnumModules::NativeEnumModules(NativeSession &PDBSession, uint32_t Index) +    : Session(PDBSession), Index(Index) {} + +uint32_t NativeEnumModules::getChildCount() const { +  return Session.getSymbolCache().getNumCompilands(); +} + +std::unique_ptr<PDBSymbol> +NativeEnumModules::getChildAtIndex(uint32_t N) const { +  return Session.getSymbolCache().getOrCreateCompiland(N); +} + +std::unique_ptr<PDBSymbol> NativeEnumModules::getNext() { +  if (Index >= getChildCount()) +    return nullptr; +  return getChildAtIndex(Index++); +} + +void NativeEnumModules::reset() { Index = 0; } + +} +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeEnumTypes.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeEnumTypes.cpp new file mode 100644 index 0000000000000..ac217df1ee48c --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeEnumTypes.cpp @@ -0,0 +1,70 @@ +//==- NativeEnumTypes.cpp - Native Type Enumerator impl ----------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h" + +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeEnumTypes::NativeEnumTypes(NativeSession &PDBSession, +                                 LazyRandomTypeCollection &Types, +                                 std::vector<codeview::TypeLeafKind> Kinds) +    : Matches(), Index(0), Session(PDBSession) { +  Optional<TypeIndex> TI = Types.getFirst(); +  while (TI) { +    CVType CVT = Types.getType(*TI); +    TypeLeafKind K = CVT.kind(); +    if (llvm::is_contained(Kinds, K)) { +      // Don't add forward refs, we'll find those later while enumerating. +      if (!isUdtForwardRef(CVT)) +        Matches.push_back(*TI); +    } else if (K == TypeLeafKind::LF_MODIFIER) { +      TypeIndex ModifiedTI = getModifiedType(CVT); +      if (!ModifiedTI.isSimple()) { +        CVType UnmodifiedCVT = Types.getType(ModifiedTI); +        // LF_MODIFIERs point to forward refs, but don't worry about that +        // here.  We're pushing the TypeIndex of the LF_MODIFIER itself, +        // so we'll worry about resolving forward refs later. +        if (llvm::is_contained(Kinds, UnmodifiedCVT.kind())) +          Matches.push_back(*TI); +      } +    } +    TI = Types.getNext(*TI); +  } +} + +NativeEnumTypes::NativeEnumTypes(NativeSession &PDBSession, +                                 std::vector<codeview::TypeIndex> Indices) +    : Matches(std::move(Indices)), Index(0), Session(PDBSession) {} + +uint32_t NativeEnumTypes::getChildCount() const { +  return static_cast<uint32_t>(Matches.size()); +} + +std::unique_ptr<PDBSymbol> NativeEnumTypes::getChildAtIndex(uint32_t N) const { +  if (N < Matches.size()) { +    SymIndexId Id = Session.getSymbolCache().findSymbolByTypeIndex(Matches[N]); +    return Session.getSymbolCache().getSymbolById(Id); +  } +  return nullptr; +} + +std::unique_ptr<PDBSymbol> NativeEnumTypes::getNext() { +  return getChildAtIndex(Index++); +} + +void NativeEnumTypes::reset() { Index = 0; } diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp new file mode 100644 index 0000000000000..3f393409129b1 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp @@ -0,0 +1,101 @@ +//===- NativeExeSymbol.cpp - native impl for PDBSymbolExe -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeExeSymbol.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumModules.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/SymbolCache.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" + +using namespace llvm; +using namespace llvm::pdb; + +static DbiStream *getDbiStreamPtr(NativeSession &Session) { +  Expected<DbiStream &> DbiS = Session.getPDBFile().getPDBDbiStream(); +  if (DbiS) +    return &DbiS.get(); + +  consumeError(DbiS.takeError()); +  return nullptr; +} + +NativeExeSymbol::NativeExeSymbol(NativeSession &Session, SymIndexId SymbolId) +    : NativeRawSymbol(Session, PDB_SymType::Exe, SymbolId), +      Dbi(getDbiStreamPtr(Session)) {} + +std::unique_ptr<IPDBEnumSymbols> +NativeExeSymbol::findChildren(PDB_SymType Type) const { +  switch (Type) { +  case PDB_SymType::Compiland: { +    return std::unique_ptr<IPDBEnumSymbols>(new NativeEnumModules(Session)); +    break; +  } +  case PDB_SymType::ArrayType: +    return Session.getSymbolCache().createTypeEnumerator(codeview::LF_ARRAY); +  case PDB_SymType::Enum: +    return Session.getSymbolCache().createTypeEnumerator(codeview::LF_ENUM); +  case PDB_SymType::PointerType: +    return Session.getSymbolCache().createTypeEnumerator(codeview::LF_POINTER); +  case PDB_SymType::UDT: +    return Session.getSymbolCache().createTypeEnumerator( +        {codeview::LF_STRUCTURE, codeview::LF_CLASS, codeview::LF_UNION, +         codeview::LF_INTERFACE}); +  case PDB_SymType::VTableShape: +    return Session.getSymbolCache().createTypeEnumerator(codeview::LF_VTSHAPE); +  case PDB_SymType::FunctionSig: +    return Session.getSymbolCache().createTypeEnumerator( +        {codeview::LF_PROCEDURE, codeview::LF_MFUNCTION}); +  case PDB_SymType::Typedef: +    return Session.getSymbolCache().createGlobalsEnumerator(codeview::S_UDT); + +  default: +    break; +  } +  return nullptr; +} + +uint32_t NativeExeSymbol::getAge() const { +  auto IS = Session.getPDBFile().getPDBInfoStream(); +  if (IS) +    return IS->getAge(); +  consumeError(IS.takeError()); +  return 0; +} + +std::string NativeExeSymbol::getSymbolsFileName() const { +  return Session.getPDBFile().getFilePath(); +} + +codeview::GUID NativeExeSymbol::getGuid() const { +  auto IS = Session.getPDBFile().getPDBInfoStream(); +  if (IS) +    return IS->getGuid(); +  consumeError(IS.takeError()); +  return codeview::GUID{{0}}; +} + +bool NativeExeSymbol::hasCTypes() const { +  auto Dbi = Session.getPDBFile().getPDBDbiStream(); +  if (Dbi) +    return Dbi->hasCTypes(); +  consumeError(Dbi.takeError()); +  return false; +} + +bool NativeExeSymbol::hasPrivateSymbols() const { +  auto Dbi = Session.getPDBFile().getPDBDbiStream(); +  if (Dbi) +    return !Dbi->isStripped(); +  consumeError(Dbi.takeError()); +  return false; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeRawSymbol.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeRawSymbol.cpp new file mode 100644 index 0000000000000..2ad552470b617 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeRawSymbol.cpp @@ -0,0 +1,734 @@ +//===- NativeRawSymbol.cpp - Native implementation of IPDBRawSymbol -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeRawSymbol.h" +#include "llvm/DebugInfo/PDB/IPDBLineNumber.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::pdb; + +NativeRawSymbol::NativeRawSymbol(NativeSession &PDBSession, PDB_SymType Tag, +                                 SymIndexId SymbolId) +    : Session(PDBSession), Tag(Tag), SymbolId(SymbolId) {} + +void NativeRawSymbol::dump(raw_ostream &OS, int Indent, +                           PdbSymbolIdField ShowIdFields, +                           PdbSymbolIdField RecurseIdFields) const { +  dumpSymbolIdField(OS, "symIndexId", SymbolId, Indent, Session, +                    PdbSymbolIdField::SymIndexId, ShowIdFields, +                    RecurseIdFields); +  dumpSymbolField(OS, "symTag", Tag, Indent); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findChildren(PDB_SymType Type) const { +  return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findChildren(PDB_SymType Type, StringRef Name, +    PDB_NameSearchFlags Flags) const { +  return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findChildrenByAddr(PDB_SymType Type, StringRef Name, +    PDB_NameSearchFlags Flags, uint32_t Section, uint32_t Offset) const { +  return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findChildrenByVA(PDB_SymType Type, StringRef Name, +   PDB_NameSearchFlags Flags, uint64_t VA) const { +  return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findChildrenByRVA(PDB_SymType Type, StringRef Name, +    PDB_NameSearchFlags Flags, uint32_t RVA) const { +  return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findInlineFramesByAddr(uint32_t Section, +                                        uint32_t Offset) const { +  return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findInlineFramesByRVA(uint32_t RVA) const { +  return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findInlineFramesByVA(uint64_t VA) const { +  return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeRawSymbol::findInlineeLines() const { +  return std::make_unique<NullEnumerator<IPDBLineNumber>>(); +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeRawSymbol::findInlineeLinesByAddr(uint32_t Section, uint32_t Offset, +                                        uint32_t Length) const { +  return std::make_unique<NullEnumerator<IPDBLineNumber>>(); +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeRawSymbol::findInlineeLinesByRVA(uint32_t RVA, uint32_t Length) const { +  return std::make_unique<NullEnumerator<IPDBLineNumber>>(); +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeRawSymbol::findInlineeLinesByVA(uint64_t VA, uint32_t Length) const { +  return std::make_unique<NullEnumerator<IPDBLineNumber>>(); +} + +void NativeRawSymbol::getDataBytes(SmallVector<uint8_t, 32> &bytes) const { +  bytes.clear(); +} + +PDB_MemberAccess NativeRawSymbol::getAccess() const { +  return PDB_MemberAccess::Private; +} + +uint32_t NativeRawSymbol::getAddressOffset() const { +  return 0; +} + +uint32_t NativeRawSymbol::getAddressSection() const { +  return 0; +} + +uint32_t NativeRawSymbol::getAge() const { +  return 0; +} + +SymIndexId NativeRawSymbol::getArrayIndexTypeId() const { return 0; } + +void NativeRawSymbol::getBackEndVersion(VersionInfo &Version) const { +  Version.Major = 0; +  Version.Minor = 0; +  Version.Build = 0; +  Version.QFE = 0; +} + +uint32_t NativeRawSymbol::getBaseDataOffset() const { +  return 0; +} + +uint32_t NativeRawSymbol::getBaseDataSlot() const { +  return 0; +} + +SymIndexId NativeRawSymbol::getBaseSymbolId() const { return 0; } + +PDB_BuiltinType NativeRawSymbol::getBuiltinType() const { +  return PDB_BuiltinType::None; +} + +uint32_t NativeRawSymbol::getBitPosition() const { +  return 0; +} + +PDB_CallingConv NativeRawSymbol::getCallingConvention() const { +  return PDB_CallingConv::FarStdCall; +} + +SymIndexId NativeRawSymbol::getClassParentId() const { return 0; } + +std::string NativeRawSymbol::getCompilerName() const { +  return {}; +} + +uint32_t NativeRawSymbol::getCount() const { +  return 0; +} + +uint32_t NativeRawSymbol::getCountLiveRanges() const { +  return 0; +} + +void NativeRawSymbol::getFrontEndVersion(VersionInfo &Version) const { +  Version.Major = 0; +  Version.Minor = 0; +  Version.Build = 0; +  Version.QFE = 0; +} + +PDB_Lang NativeRawSymbol::getLanguage() const { +  return PDB_Lang::Cobol; +} + +SymIndexId NativeRawSymbol::getLexicalParentId() const { return 0; } + +std::string NativeRawSymbol::getLibraryName() const { +  return {}; +} + +uint32_t NativeRawSymbol::getLiveRangeStartAddressOffset() const { +  return 0; +} + +uint32_t NativeRawSymbol::getLiveRangeStartAddressSection() const { +  return 0; +} + +uint32_t NativeRawSymbol::getLiveRangeStartRelativeVirtualAddress() const { +  return 0; +} + +codeview::RegisterId NativeRawSymbol::getLocalBasePointerRegisterId() const { +  return codeview::RegisterId::EAX; +} + +SymIndexId NativeRawSymbol::getLowerBoundId() const { return 0; } + +uint32_t NativeRawSymbol::getMemorySpaceKind() const { +  return 0; +} + +std::string NativeRawSymbol::getName() const { +  return {}; +} + +uint32_t NativeRawSymbol::getNumberOfAcceleratorPointerTags() const { +  return 0; +} + +uint32_t NativeRawSymbol::getNumberOfColumns() const { +  return 0; +} + +uint32_t NativeRawSymbol::getNumberOfModifiers() const { +  return 0; +} + +uint32_t NativeRawSymbol::getNumberOfRegisterIndices() const { +  return 0; +} + +uint32_t NativeRawSymbol::getNumberOfRows() const { +  return 0; +} + +std::string NativeRawSymbol::getObjectFileName() const { +  return {}; +} + +uint32_t NativeRawSymbol::getOemId() const { +  return 0; +} + +SymIndexId NativeRawSymbol::getOemSymbolId() const { return 0; } + +uint32_t NativeRawSymbol::getOffsetInUdt() const { +  return 0; +} + +PDB_Cpu NativeRawSymbol::getPlatform() const { +  return PDB_Cpu::Intel8080; +} + +uint32_t NativeRawSymbol::getRank() const { +  return 0; +} + +codeview::RegisterId NativeRawSymbol::getRegisterId() const { +  return codeview::RegisterId::EAX; +} + +uint32_t NativeRawSymbol::getRegisterType() const { +  return 0; +} + +uint32_t NativeRawSymbol::getRelativeVirtualAddress() const { +  return 0; +} + +uint32_t NativeRawSymbol::getSamplerSlot() const { +  return 0; +} + +uint32_t NativeRawSymbol::getSignature() const { +  return 0; +} + +uint32_t NativeRawSymbol::getSizeInUdt() const { +  return 0; +} + +uint32_t NativeRawSymbol::getSlot() const { +  return 0; +} + +std::string NativeRawSymbol::getSourceFileName() const { +  return {}; +} + +std::unique_ptr<IPDBLineNumber> +NativeRawSymbol::getSrcLineOnTypeDefn() const { +  return nullptr; +} + +uint32_t NativeRawSymbol::getStride() const { +  return 0; +} + +SymIndexId NativeRawSymbol::getSubTypeId() const { return 0; } + +std::string NativeRawSymbol::getSymbolsFileName() const { return {}; } + +SymIndexId NativeRawSymbol::getSymIndexId() const { return SymbolId; } + +uint32_t NativeRawSymbol::getTargetOffset() const { +  return 0; +} + +uint32_t NativeRawSymbol::getTargetRelativeVirtualAddress() const { +  return 0; +} + +uint64_t NativeRawSymbol::getTargetVirtualAddress() const { +  return 0; +} + +uint32_t NativeRawSymbol::getTargetSection() const { +  return 0; +} + +uint32_t NativeRawSymbol::getTextureSlot() const { +  return 0; +} + +uint32_t NativeRawSymbol::getTimeStamp() const { +  return 0; +} + +uint32_t NativeRawSymbol::getToken() const { +  return 0; +} + +SymIndexId NativeRawSymbol::getTypeId() const { return 0; } + +uint32_t NativeRawSymbol::getUavSlot() const { +  return 0; +} + +std::string NativeRawSymbol::getUndecoratedName() const { +  return {}; +} + +std::string NativeRawSymbol::getUndecoratedNameEx( +    PDB_UndnameFlags Flags) const { +  return {}; +} + +SymIndexId NativeRawSymbol::getUnmodifiedTypeId() const { return 0; } + +SymIndexId NativeRawSymbol::getUpperBoundId() const { return 0; } + +Variant NativeRawSymbol::getValue() const { +  return Variant(); +} + +uint32_t NativeRawSymbol::getVirtualBaseDispIndex() const { +  return 0; +} + +uint32_t NativeRawSymbol::getVirtualBaseOffset() const { +  return 0; +} + +SymIndexId NativeRawSymbol::getVirtualTableShapeId() const { return 0; } + +std::unique_ptr<PDBSymbolTypeBuiltin> +NativeRawSymbol::getVirtualBaseTableType() const { +  return nullptr; +} + +PDB_DataKind NativeRawSymbol::getDataKind() const { +  return PDB_DataKind::Unknown; +} + +PDB_SymType NativeRawSymbol::getSymTag() const { return Tag; } + +codeview::GUID NativeRawSymbol::getGuid() const { return codeview::GUID{{0}}; } + +int32_t NativeRawSymbol::getOffset() const { +  return 0; +} + +int32_t NativeRawSymbol::getThisAdjust() const { +  return 0; +} + +int32_t NativeRawSymbol::getVirtualBasePointerOffset() const { +  return 0; +} + +PDB_LocType NativeRawSymbol::getLocationType() const { +  return PDB_LocType::Null; +} + +PDB_Machine NativeRawSymbol::getMachineType() const { +  return PDB_Machine::Invalid; +} + +codeview::ThunkOrdinal NativeRawSymbol::getThunkOrdinal() const { +  return codeview::ThunkOrdinal::Standard; +} + +uint64_t NativeRawSymbol::getLength() const { +  return 0; +} + +uint64_t NativeRawSymbol::getLiveRangeLength() const { +  return 0; +} + +uint64_t NativeRawSymbol::getVirtualAddress() const { +  return 0; +} + +PDB_UdtType NativeRawSymbol::getUdtKind() const { +  return PDB_UdtType::Struct; +} + +bool NativeRawSymbol::hasConstructor() const { +  return false; +} + +bool NativeRawSymbol::hasCustomCallingConvention() const { +  return false; +} + +bool NativeRawSymbol::hasFarReturn() const { +  return false; +} + +bool NativeRawSymbol::isCode() const { +  return false; +} + +bool NativeRawSymbol::isCompilerGenerated() const { +  return false; +} + +bool NativeRawSymbol::isConstType() const { +  return false; +} + +bool NativeRawSymbol::isEditAndContinueEnabled() const { +  return false; +} + +bool NativeRawSymbol::isFunction() const { +  return false; +} + +bool NativeRawSymbol::getAddressTaken() const { +  return false; +} + +bool NativeRawSymbol::getNoStackOrdering() const { +  return false; +} + +bool NativeRawSymbol::hasAlloca() const { +  return false; +} + +bool NativeRawSymbol::hasAssignmentOperator() const { +  return false; +} + +bool NativeRawSymbol::hasCTypes() const { +  return false; +} + +bool NativeRawSymbol::hasCastOperator() const { +  return false; +} + +bool NativeRawSymbol::hasDebugInfo() const { +  return false; +} + +bool NativeRawSymbol::hasEH() const { +  return false; +} + +bool NativeRawSymbol::hasEHa() const { +  return false; +} + +bool NativeRawSymbol::hasInlAsm() const { +  return false; +} + +bool NativeRawSymbol::hasInlineAttribute() const { +  return false; +} + +bool NativeRawSymbol::hasInterruptReturn() const { +  return false; +} + +bool NativeRawSymbol::hasFramePointer() const { +  return false; +} + +bool NativeRawSymbol::hasLongJump() const { +  return false; +} + +bool NativeRawSymbol::hasManagedCode() const { +  return false; +} + +bool NativeRawSymbol::hasNestedTypes() const { +  return false; +} + +bool NativeRawSymbol::hasNoInlineAttribute() const { +  return false; +} + +bool NativeRawSymbol::hasNoReturnAttribute() const { +  return false; +} + +bool NativeRawSymbol::hasOptimizedCodeDebugInfo() const { +  return false; +} + +bool NativeRawSymbol::hasOverloadedOperator() const { +  return false; +} + +bool NativeRawSymbol::hasSEH() const { +  return false; +} + +bool NativeRawSymbol::hasSecurityChecks() const { +  return false; +} + +bool NativeRawSymbol::hasSetJump() const { +  return false; +} + +bool NativeRawSymbol::hasStrictGSCheck() const { +  return false; +} + +bool NativeRawSymbol::isAcceleratorGroupSharedLocal() const { +  return false; +} + +bool NativeRawSymbol::isAcceleratorPointerTagLiveRange() const { +  return false; +} + +bool NativeRawSymbol::isAcceleratorStubFunction() const { +  return false; +} + +bool NativeRawSymbol::isAggregated() const { +  return false; +} + +bool NativeRawSymbol::isIntroVirtualFunction() const { +  return false; +} + +bool NativeRawSymbol::isCVTCIL() const { +  return false; +} + +bool NativeRawSymbol::isConstructorVirtualBase() const { +  return false; +} + +bool NativeRawSymbol::isCxxReturnUdt() const { +  return false; +} + +bool NativeRawSymbol::isDataAligned() const { +  return false; +} + +bool NativeRawSymbol::isHLSLData() const { +  return false; +} + +bool NativeRawSymbol::isHotpatchable() const { +  return false; +} + +bool NativeRawSymbol::isIndirectVirtualBaseClass() const { +  return false; +} + +bool NativeRawSymbol::isInterfaceUdt() const { +  return false; +} + +bool NativeRawSymbol::isIntrinsic() const { +  return false; +} + +bool NativeRawSymbol::isLTCG() const { +  return false; +} + +bool NativeRawSymbol::isLocationControlFlowDependent() const { +  return false; +} + +bool NativeRawSymbol::isMSILNetmodule() const { +  return false; +} + +bool NativeRawSymbol::isMatrixRowMajor() const { +  return false; +} + +bool NativeRawSymbol::isManagedCode() const { +  return false; +} + +bool NativeRawSymbol::isMSILCode() const { +  return false; +} + +bool NativeRawSymbol::isMultipleInheritance() const { +  return false; +} + +bool NativeRawSymbol::isNaked() const { +  return false; +} + +bool NativeRawSymbol::isNested() const { +  return false; +} + +bool NativeRawSymbol::isOptimizedAway() const { +  return false; +} + +bool NativeRawSymbol::isPacked() const { +  return false; +} + +bool NativeRawSymbol::isPointerBasedOnSymbolValue() const { +  return false; +} + +bool NativeRawSymbol::isPointerToDataMember() const { +  return false; +} + +bool NativeRawSymbol::isPointerToMemberFunction() const { +  return false; +} + +bool NativeRawSymbol::isPureVirtual() const { +  return false; +} + +bool NativeRawSymbol::isRValueReference() const { +  return false; +} + +bool NativeRawSymbol::isRefUdt() const { +  return false; +} + +bool NativeRawSymbol::isReference() const { +  return false; +} + +bool NativeRawSymbol::isRestrictedType() const { +  return false; +} + +bool NativeRawSymbol::isReturnValue() const { +  return false; +} + +bool NativeRawSymbol::isSafeBuffers() const { +  return false; +} + +bool NativeRawSymbol::isScoped() const { +  return false; +} + +bool NativeRawSymbol::isSdl() const { +  return false; +} + +bool NativeRawSymbol::isSingleInheritance() const { +  return false; +} + +bool NativeRawSymbol::isSplitted() const { +  return false; +} + +bool NativeRawSymbol::isStatic() const { +  return false; +} + +bool NativeRawSymbol::hasPrivateSymbols() const { +  return false; +} + +bool NativeRawSymbol::isUnalignedType() const { +  return false; +} + +bool NativeRawSymbol::isUnreached() const { +  return false; +} + +bool NativeRawSymbol::isValueUdt() const { +  return false; +} + +bool NativeRawSymbol::isVirtual() const { +  return false; +} + +bool NativeRawSymbol::isVirtualBaseClass() const { +  return false; +} + +bool NativeRawSymbol::isVirtualInheritance() const { +  return false; +} + +bool NativeRawSymbol::isVolatileType() const { +  return false; +} + +bool NativeRawSymbol::wasInlined() const { +  return false; +} + +std::string NativeRawSymbol::getUnused() const { +  return {}; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp new file mode 100644 index 0000000000000..b45a5881dcb5c --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp @@ -0,0 +1,227 @@ +//===- NativeSession.cpp - Native implementation of IPDBSession -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBSourceFile.h" +#include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumInjectedSources.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h" +#include "llvm/DebugInfo/PDB/Native/NativeExeSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/SymbolCache.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" + +#include <algorithm> +#include <cassert> +#include <memory> +#include <utility> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; + +static DbiStream *getDbiStreamPtr(PDBFile &File) { +  Expected<DbiStream &> DbiS = File.getPDBDbiStream(); +  if (DbiS) +    return &DbiS.get(); + +  consumeError(DbiS.takeError()); +  return nullptr; +} + +NativeSession::NativeSession(std::unique_ptr<PDBFile> PdbFile, +                             std::unique_ptr<BumpPtrAllocator> Allocator) +    : Pdb(std::move(PdbFile)), Allocator(std::move(Allocator)), +      Cache(*this, getDbiStreamPtr(*Pdb)) {} + +NativeSession::~NativeSession() = default; + +Error NativeSession::createFromPdb(std::unique_ptr<MemoryBuffer> Buffer, +                                   std::unique_ptr<IPDBSession> &Session) { +  StringRef Path = Buffer->getBufferIdentifier(); +  auto Stream = std::make_unique<MemoryBufferByteStream>( +      std::move(Buffer), llvm::support::little); + +  auto Allocator = std::make_unique<BumpPtrAllocator>(); +  auto File = std::make_unique<PDBFile>(Path, std::move(Stream), *Allocator); +  if (auto EC = File->parseFileHeaders()) +    return EC; +  if (auto EC = File->parseStreamData()) +    return EC; + +  Session = +      std::make_unique<NativeSession>(std::move(File), std::move(Allocator)); + +  return Error::success(); +} + +Error NativeSession::createFromExe(StringRef Path, +                                   std::unique_ptr<IPDBSession> &Session) { +  return make_error<RawError>(raw_error_code::feature_unsupported); +} + +uint64_t NativeSession::getLoadAddress() const { return 0; } + +bool NativeSession::setLoadAddress(uint64_t Address) { return false; } + +std::unique_ptr<PDBSymbolExe> NativeSession::getGlobalScope() { +  return PDBSymbol::createAs<PDBSymbolExe>(*this, getNativeGlobalScope()); +} + +std::unique_ptr<PDBSymbol> +NativeSession::getSymbolById(SymIndexId SymbolId) const { +  return Cache.getSymbolById(SymbolId); +} + +bool NativeSession::addressForVA(uint64_t VA, uint32_t &Section, +                                 uint32_t &Offset) const { +  return false; +} + +bool NativeSession::addressForRVA(uint32_t VA, uint32_t &Section, +                                  uint32_t &Offset) const { +  return false; +} + +std::unique_ptr<PDBSymbol> +NativeSession::findSymbolByAddress(uint64_t Address, PDB_SymType Type) const { +  return nullptr; +} + +std::unique_ptr<PDBSymbol> +NativeSession::findSymbolByRVA(uint32_t RVA, PDB_SymType Type) const { +  return nullptr; +} + +std::unique_ptr<PDBSymbol> +NativeSession::findSymbolBySectOffset(uint32_t Sect, uint32_t Offset, +                                      PDB_SymType Type) const { +  return nullptr; +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeSession::findLineNumbers(const PDBSymbolCompiland &Compiland, +                               const IPDBSourceFile &File) const { +  return nullptr; +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeSession::findLineNumbersByAddress(uint64_t Address, +                                        uint32_t Length) const { +  return nullptr; +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeSession::findLineNumbersByRVA(uint32_t RVA, uint32_t Length) const { +  return nullptr; +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeSession::findLineNumbersBySectOffset(uint32_t Section, uint32_t Offset, +                                           uint32_t Length) const { +  return nullptr; +} + +std::unique_ptr<IPDBEnumSourceFiles> +NativeSession::findSourceFiles(const PDBSymbolCompiland *Compiland, +                               StringRef Pattern, +                               PDB_NameSearchFlags Flags) const { +  return nullptr; +} + +std::unique_ptr<IPDBSourceFile> +NativeSession::findOneSourceFile(const PDBSymbolCompiland *Compiland, +                                 StringRef Pattern, +                                 PDB_NameSearchFlags Flags) const { +  return nullptr; +} + +std::unique_ptr<IPDBEnumChildren<PDBSymbolCompiland>> +NativeSession::findCompilandsForSourceFile(StringRef Pattern, +                                           PDB_NameSearchFlags Flags) const { +  return nullptr; +} + +std::unique_ptr<PDBSymbolCompiland> +NativeSession::findOneCompilandForSourceFile(StringRef Pattern, +                                             PDB_NameSearchFlags Flags) const { +  return nullptr; +} + +std::unique_ptr<IPDBEnumSourceFiles> NativeSession::getAllSourceFiles() const { +  return nullptr; +} + +std::unique_ptr<IPDBEnumSourceFiles> NativeSession::getSourceFilesForCompiland( +    const PDBSymbolCompiland &Compiland) const { +  return nullptr; +} + +std::unique_ptr<IPDBSourceFile> +NativeSession::getSourceFileById(uint32_t FileId) const { +  return nullptr; +} + +std::unique_ptr<IPDBEnumDataStreams> NativeSession::getDebugStreams() const { +  return nullptr; +} + +std::unique_ptr<IPDBEnumTables> NativeSession::getEnumTables() const { +  return nullptr; +} + +std::unique_ptr<IPDBEnumInjectedSources> +NativeSession::getInjectedSources() const { +  auto ISS = Pdb->getInjectedSourceStream(); +  if (!ISS) { +    consumeError(ISS.takeError()); +    return nullptr; +  } +  auto Strings = Pdb->getStringTable(); +  if (!Strings) { +    consumeError(Strings.takeError()); +    return nullptr; +  } +  return std::make_unique<NativeEnumInjectedSources>(*Pdb, *ISS, *Strings); +} + +std::unique_ptr<IPDBEnumSectionContribs> +NativeSession::getSectionContribs() const { +  return nullptr; +} + +std::unique_ptr<IPDBEnumFrameData> +NativeSession::getFrameData() const { +  return nullptr; +} + +void NativeSession::initializeExeSymbol() { +  if (ExeSymbol == 0) +    ExeSymbol = Cache.createSymbol<NativeExeSymbol>(); +} + +NativeExeSymbol &NativeSession::getNativeGlobalScope() const { +  const_cast<NativeSession &>(*this).initializeExeSymbol(); + +  return Cache.getNativeSymbolById<NativeExeSymbol>(ExeSymbol); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeSymbolEnumerator.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeSymbolEnumerator.cpp new file mode 100644 index 0000000000000..704c1254afbfd --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeSymbolEnumerator.cpp @@ -0,0 +1,122 @@ +//===- NativeSymbolEnumerator.cpp - info about enumerators ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeSymbolEnumerator.h" + +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeSymbolEnumerator::NativeSymbolEnumerator( +    NativeSession &Session, SymIndexId Id, const NativeTypeEnum &Parent, +    codeview::EnumeratorRecord Record) +    : NativeRawSymbol(Session, PDB_SymType::Data, Id), Parent(Parent), +      Record(std::move(Record)) {} + +NativeSymbolEnumerator::~NativeSymbolEnumerator() {} + +void NativeSymbolEnumerator::dump(raw_ostream &OS, int Indent, +                                  PdbSymbolIdField ShowIdFields, +                                  PdbSymbolIdField RecurseIdFields) const { +  NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); +  dumpSymbolIdField(OS, "classParentId", getClassParentId(), Indent, Session, +                    PdbSymbolIdField::ClassParent, ShowIdFields, +                    RecurseIdFields); +  dumpSymbolIdField(OS, "lexicalParentId", getLexicalParentId(), Indent, +                    Session, PdbSymbolIdField::LexicalParent, ShowIdFields, +                    RecurseIdFields); +  dumpSymbolField(OS, "name", getName(), Indent); +  dumpSymbolIdField(OS, "typeId", getTypeId(), Indent, Session, +                    PdbSymbolIdField::Type, ShowIdFields, RecurseIdFields); +  dumpSymbolField(OS, "dataKind", getDataKind(), Indent); +  dumpSymbolField(OS, "locationType", getLocationType(), Indent); +  dumpSymbolField(OS, "constType", isConstType(), Indent); +  dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent); +  dumpSymbolField(OS, "volatileType", isVolatileType(), Indent); +  dumpSymbolField(OS, "value", getValue(), Indent); +} + +SymIndexId NativeSymbolEnumerator::getClassParentId() const { +  return Parent.getSymIndexId(); +} + +SymIndexId NativeSymbolEnumerator::getLexicalParentId() const { return 0; } + +std::string NativeSymbolEnumerator::getName() const { return Record.Name; } + +SymIndexId NativeSymbolEnumerator::getTypeId() const { +  return Parent.getTypeId(); +} + +PDB_DataKind NativeSymbolEnumerator::getDataKind() const { +  return PDB_DataKind::Constant; +} + +PDB_LocType NativeSymbolEnumerator::getLocationType() const { +  return PDB_LocType::Constant; +} + +bool NativeSymbolEnumerator::isConstType() const { return false; } + +bool NativeSymbolEnumerator::isVolatileType() const { return false; } + +bool NativeSymbolEnumerator::isUnalignedType() const { return false; } + +Variant NativeSymbolEnumerator::getValue() const { +  const NativeTypeBuiltin &BT = Parent.getUnderlyingBuiltinType(); + +  switch (BT.getBuiltinType()) { +  case PDB_BuiltinType::Int: +  case PDB_BuiltinType::Long: +  case PDB_BuiltinType::Char: { +    assert(Record.Value.isSignedIntN(BT.getLength() * 8)); +    int64_t N = Record.Value.getSExtValue(); +    switch (BT.getLength()) { +    case 1: +      return Variant{static_cast<int8_t>(N)}; +    case 2: +      return Variant{static_cast<int16_t>(N)}; +    case 4: +      return Variant{static_cast<int32_t>(N)}; +    case 8: +      return Variant{static_cast<int64_t>(N)}; +    } +    break; +  } +  case PDB_BuiltinType::UInt: +  case PDB_BuiltinType::ULong: { +    assert(Record.Value.isIntN(BT.getLength() * 8)); +    uint64_t U = Record.Value.getZExtValue(); +    switch (BT.getLength()) { +    case 1: +      return Variant{static_cast<uint8_t>(U)}; +    case 2: +      return Variant{static_cast<uint16_t>(U)}; +    case 4: +      return Variant{static_cast<uint32_t>(U)}; +    case 8: +      return Variant{static_cast<uint64_t>(U)}; +    } +    break; +  } +  case PDB_BuiltinType::Bool: { +    assert(Record.Value.isIntN(BT.getLength() * 8)); +    uint64_t U = Record.Value.getZExtValue(); +    return Variant{static_cast<bool>(U)}; +  } +  default: +    assert(false && "Invalid enumeration type"); +    break; +  } + +  return Variant{Record.Value.getSExtValue()}; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeArray.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeArray.cpp new file mode 100644 index 0000000000000..80d455ad66e95 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypeArray.cpp @@ -0,0 +1,66 @@ +//===- NativeTypeArray.cpp - info about arrays ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeTypeArray.h" + +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeTypeArray::NativeTypeArray(NativeSession &Session, SymIndexId Id, +                                 codeview::TypeIndex TI, +                                 codeview::ArrayRecord Record) +    : NativeRawSymbol(Session, PDB_SymType::ArrayType, Id), Record(Record), +      Index(TI) {} +NativeTypeArray::~NativeTypeArray() {} + +void NativeTypeArray::dump(raw_ostream &OS, int Indent, +                           PdbSymbolIdField ShowIdFields, +                           PdbSymbolIdField RecurseIdFields) const { +  NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + +  dumpSymbolField(OS, "arrayIndexTypeId", getArrayIndexTypeId(), Indent); +  dumpSymbolIdField(OS, "elementTypeId", getTypeId(), Indent, Session, +                    PdbSymbolIdField::Type, ShowIdFields, RecurseIdFields); + +  dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session, +                    PdbSymbolIdField::LexicalParent, ShowIdFields, +                    RecurseIdFields); +  dumpSymbolField(OS, "length", getLength(), Indent); +  dumpSymbolField(OS, "count", getCount(), Indent); +  dumpSymbolField(OS, "constType", isConstType(), Indent); +  dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent); +  dumpSymbolField(OS, "volatileType", isVolatileType(), Indent); +} + +SymIndexId NativeTypeArray::getArrayIndexTypeId() const { +  return Session.getSymbolCache().findSymbolByTypeIndex(Record.getIndexType()); +} + +bool NativeTypeArray::isConstType() const { return false; } + +bool NativeTypeArray::isUnalignedType() const { return false; } + +bool NativeTypeArray::isVolatileType() const { return false; } + +uint32_t NativeTypeArray::getCount() const { +  NativeRawSymbol &Element = +      Session.getSymbolCache().getNativeSymbolById(getTypeId()); +  return getLength() / Element.getLength(); +} + +SymIndexId NativeTypeArray::getTypeId() const { +  return Session.getSymbolCache().findSymbolByTypeIndex( +      Record.getElementType()); +} + +uint64_t NativeTypeArray::getLength() const { return Record.Size; }
\ No newline at end of file diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeBuiltin.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeBuiltin.cpp new file mode 100644 index 0000000000000..a08663aa91bad --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypeBuiltin.cpp @@ -0,0 +1,46 @@ +//===- NativeTypeBuiltin.cpp -------------------------------------- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeTypeBuiltin::NativeTypeBuiltin(NativeSession &PDBSession, SymIndexId Id, +                                     ModifierOptions Mods, PDB_BuiltinType T, +                                     uint64_t L) +    : NativeRawSymbol(PDBSession, PDB_SymType::BuiltinType, Id), +      Session(PDBSession), Mods(Mods), Type(T), Length(L) {} + +NativeTypeBuiltin::~NativeTypeBuiltin() {} + +void NativeTypeBuiltin::dump(raw_ostream &OS, int Indent, +                             PdbSymbolIdField ShowIdFields, +                             PdbSymbolIdField RecurseIdFields) const {} + +PDB_SymType NativeTypeBuiltin::getSymTag() const { +  return PDB_SymType::BuiltinType; +} + +PDB_BuiltinType NativeTypeBuiltin::getBuiltinType() const { return Type; } + +bool NativeTypeBuiltin::isConstType() const { +  return (Mods & ModifierOptions::Const) != ModifierOptions::None; +} + +uint64_t NativeTypeBuiltin::getLength() const { return Length; } + +bool NativeTypeBuiltin::isUnalignedType() const { +  return (Mods & ModifierOptions::Unaligned) != ModifierOptions::None; +} + +bool NativeTypeBuiltin::isVolatileType() const { +  return (Mods & ModifierOptions::Volatile) != ModifierOptions::None; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeEnum.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeEnum.cpp new file mode 100644 index 0000000000000..26ccb7daece08 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypeEnum.cpp @@ -0,0 +1,381 @@ +//===- NativeTypeEnum.cpp - info about enum type ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h" + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h" +#include "llvm/DebugInfo/PDB/Native/NativeSymbolEnumerator.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/SymbolCache.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" + +#include "llvm/Support/FormatVariadic.h" + +#include <cassert> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +namespace { +// Yea, this is a pretty terrible class name.  But if we have an enum: +// +// enum Foo { +//  A, +//  B +// }; +// +// then A and B are the "enumerators" of the "enum" Foo.  And we need +// to enumerate them. +class NativeEnumEnumEnumerators : public IPDBEnumSymbols, TypeVisitorCallbacks { +public: +  NativeEnumEnumEnumerators(NativeSession &Session, +                            const NativeTypeEnum &ClassParent); + +  uint32_t getChildCount() const override; +  std::unique_ptr<PDBSymbol> getChildAtIndex(uint32_t Index) const override; +  std::unique_ptr<PDBSymbol> getNext() override; +  void reset() override; + +private: +  Error visitKnownMember(CVMemberRecord &CVM, +                         EnumeratorRecord &Record) override; +  Error visitKnownMember(CVMemberRecord &CVM, +                         ListContinuationRecord &Record) override; + +  NativeSession &Session; +  const NativeTypeEnum &ClassParent; +  std::vector<EnumeratorRecord> Enumerators; +  Optional<TypeIndex> ContinuationIndex; +  uint32_t Index = 0; +}; +} // namespace + +NativeEnumEnumEnumerators::NativeEnumEnumEnumerators( +    NativeSession &Session, const NativeTypeEnum &ClassParent) +    : Session(Session), ClassParent(ClassParent) { +  TpiStream &Tpi = cantFail(Session.getPDBFile().getPDBTpiStream()); +  LazyRandomTypeCollection &Types = Tpi.typeCollection(); + +  ContinuationIndex = ClassParent.getEnumRecord().FieldList; +  while (ContinuationIndex) { +    CVType FieldList = Types.getType(*ContinuationIndex); +    assert(FieldList.kind() == LF_FIELDLIST); +    ContinuationIndex.reset(); +    cantFail(visitMemberRecordStream(FieldList.data(), *this)); +  } +} + +Error NativeEnumEnumEnumerators::visitKnownMember(CVMemberRecord &CVM, +                                                  EnumeratorRecord &Record) { +  Enumerators.push_back(Record); +  return Error::success(); +} + +Error NativeEnumEnumEnumerators::visitKnownMember( +    CVMemberRecord &CVM, ListContinuationRecord &Record) { +  ContinuationIndex = Record.ContinuationIndex; +  return Error::success(); +} + +uint32_t NativeEnumEnumEnumerators::getChildCount() const { +  return Enumerators.size(); +} + +std::unique_ptr<PDBSymbol> +NativeEnumEnumEnumerators::getChildAtIndex(uint32_t Index) const { +  if (Index >= getChildCount()) +    return nullptr; + +  SymIndexId Id = Session.getSymbolCache() +                      .getOrCreateFieldListMember<NativeSymbolEnumerator>( +                          ClassParent.getEnumRecord().FieldList, Index, +                          ClassParent, Enumerators[Index]); +  return Session.getSymbolCache().getSymbolById(Id); +} + +std::unique_ptr<PDBSymbol> NativeEnumEnumEnumerators::getNext() { +  if (Index >= getChildCount()) +    return nullptr; + +  return getChildAtIndex(Index++); +} + +void NativeEnumEnumEnumerators::reset() { Index = 0; } + +NativeTypeEnum::NativeTypeEnum(NativeSession &Session, SymIndexId Id, +                               TypeIndex Index, EnumRecord Record) +    : NativeRawSymbol(Session, PDB_SymType::Enum, Id), Index(Index), +      Record(std::move(Record)) {} + +NativeTypeEnum::NativeTypeEnum(NativeSession &Session, SymIndexId Id, +                               NativeTypeEnum &UnmodifiedType, +                               codeview::ModifierRecord Modifier) +    : NativeRawSymbol(Session, PDB_SymType::Enum, Id), +      UnmodifiedType(&UnmodifiedType), Modifiers(std::move(Modifier)) {} + +NativeTypeEnum::~NativeTypeEnum() {} + +void NativeTypeEnum::dump(raw_ostream &OS, int Indent, +                          PdbSymbolIdField ShowIdFields, +                          PdbSymbolIdField RecurseIdFields) const { +  NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + +  dumpSymbolField(OS, "baseType", static_cast<uint32_t>(getBuiltinType()), +                  Indent); +  dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session, +                    PdbSymbolIdField::LexicalParent, ShowIdFields, +                    RecurseIdFields); +  dumpSymbolField(OS, "name", getName(), Indent); +  dumpSymbolIdField(OS, "typeId", getTypeId(), Indent, Session, +                    PdbSymbolIdField::Type, ShowIdFields, RecurseIdFields); +  if (Modifiers.hasValue()) +    dumpSymbolIdField(OS, "unmodifiedTypeId", getUnmodifiedTypeId(), Indent, +                      Session, PdbSymbolIdField::UnmodifiedType, ShowIdFields, +                      RecurseIdFields); +  dumpSymbolField(OS, "length", getLength(), Indent); +  dumpSymbolField(OS, "constructor", hasConstructor(), Indent); +  dumpSymbolField(OS, "constType", isConstType(), Indent); +  dumpSymbolField(OS, "hasAssignmentOperator", hasAssignmentOperator(), Indent); +  dumpSymbolField(OS, "hasCastOperator", hasCastOperator(), Indent); +  dumpSymbolField(OS, "hasNestedTypes", hasNestedTypes(), Indent); +  dumpSymbolField(OS, "overloadedOperator", hasOverloadedOperator(), Indent); +  dumpSymbolField(OS, "isInterfaceUdt", isInterfaceUdt(), Indent); +  dumpSymbolField(OS, "intrinsic", isIntrinsic(), Indent); +  dumpSymbolField(OS, "nested", isNested(), Indent); +  dumpSymbolField(OS, "packed", isPacked(), Indent); +  dumpSymbolField(OS, "isRefUdt", isRefUdt(), Indent); +  dumpSymbolField(OS, "scoped", isScoped(), Indent); +  dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent); +  dumpSymbolField(OS, "isValueUdt", isValueUdt(), Indent); +  dumpSymbolField(OS, "volatileType", isVolatileType(), Indent); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeTypeEnum::findChildren(PDB_SymType Type) const { +  if (Type != PDB_SymType::Data) +    return std::make_unique<NullEnumerator<PDBSymbol>>(); + +  const NativeTypeEnum *ClassParent = nullptr; +  if (!Modifiers) +    ClassParent = this; +  else +    ClassParent = UnmodifiedType; +  return std::make_unique<NativeEnumEnumEnumerators>(Session, *ClassParent); +} + +PDB_SymType NativeTypeEnum::getSymTag() const { return PDB_SymType::Enum; } + +PDB_BuiltinType NativeTypeEnum::getBuiltinType() const { +  if (UnmodifiedType) +    return UnmodifiedType->getBuiltinType(); + +  Session.getSymbolCache().findSymbolByTypeIndex(Record->getUnderlyingType()); + +  codeview::TypeIndex Underlying = Record->getUnderlyingType(); + +  // This indicates a corrupt record. +  if (!Underlying.isSimple() || +      Underlying.getSimpleMode() != SimpleTypeMode::Direct) { +    return PDB_BuiltinType::None; +  } + +  switch (Underlying.getSimpleKind()) { +  case SimpleTypeKind::Boolean128: +  case SimpleTypeKind::Boolean64: +  case SimpleTypeKind::Boolean32: +  case SimpleTypeKind::Boolean16: +  case SimpleTypeKind::Boolean8: +    return PDB_BuiltinType::Bool; +  case SimpleTypeKind::NarrowCharacter: +  case SimpleTypeKind::UnsignedCharacter: +  case SimpleTypeKind::SignedCharacter: +    return PDB_BuiltinType::Char; +  case SimpleTypeKind::WideCharacter: +    return PDB_BuiltinType::WCharT; +  case SimpleTypeKind::Character16: +    return PDB_BuiltinType::Char16; +  case SimpleTypeKind::Character32: +    return PDB_BuiltinType::Char32; +  case SimpleTypeKind::Int128: +  case SimpleTypeKind::Int128Oct: +  case SimpleTypeKind::Int16: +  case SimpleTypeKind::Int16Short: +  case SimpleTypeKind::Int32: +  case SimpleTypeKind::Int32Long: +  case SimpleTypeKind::Int64: +  case SimpleTypeKind::Int64Quad: +    return PDB_BuiltinType::Int; +  case SimpleTypeKind::UInt128: +  case SimpleTypeKind::UInt128Oct: +  case SimpleTypeKind::UInt16: +  case SimpleTypeKind::UInt16Short: +  case SimpleTypeKind::UInt32: +  case SimpleTypeKind::UInt32Long: +  case SimpleTypeKind::UInt64: +  case SimpleTypeKind::UInt64Quad: +    return PDB_BuiltinType::UInt; +  case SimpleTypeKind::HResult: +    return PDB_BuiltinType::HResult; +  case SimpleTypeKind::Complex16: +  case SimpleTypeKind::Complex32: +  case SimpleTypeKind::Complex32PartialPrecision: +  case SimpleTypeKind::Complex64: +  case SimpleTypeKind::Complex80: +  case SimpleTypeKind::Complex128: +    return PDB_BuiltinType::Complex; +  case SimpleTypeKind::Float16: +  case SimpleTypeKind::Float32: +  case SimpleTypeKind::Float32PartialPrecision: +  case SimpleTypeKind::Float48: +  case SimpleTypeKind::Float64: +  case SimpleTypeKind::Float80: +  case SimpleTypeKind::Float128: +    return PDB_BuiltinType::Float; +  default: +    return PDB_BuiltinType::None; +  } +  llvm_unreachable("Unreachable"); +} + +SymIndexId NativeTypeEnum::getUnmodifiedTypeId() const { +  return UnmodifiedType ? UnmodifiedType->getSymIndexId() : 0; +} + +bool NativeTypeEnum::hasConstructor() const { +  if (UnmodifiedType) +    return UnmodifiedType->hasConstructor(); + +  return bool(Record->getOptions() & +              codeview::ClassOptions::HasConstructorOrDestructor); +} + +bool NativeTypeEnum::hasAssignmentOperator() const { +  if (UnmodifiedType) +    return UnmodifiedType->hasAssignmentOperator(); + +  return bool(Record->getOptions() & +              codeview::ClassOptions::HasOverloadedAssignmentOperator); +} + +bool NativeTypeEnum::hasNestedTypes() const { +  if (UnmodifiedType) +    return UnmodifiedType->hasNestedTypes(); + +  return bool(Record->getOptions() & +              codeview::ClassOptions::ContainsNestedClass); +} + +bool NativeTypeEnum::isIntrinsic() const { +  if (UnmodifiedType) +    return UnmodifiedType->isIntrinsic(); + +  return bool(Record->getOptions() & codeview::ClassOptions::Intrinsic); +} + +bool NativeTypeEnum::hasCastOperator() const { +  if (UnmodifiedType) +    return UnmodifiedType->hasCastOperator(); + +  return bool(Record->getOptions() & +              codeview::ClassOptions::HasConversionOperator); +} + +uint64_t NativeTypeEnum::getLength() const { +  if (UnmodifiedType) +    return UnmodifiedType->getLength(); + +  const auto Id = Session.getSymbolCache().findSymbolByTypeIndex( +      Record->getUnderlyingType()); +  const auto UnderlyingType = +      Session.getConcreteSymbolById<PDBSymbolTypeBuiltin>(Id); +  return UnderlyingType ? UnderlyingType->getLength() : 0; +} + +std::string NativeTypeEnum::getName() const { +  if (UnmodifiedType) +    return UnmodifiedType->getName(); + +  return Record->getName(); +} + +bool NativeTypeEnum::isNested() const { +  if (UnmodifiedType) +    return UnmodifiedType->isNested(); + +  return bool(Record->getOptions() & codeview::ClassOptions::Nested); +} + +bool NativeTypeEnum::hasOverloadedOperator() const { +  if (UnmodifiedType) +    return UnmodifiedType->hasOverloadedOperator(); + +  return bool(Record->getOptions() & +              codeview::ClassOptions::HasOverloadedOperator); +} + +bool NativeTypeEnum::isPacked() const { +  if (UnmodifiedType) +    return UnmodifiedType->isPacked(); + +  return bool(Record->getOptions() & codeview::ClassOptions::Packed); +} + +bool NativeTypeEnum::isScoped() const { +  if (UnmodifiedType) +    return UnmodifiedType->isScoped(); + +  return bool(Record->getOptions() & codeview::ClassOptions::Scoped); +} + +SymIndexId NativeTypeEnum::getTypeId() const { +  if (UnmodifiedType) +    return UnmodifiedType->getTypeId(); + +  return Session.getSymbolCache().findSymbolByTypeIndex( +      Record->getUnderlyingType()); +} + +bool NativeTypeEnum::isRefUdt() const { return false; } + +bool NativeTypeEnum::isValueUdt() const { return false; } + +bool NativeTypeEnum::isInterfaceUdt() const { return false; } + +bool NativeTypeEnum::isConstType() const { +  if (!Modifiers) +    return false; +  return ((Modifiers->getModifiers() & ModifierOptions::Const) != +          ModifierOptions::None); +} + +bool NativeTypeEnum::isVolatileType() const { +  if (!Modifiers) +    return false; +  return ((Modifiers->getModifiers() & ModifierOptions::Volatile) != +          ModifierOptions::None); +} + +bool NativeTypeEnum::isUnalignedType() const { +  if (!Modifiers) +    return false; +  return ((Modifiers->getModifiers() & ModifierOptions::Unaligned) != +          ModifierOptions::None); +} + +const NativeTypeBuiltin &NativeTypeEnum::getUnderlyingBuiltinType() const { +  if (UnmodifiedType) +    return UnmodifiedType->getUnderlyingBuiltinType(); + +  return Session.getSymbolCache().getNativeSymbolById<NativeTypeBuiltin>( +      getTypeId()); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeFunctionSig.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeFunctionSig.cpp new file mode 100644 index 0000000000000..f98a4c3043eb8 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypeFunctionSig.cpp @@ -0,0 +1,199 @@ +//===- NativeTypeFunctionSig.cpp - info about function signature -*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeTypeFunctionSig.h" + +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +namespace { +// This is kind of a silly class, hence why we keep it private to the file. +// It's only purpose is to wrap the real type record.  I guess this is so that +// we can have the lexical parent point to the function instead of the global +// scope. +class NativeTypeFunctionArg : public NativeRawSymbol { +public: +  NativeTypeFunctionArg(NativeSession &Session, +                        std::unique_ptr<PDBSymbol> RealType) +      : NativeRawSymbol(Session, PDB_SymType::FunctionArg, 0), +        RealType(std::move(RealType)) {} + +  void dump(raw_ostream &OS, int Indent, PdbSymbolIdField ShowIdFields, +            PdbSymbolIdField RecurseIdFields) const override { +    NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + +    dumpSymbolIdField(OS, "typeId", getTypeId(), Indent, Session, +                      PdbSymbolIdField::Type, ShowIdFields, RecurseIdFields); +  } + +  SymIndexId getTypeId() const override { return RealType->getSymIndexId(); } + +  std::unique_ptr<PDBSymbol> RealType; +}; + +class NativeEnumFunctionArgs : public IPDBEnumChildren<PDBSymbol> { +public: +  NativeEnumFunctionArgs(NativeSession &Session, +                         std::unique_ptr<NativeEnumTypes> TypeEnumerator) +      : Session(Session), TypeEnumerator(std::move(TypeEnumerator)) {} + +  uint32_t getChildCount() const override { +    return TypeEnumerator->getChildCount(); +  } +  std::unique_ptr<PDBSymbol> getChildAtIndex(uint32_t Index) const override { +    return wrap(TypeEnumerator->getChildAtIndex(Index)); +  } +  std::unique_ptr<PDBSymbol> getNext() override { +    return wrap(TypeEnumerator->getNext()); +  } + +  void reset() override { TypeEnumerator->reset(); } + +private: +  std::unique_ptr<PDBSymbol> wrap(std::unique_ptr<PDBSymbol> S) const { +    if (!S) +      return nullptr; +    auto NTFA = std::make_unique<NativeTypeFunctionArg>(Session, std::move(S)); +    return PDBSymbol::create(Session, std::move(NTFA)); +  } +  NativeSession &Session; +  std::unique_ptr<NativeEnumTypes> TypeEnumerator; +}; +} // namespace + +NativeTypeFunctionSig::NativeTypeFunctionSig(NativeSession &Session, +                                             SymIndexId Id, +                                             codeview::TypeIndex Index, +                                             codeview::ProcedureRecord Proc) +    : NativeRawSymbol(Session, PDB_SymType::FunctionSig, Id), +      Proc(std::move(Proc)), Index(Index), IsMemberFunction(false) {} + +NativeTypeFunctionSig::NativeTypeFunctionSig( +    NativeSession &Session, SymIndexId Id, codeview::TypeIndex Index, +    codeview::MemberFunctionRecord MemberFunc) +    : NativeRawSymbol(Session, PDB_SymType::FunctionSig, Id), +      MemberFunc(std::move(MemberFunc)), Index(Index), IsMemberFunction(true) {} + +void NativeTypeFunctionSig::initialize() { +  if (IsMemberFunction) { +    ClassParentId = +        Session.getSymbolCache().findSymbolByTypeIndex(MemberFunc.ClassType); +    initializeArgList(MemberFunc.ArgumentList); +  } else { +    initializeArgList(Proc.ArgumentList); +  } +} + +NativeTypeFunctionSig::~NativeTypeFunctionSig() {} + +void NativeTypeFunctionSig::initializeArgList(codeview::TypeIndex ArgListTI) { +  TpiStream &Tpi = cantFail(Session.getPDBFile().getPDBTpiStream()); +  CVType CVT = Tpi.typeCollection().getType(ArgListTI); + +  cantFail(TypeDeserializer::deserializeAs<ArgListRecord>(CVT, ArgList)); +} + +void NativeTypeFunctionSig::dump(raw_ostream &OS, int Indent, +                                 PdbSymbolIdField ShowIdFields, +                                 PdbSymbolIdField RecurseIdFields) const { + +  NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + +  dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session, +                    PdbSymbolIdField::LexicalParent, ShowIdFields, +                    RecurseIdFields); + +  dumpSymbolField(OS, "callingConvention", getCallingConvention(), Indent); +  dumpSymbolField(OS, "count", getCount(), Indent); +  dumpSymbolIdField(OS, "typeId", getTypeId(), Indent, Session, +                    PdbSymbolIdField::Type, ShowIdFields, RecurseIdFields); +  if (IsMemberFunction) +    dumpSymbolField(OS, "thisAdjust", getThisAdjust(), Indent); +  dumpSymbolField(OS, "constructor", hasConstructor(), Indent); +  dumpSymbolField(OS, "constType", isConstType(), Indent); +  dumpSymbolField(OS, "isConstructorVirtualBase", isConstructorVirtualBase(), +                  Indent); +  dumpSymbolField(OS, "isCxxReturnUdt", isCxxReturnUdt(), Indent); +  dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent); +  dumpSymbolField(OS, "volatileType", isVolatileType(), Indent); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeTypeFunctionSig::findChildren(PDB_SymType Type) const { +  if (Type != PDB_SymType::FunctionArg) +    return std::make_unique<NullEnumerator<PDBSymbol>>(); + +  auto NET = std::make_unique<NativeEnumTypes>(Session, +                                                /* copy */ ArgList.ArgIndices); +  return std::unique_ptr<IPDBEnumSymbols>( +      new NativeEnumFunctionArgs(Session, std::move(NET))); +} + +SymIndexId NativeTypeFunctionSig::getClassParentId() const { +  if (!IsMemberFunction) +    return 0; + +  return ClassParentId; +} + +PDB_CallingConv NativeTypeFunctionSig::getCallingConvention() const { +  return IsMemberFunction ? MemberFunc.CallConv : Proc.CallConv; +} + +uint32_t NativeTypeFunctionSig::getCount() const { +  return IsMemberFunction ? (1 + MemberFunc.getParameterCount()) +                          : Proc.getParameterCount(); +} + +SymIndexId NativeTypeFunctionSig::getTypeId() const { +  TypeIndex ReturnTI = +      IsMemberFunction ? MemberFunc.getReturnType() : Proc.getReturnType(); + +  SymIndexId Result = Session.getSymbolCache().findSymbolByTypeIndex(ReturnTI); +  return Result; +} + +int32_t NativeTypeFunctionSig::getThisAdjust() const { +  return IsMemberFunction ? MemberFunc.getThisPointerAdjustment() : 0; +} + +bool NativeTypeFunctionSig::hasConstructor() const { +  if (!IsMemberFunction) +    return false; + +  return (MemberFunc.getOptions() & FunctionOptions::Constructor) != +         FunctionOptions::None; +} + +bool NativeTypeFunctionSig::isConstType() const { return false; } + +bool NativeTypeFunctionSig::isConstructorVirtualBase() const { +  if (!IsMemberFunction) +    return false; + +  return (MemberFunc.getOptions() & +          FunctionOptions::ConstructorWithVirtualBases) != +         FunctionOptions::None; +} + +bool NativeTypeFunctionSig::isCxxReturnUdt() const { +  FunctionOptions Options = +      IsMemberFunction ? MemberFunc.getOptions() : Proc.getOptions(); +  return (Options & FunctionOptions::CxxReturnUdt) != FunctionOptions::None; +} + +bool NativeTypeFunctionSig::isUnalignedType() const { return false; } + +bool NativeTypeFunctionSig::isVolatileType() const { return false; } diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypePointer.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypePointer.cpp new file mode 100644 index 0000000000000..32dcfc2359541 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypePointer.cpp @@ -0,0 +1,193 @@ +//===- NativeTypePointer.cpp - info about pointer type ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeTypePointer.h" + +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" + +#include <cassert> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeTypePointer::NativeTypePointer(NativeSession &Session, SymIndexId Id, +                                     codeview::TypeIndex TI) +    : NativeRawSymbol(Session, PDB_SymType::PointerType, Id), TI(TI) { +  assert(TI.isSimple()); +  assert(TI.getSimpleMode() != SimpleTypeMode::Direct); +} + +NativeTypePointer::NativeTypePointer(NativeSession &Session, SymIndexId Id, +                                     codeview::TypeIndex TI, +                                     codeview::PointerRecord Record) +    : NativeRawSymbol(Session, PDB_SymType::PointerType, Id), TI(TI), +      Record(std::move(Record)) {} + +NativeTypePointer::~NativeTypePointer() {} + +void NativeTypePointer::dump(raw_ostream &OS, int Indent, +                             PdbSymbolIdField ShowIdFields, +                             PdbSymbolIdField RecurseIdFields) const { +  NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + +  if (isMemberPointer()) { +    dumpSymbolIdField(OS, "classParentId", getClassParentId(), Indent, Session, +                      PdbSymbolIdField::ClassParent, ShowIdFields, +                      RecurseIdFields); +  } +  dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session, +                    PdbSymbolIdField::LexicalParent, ShowIdFields, +                    RecurseIdFields); +  dumpSymbolIdField(OS, "typeId", getTypeId(), Indent, Session, +                    PdbSymbolIdField::Type, ShowIdFields, RecurseIdFields); +  dumpSymbolField(OS, "length", getLength(), Indent); +  dumpSymbolField(OS, "constType", isConstType(), Indent); +  dumpSymbolField(OS, "isPointerToDataMember", isPointerToDataMember(), Indent); +  dumpSymbolField(OS, "isPointerToMemberFunction", isPointerToMemberFunction(), +                  Indent); +  dumpSymbolField(OS, "RValueReference", isRValueReference(), Indent); +  dumpSymbolField(OS, "reference", isReference(), Indent); +  dumpSymbolField(OS, "restrictedType", isRestrictedType(), Indent); +  if (isMemberPointer()) { +    if (isSingleInheritance()) +      dumpSymbolField(OS, "isSingleInheritance", 1, Indent); +    else if (isMultipleInheritance()) +      dumpSymbolField(OS, "isMultipleInheritance", 1, Indent); +    else if (isVirtualInheritance()) +      dumpSymbolField(OS, "isVirtualInheritance", 1, Indent); +  } +  dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent); +  dumpSymbolField(OS, "volatileType", isVolatileType(), Indent); +} + +SymIndexId NativeTypePointer::getClassParentId() const { +  if (!isMemberPointer()) +    return 0; + +  assert(Record); +  const MemberPointerInfo &MPI = Record->getMemberInfo(); +  return Session.getSymbolCache().findSymbolByTypeIndex(MPI.ContainingType); +} + +uint64_t NativeTypePointer::getLength() const { +  if (Record) +    return Record->getSize(); + +  switch (TI.getSimpleMode()) { +  case SimpleTypeMode::NearPointer: +  case SimpleTypeMode::FarPointer: +  case SimpleTypeMode::HugePointer: +    return 2; +  case SimpleTypeMode::NearPointer32: +  case SimpleTypeMode::FarPointer32: +    return 4; +  case SimpleTypeMode::NearPointer64: +    return 8; +  case SimpleTypeMode::NearPointer128: +    return 16; +  default: +    assert(false && "invalid simple type mode!"); +  } +  return 0; +} + +SymIndexId NativeTypePointer::getTypeId() const { +  // This is the pointee SymIndexId. +  TypeIndex Referent = Record ? Record->ReferentType : TI.makeDirect(); + +  return Session.getSymbolCache().findSymbolByTypeIndex(Referent); +} + +bool NativeTypePointer::isReference() const { +  if (!Record) +    return false; +  return Record->getMode() == PointerMode::LValueReference; +} + +bool NativeTypePointer::isRValueReference() const { +  if (!Record) +    return false; +  return Record->getMode() == PointerMode::RValueReference; +} + +bool NativeTypePointer::isPointerToDataMember() const { +  if (!Record) +    return false; +  return Record->getMode() == PointerMode::PointerToDataMember; +} + +bool NativeTypePointer::isPointerToMemberFunction() const { +  if (!Record) +    return false; +  return Record->getMode() == PointerMode::PointerToMemberFunction; +} + +bool NativeTypePointer::isConstType() const { +  if (!Record) +    return false; +  return (Record->getOptions() & PointerOptions::Const) != PointerOptions::None; +} + +bool NativeTypePointer::isRestrictedType() const { +  if (!Record) +    return false; +  return (Record->getOptions() & PointerOptions::Restrict) != +         PointerOptions::None; +} + +bool NativeTypePointer::isVolatileType() const { +  if (!Record) +    return false; +  return (Record->getOptions() & PointerOptions::Volatile) != +         PointerOptions::None; +} + +bool NativeTypePointer::isUnalignedType() const { +  if (!Record) +    return false; +  return (Record->getOptions() & PointerOptions::Unaligned) != +         PointerOptions::None; +} + +static inline bool isInheritanceKind(const MemberPointerInfo &MPI, +                                     PointerToMemberRepresentation P1, +                                     PointerToMemberRepresentation P2) { +  return (MPI.getRepresentation() == P1 || MPI.getRepresentation() == P2); +} + +bool NativeTypePointer::isSingleInheritance() const { +  if (!isMemberPointer()) +    return false; +  return isInheritanceKind( +      Record->getMemberInfo(), +      PointerToMemberRepresentation::SingleInheritanceData, +      PointerToMemberRepresentation::SingleInheritanceFunction); +} + +bool NativeTypePointer::isMultipleInheritance() const { +  if (!isMemberPointer()) +    return false; +  return isInheritanceKind( +      Record->getMemberInfo(), +      PointerToMemberRepresentation::MultipleInheritanceData, +      PointerToMemberRepresentation::MultipleInheritanceFunction); +} + +bool NativeTypePointer::isVirtualInheritance() const { +  if (!isMemberPointer()) +    return false; +  return isInheritanceKind( +      Record->getMemberInfo(), +      PointerToMemberRepresentation::VirtualInheritanceData, +      PointerToMemberRepresentation::VirtualInheritanceFunction); +} + +bool NativeTypePointer::isMemberPointer() const { +  return isPointerToDataMember() || isPointerToMemberFunction(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeTypedef.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeTypedef.cpp new file mode 100644 index 0000000000000..60b3732822670 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypeTypedef.cpp @@ -0,0 +1,27 @@ +#include "llvm/DebugInfo/PDB/Native/NativeTypeTypedef.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeTypeTypedef::NativeTypeTypedef(NativeSession &Session, SymIndexId Id, +                                     codeview::UDTSym Typedef) +    : NativeRawSymbol(Session, PDB_SymType::Typedef, Id), +      Record(std::move(Typedef)) {} + +NativeTypeTypedef::~NativeTypeTypedef() {} + +void NativeTypeTypedef::dump(raw_ostream &OS, int Indent, +                             PdbSymbolIdField ShowIdFields, +                             PdbSymbolIdField RecurseIdFields) const { +  NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); +  dumpSymbolField(OS, "name", getName(), Indent); +  dumpSymbolIdField(OS, "typeId", getTypeId(), Indent, Session, +                    PdbSymbolIdField::Type, ShowIdFields, RecurseIdFields); +} + +std::string NativeTypeTypedef::getName() const { return Record.Name; } + +SymIndexId NativeTypeTypedef::getTypeId() const { +  return Session.getSymbolCache().findSymbolByTypeIndex(Record.Type); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeUDT.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeUDT.cpp new file mode 100644 index 0000000000000..be67846c0b246 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypeUDT.cpp @@ -0,0 +1,220 @@ +//===- NativeTypeUDT.cpp - info about class/struct type ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeTypeUDT.h" + +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" + +#include <cassert> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeTypeUDT::NativeTypeUDT(NativeSession &Session, SymIndexId Id, +                             codeview::TypeIndex TI, codeview::ClassRecord CR) +    : NativeRawSymbol(Session, PDB_SymType::UDT, Id), Index(TI), +      Class(std::move(CR)), Tag(Class.getPointer()) {} + +NativeTypeUDT::NativeTypeUDT(NativeSession &Session, SymIndexId Id, +                             codeview::TypeIndex TI, codeview::UnionRecord UR) +    : NativeRawSymbol(Session, PDB_SymType::UDT, Id), Index(TI), +      Union(std::move(UR)), Tag(Union.getPointer()) {} + +NativeTypeUDT::NativeTypeUDT(NativeSession &Session, SymIndexId Id, +                             NativeTypeUDT &UnmodifiedType, +                             codeview::ModifierRecord Modifier) +    : NativeRawSymbol(Session, PDB_SymType::UDT, Id), +      UnmodifiedType(&UnmodifiedType), Modifiers(std::move(Modifier)) {} + +NativeTypeUDT::~NativeTypeUDT() {} + +void NativeTypeUDT::dump(raw_ostream &OS, int Indent, +                         PdbSymbolIdField ShowIdFields, +                         PdbSymbolIdField RecurseIdFields) const { + +  NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + +  dumpSymbolField(OS, "name", getName(), Indent); +  dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session, +                    PdbSymbolIdField::LexicalParent, ShowIdFields, +                    RecurseIdFields); +  if (Modifiers.hasValue()) +    dumpSymbolIdField(OS, "unmodifiedTypeId", getUnmodifiedTypeId(), Indent, +                      Session, PdbSymbolIdField::UnmodifiedType, ShowIdFields, +                      RecurseIdFields); +  if (getUdtKind() != PDB_UdtType::Union) +    dumpSymbolField(OS, "virtualTableShapeId", getVirtualTableShapeId(), +                    Indent); +  dumpSymbolField(OS, "length", getLength(), Indent); +  dumpSymbolField(OS, "udtKind", getUdtKind(), Indent); +  dumpSymbolField(OS, "constructor", hasConstructor(), Indent); +  dumpSymbolField(OS, "constType", isConstType(), Indent); +  dumpSymbolField(OS, "hasAssignmentOperator", hasAssignmentOperator(), Indent); +  dumpSymbolField(OS, "hasCastOperator", hasCastOperator(), Indent); +  dumpSymbolField(OS, "hasNestedTypes", hasNestedTypes(), Indent); +  dumpSymbolField(OS, "overloadedOperator", hasOverloadedOperator(), Indent); +  dumpSymbolField(OS, "isInterfaceUdt", isInterfaceUdt(), Indent); +  dumpSymbolField(OS, "intrinsic", isIntrinsic(), Indent); +  dumpSymbolField(OS, "nested", isNested(), Indent); +  dumpSymbolField(OS, "packed", isPacked(), Indent); +  dumpSymbolField(OS, "isRefUdt", isRefUdt(), Indent); +  dumpSymbolField(OS, "scoped", isScoped(), Indent); +  dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent); +  dumpSymbolField(OS, "isValueUdt", isValueUdt(), Indent); +  dumpSymbolField(OS, "volatileType", isVolatileType(), Indent); +} + +std::string NativeTypeUDT::getName() const { +  if (UnmodifiedType) +    return UnmodifiedType->getName(); + +  return Tag->getName(); +} + +SymIndexId NativeTypeUDT::getLexicalParentId() const { return 0; } + +SymIndexId NativeTypeUDT::getUnmodifiedTypeId() const { +  if (UnmodifiedType) +    return UnmodifiedType->getSymIndexId(); + +  return 0; +} + +SymIndexId NativeTypeUDT::getVirtualTableShapeId() const { +  if (UnmodifiedType) +    return UnmodifiedType->getVirtualTableShapeId(); + +  if (Class) +    return Session.getSymbolCache().findSymbolByTypeIndex(Class->VTableShape); + +  return 0; +} + +uint64_t NativeTypeUDT::getLength() const { +  if (UnmodifiedType) +    return UnmodifiedType->getLength(); + +  if (Class) +    return Class->getSize(); + +  return Union->getSize(); +} + +PDB_UdtType NativeTypeUDT::getUdtKind() const { +  if (UnmodifiedType) +    return UnmodifiedType->getUdtKind(); + +  switch (Tag->Kind) { +  case TypeRecordKind::Class: +    return PDB_UdtType::Class; +  case TypeRecordKind::Union: +    return PDB_UdtType::Union; +  case TypeRecordKind::Struct: +    return PDB_UdtType::Struct; +  case TypeRecordKind::Interface: +    return PDB_UdtType::Interface; +  default: +    llvm_unreachable("Unexected udt kind"); +  } +} + +bool NativeTypeUDT::hasConstructor() const { +  if (UnmodifiedType) +    return UnmodifiedType->hasConstructor(); + +  return (Tag->Options & ClassOptions::HasConstructorOrDestructor) != +         ClassOptions::None; +} + +bool NativeTypeUDT::isConstType() const { +  if (!Modifiers) +    return false; +  return (Modifiers->Modifiers & ModifierOptions::Const) != +         ModifierOptions::None; +} + +bool NativeTypeUDT::hasAssignmentOperator() const { +  if (UnmodifiedType) +    return UnmodifiedType->hasAssignmentOperator(); + +  return (Tag->Options & ClassOptions::HasOverloadedAssignmentOperator) != +         ClassOptions::None; +} + +bool NativeTypeUDT::hasCastOperator() const { +  if (UnmodifiedType) +    return UnmodifiedType->hasCastOperator(); + +  return (Tag->Options & ClassOptions::HasConversionOperator) != +         ClassOptions::None; +} + +bool NativeTypeUDT::hasNestedTypes() const { +  if (UnmodifiedType) +    return UnmodifiedType->hasNestedTypes(); + +  return (Tag->Options & ClassOptions::ContainsNestedClass) != +         ClassOptions::None; +} + +bool NativeTypeUDT::hasOverloadedOperator() const { +  if (UnmodifiedType) +    return UnmodifiedType->hasOverloadedOperator(); + +  return (Tag->Options & ClassOptions::HasOverloadedOperator) != +         ClassOptions::None; +} + +bool NativeTypeUDT::isInterfaceUdt() const { return false; } + +bool NativeTypeUDT::isIntrinsic() const { +  if (UnmodifiedType) +    return UnmodifiedType->isIntrinsic(); + +  return (Tag->Options & ClassOptions::Intrinsic) != ClassOptions::None; +} + +bool NativeTypeUDT::isNested() const { +  if (UnmodifiedType) +    return UnmodifiedType->isNested(); + +  return (Tag->Options & ClassOptions::Nested) != ClassOptions::None; +} + +bool NativeTypeUDT::isPacked() const { +  if (UnmodifiedType) +    return UnmodifiedType->isPacked(); + +  return (Tag->Options & ClassOptions::Packed) != ClassOptions::None; +} + +bool NativeTypeUDT::isRefUdt() const { return false; } + +bool NativeTypeUDT::isScoped() const { +  if (UnmodifiedType) +    return UnmodifiedType->isScoped(); + +  return (Tag->Options & ClassOptions::Scoped) != ClassOptions::None; +} + +bool NativeTypeUDT::isValueUdt() const { return false; } + +bool NativeTypeUDT::isUnalignedType() const { +  if (!Modifiers) +    return false; +  return (Modifiers->Modifiers & ModifierOptions::Unaligned) != +         ModifierOptions::None; +} + +bool NativeTypeUDT::isVolatileType() const { +  if (!Modifiers) +    return false; +  return (Modifiers->Modifiers & ModifierOptions::Volatile) != +         ModifierOptions::None; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeVTShape.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeVTShape.cpp new file mode 100644 index 0000000000000..837fe19ec88c0 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypeVTShape.cpp @@ -0,0 +1,35 @@ +#include "llvm/DebugInfo/PDB/Native/NativeTypeVTShape.h" + +using namespace llvm; +using namespace llvm::pdb; + +// Create a pointer record for a non-simple type. +NativeTypeVTShape::NativeTypeVTShape(NativeSession &Session, SymIndexId Id, +                                     codeview::TypeIndex TI, +                                     codeview::VFTableShapeRecord SR) +    : NativeRawSymbol(Session, PDB_SymType::VTableShape, Id), TI(TI), +      Record(std::move(SR)) {} + +NativeTypeVTShape::~NativeTypeVTShape() {} + +void NativeTypeVTShape::dump(raw_ostream &OS, int Indent, +                             PdbSymbolIdField ShowIdFields, +                             PdbSymbolIdField RecurseIdFields) const { +  NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + +  dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session, +                    PdbSymbolIdField::LexicalParent, ShowIdFields, +                    RecurseIdFields); +  dumpSymbolField(OS, "count", getCount(), Indent); +  dumpSymbolField(OS, "constType", isConstType(), Indent); +  dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent); +  dumpSymbolField(OS, "volatileType", isVolatileType(), Indent); +} + +bool NativeTypeVTShape::isConstType() const { return false; } + +bool NativeTypeVTShape::isVolatileType() const { return false; } + +bool NativeTypeVTShape::isUnalignedType() const { return false; } + +uint32_t NativeTypeVTShape::getCount() const { return Record.Slots.size(); } diff --git a/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp b/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp new file mode 100644 index 0000000000000..9ac226b89139b --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp @@ -0,0 +1,507 @@ +//===- PDBFile.cpp - Low level interface to a PDB file ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/InjectedSourceStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Support/BinaryStream.h" +#include "llvm/Support/BinaryStreamArray.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Path.h" +#include <algorithm> +#include <cassert> +#include <cstdint> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +namespace { +typedef FixedStreamArray<support::ulittle32_t> ulittle_array; +} // end anonymous namespace + +PDBFile::PDBFile(StringRef Path, std::unique_ptr<BinaryStream> PdbFileBuffer, +                 BumpPtrAllocator &Allocator) +    : FilePath(Path), Allocator(Allocator), Buffer(std::move(PdbFileBuffer)) {} + +PDBFile::~PDBFile() = default; + +StringRef PDBFile::getFilePath() const { return FilePath; } + +StringRef PDBFile::getFileDirectory() const { +  return sys::path::parent_path(FilePath); +} + +uint32_t PDBFile::getBlockSize() const { return ContainerLayout.SB->BlockSize; } + +uint32_t PDBFile::getFreeBlockMapBlock() const { +  return ContainerLayout.SB->FreeBlockMapBlock; +} + +uint32_t PDBFile::getBlockCount() const { +  return ContainerLayout.SB->NumBlocks; +} + +uint32_t PDBFile::getNumDirectoryBytes() const { +  return ContainerLayout.SB->NumDirectoryBytes; +} + +uint32_t PDBFile::getBlockMapIndex() const { +  return ContainerLayout.SB->BlockMapAddr; +} + +uint32_t PDBFile::getUnknown1() const { return ContainerLayout.SB->Unknown1; } + +uint32_t PDBFile::getNumDirectoryBlocks() const { +  return msf::bytesToBlocks(ContainerLayout.SB->NumDirectoryBytes, +                            ContainerLayout.SB->BlockSize); +} + +uint64_t PDBFile::getBlockMapOffset() const { +  return (uint64_t)ContainerLayout.SB->BlockMapAddr * +         ContainerLayout.SB->BlockSize; +} + +uint32_t PDBFile::getNumStreams() const { +  return ContainerLayout.StreamSizes.size(); +} + +uint32_t PDBFile::getMaxStreamSize() const { +  return *std::max_element(ContainerLayout.StreamSizes.begin(), +                           ContainerLayout.StreamSizes.end()); +} + +uint32_t PDBFile::getStreamByteSize(uint32_t StreamIndex) const { +  return ContainerLayout.StreamSizes[StreamIndex]; +} + +ArrayRef<support::ulittle32_t> +PDBFile::getStreamBlockList(uint32_t StreamIndex) const { +  return ContainerLayout.StreamMap[StreamIndex]; +} + +uint32_t PDBFile::getFileSize() const { return Buffer->getLength(); } + +Expected<ArrayRef<uint8_t>> PDBFile::getBlockData(uint32_t BlockIndex, +                                                  uint32_t NumBytes) const { +  uint64_t StreamBlockOffset = msf::blockToOffset(BlockIndex, getBlockSize()); + +  ArrayRef<uint8_t> Result; +  if (auto EC = Buffer->readBytes(StreamBlockOffset, NumBytes, Result)) +    return std::move(EC); +  return Result; +} + +Error PDBFile::setBlockData(uint32_t BlockIndex, uint32_t Offset, +                            ArrayRef<uint8_t> Data) const { +  return make_error<RawError>(raw_error_code::not_writable, +                              "PDBFile is immutable"); +} + +Error PDBFile::parseFileHeaders() { +  BinaryStreamReader Reader(*Buffer); + +  // Initialize SB. +  const msf::SuperBlock *SB = nullptr; +  if (auto EC = Reader.readObject(SB)) { +    consumeError(std::move(EC)); +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "MSF superblock is missing"); +  } + +  if (auto EC = msf::validateSuperBlock(*SB)) +    return EC; + +  if (Buffer->getLength() % SB->BlockSize != 0) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "File size is not a multiple of block size"); +  ContainerLayout.SB = SB; + +  // Initialize Free Page Map. +  ContainerLayout.FreePageMap.resize(SB->NumBlocks); +  // The Fpm exists either at block 1 or block 2 of the MSF.  However, this +  // allows for a maximum of getBlockSize() * 8 blocks bits in the Fpm, and +  // thusly an equal number of total blocks in the file.  For a block size +  // of 4KiB (very common), this would yield 32KiB total blocks in file, for a +  // maximum file size of 32KiB * 4KiB = 128MiB.  Obviously this won't do, so +  // the Fpm is split across the file at `getBlockSize()` intervals.  As a +  // result, every block whose index is of the form |{1,2} + getBlockSize() * k| +  // for any non-negative integer k is an Fpm block.  In theory, we only really +  // need to reserve blocks of the form |{1,2} + getBlockSize() * 8 * k|, but +  // current versions of the MSF format already expect the Fpm to be arranged +  // at getBlockSize() intervals, so we have to be compatible. +  // See the function fpmPn() for more information: +  // https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/msf/msf.cpp#L489 +  auto FpmStream = +      MappedBlockStream::createFpmStream(ContainerLayout, *Buffer, Allocator); +  BinaryStreamReader FpmReader(*FpmStream); +  ArrayRef<uint8_t> FpmBytes; +  if (auto EC = FpmReader.readBytes(FpmBytes, FpmReader.bytesRemaining())) +    return EC; +  uint32_t BlocksRemaining = getBlockCount(); +  uint32_t BI = 0; +  for (auto Byte : FpmBytes) { +    uint32_t BlocksThisByte = std::min(BlocksRemaining, 8U); +    for (uint32_t I = 0; I < BlocksThisByte; ++I) { +      if (Byte & (1 << I)) +        ContainerLayout.FreePageMap[BI] = true; +      --BlocksRemaining; +      ++BI; +    } +  } + +  Reader.setOffset(getBlockMapOffset()); +  if (auto EC = Reader.readArray(ContainerLayout.DirectoryBlocks, +                                 getNumDirectoryBlocks())) +    return EC; + +  return Error::success(); +} + +Error PDBFile::parseStreamData() { +  assert(ContainerLayout.SB); +  if (DirectoryStream) +    return Error::success(); + +  uint32_t NumStreams = 0; + +  // Normally you can't use a MappedBlockStream without having fully parsed the +  // PDB file, because it accesses the directory and various other things, which +  // is exactly what we are attempting to parse.  By specifying a custom +  // subclass of IPDBStreamData which only accesses the fields that have already +  // been parsed, we can avoid this and reuse MappedBlockStream. +  auto DS = MappedBlockStream::createDirectoryStream(ContainerLayout, *Buffer, +                                                     Allocator); +  BinaryStreamReader Reader(*DS); +  if (auto EC = Reader.readInteger(NumStreams)) +    return EC; + +  if (auto EC = Reader.readArray(ContainerLayout.StreamSizes, NumStreams)) +    return EC; +  for (uint32_t I = 0; I < NumStreams; ++I) { +    uint32_t StreamSize = getStreamByteSize(I); +    // FIXME: What does StreamSize ~0U mean? +    uint64_t NumExpectedStreamBlocks = +        StreamSize == UINT32_MAX +            ? 0 +            : msf::bytesToBlocks(StreamSize, ContainerLayout.SB->BlockSize); + +    // For convenience, we store the block array contiguously.  This is because +    // if someone calls setStreamMap(), it is more convenient to be able to call +    // it with an ArrayRef instead of setting up a StreamRef.  Since the +    // DirectoryStream is cached in the class and thus lives for the life of the +    // class, we can be guaranteed that readArray() will return a stable +    // reference, even if it has to allocate from its internal pool. +    ArrayRef<support::ulittle32_t> Blocks; +    if (auto EC = Reader.readArray(Blocks, NumExpectedStreamBlocks)) +      return EC; +    for (uint32_t Block : Blocks) { +      uint64_t BlockEndOffset = +          (uint64_t)(Block + 1) * ContainerLayout.SB->BlockSize; +      if (BlockEndOffset > getFileSize()) +        return make_error<RawError>(raw_error_code::corrupt_file, +                                    "Stream block map is corrupt."); +    } +    ContainerLayout.StreamMap.push_back(Blocks); +  } + +  // We should have read exactly SB->NumDirectoryBytes bytes. +  assert(Reader.bytesRemaining() == 0); +  DirectoryStream = std::move(DS); +  return Error::success(); +} + +ArrayRef<support::ulittle32_t> PDBFile::getDirectoryBlockArray() const { +  return ContainerLayout.DirectoryBlocks; +} + +std::unique_ptr<MappedBlockStream> +PDBFile::createIndexedStream(uint16_t SN) const { +  if (SN == kInvalidStreamIndex) +    return nullptr; +  return MappedBlockStream::createIndexedStream(ContainerLayout, *Buffer, SN, +                                                Allocator); +} + +MSFStreamLayout PDBFile::getStreamLayout(uint32_t StreamIdx) const { +  MSFStreamLayout Result; +  auto Blocks = getStreamBlockList(StreamIdx); +  Result.Blocks.assign(Blocks.begin(), Blocks.end()); +  Result.Length = getStreamByteSize(StreamIdx); +  return Result; +} + +msf::MSFStreamLayout PDBFile::getFpmStreamLayout() const { +  return msf::getFpmStreamLayout(ContainerLayout); +} + +Expected<GlobalsStream &> PDBFile::getPDBGlobalsStream() { +  if (!Globals) { +    auto DbiS = getPDBDbiStream(); +    if (!DbiS) +      return DbiS.takeError(); + +    auto GlobalS = +        safelyCreateIndexedStream(DbiS->getGlobalSymbolStreamIndex()); +    if (!GlobalS) +      return GlobalS.takeError(); +    auto TempGlobals = std::make_unique<GlobalsStream>(std::move(*GlobalS)); +    if (auto EC = TempGlobals->reload()) +      return std::move(EC); +    Globals = std::move(TempGlobals); +  } +  return *Globals; +} + +Expected<InfoStream &> PDBFile::getPDBInfoStream() { +  if (!Info) { +    auto InfoS = safelyCreateIndexedStream(StreamPDB); +    if (!InfoS) +      return InfoS.takeError(); +    auto TempInfo = std::make_unique<InfoStream>(std::move(*InfoS)); +    if (auto EC = TempInfo->reload()) +      return std::move(EC); +    Info = std::move(TempInfo); +  } +  return *Info; +} + +Expected<DbiStream &> PDBFile::getPDBDbiStream() { +  if (!Dbi) { +    auto DbiS = safelyCreateIndexedStream(StreamDBI); +    if (!DbiS) +      return DbiS.takeError(); +    auto TempDbi = std::make_unique<DbiStream>(std::move(*DbiS)); +    if (auto EC = TempDbi->reload(this)) +      return std::move(EC); +    Dbi = std::move(TempDbi); +  } +  return *Dbi; +} + +Expected<TpiStream &> PDBFile::getPDBTpiStream() { +  if (!Tpi) { +    auto TpiS = safelyCreateIndexedStream(StreamTPI); +    if (!TpiS) +      return TpiS.takeError(); +    auto TempTpi = std::make_unique<TpiStream>(*this, std::move(*TpiS)); +    if (auto EC = TempTpi->reload()) +      return std::move(EC); +    Tpi = std::move(TempTpi); +  } +  return *Tpi; +} + +Expected<TpiStream &> PDBFile::getPDBIpiStream() { +  if (!Ipi) { +    if (!hasPDBIpiStream()) +      return make_error<RawError>(raw_error_code::no_stream); + +    auto IpiS = safelyCreateIndexedStream(StreamIPI); +    if (!IpiS) +      return IpiS.takeError(); +    auto TempIpi = std::make_unique<TpiStream>(*this, std::move(*IpiS)); +    if (auto EC = TempIpi->reload()) +      return std::move(EC); +    Ipi = std::move(TempIpi); +  } +  return *Ipi; +} + +Expected<PublicsStream &> PDBFile::getPDBPublicsStream() { +  if (!Publics) { +    auto DbiS = getPDBDbiStream(); +    if (!DbiS) +      return DbiS.takeError(); + +    auto PublicS = +        safelyCreateIndexedStream(DbiS->getPublicSymbolStreamIndex()); +    if (!PublicS) +      return PublicS.takeError(); +    auto TempPublics = std::make_unique<PublicsStream>(std::move(*PublicS)); +    if (auto EC = TempPublics->reload()) +      return std::move(EC); +    Publics = std::move(TempPublics); +  } +  return *Publics; +} + +Expected<SymbolStream &> PDBFile::getPDBSymbolStream() { +  if (!Symbols) { +    auto DbiS = getPDBDbiStream(); +    if (!DbiS) +      return DbiS.takeError(); + +    uint32_t SymbolStreamNum = DbiS->getSymRecordStreamIndex(); +    auto SymbolS = safelyCreateIndexedStream(SymbolStreamNum); +    if (!SymbolS) +      return SymbolS.takeError(); + +    auto TempSymbols = std::make_unique<SymbolStream>(std::move(*SymbolS)); +    if (auto EC = TempSymbols->reload()) +      return std::move(EC); +    Symbols = std::move(TempSymbols); +  } +  return *Symbols; +} + +Expected<PDBStringTable &> PDBFile::getStringTable() { +  if (!Strings) { +    auto NS = safelyCreateNamedStream("/names"); +    if (!NS) +      return NS.takeError(); + +    auto N = std::make_unique<PDBStringTable>(); +    BinaryStreamReader Reader(**NS); +    if (auto EC = N->reload(Reader)) +      return std::move(EC); +    assert(Reader.bytesRemaining() == 0); +    StringTableStream = std::move(*NS); +    Strings = std::move(N); +  } +  return *Strings; +} + +Expected<InjectedSourceStream &> PDBFile::getInjectedSourceStream() { +  if (!InjectedSources) { +    auto IJS = safelyCreateNamedStream("/src/headerblock"); +    if (!IJS) +      return IJS.takeError(); + +    auto Strings = getStringTable(); +    if (!Strings) +      return Strings.takeError(); + +    auto IJ = std::make_unique<InjectedSourceStream>(std::move(*IJS)); +    if (auto EC = IJ->reload(*Strings)) +      return std::move(EC); +    InjectedSources = std::move(IJ); +  } +  return *InjectedSources; +} + +uint32_t PDBFile::getPointerSize() { +  auto DbiS = getPDBDbiStream(); +  if (!DbiS) +    return 0; +  PDB_Machine Machine = DbiS->getMachineType(); +  if (Machine == PDB_Machine::Amd64) +    return 8; +  return 4; +} + +bool PDBFile::hasPDBDbiStream() const { +  return StreamDBI < getNumStreams() && getStreamByteSize(StreamDBI) > 0; +} + +bool PDBFile::hasPDBGlobalsStream() { +  auto DbiS = getPDBDbiStream(); +  if (!DbiS) { +    consumeError(DbiS.takeError()); +    return false; +  } + +  return DbiS->getGlobalSymbolStreamIndex() < getNumStreams(); +} + +bool PDBFile::hasPDBInfoStream() const { return StreamPDB < getNumStreams(); } + +bool PDBFile::hasPDBIpiStream() const { +  if (!hasPDBInfoStream()) +    return false; + +  if (StreamIPI >= getNumStreams()) +    return false; + +  auto &InfoStream = cantFail(const_cast<PDBFile *>(this)->getPDBInfoStream()); +  return InfoStream.containsIdStream(); +} + +bool PDBFile::hasPDBPublicsStream() { +  auto DbiS = getPDBDbiStream(); +  if (!DbiS) { +    consumeError(DbiS.takeError()); +    return false; +  } +  return DbiS->getPublicSymbolStreamIndex() < getNumStreams(); +} + +bool PDBFile::hasPDBSymbolStream() { +  auto DbiS = getPDBDbiStream(); +  if (!DbiS) +    return false; +  return DbiS->getSymRecordStreamIndex() < getNumStreams(); +} + +bool PDBFile::hasPDBTpiStream() const { return StreamTPI < getNumStreams(); } + +bool PDBFile::hasPDBStringTable() { +  auto IS = getPDBInfoStream(); +  if (!IS) +    return false; +  Expected<uint32_t> ExpectedNSI = IS->getNamedStreamIndex("/names"); +  if (!ExpectedNSI) { +    consumeError(ExpectedNSI.takeError()); +    return false; +  } +  assert(*ExpectedNSI < getNumStreams()); +  return true; +} + +bool PDBFile::hasPDBInjectedSourceStream() { +  auto IS = getPDBInfoStream(); +  if (!IS) +    return false; +  Expected<uint32_t> ExpectedNSI = IS->getNamedStreamIndex("/src/headerblock"); +  if (!ExpectedNSI) { +    consumeError(ExpectedNSI.takeError()); +    return false; +  } +  assert(*ExpectedNSI < getNumStreams()); +  return true; +} + +/// Wrapper around MappedBlockStream::createIndexedStream() that checks if a +/// stream with that index actually exists.  If it does not, the return value +/// will have an MSFError with code msf_error_code::no_stream.  Else, the return +/// value will contain the stream returned by createIndexedStream(). +Expected<std::unique_ptr<MappedBlockStream>> +PDBFile::safelyCreateIndexedStream(uint32_t StreamIndex) const { +  if (StreamIndex >= getNumStreams()) +    // This rejects kInvalidStreamIndex with an error as well. +    return make_error<RawError>(raw_error_code::no_stream); +  return createIndexedStream(StreamIndex); +} + +Expected<std::unique_ptr<MappedBlockStream>> +PDBFile::safelyCreateNamedStream(StringRef Name) { +  auto IS = getPDBInfoStream(); +  if (!IS) +    return IS.takeError(); + +  Expected<uint32_t> ExpectedNSI = IS->getNamedStreamIndex(Name); +  if (!ExpectedNSI) +    return ExpectedNSI.takeError(); +  uint32_t NameStreamIndex = *ExpectedNSI; + +  return safelyCreateIndexedStream(NameStreamIndex); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp new file mode 100644 index 0000000000000..aa32887243900 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp @@ -0,0 +1,357 @@ +//===- PDBFileBuilder.cpp - PDB File Creation -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" + +#include "llvm/ADT/BitVector.h" + +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" +#include "llvm/Support/BinaryStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/xxhash.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; +using namespace llvm::support; + +PDBFileBuilder::PDBFileBuilder(BumpPtrAllocator &Allocator) +    : Allocator(Allocator), InjectedSourceHashTraits(Strings), +      InjectedSourceTable(2) {} + +PDBFileBuilder::~PDBFileBuilder() {} + +Error PDBFileBuilder::initialize(uint32_t BlockSize) { +  auto ExpectedMsf = MSFBuilder::create(Allocator, BlockSize); +  if (!ExpectedMsf) +    return ExpectedMsf.takeError(); +  Msf = std::make_unique<MSFBuilder>(std::move(*ExpectedMsf)); +  return Error::success(); +} + +MSFBuilder &PDBFileBuilder::getMsfBuilder() { return *Msf; } + +InfoStreamBuilder &PDBFileBuilder::getInfoBuilder() { +  if (!Info) +    Info = std::make_unique<InfoStreamBuilder>(*Msf, NamedStreams); +  return *Info; +} + +DbiStreamBuilder &PDBFileBuilder::getDbiBuilder() { +  if (!Dbi) +    Dbi = std::make_unique<DbiStreamBuilder>(*Msf); +  return *Dbi; +} + +TpiStreamBuilder &PDBFileBuilder::getTpiBuilder() { +  if (!Tpi) +    Tpi = std::make_unique<TpiStreamBuilder>(*Msf, StreamTPI); +  return *Tpi; +} + +TpiStreamBuilder &PDBFileBuilder::getIpiBuilder() { +  if (!Ipi) +    Ipi = std::make_unique<TpiStreamBuilder>(*Msf, StreamIPI); +  return *Ipi; +} + +PDBStringTableBuilder &PDBFileBuilder::getStringTableBuilder() { +  return Strings; +} + +GSIStreamBuilder &PDBFileBuilder::getGsiBuilder() { +  if (!Gsi) +    Gsi = std::make_unique<GSIStreamBuilder>(*Msf); +  return *Gsi; +} + +Expected<uint32_t> PDBFileBuilder::allocateNamedStream(StringRef Name, +                                                       uint32_t Size) { +  auto ExpectedStream = Msf->addStream(Size); +  if (ExpectedStream) +    NamedStreams.set(Name, *ExpectedStream); +  return ExpectedStream; +} + +Error PDBFileBuilder::addNamedStream(StringRef Name, StringRef Data) { +  Expected<uint32_t> ExpectedIndex = allocateNamedStream(Name, Data.size()); +  if (!ExpectedIndex) +    return ExpectedIndex.takeError(); +  assert(NamedStreamData.count(*ExpectedIndex) == 0); +  NamedStreamData[*ExpectedIndex] = Data; +  return Error::success(); +} + +void PDBFileBuilder::addInjectedSource(StringRef Name, +                                       std::unique_ptr<MemoryBuffer> Buffer) { +  // Stream names must be exact matches, since they get looked up in a hash +  // table and the hash value is dependent on the exact contents of the string. +  // link.exe lowercases a path and converts / to \, so we must do the same. +  SmallString<64> VName; +  sys::path::native(Name.lower(), VName); + +  uint32_t NI = getStringTableBuilder().insert(Name); +  uint32_t VNI = getStringTableBuilder().insert(VName); + +  InjectedSourceDescriptor Desc; +  Desc.Content = std::move(Buffer); +  Desc.NameIndex = NI; +  Desc.VNameIndex = VNI; +  Desc.StreamName = "/src/files/"; + +  Desc.StreamName += VName; + +  InjectedSources.push_back(std::move(Desc)); +} + +Error PDBFileBuilder::finalizeMsfLayout() { + +  if (Ipi && Ipi->getRecordCount() > 0) { +    // In theory newer PDBs always have an ID stream, but by saying that we're +    // only going to *really* have an ID stream if there is at least one ID +    // record, we leave open the opportunity to test older PDBs such as those +    // that don't have an ID stream. +    auto &Info = getInfoBuilder(); +    Info.addFeature(PdbRaw_FeatureSig::VC140); +  } + +  uint32_t StringsLen = Strings.calculateSerializedSize(); + +  Expected<uint32_t> SN = allocateNamedStream("/LinkInfo", 0); +  if (!SN) +    return SN.takeError(); + +  if (Gsi) { +    if (auto EC = Gsi->finalizeMsfLayout()) +      return EC; +    if (Dbi) { +      Dbi->setPublicsStreamIndex(Gsi->getPublicsStreamIndex()); +      Dbi->setGlobalsStreamIndex(Gsi->getGlobalsStreamIndex()); +      Dbi->setSymbolRecordStreamIndex(Gsi->getRecordStreamIdx()); +    } +  } +  if (Tpi) { +    if (auto EC = Tpi->finalizeMsfLayout()) +      return EC; +  } +  if (Dbi) { +    if (auto EC = Dbi->finalizeMsfLayout()) +      return EC; +  } +  SN = allocateNamedStream("/names", StringsLen); +  if (!SN) +    return SN.takeError(); + +  if (Ipi) { +    if (auto EC = Ipi->finalizeMsfLayout()) +      return EC; +  } + +  // Do this last, since it relies on the named stream map being complete, and +  // that can be updated by previous steps in the finalization. +  if (Info) { +    if (auto EC = Info->finalizeMsfLayout()) +      return EC; +  } + +  if (!InjectedSources.empty()) { +    for (const auto &IS : InjectedSources) { +      JamCRC CRC(0); +      CRC.update(arrayRefFromStringRef(IS.Content->getBuffer())); + +      SrcHeaderBlockEntry Entry; +      ::memset(&Entry, 0, sizeof(SrcHeaderBlockEntry)); +      Entry.Size = sizeof(SrcHeaderBlockEntry); +      Entry.FileSize = IS.Content->getBufferSize(); +      Entry.FileNI = IS.NameIndex; +      Entry.VFileNI = IS.VNameIndex; +      Entry.ObjNI = 1; +      Entry.IsVirtual = 0; +      Entry.Version = +          static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne); +      Entry.CRC = CRC.getCRC(); +      StringRef VName = getStringTableBuilder().getStringForId(IS.VNameIndex); +      InjectedSourceTable.set_as(VName, std::move(Entry), +                                 InjectedSourceHashTraits); +    } + +    uint32_t SrcHeaderBlockSize = +        sizeof(SrcHeaderBlockHeader) + +        InjectedSourceTable.calculateSerializedLength(); +    SN = allocateNamedStream("/src/headerblock", SrcHeaderBlockSize); +    if (!SN) +      return SN.takeError(); +    for (const auto &IS : InjectedSources) { +      SN = allocateNamedStream(IS.StreamName, IS.Content->getBufferSize()); +      if (!SN) +        return SN.takeError(); +    } +  } + +  // Do this last, since it relies on the named stream map being complete, and +  // that can be updated by previous steps in the finalization. +  if (Info) { +    if (auto EC = Info->finalizeMsfLayout()) +      return EC; +  } + +  return Error::success(); +} + +Expected<uint32_t> PDBFileBuilder::getNamedStreamIndex(StringRef Name) const { +  uint32_t SN = 0; +  if (!NamedStreams.get(Name, SN)) +    return llvm::make_error<pdb::RawError>(raw_error_code::no_stream); +  return SN; +} + +void PDBFileBuilder::commitSrcHeaderBlock(WritableBinaryStream &MsfBuffer, +                                          const msf::MSFLayout &Layout) { +  assert(!InjectedSourceTable.empty()); + +  uint32_t SN = cantFail(getNamedStreamIndex("/src/headerblock")); +  auto Stream = WritableMappedBlockStream::createIndexedStream( +      Layout, MsfBuffer, SN, Allocator); +  BinaryStreamWriter Writer(*Stream); + +  SrcHeaderBlockHeader Header; +  ::memset(&Header, 0, sizeof(Header)); +  Header.Version = static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne); +  Header.Size = Writer.bytesRemaining(); + +  cantFail(Writer.writeObject(Header)); +  cantFail(InjectedSourceTable.commit(Writer)); + +  assert(Writer.bytesRemaining() == 0); +} + +void PDBFileBuilder::commitInjectedSources(WritableBinaryStream &MsfBuffer, +                                           const msf::MSFLayout &Layout) { +  if (InjectedSourceTable.empty()) +    return; + +  commitSrcHeaderBlock(MsfBuffer, Layout); + +  for (const auto &IS : InjectedSources) { +    uint32_t SN = cantFail(getNamedStreamIndex(IS.StreamName)); + +    auto SourceStream = WritableMappedBlockStream::createIndexedStream( +        Layout, MsfBuffer, SN, Allocator); +    BinaryStreamWriter SourceWriter(*SourceStream); +    assert(SourceWriter.bytesRemaining() == IS.Content->getBufferSize()); +    cantFail(SourceWriter.writeBytes( +        arrayRefFromStringRef(IS.Content->getBuffer()))); +  } +} + +Error PDBFileBuilder::commit(StringRef Filename, codeview::GUID *Guid) { +  assert(!Filename.empty()); +  if (auto EC = finalizeMsfLayout()) +    return EC; + +  MSFLayout Layout; +  Expected<FileBufferByteStream> ExpectedMsfBuffer = +      Msf->commit(Filename, Layout); +  if (!ExpectedMsfBuffer) +    return ExpectedMsfBuffer.takeError(); +  FileBufferByteStream Buffer = std::move(*ExpectedMsfBuffer); + +  auto ExpectedSN = getNamedStreamIndex("/names"); +  if (!ExpectedSN) +    return ExpectedSN.takeError(); + +  auto NS = WritableMappedBlockStream::createIndexedStream( +      Layout, Buffer, *ExpectedSN, Allocator); +  BinaryStreamWriter NSWriter(*NS); +  if (auto EC = Strings.commit(NSWriter)) +    return EC; + +  for (const auto &NSE : NamedStreamData) { +    if (NSE.second.empty()) +      continue; + +    auto NS = WritableMappedBlockStream::createIndexedStream( +        Layout, Buffer, NSE.first, Allocator); +    BinaryStreamWriter NSW(*NS); +    if (auto EC = NSW.writeBytes(arrayRefFromStringRef(NSE.second))) +      return EC; +  } + +  if (Info) { +    if (auto EC = Info->commit(Layout, Buffer)) +      return EC; +  } + +  if (Dbi) { +    if (auto EC = Dbi->commit(Layout, Buffer)) +      return EC; +  } + +  if (Tpi) { +    if (auto EC = Tpi->commit(Layout, Buffer)) +      return EC; +  } + +  if (Ipi) { +    if (auto EC = Ipi->commit(Layout, Buffer)) +      return EC; +  } + +  if (Gsi) { +    if (auto EC = Gsi->commit(Layout, Buffer)) +      return EC; +  } + +  auto InfoStreamBlocks = Layout.StreamMap[StreamPDB]; +  assert(!InfoStreamBlocks.empty()); +  uint64_t InfoStreamFileOffset = +      blockToOffset(InfoStreamBlocks.front(), Layout.SB->BlockSize); +  InfoStreamHeader *H = reinterpret_cast<InfoStreamHeader *>( +      Buffer.getBufferStart() + InfoStreamFileOffset); + +  commitInjectedSources(Buffer, Layout); + +  // Set the build id at the very end, after every other byte of the PDB +  // has been written. +  if (Info->hashPDBContentsToGUID()) { +    // Compute a hash of all sections of the output file. +    uint64_t Digest = +        xxHash64({Buffer.getBufferStart(), Buffer.getBufferEnd()}); + +    H->Age = 1; + +    memcpy(H->Guid.Guid, &Digest, 8); +    // xxhash only gives us 8 bytes, so put some fixed data in the other half. +    memcpy(H->Guid.Guid + 8, "LLD PDB.", 8); + +    // Put the hash in the Signature field too. +    H->Signature = static_cast<uint32_t>(Digest); + +    // Return GUID to caller. +    memcpy(Guid, H->Guid.Guid, 16); +  } else { +    H->Age = Info->getAge(); +    H->Guid = Info->getGuid(); +    Optional<uint32_t> Sig = Info->getSignature(); +    H->Signature = Sig.hasValue() ? *Sig : time(nullptr); +  } + +  return Buffer.commit(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/PDBStringTable.cpp b/llvm/lib/DebugInfo/PDB/Native/PDBStringTable.cpp new file mode 100644 index 0000000000000..2be1656e06bbd --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/PDBStringTable.cpp @@ -0,0 +1,140 @@ +//===- PDBStringTable.cpp - PDB String Table ---------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::support; +using namespace llvm::pdb; + +uint32_t PDBStringTable::getByteSize() const { return Header->ByteSize; } +uint32_t PDBStringTable::getNameCount() const { return NameCount; } +uint32_t PDBStringTable::getHashVersion() const { return Header->HashVersion; } +uint32_t PDBStringTable::getSignature() const { return Header->Signature; } + +Error PDBStringTable::readHeader(BinaryStreamReader &Reader) { +  if (auto EC = Reader.readObject(Header)) +    return EC; + +  if (Header->Signature != PDBStringTableSignature) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Invalid hash table signature"); +  if (Header->HashVersion != 1 && Header->HashVersion != 2) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Unsupported hash version"); + +  assert(Reader.bytesRemaining() == 0); +  return Error::success(); +} + +Error PDBStringTable::readStrings(BinaryStreamReader &Reader) { +  BinaryStreamRef Stream; +  if (auto EC = Reader.readStreamRef(Stream)) +    return EC; + +  if (auto EC = Strings.initialize(Stream)) { +    return joinErrors(std::move(EC), +                      make_error<RawError>(raw_error_code::corrupt_file, +                                           "Invalid hash table byte length")); +  } + +  assert(Reader.bytesRemaining() == 0); +  return Error::success(); +} + +const codeview::DebugStringTableSubsectionRef & +PDBStringTable::getStringTable() const { +  return Strings; +} + +Error PDBStringTable::readHashTable(BinaryStreamReader &Reader) { +  const support::ulittle32_t *HashCount; +  if (auto EC = Reader.readObject(HashCount)) +    return EC; + +  if (auto EC = Reader.readArray(IDs, *HashCount)) { +    return joinErrors(std::move(EC), +                      make_error<RawError>(raw_error_code::corrupt_file, +                                           "Could not read bucket array")); +  } + +  return Error::success(); +} + +Error PDBStringTable::readEpilogue(BinaryStreamReader &Reader) { +  if (auto EC = Reader.readInteger(NameCount)) +    return EC; + +  assert(Reader.bytesRemaining() == 0); +  return Error::success(); +} + +Error PDBStringTable::reload(BinaryStreamReader &Reader) { + +  BinaryStreamReader SectionReader; + +  std::tie(SectionReader, Reader) = Reader.split(sizeof(PDBStringTableHeader)); +  if (auto EC = readHeader(SectionReader)) +    return EC; + +  std::tie(SectionReader, Reader) = Reader.split(Header->ByteSize); +  if (auto EC = readStrings(SectionReader)) +    return EC; + +  // We don't know how long the hash table is until we parse it, so let the +  // function responsible for doing that figure it out. +  if (auto EC = readHashTable(Reader)) +    return EC; + +  std::tie(SectionReader, Reader) = Reader.split(sizeof(uint32_t)); +  if (auto EC = readEpilogue(SectionReader)) +    return EC; + +  assert(Reader.bytesRemaining() == 0); +  return Error::success(); +} + +Expected<StringRef> PDBStringTable::getStringForID(uint32_t ID) const { +  return Strings.getString(ID); +} + +Expected<uint32_t> PDBStringTable::getIDForString(StringRef Str) const { +  uint32_t Hash = +      (Header->HashVersion == 1) ? hashStringV1(Str) : hashStringV2(Str); +  size_t Count = IDs.size(); +  uint32_t Start = Hash % Count; +  for (size_t I = 0; I < Count; ++I) { +    // The hash is just a starting point for the search, but if it +    // doesn't work we should find the string no matter what, because +    // we iterate the entire array. +    uint32_t Index = (Start + I) % Count; + +    // If we find 0, it means the item isn't in the hash table. +    uint32_t ID = IDs[Index]; +    if (ID == 0) +      return make_error<RawError>(raw_error_code::no_entry); +    auto ExpectedStr = getStringForID(ID); +    if (!ExpectedStr) +      return ExpectedStr.takeError(); + +    if (*ExpectedStr == Str) +      return ID; +  } +  return make_error<RawError>(raw_error_code::no_entry); +} + +FixedStreamArray<support::ulittle32_t> PDBStringTable::name_ids() const { +  return IDs; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp new file mode 100644 index 0000000000000..f7f36901e4d49 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp @@ -0,0 +1,229 @@ +//===- PDBStringTableBuilder.cpp - PDB String Table -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" + +#include <map> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; +using namespace llvm::support::endian; +using namespace llvm::pdb; + +StringTableHashTraits::StringTableHashTraits(PDBStringTableBuilder &Table) +    : Table(&Table) {} + +uint32_t StringTableHashTraits::hashLookupKey(StringRef S) const { +  // The reference implementation doesn't include code for /src/headerblock +  // handling, but it can only read natvis entries lld's PDB files if +  // this hash function truncates the hash to 16 bit. +  // PDB/include/misc.h in the reference implementation has a hashSz() function +  // that returns an unsigned short, that seems what's being used for +  // /src/headerblock. +  return static_cast<uint16_t>(Table->getIdForString(S)); +} + +StringRef StringTableHashTraits::storageKeyToLookupKey(uint32_t Offset) const { +  return Table->getStringForId(Offset); +} + +uint32_t StringTableHashTraits::lookupKeyToStorageKey(StringRef S) { +  return Table->insert(S); +} + +uint32_t PDBStringTableBuilder::insert(StringRef S) { +  return Strings.insert(S); +} + +uint32_t PDBStringTableBuilder::getIdForString(StringRef S) const { +  return Strings.getIdForString(S); +} + +StringRef PDBStringTableBuilder::getStringForId(uint32_t Id) const { +  return Strings.getStringForId(Id); +} + +static uint32_t computeBucketCount(uint32_t NumStrings) { +  // This is a precomputed list of Buckets given the specified number of +  // strings.  Matching the reference algorithm exactly is not strictly +  // necessary for correctness, but it helps when comparing LLD's PDBs with +  // Microsoft's PDBs so as to eliminate superfluous differences. +  // The reference implementation does (in nmt.h, NMT::grow()): +  //   unsigned StringCount = 0; +  //   unsigned BucketCount = 1; +  //   fn insert() { +  //     ++StringCount; +  //     if (BucketCount * 3 / 4 < StringCount) +  //       BucketCount = BucketCount * 3 / 2 + 1; +  //   } +  // This list contains all StringCount, BucketCount pairs where BucketCount was +  // just incremented.  It ends before the first BucketCount entry where +  // BucketCount * 3 would overflow a 32-bit unsigned int. +  static std::map<uint32_t, uint32_t> StringsToBuckets = { +      {0, 1}, +      {1, 2}, +      {2, 4}, +      {4, 7}, +      {6, 11}, +      {9, 17}, +      {13, 26}, +      {20, 40}, +      {31, 61}, +      {46, 92}, +      {70, 139}, +      {105, 209}, +      {157, 314}, +      {236, 472}, +      {355, 709}, +      {532, 1064}, +      {799, 1597}, +      {1198, 2396}, +      {1798, 3595}, +      {2697, 5393}, +      {4045, 8090}, +      {6068, 12136}, +      {9103, 18205}, +      {13654, 27308}, +      {20482, 40963}, +      {30723, 61445}, +      {46084, 92168}, +      {69127, 138253}, +      {103690, 207380}, +      {155536, 311071}, +      {233304, 466607}, +      {349956, 699911}, +      {524934, 1049867}, +      {787401, 1574801}, +      {1181101, 2362202}, +      {1771652, 3543304}, +      {2657479, 5314957}, +      {3986218, 7972436}, +      {5979328, 11958655}, +      {8968992, 17937983}, +      {13453488, 26906975}, +      {20180232, 40360463}, +      {30270348, 60540695}, +      {45405522, 90811043}, +      {68108283, 136216565}, +      {102162424, 204324848}, +      {153243637, 306487273}, +      {229865455, 459730910}, +      {344798183, 689596366}, +      {517197275, 1034394550}, +      {775795913, 1551591826}, +      {1163693870, 2327387740}}; +  auto Entry = StringsToBuckets.lower_bound(NumStrings); +  assert(Entry != StringsToBuckets.end()); +  return Entry->second; +} + +uint32_t PDBStringTableBuilder::calculateHashTableSize() const { +  uint32_t Size = sizeof(uint32_t); // Hash table begins with 4-byte size field. +  Size += sizeof(uint32_t) * computeBucketCount(Strings.size()); + +  return Size; +} + +uint32_t PDBStringTableBuilder::calculateSerializedSize() const { +  uint32_t Size = 0; +  Size += sizeof(PDBStringTableHeader); +  Size += Strings.calculateSerializedSize(); +  Size += calculateHashTableSize(); +  Size += sizeof(uint32_t); // The /names stream ends with the string count. +  return Size; +} + +void PDBStringTableBuilder::setStrings( +    const codeview::DebugStringTableSubsection &Strings) { +  this->Strings = Strings; +} + +Error PDBStringTableBuilder::writeHeader(BinaryStreamWriter &Writer) const { +  // Write a header +  PDBStringTableHeader H; +  H.Signature = PDBStringTableSignature; +  H.HashVersion = 1; +  H.ByteSize = Strings.calculateSerializedSize(); +  if (auto EC = Writer.writeObject(H)) +    return EC; +  assert(Writer.bytesRemaining() == 0); +  return Error::success(); +} + +Error PDBStringTableBuilder::writeStrings(BinaryStreamWriter &Writer) const { +  if (auto EC = Strings.commit(Writer)) +    return EC; + +  assert(Writer.bytesRemaining() == 0); +  return Error::success(); +} + +Error PDBStringTableBuilder::writeHashTable(BinaryStreamWriter &Writer) const { +  // Write a hash table. +  uint32_t BucketCount = computeBucketCount(Strings.size()); +  if (auto EC = Writer.writeInteger(BucketCount)) +    return EC; +  std::vector<ulittle32_t> Buckets(BucketCount); + +  for (auto &Pair : Strings) { +    StringRef S = Pair.getKey(); +    uint32_t Offset = Pair.getValue(); +    uint32_t Hash = hashStringV1(S); + +    for (uint32_t I = 0; I != BucketCount; ++I) { +      uint32_t Slot = (Hash + I) % BucketCount; +      if (Buckets[Slot] != 0) +        continue; +      Buckets[Slot] = Offset; +      break; +    } +  } + +  if (auto EC = Writer.writeArray(ArrayRef<ulittle32_t>(Buckets))) +    return EC; + +  assert(Writer.bytesRemaining() == 0); +  return Error::success(); +} + +Error PDBStringTableBuilder::writeEpilogue(BinaryStreamWriter &Writer) const { +  if (auto EC = Writer.writeInteger<uint32_t>(Strings.size())) +    return EC; +  assert(Writer.bytesRemaining() == 0); +  return Error::success(); +} + +Error PDBStringTableBuilder::commit(BinaryStreamWriter &Writer) const { +  BinaryStreamWriter SectionWriter; + +  std::tie(SectionWriter, Writer) = Writer.split(sizeof(PDBStringTableHeader)); +  if (auto EC = writeHeader(SectionWriter)) +    return EC; + +  std::tie(SectionWriter, Writer) = +      Writer.split(Strings.calculateSerializedSize()); +  if (auto EC = writeStrings(SectionWriter)) +    return EC; + +  std::tie(SectionWriter, Writer) = Writer.split(calculateHashTableSize()); +  if (auto EC = writeHashTable(SectionWriter)) +    return EC; + +  std::tie(SectionWriter, Writer) = Writer.split(sizeof(uint32_t)); +  if (auto EC = writeEpilogue(SectionWriter)) +    return EC; + +  return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/PublicsStream.cpp b/llvm/lib/DebugInfo/PDB/Native/PublicsStream.cpp new file mode 100644 index 0000000000000..a33bf03bf8fb3 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/PublicsStream.cpp @@ -0,0 +1,101 @@ +//===- PublicsStream.cpp - PDB Public Symbol Stream -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// The data structures defined in this file are based on the reference +// implementation which is available at +// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h +// +// When you are reading the reference source code, you'd find the +// information below useful. +// +//  - ppdb1->m_fMinimalDbgInfo seems to be always true. +//  - SMALLBUCKETS macro is defined. +// +// The reference doesn't compile, so I learned just by reading code. +// It's not guaranteed to be correct. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cstdint> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; +using namespace llvm::pdb; + +PublicsStream::PublicsStream(std::unique_ptr<MappedBlockStream> Stream) +    : Stream(std::move(Stream)) {} + +PublicsStream::~PublicsStream() = default; + +uint32_t PublicsStream::getSymHash() const { return Header->SymHash; } +uint16_t PublicsStream::getThunkTableSection() const { +  return Header->ISectThunkTable; +} +uint32_t PublicsStream::getThunkTableOffset() const { +  return Header->OffThunkTable; +} + +// Publics stream contains fixed-size headers and a serialized hash table. +// This implementation is not complete yet. It reads till the end of the +// stream so that we verify the stream is at least not corrupted. However, +// we skip over the hash table which we believe contains information about +// public symbols. +Error PublicsStream::reload() { +  BinaryStreamReader Reader(*Stream); + +  // Check stream size. +  if (Reader.bytesRemaining() < +      sizeof(PublicsStreamHeader) + sizeof(GSIHashHeader)) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Publics Stream does not contain a header."); + +  // Read PSGSIHDR struct. +  if (Reader.readObject(Header)) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Publics Stream does not contain a header."); + +  // Read the hash table. +  if (auto E = PublicsTable.read(Reader)) +    return E; + +  // Something called "address map" follows. +  uint32_t NumAddressMapEntries = Header->AddrMap / sizeof(uint32_t); +  if (auto EC = Reader.readArray(AddressMap, NumAddressMapEntries)) +    return joinErrors(std::move(EC), +                      make_error<RawError>(raw_error_code::corrupt_file, +                                           "Could not read an address map.")); + +  // Something called "thunk map" follows. +  if (auto EC = Reader.readArray(ThunkMap, Header->NumThunks)) +    return joinErrors(std::move(EC), +                      make_error<RawError>(raw_error_code::corrupt_file, +                                           "Could not read a thunk map.")); + +  // Something called "section map" follows. +  if (Reader.bytesRemaining() > 0) { +    if (auto EC = Reader.readArray(SectionOffsets, Header->NumSections)) +      return joinErrors(std::move(EC), +                        make_error<RawError>(raw_error_code::corrupt_file, +                                             "Could not read a section map.")); +  } + +  if (Reader.bytesRemaining() > 0) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Corrupted publics stream."); +  return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/RawError.cpp b/llvm/lib/DebugInfo/PDB/Native/RawError.cpp new file mode 100644 index 0000000000000..ed6cf08396755 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/RawError.cpp @@ -0,0 +1,53 @@ +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" + +using namespace llvm; +using namespace llvm::pdb; + +namespace { +// FIXME: This class is only here to support the transition to llvm::Error. It +// will be removed once this transition is complete. Clients should prefer to +// deal with the Error value directly, rather than converting to error_code. +class RawErrorCategory : public std::error_category { +public: +  const char *name() const noexcept override { return "llvm.pdb.raw"; } +  std::string message(int Condition) const override { +    switch (static_cast<raw_error_code>(Condition)) { +    case raw_error_code::unspecified: +      return "An unknown error has occurred."; +    case raw_error_code::feature_unsupported: +      return "The feature is unsupported by the implementation."; +    case raw_error_code::invalid_format: +      return "The record is in an unexpected format."; +    case raw_error_code::corrupt_file: +      return "The PDB file is corrupt."; +    case raw_error_code::insufficient_buffer: +      return "The buffer is not large enough to read the requested number of " +             "bytes."; +    case raw_error_code::no_stream: +      return "The specified stream could not be loaded."; +    case raw_error_code::index_out_of_bounds: +      return "The specified item does not exist in the array."; +    case raw_error_code::invalid_block_address: +      return "The specified block address is not valid."; +    case raw_error_code::duplicate_entry: +      return "The entry already exists."; +    case raw_error_code::no_entry: +      return "The entry does not exist."; +    case raw_error_code::not_writable: +      return "The PDB does not support writing."; +    case raw_error_code::stream_too_long: +      return "The stream was longer than expected."; +    case raw_error_code::invalid_tpi_hash: +      return "The Type record has an invalid hash value."; +    } +    llvm_unreachable("Unrecognized raw_error_code"); +  } +}; +} // namespace + +static llvm::ManagedStatic<RawErrorCategory> RawCategory; +const std::error_category &llvm::pdb::RawErrCategory() { return *RawCategory; } + +char RawError::ID; diff --git a/llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp b/llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp new file mode 100644 index 0000000000000..5cdd628312fe5 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp @@ -0,0 +1,299 @@ +#include "llvm/DebugInfo/PDB/Native/SymbolCache.h" + +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumGlobals.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h" +#include "llvm/DebugInfo/PDB/Native/NativeRawSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeArray.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypePointer.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeTypedef.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeUDT.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeVTShape.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +// Maps codeview::SimpleTypeKind of a built-in type to the parameters necessary +// to instantiate a NativeBuiltinSymbol for that type. +static const struct BuiltinTypeEntry { +  codeview::SimpleTypeKind Kind; +  PDB_BuiltinType Type; +  uint32_t Size; +} BuiltinTypes[] = { +    {codeview::SimpleTypeKind::None, PDB_BuiltinType::None, 0}, +    {codeview::SimpleTypeKind::Void, PDB_BuiltinType::Void, 0}, +    {codeview::SimpleTypeKind::HResult, PDB_BuiltinType::HResult, 4}, +    {codeview::SimpleTypeKind::Int16Short, PDB_BuiltinType::Int, 2}, +    {codeview::SimpleTypeKind::UInt16Short, PDB_BuiltinType::UInt, 2}, +    {codeview::SimpleTypeKind::Int32, PDB_BuiltinType::Int, 4}, +    {codeview::SimpleTypeKind::UInt32, PDB_BuiltinType::UInt, 4}, +    {codeview::SimpleTypeKind::Int32Long, PDB_BuiltinType::Int, 4}, +    {codeview::SimpleTypeKind::UInt32Long, PDB_BuiltinType::UInt, 4}, +    {codeview::SimpleTypeKind::Int64Quad, PDB_BuiltinType::Int, 8}, +    {codeview::SimpleTypeKind::UInt64Quad, PDB_BuiltinType::UInt, 8}, +    {codeview::SimpleTypeKind::NarrowCharacter, PDB_BuiltinType::Char, 1}, +    {codeview::SimpleTypeKind::WideCharacter, PDB_BuiltinType::WCharT, 2}, +    {codeview::SimpleTypeKind::Character16, PDB_BuiltinType::Char16, 2}, +    {codeview::SimpleTypeKind::Character32, PDB_BuiltinType::Char32, 4}, +    {codeview::SimpleTypeKind::SignedCharacter, PDB_BuiltinType::Char, 1}, +    {codeview::SimpleTypeKind::UnsignedCharacter, PDB_BuiltinType::UInt, 1}, +    {codeview::SimpleTypeKind::Float32, PDB_BuiltinType::Float, 4}, +    {codeview::SimpleTypeKind::Float64, PDB_BuiltinType::Float, 8}, +    {codeview::SimpleTypeKind::Float80, PDB_BuiltinType::Float, 10}, +    {codeview::SimpleTypeKind::Boolean8, PDB_BuiltinType::Bool, 1}, +    // This table can be grown as necessary, but these are the only types we've +    // needed so far. +}; + +SymbolCache::SymbolCache(NativeSession &Session, DbiStream *Dbi) +    : Session(Session), Dbi(Dbi) { +  // Id 0 is reserved for the invalid symbol. +  Cache.push_back(nullptr); + +  if (Dbi) +    Compilands.resize(Dbi->modules().getModuleCount()); +} + +std::unique_ptr<IPDBEnumSymbols> +SymbolCache::createTypeEnumerator(TypeLeafKind Kind) { +  return createTypeEnumerator(std::vector<TypeLeafKind>{Kind}); +} + +std::unique_ptr<IPDBEnumSymbols> +SymbolCache::createTypeEnumerator(std::vector<TypeLeafKind> Kinds) { +  auto Tpi = Session.getPDBFile().getPDBTpiStream(); +  if (!Tpi) { +    consumeError(Tpi.takeError()); +    return nullptr; +  } +  auto &Types = Tpi->typeCollection(); +  return std::unique_ptr<IPDBEnumSymbols>( +      new NativeEnumTypes(Session, Types, std::move(Kinds))); +} + +std::unique_ptr<IPDBEnumSymbols> +SymbolCache::createGlobalsEnumerator(codeview::SymbolKind Kind) { +  return std::unique_ptr<IPDBEnumSymbols>( +      new NativeEnumGlobals(Session, {Kind})); +} + +SymIndexId SymbolCache::createSimpleType(TypeIndex Index, +                                         ModifierOptions Mods) { +  if (Index.getSimpleMode() != codeview::SimpleTypeMode::Direct) +    return createSymbol<NativeTypePointer>(Index); + +  const auto Kind = Index.getSimpleKind(); +  const auto It = std::find_if( +      std::begin(BuiltinTypes), std::end(BuiltinTypes), +      [Kind](const BuiltinTypeEntry &Builtin) { return Builtin.Kind == Kind; }); +  if (It == std::end(BuiltinTypes)) +    return 0; +  return createSymbol<NativeTypeBuiltin>(Mods, It->Type, It->Size); +} + +SymIndexId +SymbolCache::createSymbolForModifiedType(codeview::TypeIndex ModifierTI, +                                         codeview::CVType CVT) { +  ModifierRecord Record; +  if (auto EC = TypeDeserializer::deserializeAs<ModifierRecord>(CVT, Record)) { +    consumeError(std::move(EC)); +    return 0; +  } + +  if (Record.ModifiedType.isSimple()) +    return createSimpleType(Record.ModifiedType, Record.Modifiers); + +  // Make sure we create and cache a record for the unmodified type. +  SymIndexId UnmodifiedId = findSymbolByTypeIndex(Record.ModifiedType); +  NativeRawSymbol &UnmodifiedNRS = *Cache[UnmodifiedId]; + +  switch (UnmodifiedNRS.getSymTag()) { +  case PDB_SymType::Enum: +    return createSymbol<NativeTypeEnum>( +        static_cast<NativeTypeEnum &>(UnmodifiedNRS), std::move(Record)); +  case PDB_SymType::UDT: +    return createSymbol<NativeTypeUDT>( +        static_cast<NativeTypeUDT &>(UnmodifiedNRS), std::move(Record)); +  default: +    // No other types can be modified.  (LF_POINTER, for example, records +    // its modifiers a different way. +    assert(false && "Invalid LF_MODIFIER record"); +    break; +  } +  return 0; +} + +SymIndexId SymbolCache::findSymbolByTypeIndex(codeview::TypeIndex Index) { +  // First see if it's already in our cache. +  const auto Entry = TypeIndexToSymbolId.find(Index); +  if (Entry != TypeIndexToSymbolId.end()) +    return Entry->second; + +  // Symbols for built-in types are created on the fly. +  if (Index.isSimple()) { +    SymIndexId Result = createSimpleType(Index, ModifierOptions::None); +    assert(TypeIndexToSymbolId.count(Index) == 0); +    TypeIndexToSymbolId[Index] = Result; +    return Result; +  } + +  // We need to instantiate and cache the desired type symbol. +  auto Tpi = Session.getPDBFile().getPDBTpiStream(); +  if (!Tpi) { +    consumeError(Tpi.takeError()); +    return 0; +  } +  codeview::LazyRandomTypeCollection &Types = Tpi->typeCollection(); +  codeview::CVType CVT = Types.getType(Index); + +  if (isUdtForwardRef(CVT)) { +    Expected<TypeIndex> EFD = Tpi->findFullDeclForForwardRef(Index); + +    if (!EFD) +      consumeError(EFD.takeError()); +    else if (*EFD != Index) { +      assert(!isUdtForwardRef(Types.getType(*EFD))); +      SymIndexId Result = findSymbolByTypeIndex(*EFD); +      // Record a mapping from ForwardRef -> SymIndex of complete type so that +      // we'll take the fast path next time. +      assert(TypeIndexToSymbolId.count(Index) == 0); +      TypeIndexToSymbolId[Index] = Result; +      return Result; +    } +  } + +  // At this point if we still have a forward ref udt it means the full decl was +  // not in the PDB.  We just have to deal with it and use the forward ref. +  SymIndexId Id = 0; +  switch (CVT.kind()) { +  case codeview::LF_ENUM: +    Id = createSymbolForType<NativeTypeEnum, EnumRecord>(Index, std::move(CVT)); +    break; +  case codeview::LF_ARRAY: +    Id = createSymbolForType<NativeTypeArray, ArrayRecord>(Index, +                                                           std::move(CVT)); +    break; +  case codeview::LF_CLASS: +  case codeview::LF_STRUCTURE: +  case codeview::LF_INTERFACE: +    Id = createSymbolForType<NativeTypeUDT, ClassRecord>(Index, std::move(CVT)); +    break; +  case codeview::LF_UNION: +    Id = createSymbolForType<NativeTypeUDT, UnionRecord>(Index, std::move(CVT)); +    break; +  case codeview::LF_POINTER: +    Id = createSymbolForType<NativeTypePointer, PointerRecord>(Index, +                                                               std::move(CVT)); +    break; +  case codeview::LF_MODIFIER: +    Id = createSymbolForModifiedType(Index, std::move(CVT)); +    break; +  case codeview::LF_PROCEDURE: +    Id = createSymbolForType<NativeTypeFunctionSig, ProcedureRecord>( +        Index, std::move(CVT)); +    break; +  case codeview::LF_MFUNCTION: +    Id = createSymbolForType<NativeTypeFunctionSig, MemberFunctionRecord>( +        Index, std::move(CVT)); +    break; +  case codeview::LF_VTSHAPE: +    Id = createSymbolForType<NativeTypeVTShape, VFTableShapeRecord>( +        Index, std::move(CVT)); +    break; +  default: +    Id = createSymbolPlaceholder(); +    break; +  } +  if (Id != 0) { +    assert(TypeIndexToSymbolId.count(Index) == 0); +    TypeIndexToSymbolId[Index] = Id; +  } +  return Id; +} + +std::unique_ptr<PDBSymbol> +SymbolCache::getSymbolById(SymIndexId SymbolId) const { +  assert(SymbolId < Cache.size()); + +  // Id 0 is reserved. +  if (SymbolId == 0 || SymbolId >= Cache.size()) +    return nullptr; + +  // Make sure to handle the case where we've inserted a placeholder symbol +  // for types we don't yet suppport. +  NativeRawSymbol *NRS = Cache[SymbolId].get(); +  if (!NRS) +    return nullptr; + +  return PDBSymbol::create(Session, *NRS); +} + +NativeRawSymbol &SymbolCache::getNativeSymbolById(SymIndexId SymbolId) const { +  return *Cache[SymbolId]; +} + +uint32_t SymbolCache::getNumCompilands() const { +  if (!Dbi) +    return 0; + +  return Dbi->modules().getModuleCount(); +} + +SymIndexId SymbolCache::getOrCreateGlobalSymbolByOffset(uint32_t Offset) { +  auto Iter = GlobalOffsetToSymbolId.find(Offset); +  if (Iter != GlobalOffsetToSymbolId.end()) +    return Iter->second; + +  SymbolStream &SS = cantFail(Session.getPDBFile().getPDBSymbolStream()); +  CVSymbol CVS = SS.readRecord(Offset); +  SymIndexId Id = 0; +  switch (CVS.kind()) { +  case SymbolKind::S_UDT: { +    UDTSym US = cantFail(SymbolDeserializer::deserializeAs<UDTSym>(CVS)); +    Id = createSymbol<NativeTypeTypedef>(std::move(US)); +    break; +  } +  default: +    Id = createSymbolPlaceholder(); +    break; +  } +  if (Id != 0) { +    assert(GlobalOffsetToSymbolId.count(Offset) == 0); +    GlobalOffsetToSymbolId[Offset] = Id; +  } + +  return Id; +} + +std::unique_ptr<PDBSymbolCompiland> +SymbolCache::getOrCreateCompiland(uint32_t Index) { +  if (!Dbi) +    return nullptr; + +  if (Index >= Compilands.size()) +    return nullptr; + +  if (Compilands[Index] == 0) { +    const DbiModuleList &Modules = Dbi->modules(); +    Compilands[Index] = +        createSymbol<NativeCompilandSymbol>(Modules.getModuleDescriptor(Index)); +  } + +  return Session.getConcreteSymbolById<PDBSymbolCompiland>(Compilands[Index]); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/SymbolStream.cpp b/llvm/lib/DebugInfo/PDB/Native/SymbolStream.cpp new file mode 100644 index 0000000000000..003840b6e67e8 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/SymbolStream.cpp @@ -0,0 +1,45 @@ +//===- SymbolStream.cpp - PDB Symbol Stream Access ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" + +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; +using namespace llvm::pdb; + +SymbolStream::SymbolStream(std::unique_ptr<MappedBlockStream> Stream) +    : Stream(std::move(Stream)) {} + +SymbolStream::~SymbolStream() {} + +Error SymbolStream::reload() { +  BinaryStreamReader Reader(*Stream); + +  if (auto EC = Reader.readArray(SymbolRecords, Stream->getLength())) +    return EC; + +  return Error::success(); +} + +iterator_range<codeview::CVSymbolArray::Iterator> +SymbolStream::getSymbols(bool *HadError) const { +  return llvm::make_range(SymbolRecords.begin(HadError), SymbolRecords.end()); +} + +Error SymbolStream::commit() { return Error::success(); } + +codeview::CVSymbol SymbolStream::readRecord(uint32_t Offset) const { +  return *SymbolRecords.at(Offset); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp new file mode 100644 index 0000000000000..b71b2b1581441 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp @@ -0,0 +1,129 @@ +//===- TpiHashing.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" + +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/Support/CRC.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +// Corresponds to `fUDTAnon`. +static bool isAnonymous(StringRef Name) { +  return Name == "<unnamed-tag>" || Name == "__unnamed" || +         Name.endswith("::<unnamed-tag>") || Name.endswith("::__unnamed"); +} + +// Computes the hash for a user-defined type record. This could be a struct, +// class, union, or enum. +static uint32_t getHashForUdt(const TagRecord &Rec, +                              ArrayRef<uint8_t> FullRecord) { +  ClassOptions Opts = Rec.getOptions(); +  bool ForwardRef = bool(Opts & ClassOptions::ForwardReference); +  bool Scoped = bool(Opts & ClassOptions::Scoped); +  bool HasUniqueName = bool(Opts & ClassOptions::HasUniqueName); +  bool IsAnon = HasUniqueName && isAnonymous(Rec.getName()); + +  if (!ForwardRef && !Scoped && !IsAnon) +    return hashStringV1(Rec.getName()); +  if (!ForwardRef && HasUniqueName && !IsAnon) +    return hashStringV1(Rec.getUniqueName()); +  return hashBufferV8(FullRecord); +} + +template <typename T> +static Expected<uint32_t> getHashForUdt(const CVType &Rec) { +  T Deserialized; +  if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec), +                                               Deserialized)) +    return std::move(E); +  return getHashForUdt(Deserialized, Rec.data()); +} + +template <typename T> +static Expected<TagRecordHash> getTagRecordHashForUdt(const CVType &Rec) { +  T Deserialized; +  if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec), +                                               Deserialized)) +    return std::move(E); + +  ClassOptions Opts = Deserialized.getOptions(); + +  bool ForwardRef = bool(Opts & ClassOptions::ForwardReference); + +  uint32_t ThisRecordHash = getHashForUdt(Deserialized, Rec.data()); + +  // If we don't have a forward ref we can't compute the hash of it from the +  // full record because it requires hashing the entire buffer. +  if (!ForwardRef) +    return TagRecordHash{std::move(Deserialized), ThisRecordHash, 0}; + +  bool Scoped = bool(Opts & ClassOptions::Scoped); + +  StringRef NameToHash = +      Scoped ? Deserialized.getUniqueName() : Deserialized.getName(); +  uint32_t FullHash = hashStringV1(NameToHash); +  return TagRecordHash{std::move(Deserialized), FullHash, ThisRecordHash}; +} + +template <typename T> +static Expected<uint32_t> getSourceLineHash(const CVType &Rec) { +  T Deserialized; +  if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec), +                                               Deserialized)) +    return std::move(E); +  char Buf[4]; +  support::endian::write32le(Buf, Deserialized.getUDT().getIndex()); +  return hashStringV1(StringRef(Buf, 4)); +} + +Expected<TagRecordHash> llvm::pdb::hashTagRecord(const codeview::CVType &Type) { +  switch (Type.kind()) { +  case LF_CLASS: +  case LF_STRUCTURE: +  case LF_INTERFACE: +    return getTagRecordHashForUdt<ClassRecord>(Type); +  case LF_UNION: +    return getTagRecordHashForUdt<UnionRecord>(Type); +  case LF_ENUM: +    return getTagRecordHashForUdt<EnumRecord>(Type); +  default: +    assert(false && "Type is not a tag record!"); +  } +  return make_error<StringError>("Invalid record type", +                                 inconvertibleErrorCode()); +} + +Expected<uint32_t> llvm::pdb::hashTypeRecord(const CVType &Rec) { +  switch (Rec.kind()) { +  case LF_CLASS: +  case LF_STRUCTURE: +  case LF_INTERFACE: +    return getHashForUdt<ClassRecord>(Rec); +  case LF_UNION: +    return getHashForUdt<UnionRecord>(Rec); +  case LF_ENUM: +    return getHashForUdt<EnumRecord>(Rec); + +  case LF_UDT_SRC_LINE: +    return getSourceLineHash<UdtSourceLineRecord>(Rec); +  case LF_UDT_MOD_SRC_LINE: +    return getSourceLineHash<UdtModSourceLineRecord>(Rec); + +  default: +    break; +  } + +  // Run CRC32 over the bytes. This corresponds to `hashBufv8`. +  JamCRC JC(/*Init=*/0U); +  JC.update(Rec.data()); +  return JC.getCRC(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp new file mode 100644 index 0000000000000..ac19db03fab2f --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp @@ -0,0 +1,246 @@ +//===- TpiStream.cpp - PDB Type Info (TPI) Stream 2 Access ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" + +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cstdint> +#include <vector> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::support; +using namespace llvm::msf; +using namespace llvm::pdb; + +TpiStream::TpiStream(PDBFile &File, std::unique_ptr<MappedBlockStream> Stream) +    : Pdb(File), Stream(std::move(Stream)) {} + +TpiStream::~TpiStream() = default; + +Error TpiStream::reload() { +  BinaryStreamReader Reader(*Stream); + +  if (Reader.bytesRemaining() < sizeof(TpiStreamHeader)) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "TPI Stream does not contain a header."); + +  if (Reader.readObject(Header)) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "TPI Stream does not contain a header."); + +  if (Header->Version != PdbTpiV80) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Unsupported TPI Version."); + +  if (Header->HeaderSize != sizeof(TpiStreamHeader)) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "Corrupt TPI Header size."); + +  if (Header->HashKeySize != sizeof(ulittle32_t)) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "TPI Stream expected 4 byte hash key size."); + +  if (Header->NumHashBuckets < MinTpiHashBuckets || +      Header->NumHashBuckets > MaxTpiHashBuckets) +    return make_error<RawError>(raw_error_code::corrupt_file, +                                "TPI Stream Invalid number of hash buckets."); + +  // The actual type records themselves come from this stream +  if (auto EC = +          Reader.readSubstream(TypeRecordsSubstream, Header->TypeRecordBytes)) +    return EC; + +  BinaryStreamReader RecordReader(TypeRecordsSubstream.StreamData); +  if (auto EC = +          RecordReader.readArray(TypeRecords, TypeRecordsSubstream.size())) +    return EC; + +  // Hash indices, hash values, etc come from the hash stream. +  if (Header->HashStreamIndex != kInvalidStreamIndex) { +    auto HS = Pdb.safelyCreateIndexedStream(Header->HashStreamIndex); +    if (!HS) { +      consumeError(HS.takeError()); +      return make_error<RawError>(raw_error_code::corrupt_file, +                                  "Invalid TPI hash stream index."); +    } +    BinaryStreamReader HSR(**HS); + +    // There should be a hash value for every type record, or no hashes at all. +    uint32_t NumHashValues = +        Header->HashValueBuffer.Length / sizeof(ulittle32_t); +    if (NumHashValues != getNumTypeRecords() && NumHashValues != 0) +      return make_error<RawError>( +          raw_error_code::corrupt_file, +          "TPI hash count does not match with the number of type records."); +    HSR.setOffset(Header->HashValueBuffer.Off); +    if (auto EC = HSR.readArray(HashValues, NumHashValues)) +      return EC; + +    HSR.setOffset(Header->IndexOffsetBuffer.Off); +    uint32_t NumTypeIndexOffsets = +        Header->IndexOffsetBuffer.Length / sizeof(TypeIndexOffset); +    if (auto EC = HSR.readArray(TypeIndexOffsets, NumTypeIndexOffsets)) +      return EC; + +    if (Header->HashAdjBuffer.Length > 0) { +      HSR.setOffset(Header->HashAdjBuffer.Off); +      if (auto EC = HashAdjusters.load(HSR)) +        return EC; +    } + +    HashStream = std::move(*HS); +  } + +  Types = std::make_unique<LazyRandomTypeCollection>( +      TypeRecords, getNumTypeRecords(), getTypeIndexOffsets()); +  return Error::success(); +} + +PdbRaw_TpiVer TpiStream::getTpiVersion() const { +  uint32_t Value = Header->Version; +  return static_cast<PdbRaw_TpiVer>(Value); +} + +uint32_t TpiStream::TypeIndexBegin() const { return Header->TypeIndexBegin; } + +uint32_t TpiStream::TypeIndexEnd() const { return Header->TypeIndexEnd; } + +uint32_t TpiStream::getNumTypeRecords() const { +  return TypeIndexEnd() - TypeIndexBegin(); +} + +uint16_t TpiStream::getTypeHashStreamIndex() const { +  return Header->HashStreamIndex; +} + +uint16_t TpiStream::getTypeHashStreamAuxIndex() const { +  return Header->HashAuxStreamIndex; +} + +uint32_t TpiStream::getNumHashBuckets() const { return Header->NumHashBuckets; } +uint32_t TpiStream::getHashKeySize() const { return Header->HashKeySize; } + +void TpiStream::buildHashMap() { +  if (!HashMap.empty()) +    return; +  if (HashValues.empty()) +    return; + +  HashMap.resize(Header->NumHashBuckets); + +  TypeIndex TIB{Header->TypeIndexBegin}; +  TypeIndex TIE{Header->TypeIndexEnd}; +  while (TIB < TIE) { +    uint32_t HV = HashValues[TIB.toArrayIndex()]; +    HashMap[HV].push_back(TIB++); +  } +} + +std::vector<TypeIndex> TpiStream::findRecordsByName(StringRef Name) const { +  if (!supportsTypeLookup()) +    const_cast<TpiStream*>(this)->buildHashMap(); + +  uint32_t Bucket = hashStringV1(Name) % Header->NumHashBuckets; +  if (Bucket > HashMap.size()) +    return {}; + +  std::vector<TypeIndex> Result; +  for (TypeIndex TI : HashMap[Bucket]) { +    std::string ThisName = computeTypeName(*Types, TI); +    if (ThisName == Name) +      Result.push_back(TI); +  } +  return Result; +} + +bool TpiStream::supportsTypeLookup() const { return !HashMap.empty(); } + +Expected<TypeIndex> +TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const { +  if (!supportsTypeLookup()) +    const_cast<TpiStream*>(this)->buildHashMap(); + +  CVType F = Types->getType(ForwardRefTI); +  if (!isUdtForwardRef(F)) +    return ForwardRefTI; + +  Expected<TagRecordHash> ForwardTRH = hashTagRecord(F); +  if (!ForwardTRH) +    return ForwardTRH.takeError(); + +  uint32_t BucketIdx = ForwardTRH->FullRecordHash % Header->NumHashBuckets; + +  for (TypeIndex TI : HashMap[BucketIdx]) { +    CVType CVT = Types->getType(TI); +    if (CVT.kind() != F.kind()) +      continue; + +    Expected<TagRecordHash> FullTRH = hashTagRecord(CVT); +    if (!FullTRH) +      return FullTRH.takeError(); +    if (ForwardTRH->FullRecordHash != FullTRH->FullRecordHash) +      continue; +    TagRecord &ForwardTR = ForwardTRH->getRecord(); +    TagRecord &FullTR = FullTRH->getRecord(); + +    if (!ForwardTR.hasUniqueName()) { +      if (ForwardTR.getName() == FullTR.getName()) +        return TI; +      continue; +    } + +    if (!FullTR.hasUniqueName()) +      continue; +    if (ForwardTR.getUniqueName() == FullTR.getUniqueName()) +      return TI; +  } +  return ForwardRefTI; +} + +codeview::CVType TpiStream::getType(codeview::TypeIndex Index) { +  assert(!Index.isSimple()); +  return Types->getType(Index); +} + +BinarySubstreamRef TpiStream::getTypeRecordsSubstream() const { +  return TypeRecordsSubstream; +} + +FixedStreamArray<support::ulittle32_t> TpiStream::getHashValues() const { +  return HashValues; +} + +FixedStreamArray<TypeIndexOffset> TpiStream::getTypeIndexOffsets() const { +  return TypeIndexOffsets; +} + +HashTable<support::ulittle32_t> &TpiStream::getHashAdjusters() { +  return HashAdjusters; +} + +CVTypeRange TpiStream::types(bool *HadError) const { +  return make_range(TypeRecords.begin(HadError), TypeRecords.end()); +} + +Error TpiStream::commit() { return Error::success(); } diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp new file mode 100644 index 0000000000000..4f10f8524a9b1 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp @@ -0,0 +1,178 @@ +//===- TpiStreamBuilder.cpp -   -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamArray.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cstdint> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; +using namespace llvm::support; + +TpiStreamBuilder::TpiStreamBuilder(MSFBuilder &Msf, uint32_t StreamIdx) +    : Msf(Msf), Allocator(Msf.getAllocator()), Header(nullptr), Idx(StreamIdx) { +} + +TpiStreamBuilder::~TpiStreamBuilder() = default; + +void TpiStreamBuilder::setVersionHeader(PdbRaw_TpiVer Version) { +  VerHeader = Version; +} + +void TpiStreamBuilder::addTypeRecord(ArrayRef<uint8_t> Record, +                                     Optional<uint32_t> Hash) { +  // If we just crossed an 8KB threshold, add a type index offset. +  size_t NewSize = TypeRecordBytes + Record.size(); +  constexpr size_t EightKB = 8 * 1024; +  if (NewSize / EightKB > TypeRecordBytes / EightKB || TypeRecords.empty()) { +    TypeIndexOffsets.push_back( +        {codeview::TypeIndex(codeview::TypeIndex::FirstNonSimpleIndex + +                             TypeRecords.size()), +         ulittle32_t(TypeRecordBytes)}); +  } +  TypeRecordBytes = NewSize; + +  TypeRecords.push_back(Record); +  if (Hash) +    TypeHashes.push_back(*Hash); +} + +Error TpiStreamBuilder::finalize() { +  if (Header) +    return Error::success(); + +  TpiStreamHeader *H = Allocator.Allocate<TpiStreamHeader>(); + +  uint32_t Count = TypeRecords.size(); + +  H->Version = VerHeader; +  H->HeaderSize = sizeof(TpiStreamHeader); +  H->TypeIndexBegin = codeview::TypeIndex::FirstNonSimpleIndex; +  H->TypeIndexEnd = H->TypeIndexBegin + Count; +  H->TypeRecordBytes = TypeRecordBytes; + +  H->HashStreamIndex = HashStreamIndex; +  H->HashAuxStreamIndex = kInvalidStreamIndex; +  H->HashKeySize = sizeof(ulittle32_t); +  H->NumHashBuckets = MaxTpiHashBuckets - 1; + +  // Recall that hash values go into a completely different stream identified by +  // the `HashStreamIndex` field of the `TpiStreamHeader`.  Therefore, the data +  // begins at offset 0 of this independent stream. +  H->HashValueBuffer.Off = 0; +  H->HashValueBuffer.Length = calculateHashBufferSize(); + +  // We never write any adjustments into our PDBs, so this is usually some +  // offset with zero length. +  H->HashAdjBuffer.Off = H->HashValueBuffer.Off + H->HashValueBuffer.Length; +  H->HashAdjBuffer.Length = 0; + +  H->IndexOffsetBuffer.Off = H->HashAdjBuffer.Off + H->HashAdjBuffer.Length; +  H->IndexOffsetBuffer.Length = calculateIndexOffsetSize(); + +  Header = H; +  return Error::success(); +} + +uint32_t TpiStreamBuilder::calculateSerializedLength() { +  return sizeof(TpiStreamHeader) + TypeRecordBytes; +} + +uint32_t TpiStreamBuilder::calculateHashBufferSize() const { +  assert((TypeRecords.size() == TypeHashes.size() || TypeHashes.empty()) && +         "either all or no type records should have hashes"); +  return TypeHashes.size() * sizeof(ulittle32_t); +} + +uint32_t TpiStreamBuilder::calculateIndexOffsetSize() const { +  return TypeIndexOffsets.size() * sizeof(codeview::TypeIndexOffset); +} + +Error TpiStreamBuilder::finalizeMsfLayout() { +  uint32_t Length = calculateSerializedLength(); +  if (auto EC = Msf.setStreamSize(Idx, Length)) +    return EC; + +  uint32_t HashStreamSize = +      calculateHashBufferSize() + calculateIndexOffsetSize(); + +  if (HashStreamSize == 0) +    return Error::success(); + +  auto ExpectedIndex = Msf.addStream(HashStreamSize); +  if (!ExpectedIndex) +    return ExpectedIndex.takeError(); +  HashStreamIndex = *ExpectedIndex; +  if (!TypeHashes.empty()) { +    ulittle32_t *H = Allocator.Allocate<ulittle32_t>(TypeHashes.size()); +    MutableArrayRef<ulittle32_t> HashBuffer(H, TypeHashes.size()); +    for (uint32_t I = 0; I < TypeHashes.size(); ++I) { +      HashBuffer[I] = TypeHashes[I] % (MaxTpiHashBuckets - 1); +    } +    ArrayRef<uint8_t> Bytes( +        reinterpret_cast<const uint8_t *>(HashBuffer.data()), +        calculateHashBufferSize()); +    HashValueStream = +        std::make_unique<BinaryByteStream>(Bytes, llvm::support::little); +  } +  return Error::success(); +} + +Error TpiStreamBuilder::commit(const msf::MSFLayout &Layout, +                               WritableBinaryStreamRef Buffer) { +  if (auto EC = finalize()) +    return EC; + +  auto InfoS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer, +                                                              Idx, Allocator); + +  BinaryStreamWriter Writer(*InfoS); +  if (auto EC = Writer.writeObject(*Header)) +    return EC; + +  for (auto Rec : TypeRecords) { +    assert(!Rec.empty()); // An empty record will not write anything, but it +                          // would shift all offsets from here on. +    if (auto EC = Writer.writeBytes(Rec)) +      return EC; +  } + +  if (HashStreamIndex != kInvalidStreamIndex) { +    auto HVS = WritableMappedBlockStream::createIndexedStream( +        Layout, Buffer, HashStreamIndex, Allocator); +    BinaryStreamWriter HW(*HVS); +    if (HashValueStream) { +      if (auto EC = HW.writeStreamRef(*HashValueStream)) +        return EC; +    } + +    for (auto &IndexOffset : TypeIndexOffsets) { +      if (auto EC = HW.writeObject(IndexOffset)) +        return EC; +    } +  } + +  return Error::success(); +} | 
