blob: 4b89280cbdb933786ca93a5ff4603a601d67adab [file] [log] [blame] [edit]
//===----------------------------------------------------------------------===//
//
// 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/PublicsStream.h"
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"
#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
#include "llvm/Support/BinaryByteStream.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace llvm::pdb;
namespace {
struct PublicSym {
llvm::StringRef Name;
uint16_t Segment;
uint32_t Offset;
};
class MockPublics {
public:
MockPublics(size_t StreamSize, BumpPtrAllocator &Alloc,
msf::MSFBuilder Builder);
static Expected<std::unique_ptr<MockPublics>>
create(BumpPtrAllocator &Allocator, size_t StreamSize);
void addPublics(ArrayRef<PublicSym> Syms);
Error finish();
PublicsStream *publicsStream();
SymbolStream *symbolStream();
MutableBinaryByteStream &stream() { return Stream; }
private:
MutableBinaryByteStream Stream;
msf::MSFBuilder MsfBuilder;
std::optional<msf::MSFLayout> MsfLayout;
GSIStreamBuilder Gsi;
std::unique_ptr<PublicsStream> Publics;
std::unique_ptr<SymbolStream> Symbols;
};
MockPublics::MockPublics(size_t StreamSize, BumpPtrAllocator &Allocator,
msf::MSFBuilder Builder)
: Stream({Allocator.Allocate<uint8_t>(StreamSize), StreamSize},
llvm::endianness::little),
MsfBuilder(std::move(Builder)), Gsi(this->MsfBuilder) {}
Expected<std::unique_ptr<MockPublics>>
MockPublics::create(BumpPtrAllocator &Allocator, size_t StreamSize) {
auto ExpectedMsf = msf::MSFBuilder::create(Allocator, 4096);
if (!ExpectedMsf)
return ExpectedMsf.takeError();
return std::make_unique<MockPublics>(StreamSize, Allocator,
std::move(*ExpectedMsf));
}
void MockPublics::addPublics(ArrayRef<PublicSym> Publics) {
std::vector<BulkPublic> Bulks;
for (const auto &Sym : Publics) {
BulkPublic BP;
BP.Name = Sym.Name.data();
BP.NameLen = Sym.Name.size();
BP.Offset = Sym.Offset;
BP.Segment = Sym.Segment;
Bulks.emplace_back(BP);
}
Gsi.addPublicSymbols(std::move(Bulks));
}
Error MockPublics::finish() {
auto Err = Gsi.finalizeMsfLayout();
if (Err)
return Err;
auto ExpectedLayout = MsfBuilder.generateLayout();
if (!ExpectedLayout)
return ExpectedLayout.takeError();
MsfLayout = std::move(*ExpectedLayout);
return Gsi.commit(*MsfLayout, Stream);
}
PublicsStream *MockPublics::publicsStream() {
if (!Publics) {
Publics = std::make_unique<PublicsStream>(
msf::MappedBlockStream::createIndexedStream(*MsfLayout, Stream,
Gsi.getPublicsStreamIndex(),
MsfBuilder.getAllocator()));
}
return Publics.get();
}
SymbolStream *MockPublics::symbolStream() {
if (!Symbols) {
Symbols = std::make_unique<SymbolStream>(
msf::MappedBlockStream::createIndexedStream(*MsfLayout, Stream,
Gsi.getRecordStreamIndex(),
MsfBuilder.getAllocator()));
}
return Symbols.get();
}
std::array GSymbols{
PublicSym{"??0Base@@QEAA@XZ", /*Segment=*/1, /*Offset=*/0},
PublicSym{"??0Derived@@QEAA@XZ", /*Segment=*/1, /*Offset=*/32},
PublicSym{"??0Derived2@@QEAA@XZ", /*Segment=*/1, /*Offset=*/32},
PublicSym{"??0Derived3@@QEAA@XZ", /*Segment=*/1, /*Offset=*/80},
PublicSym{"??1Base@@UEAA@XZ", /*Segment=*/1, /*Offset=*/160},
PublicSym{"??1Derived@@UEAA@XZ", /*Segment=*/1, /*Offset=*/176},
PublicSym{"??1Derived2@@UEAA@XZ", /*Segment=*/1, /*Offset=*/176},
PublicSym{"??1Derived3@@UEAA@XZ", /*Segment=*/1, /*Offset=*/208},
PublicSym{"??3@YAXPEAX_K@Z", /*Segment=*/1, /*Offset=*/256},
PublicSym{"??_EDerived3@@W7EAAPEAXI@Z", /*Segment=*/1, /*Offset=*/268},
PublicSym{"??_GBase@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/288},
PublicSym{"??_EBase@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/288},
PublicSym{"??_EDerived2@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/352},
PublicSym{"??_EDerived@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/352},
PublicSym{"??_GDerived@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/352},
PublicSym{"??_GDerived2@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/352},
PublicSym{"??_EDerived3@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/416},
PublicSym{"??_GDerived3@@UEAAPEAXI@Z", /*Segment=*/1, /*Offset=*/416},
PublicSym{"?AMethod@AClass@@QEAAXHPEAD@Z", /*Segment=*/1, /*Offset=*/480},
PublicSym{"?Something@AClass@@SA_ND@Z", /*Segment=*/1, /*Offset=*/496},
PublicSym{"?dup1@@YAHH@Z", /*Segment=*/1, /*Offset=*/544},
PublicSym{"?dup3@@YAHH@Z", /*Segment=*/1, /*Offset=*/544},
PublicSym{"?dup2@@YAHH@Z", /*Segment=*/1, /*Offset=*/544},
PublicSym{"?foobar@@YAHH@Z", /*Segment=*/1, /*Offset=*/560},
PublicSym{"main", /*Segment=*/1, /*Offset=*/576},
PublicSym{"??_7Base@@6B@", /*Segment=*/2, /*Offset=*/0},
PublicSym{"??_7Derived@@6B@", /*Segment=*/2, /*Offset=*/8},
PublicSym{"??_7Derived2@@6B@", /*Segment=*/2, /*Offset=*/8},
PublicSym{"??_7Derived3@@6BDerived2@@@", /*Segment=*/2, /*Offset=*/16},
PublicSym{"??_7Derived3@@6BDerived@@@", /*Segment=*/2, /*Offset=*/24},
PublicSym{"?AGlobal@@3HA", /*Segment=*/3, /*Offset=*/0},
};
} // namespace
static std::pair<uint32_t, uint32_t>
nthSymbolAddress(PublicsStream *Publics, SymbolStream *Symbols, size_t N) {
auto Index = Publics->getAddressMap()[N].value();
codeview::CVSymbol Sym = Symbols->readRecord(Index);
auto ExpectedPub =
codeview::SymbolDeserializer::deserializeAs<codeview::PublicSym32>(Sym);
if (!ExpectedPub)
return std::pair(0, 0);
return std::pair(ExpectedPub->Segment, ExpectedPub->Offset);
}
TEST(PublicsStreamTest, FindByAddress) {
BumpPtrAllocator Allocator;
auto ExpectedMock = MockPublics::create(Allocator, 1 << 20);
ASSERT_TRUE(bool(ExpectedMock));
std::unique_ptr<MockPublics> Mock = std::move(*ExpectedMock);
Mock->addPublics(GSymbols);
Error Err = Mock->finish();
ASSERT_FALSE(Err) << Err;
auto *Publics = Mock->publicsStream();
ASSERT_NE(Publics, nullptr);
Err = Publics->reload();
ASSERT_FALSE(Err) << Err;
auto *Symbols = Mock->symbolStream();
ASSERT_NE(Symbols, nullptr);
Err = Symbols->reload();
ASSERT_FALSE(Err) << Err;
auto VTableDerived = Publics->findByAddress(*Symbols, 2, 8);
ASSERT_TRUE(VTableDerived.has_value());
// both derived and derived2 have their vftables there - but derived2 is first
// (due to ICF)
ASSERT_EQ(VTableDerived->first.Name, "??_7Derived2@@6B@");
ASSERT_EQ(VTableDerived->second, 26u);
// Again, make sure that we find the first symbol
auto VectorDtorDerived = Publics->findByAddress(*Symbols, 1, 352);
ASSERT_TRUE(VectorDtorDerived.has_value());
ASSERT_EQ(VectorDtorDerived->first.Name, "??_EDerived2@@UEAAPEAXI@Z");
ASSERT_EQ(VectorDtorDerived->second, 12u);
ASSERT_EQ(nthSymbolAddress(Publics, Symbols, 13), std::pair(1u, 352u));
ASSERT_EQ(nthSymbolAddress(Publics, Symbols, 14), std::pair(1u, 352u));
ASSERT_EQ(nthSymbolAddress(Publics, Symbols, 15), std::pair(1u, 352u));
ASSERT_EQ(nthSymbolAddress(Publics, Symbols, 16), std::pair(1u, 416u));
ASSERT_FALSE(Publics->findByAddress(*Symbols, 2, 7).has_value());
ASSERT_FALSE(Publics->findByAddress(*Symbols, 2, 9).has_value());
auto GlobalSym = Publics->findByAddress(*Symbols, 3, 0);
ASSERT_TRUE(GlobalSym.has_value());
ASSERT_EQ(GlobalSym->first.Name, "?AGlobal@@3HA");
ASSERT_EQ(GlobalSym->second, 30u);
// test corrupt debug info
codeview::CVSymbol GlobalCVSym =
Symbols->readRecord(Publics->getAddressMap()[30]);
ASSERT_EQ(GlobalCVSym.kind(), codeview::S_PUB32);
// CVSymbol::data returns a pointer to const data, so we modify the backing
// data
uint8_t *PDBData = Mock->stream().data().data();
auto Offset = GlobalCVSym.data().data() - PDBData;
reinterpret_cast<codeview::RecordPrefix *>(PDBData + Offset)->RecordKind =
codeview::S_GDATA32;
ASSERT_EQ(GlobalCVSym.kind(), codeview::S_GDATA32);
GlobalSym = Publics->findByAddress(*Symbols, 3, 0);
ASSERT_FALSE(GlobalSym.has_value());
}