|
| 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-exception |
| 6 | +// |
| 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 | + |
| 22 | +using namespace llvm; |
| 23 | +using namespace llvm::pdb; |
| 24 | + |
| 25 | +namespace { |
| 26 | +struct PublicSym { |
| 27 | + llvm::StringRef Name; |
| 28 | + uint16_t Segment; |
| 29 | + uint32_t Offset; |
| 30 | +}; |
| 31 | + |
| 32 | +class MockPublics { |
| 33 | +public: |
| 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 | + |
| 47 | +private: |
| 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 | + |
| 59 | +MockPublics::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 | + |
| 65 | +Expected<std::unique_ptr<MockPublics>> |
| 66 | +MockPublics::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 | + |
| 74 | +void 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 | + |
| 87 | +Error 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 | + |
| 100 | +PublicsStream *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 | + |
| 110 | +SymbolStream *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 | + |
| 120 | +std::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 | +} // namespace |
| 155 | + |
| 156 | +static std::pair<uint32_t, uint32_t> |
| 157 | +nthSymbolAddress(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 | + |
| 167 | +TEST(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 first |
| 190 | + // (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 symbol |
| 195 | + 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 info |
| 213 | + 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 backing |
| 217 | + // data |
| 218 | + 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 | +} |
0 commit comments