149 lines · cpp
1//===- PublicsStream.cpp - PDB Public Symbol Stream -----------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// The data structures defined in this file are based on the reference10// implementation which is available at11// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h12//13// When you are reading the reference source code, you'd find the14// information below useful.15//16// - ppdb1->m_fMinimalDbgInfo seems to be always true.17// - SMALLBUCKETS macro is defined.18//19// The reference doesn't compile, so I learned just by reading code.20// It's not guaranteed to be correct.21//22//===----------------------------------------------------------------------===//23 24#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"25#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"26#include "llvm/DebugInfo/CodeView/SymbolRecord.h"27#include "llvm/DebugInfo/MSF/MappedBlockStream.h"28#include "llvm/DebugInfo/PDB/Native/RawError.h"29#include "llvm/DebugInfo/PDB/Native/RawTypes.h"30#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"31#include "llvm/Support/BinaryStreamReader.h"32#include "llvm/Support/Error.h"33#include <cstdint>34 35using namespace llvm;36using namespace llvm::msf;37using namespace llvm::support;38using namespace llvm::pdb;39 40PublicsStream::PublicsStream(std::unique_ptr<MappedBlockStream> Stream)41 : Stream(std::move(Stream)) {}42 43PublicsStream::~PublicsStream() = default;44 45uint32_t PublicsStream::getSymHash() const { return Header->SymHash; }46uint16_t PublicsStream::getThunkTableSection() const {47 return Header->ISectThunkTable;48}49uint32_t PublicsStream::getThunkTableOffset() const {50 return Header->OffThunkTable;51}52 53// Publics stream contains fixed-size headers and a serialized hash table.54// This implementation is not complete yet. It reads till the end of the55// stream so that we verify the stream is at least not corrupted. However,56// we skip over the hash table which we believe contains information about57// public symbols.58Error PublicsStream::reload() {59 BinaryStreamReader Reader(*Stream);60 61 // Check stream size.62 if (Reader.bytesRemaining() <63 sizeof(PublicsStreamHeader) + sizeof(GSIHashHeader))64 return make_error<RawError>(raw_error_code::corrupt_file,65 "Publics Stream does not contain a header.");66 67 // Read PSGSIHDR struct.68 if (Reader.readObject(Header))69 return make_error<RawError>(raw_error_code::corrupt_file,70 "Publics Stream does not contain a header.");71 72 // Read the hash table.73 if (auto E = PublicsTable.read(Reader))74 return E;75 76 // Something called "address map" follows.77 uint32_t NumAddressMapEntries = Header->AddrMap / sizeof(uint32_t);78 if (auto EC = Reader.readArray(AddressMap, NumAddressMapEntries))79 return joinErrors(std::move(EC),80 make_error<RawError>(raw_error_code::corrupt_file,81 "Could not read an address map."));82 83 // Something called "thunk map" follows.84 if (auto EC = Reader.readArray(ThunkMap, Header->NumThunks))85 return joinErrors(std::move(EC),86 make_error<RawError>(raw_error_code::corrupt_file,87 "Could not read a thunk map."));88 89 // Something called "section map" follows.90 if (Reader.bytesRemaining() > 0) {91 if (auto EC = Reader.readArray(SectionOffsets, Header->NumSections))92 return joinErrors(std::move(EC),93 make_error<RawError>(raw_error_code::corrupt_file,94 "Could not read a section map."));95 }96 97 if (Reader.bytesRemaining() > 0)98 return make_error<RawError>(raw_error_code::corrupt_file,99 "Corrupted publics stream.");100 return Error::success();101}102 103// This is a reimplementation of NearestSym:104// https://github.com/microsoft/microsoft-pdb/blob/805655a28bd8198004be2ac27e6e0290121a5e89/PDB/dbi/gsi.cpp#L1492-L1581105std::optional<std::pair<codeview::PublicSym32, size_t>>106PublicsStream::findByAddress(const SymbolStream &Symbols, uint16_t Segment,107 uint32_t Offset) const {108 // The address map is sorted by address, so we can use lower_bound to find the109 // position. Each element is an offset into the symbols for a public symbol.110 auto It = llvm::lower_bound(111 AddressMap, std::tuple(Segment, Offset),112 [&](support::ulittle32_t Cur, auto Addr) {113 auto Sym = Symbols.readRecord(Cur.value());114 if (Sym.kind() != codeview::S_PUB32)115 return false; // stop here, this is most likely corrupted debug info116 117 auto Psym =118 codeview::SymbolDeserializer::deserializeAs<codeview::PublicSym32>(119 Sym);120 if (!Psym) {121 consumeError(Psym.takeError());122 return false;123 }124 125 return std::tie(Psym->Segment, Psym->Offset) < Addr;126 });127 128 if (It == AddressMap.end())129 return std::nullopt;130 131 auto Sym = Symbols.readRecord(It->value());132 if (Sym.kind() != codeview::S_PUB32)133 return std::nullopt; // this is most likely corrupted debug info134 135 auto MaybePsym =136 codeview::SymbolDeserializer::deserializeAs<codeview::PublicSym32>(Sym);137 if (!MaybePsym) {138 consumeError(MaybePsym.takeError());139 return std::nullopt;140 }141 codeview::PublicSym32 Psym = std::move(*MaybePsym);142 143 if (std::tuple(Segment, Offset) != std::tuple(Psym.Segment, Psym.Offset))144 return std::nullopt;145 146 std::ptrdiff_t IterOffset = It - AddressMap.begin();147 return std::pair{Psym, static_cast<size_t>(IterOffset)};148}149