diff options
Diffstat (limited to 'lib/DebugInfo/PDB/UDTLayout.cpp')
-rw-r--r-- | lib/DebugInfo/PDB/UDTLayout.cpp | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/lib/DebugInfo/PDB/UDTLayout.cpp b/lib/DebugInfo/PDB/UDTLayout.cpp new file mode 100644 index 000000000000..61cef093d4ce --- /dev/null +++ b/lib/DebugInfo/PDB/UDTLayout.cpp @@ -0,0 +1,335 @@ +//===- UDTLayout.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/UDTLayout.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +static std::unique_ptr<PDBSymbol> getSymbolType(const PDBSymbol &Symbol) { + const IPDBSession &Session = Symbol.getSession(); + const IPDBRawSymbol &RawSymbol = Symbol.getRawSymbol(); + uint32_t TypeId = RawSymbol.getTypeId(); + return Session.getSymbolById(TypeId); +} + +static uint32_t getTypeLength(const PDBSymbol &Symbol) { + auto SymbolType = getSymbolType(Symbol); + const IPDBRawSymbol &RawType = SymbolType->getRawSymbol(); + + return RawType.getLength(); +} + +StorageItemBase::StorageItemBase(const UDTLayoutBase &Parent, + const PDBSymbol &Symbol, + const std::string &Name, + uint32_t OffsetInParent, uint32_t Size) + : Parent(Parent), Symbol(Symbol), Name(Name), + OffsetInParent(OffsetInParent), SizeOf(Size) { + UsedBytes.resize(SizeOf, true); +} + +uint32_t StorageItemBase::deepPaddingSize() const { + // sizeof(Field) - sizeof(typeof(Field)) is trailing padding. + return SizeOf - getTypeLength(Symbol); +} + +DataMemberLayoutItem::DataMemberLayoutItem( + const UDTLayoutBase &Parent, std::unique_ptr<PDBSymbolData> DataMember) + : StorageItemBase(Parent, *DataMember, DataMember->getName(), + DataMember->getOffset(), getTypeLength(*DataMember)), + DataMember(std::move(DataMember)) { + auto Type = this->DataMember->getType(); + if (auto UDT = unique_dyn_cast<PDBSymbolTypeUDT>(Type)) { + // UDT data members might have padding in between fields, but otherwise + // a member should occupy its entire storage. + UsedBytes.resize(SizeOf, false); + UdtLayout = llvm::make_unique<ClassLayout>(std::move(UDT)); + } +} + +const PDBSymbolData &DataMemberLayoutItem::getDataMember() { + return *dyn_cast<PDBSymbolData>(&Symbol); +} + +bool DataMemberLayoutItem::hasUDTLayout() const { return UdtLayout != nullptr; } + +const ClassLayout &DataMemberLayoutItem::getUDTLayout() const { + return *UdtLayout; +} + +uint32_t DataMemberLayoutItem::deepPaddingSize() const { + uint32_t Result = StorageItemBase::deepPaddingSize(); + if (UdtLayout) + Result += UdtLayout->deepPaddingSize(); + return Result; +} + +VTableLayoutItem::VTableLayoutItem(const UDTLayoutBase &Parent, + std::unique_ptr<PDBSymbolTypeVTable> VTable) + : StorageItemBase(Parent, *VTable, "<vtbl>", 0, getTypeLength(*VTable)), + VTable(std::move(VTable)) { + auto VTableType = cast<PDBSymbolTypePointer>(this->VTable->getType()); + ElementSize = VTableType->getLength(); + + Shape = + unique_dyn_cast<PDBSymbolTypeVTableShape>(VTableType->getPointeeType()); + if (Shape) + VTableFuncs.resize(Shape->getCount()); +} + +UDTLayoutBase::UDTLayoutBase(const PDBSymbol &Symbol, const std::string &Name, + uint32_t Size) + : SymbolBase(Symbol), Name(Name), SizeOf(Size) { + UsedBytes.resize(Size); + ChildrenPerByte.resize(Size); + initializeChildren(Symbol); +} + +ClassLayout::ClassLayout(const PDBSymbolTypeUDT &UDT) + : UDTLayoutBase(UDT, UDT.getName(), UDT.getLength()), UDT(UDT) {} + +ClassLayout::ClassLayout(std::unique_ptr<PDBSymbolTypeUDT> UDT) + : ClassLayout(*UDT) { + OwnedStorage = std::move(UDT); +} + +BaseClassLayout::BaseClassLayout(const UDTLayoutBase &Parent, + std::unique_ptr<PDBSymbolTypeBaseClass> Base) + : UDTLayoutBase(*Base, Base->getName(), Base->getLength()), + StorageItemBase(Parent, *Base, Base->getName(), Base->getOffset(), + Base->getLength()), + Base(std::move(Base)) { + IsVirtualBase = this->Base->isVirtualBaseClass(); +} + +uint32_t UDTLayoutBase::shallowPaddingSize() const { + return UsedBytes.size() - UsedBytes.count(); +} + +uint32_t UDTLayoutBase::deepPaddingSize() const { + uint32_t Result = shallowPaddingSize(); + for (auto &Child : ChildStorage) + Result += Child->deepPaddingSize(); + return Result; +} + +void UDTLayoutBase::initializeChildren(const PDBSymbol &Sym) { + // Handled bases first, followed by VTables, followed by data members, + // followed by functions, followed by other. This ordering is necessary + // so that bases and vtables get initialized before any functions which + // may override them. + + UniquePtrVector<PDBSymbolTypeBaseClass> Bases; + UniquePtrVector<PDBSymbolTypeVTable> VTables; + UniquePtrVector<PDBSymbolData> Members; + auto Children = Sym.findAllChildren(); + while (auto Child = Children->getNext()) { + if (auto Base = unique_dyn_cast<PDBSymbolTypeBaseClass>(Child)) { + if (Base->isVirtualBaseClass()) + VirtualBases.push_back(std::move(Base)); + else + Bases.push_back(std::move(Base)); + } + + else if (auto Data = unique_dyn_cast<PDBSymbolData>(Child)) { + if (Data->getDataKind() == PDB_DataKind::Member) + Members.push_back(std::move(Data)); + else + Other.push_back(std::move(Child)); + } else if (auto VT = unique_dyn_cast<PDBSymbolTypeVTable>(Child)) + VTables.push_back(std::move(VT)); + else if (auto Func = unique_dyn_cast<PDBSymbolFunc>(Child)) + Funcs.push_back(std::move(Func)); + else + Other.push_back(std::move(Child)); + } + + for (auto &Base : Bases) { + auto BL = llvm::make_unique<BaseClassLayout>(*this, std::move(Base)); + BaseClasses.push_back(BL.get()); + + addChildToLayout(std::move(BL)); + } + + for (auto &VT : VTables) { + auto VTLayout = llvm::make_unique<VTableLayoutItem>(*this, std::move(VT)); + + VTable = VTLayout.get(); + + addChildToLayout(std::move(VTLayout)); + continue; + } + + for (auto &Data : Members) { + auto DM = llvm::make_unique<DataMemberLayoutItem>(*this, std::move(Data)); + + addChildToLayout(std::move(DM)); + } + + for (auto &Func : Funcs) { + if (!Func->isVirtual()) + continue; + + if (Func->isIntroVirtualFunction()) + addVirtualIntro(*Func); + else + addVirtualOverride(*Func); + } +} + +void UDTLayoutBase::addVirtualIntro(PDBSymbolFunc &Func) { + // Kind of a hack, but we prefer the more common destructor name that people + // are familiar with, e.g. ~ClassName. It seems there are always both and + // the vector deleting destructor overwrites the nice destructor, so just + // ignore the vector deleting destructor. + if (Func.getName() == "__vecDelDtor") + return; + + if (!VTable) { + // FIXME: Handle this. What's most likely happening is we have an intro + // virtual in a derived class where the base also has an intro virtual. + // In this case the vtable lives in the base. What we really need is + // for each UDTLayoutBase to contain a list of all its vtables, and + // then propagate this list up the hierarchy so that derived classes have + // direct access to their bases' vtables. + return; + } + + uint32_t Stride = VTable->getElementSize(); + + uint32_t Index = Func.getVirtualBaseOffset(); + assert(Index % Stride == 0); + Index /= Stride; + + VTable->setFunction(Index, Func); +} + +VTableLayoutItem *UDTLayoutBase::findVTableAtOffset(uint32_t RelativeOffset) { + if (VTable && VTable->getOffsetInParent() == RelativeOffset) + return VTable; + for (auto Base : BaseClasses) { + uint32_t Begin = Base->getOffsetInParent(); + uint32_t End = Begin + Base->getSize(); + if (RelativeOffset < Begin || RelativeOffset >= End) + continue; + + return Base->findVTableAtOffset(RelativeOffset - Begin); + } + + return nullptr; +} + +void UDTLayoutBase::addVirtualOverride(PDBSymbolFunc &Func) { + auto Signature = Func.getSignature(); + auto ThisAdjust = Signature->getThisAdjust(); + // ThisAdjust tells us which VTable we're looking for. Specifically, it's + // the offset into the current class of the VTable we're looking for. So + // look through the base hierarchy until we find one such that + // AbsoluteOffset(VT) == ThisAdjust + VTableLayoutItem *VT = findVTableAtOffset(ThisAdjust); + if (!VT) { + // FIXME: There really should be a vtable here. If there's not it probably + // means that the vtable is in a virtual base, which we don't yet support. + assert(!VirtualBases.empty()); + return; + } + int32_t OverrideIndex = -1; + // Now we've found the VTable. Func will not have a virtual base offset set, + // so instead we need to compare names and signatures. We iterate each item + // in the VTable. All items should already have non null entries because they + // were initialized by the intro virtual, which was guaranteed to come before. + for (auto ItemAndIndex : enumerate(VT->funcs())) { + auto Item = ItemAndIndex.value(); + assert(Item); + // If the name doesn't match, this isn't an override. Note that it's ok + // for the return type to not match (e.g. co-variant return). + if (Item->getName() != Func.getName()) { + if (Item->isDestructor() && Func.isDestructor()) { + OverrideIndex = ItemAndIndex.index(); + break; + } + continue; + } + // Now make sure it's the right overload. Get the signature of the existing + // vtable method and make sure it has the same arglist and the same cv-ness. + auto ExistingSig = Item->getSignature(); + if (ExistingSig->isConstType() != Signature->isConstType()) + continue; + if (ExistingSig->isVolatileType() != Signature->isVolatileType()) + continue; + + // Now compare arguments. Using the raw bytes of the PDB this would be + // trivial + // because there is an ArgListId and they should be identical. But DIA + // doesn't + // expose this, so the best we can do is iterate each argument and confirm + // that + // each one is identical. + if (ExistingSig->getCount() != Signature->getCount()) + continue; + bool IsMatch = true; + auto ExistingEnumerator = ExistingSig->getArguments(); + auto NewEnumerator = Signature->getArguments(); + for (uint32_t I = 0; I < ExistingEnumerator->getChildCount(); ++I) { + auto ExistingArg = ExistingEnumerator->getNext(); + auto NewArg = NewEnumerator->getNext(); + if (ExistingArg->getSymIndexId() != NewArg->getSymIndexId()) { + IsMatch = false; + break; + } + } + if (!IsMatch) + continue; + + // It's a match! Stick the new function into the VTable. + OverrideIndex = ItemAndIndex.index(); + break; + } + if (OverrideIndex == -1) { + // FIXME: This is probably due to one of the other FIXMEs in this file. + return; + } + VT->setFunction(OverrideIndex, Func); +} + +void UDTLayoutBase::addChildToLayout(std::unique_ptr<StorageItemBase> Child) { + uint32_t Begin = Child->getOffsetInParent(); + uint32_t End = Begin + Child->getSize(); + // Due to the empty base optimization, End might point outside the bounds of + // the parent class. If that happens, just clamp the value. + End = std::min(End, getClassSize()); + + UsedBytes.set(Begin, End); + while (Begin != End) { + ChildrenPerByte[Begin].push_back(Child.get()); + ++Begin; + } + + auto Loc = std::upper_bound( + ChildStorage.begin(), ChildStorage.end(), Begin, + [](uint32_t Off, const std::unique_ptr<StorageItemBase> &Item) { + return Off < Item->getOffsetInParent(); + }); + + ChildStorage.insert(Loc, std::move(Child)); +}
\ No newline at end of file |