227 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"10#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"11#include "llvm/DebugInfo/CodeView/SymbolRecord.h"12#include "llvm/DebugInfo/MSF/MSFBuilder.h"13#include "llvm/DebugInfo/MSF/MappedBlockStream.h"14#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h"15#include "llvm/DebugInfo/PDB/Native/PDBFile.h"16#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"17#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"18#include "llvm/Support/BinaryByteStream.h"19 20#include "gtest/gtest.h"21 22using namespace llvm;23using namespace llvm::pdb;24 25namespace {26struct PublicSym {27 llvm::StringRef Name;28 uint16_t Segment;29 uint32_t Offset;30};31 32class MockPublics {33public:34 MockPublics(size_t StreamSize, BumpPtrAllocator &Alloc,35 msf::MSFBuilder Builder);36 static Expected<std::unique_ptr<MockPublics>>37 create(BumpPtrAllocator &Allocator, size_t StreamSize);38 39 void addPublics(ArrayRef<PublicSym> Syms);40 Error finish();41 42 PublicsStream *publicsStream();43 SymbolStream *symbolStream();44 45 MutableBinaryByteStream &stream() { return Stream; }46 47private:48 MutableBinaryByteStream Stream;49 50 msf::MSFBuilder MsfBuilder;51 std::optional<msf::MSFLayout> MsfLayout;52 53 GSIStreamBuilder Gsi;54 55 std::unique_ptr<PublicsStream> Publics;56 std::unique_ptr<SymbolStream> Symbols;57};58 59MockPublics::MockPublics(size_t StreamSize, BumpPtrAllocator &Allocator,60 msf::MSFBuilder Builder)61 : Stream({Allocator.Allocate<uint8_t>(StreamSize), StreamSize},62 llvm::endianness::little),63 MsfBuilder(std::move(Builder)), Gsi(this->MsfBuilder) {}64 65Expected<std::unique_ptr<MockPublics>>66MockPublics::create(BumpPtrAllocator &Allocator, size_t StreamSize) {67 auto ExpectedMsf = msf::MSFBuilder::create(Allocator, 4096);68 if (!ExpectedMsf)69 return ExpectedMsf.takeError();70 return std::make_unique<MockPublics>(StreamSize, Allocator,71 std::move(*ExpectedMsf));72}73 74void MockPublics::addPublics(ArrayRef<PublicSym> Publics) {75 std::vector<BulkPublic> Bulks;76 for (const auto &Sym : Publics) {77 BulkPublic BP;78 BP.Name = Sym.Name.data();79 BP.NameLen = Sym.Name.size();80 BP.Offset = Sym.Offset;81 BP.Segment = Sym.Segment;82 Bulks.emplace_back(BP);83 }84 Gsi.addPublicSymbols(std::move(Bulks));85}86 87Error MockPublics::finish() {88 auto Err = Gsi.finalizeMsfLayout();89 if (Err)90 return Err;91 92 auto ExpectedLayout = MsfBuilder.generateLayout();93 if (!ExpectedLayout)94 return ExpectedLayout.takeError();95 MsfLayout = std::move(*ExpectedLayout);96 97 return Gsi.commit(*MsfLayout, Stream);98}99 100PublicsStream *MockPublics::publicsStream() {101 if (!Publics) {102 Publics = std::make_unique<PublicsStream>(103 msf::MappedBlockStream::createIndexedStream(*MsfLayout, Stream,104 Gsi.getPublicsStreamIndex(),105 MsfBuilder.getAllocator()));106 }107 return Publics.get();108}109 110SymbolStream *MockPublics::symbolStream() {111 if (!Symbols) {112 Symbols = std::make_unique<SymbolStream>(113 msf::MappedBlockStream::createIndexedStream(*MsfLayout, Stream,114 Gsi.getRecordStreamIndex(),115 MsfBuilder.getAllocator()));116 }117 return Symbols.get();118}119 120std::array GSymbols{121 PublicSym{"??0Base@@QEAA@XZ", /*Segment=*/1, /*Offset=*/0},122 PublicSym{"??0Derived@@QEAA@XZ", /*Segment=*/1, /*Offset=*/32},123 PublicSym{"??0Derived2@@QEAA@XZ", /*Segment=*/1, /*Offset=*/32},124 PublicSym{"??0Derived3@@QEAA@XZ", /*Segment=*/1, /*Offset=*/80},125 PublicSym{"??1Base@@UEAA@XZ", /*Segment=*/1, /*Offset=*/160},126 PublicSym{"??1Derived@@UEAA@XZ", /*Segment=*/1, /*Offset=*/176},127 PublicSym{"??1Derived2@@UEAA@XZ", /*Segment=*/1, /*Offset=*/176},128 PublicSym{"??1Derived3@@UEAA@XZ", /*Segment=*/1, /*Offset=*/208},129 PublicSym{"??3@YAXPEAX_K@Z", /*Segment=*/1, /*Offset=*/256},130 PublicSym{"??_EDerived3@@W7EAAPEAXI@Z", /*Segment=*/1, /*Offset=*/268},131 PublicSym{"??_GBase@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/288},132 PublicSym{"??_EBase@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/288},133 PublicSym{"??_EDerived2@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/352},134 PublicSym{"??_EDerived@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/352},135 PublicSym{"??_GDerived@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/352},136 PublicSym{"??_GDerived2@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/352},137 PublicSym{"??_EDerived3@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/416},138 PublicSym{"??_GDerived3@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/416},139 PublicSym{"?AMethod@AClass@@QEAAXHPEAD@Z", /*Segment=*/1, /*Offset=*/480},140 PublicSym{"?Something@AClass@@SA_ND@Z", /*Segment=*/1, /*Offset=*/496},141 PublicSym{"?dup1@@YAHH@Z", /*Segment=*/1, /*Offset=*/544},142 PublicSym{"?dup3@@YAHH@Z", /*Segment=*/1, /*Offset=*/544},143 PublicSym{"?dup2@@YAHH@Z", /*Segment=*/1, /*Offset=*/544},144 PublicSym{"?foobar@@YAHH@Z", /*Segment=*/1, /*Offset=*/560},145 PublicSym{"main", /*Segment=*/1, /*Offset=*/576},146 PublicSym{"??_7Base@@6B@", /*Segment=*/2, /*Offset=*/0},147 PublicSym{"??_7Derived@@6B@", /*Segment=*/2, /*Offset=*/8},148 PublicSym{"??_7Derived2@@6B@", /*Segment=*/2, /*Offset=*/8},149 PublicSym{"??_7Derived3@@6BDerived2@@@", /*Segment=*/2, /*Offset=*/16},150 PublicSym{"??_7Derived3@@6BDerived@@@", /*Segment=*/2, /*Offset=*/24},151 PublicSym{"?AGlobal@@3HA", /*Segment=*/3, /*Offset=*/0},152};153 154} // namespace155 156static std::pair<uint32_t, uint32_t>157nthSymbolAddress(PublicsStream *Publics, SymbolStream *Symbols, size_t N) {158 auto Index = Publics->getAddressMap()[N].value();159 codeview::CVSymbol Sym = Symbols->readRecord(Index);160 auto ExpectedPub =161 codeview::SymbolDeserializer::deserializeAs<codeview::PublicSym32>(Sym);162 if (!ExpectedPub)163 return std::pair(0, 0);164 return std::pair(ExpectedPub->Segment, ExpectedPub->Offset);165}166 167TEST(PublicsStreamTest, FindByAddress) {168 BumpPtrAllocator Allocator;169 auto ExpectedMock = MockPublics::create(Allocator, 1 << 20);170 ASSERT_TRUE(bool(ExpectedMock));171 std::unique_ptr<MockPublics> Mock = std::move(*ExpectedMock);172 173 Mock->addPublics(GSymbols);174 Error Err = Mock->finish();175 ASSERT_FALSE(Err) << Err;176 177 auto *Publics = Mock->publicsStream();178 ASSERT_NE(Publics, nullptr);179 Err = Publics->reload();180 ASSERT_FALSE(Err) << Err;181 182 auto *Symbols = Mock->symbolStream();183 ASSERT_NE(Symbols, nullptr);184 Err = Symbols->reload();185 ASSERT_FALSE(Err) << Err;186 187 auto VTableDerived = Publics->findByAddress(*Symbols, 2, 8);188 ASSERT_TRUE(VTableDerived.has_value());189 // both derived and derived2 have their vftables there - but derived2 is first190 // (due to ICF)191 ASSERT_EQ(VTableDerived->first.Name, "??_7Derived2@@6B@");192 ASSERT_EQ(VTableDerived->second, 26u);193 194 // Again, make sure that we find the first symbol195 auto VectorDtorDerived = Publics->findByAddress(*Symbols, 1, 352);196 ASSERT_TRUE(VectorDtorDerived.has_value());197 ASSERT_EQ(VectorDtorDerived->first.Name, "??_EDerived2@@UEAAPEAXI@Z");198 ASSERT_EQ(VectorDtorDerived->second, 12u);199 ASSERT_EQ(nthSymbolAddress(Publics, Symbols, 13), std::pair(1u, 352u));200 ASSERT_EQ(nthSymbolAddress(Publics, Symbols, 14), std::pair(1u, 352u));201 ASSERT_EQ(nthSymbolAddress(Publics, Symbols, 15), std::pair(1u, 352u));202 ASSERT_EQ(nthSymbolAddress(Publics, Symbols, 16), std::pair(1u, 416u));203 204 ASSERT_FALSE(Publics->findByAddress(*Symbols, 2, 7).has_value());205 ASSERT_FALSE(Publics->findByAddress(*Symbols, 2, 9).has_value());206 207 auto GlobalSym = Publics->findByAddress(*Symbols, 3, 0);208 ASSERT_TRUE(GlobalSym.has_value());209 ASSERT_EQ(GlobalSym->first.Name, "?AGlobal@@3HA");210 ASSERT_EQ(GlobalSym->second, 30u);211 212 // test corrupt debug info213 codeview::CVSymbol GlobalCVSym =214 Symbols->readRecord(Publics->getAddressMap()[30]);215 ASSERT_EQ(GlobalCVSym.kind(), codeview::S_PUB32);216 // CVSymbol::data returns a pointer to const data, so we modify the backing217 // data218 uint8_t *PDBData = Mock->stream().data().data();219 auto Offset = GlobalCVSym.data().data() - PDBData;220 reinterpret_cast<codeview::RecordPrefix *>(PDBData + Offset)->RecordKind =221 codeview::S_GDATA32;222 ASSERT_EQ(GlobalCVSym.kind(), codeview::S_GDATA32);223 224 GlobalSym = Publics->findByAddress(*Symbols, 3, 0);225 ASSERT_FALSE(GlobalSym.has_value());226}227