|  | //===- UDTLayout.cpp ------------------------------------------------------===// | 
|  | // | 
|  | // 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/UDTLayout.h" | 
|  | #include "llvm/ADT/ArrayRef.h" | 
|  | #include "llvm/ADT/BitVector.h" | 
|  | #include "llvm/ADT/STLExtras.h" | 
|  | #include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" | 
|  | #include "llvm/DebugInfo/PDB/IPDBSession.h" | 
|  | #include "llvm/DebugInfo/PDB/PDBSymbol.h" | 
|  | #include "llvm/DebugInfo/PDB/PDBSymbolData.h" | 
|  | #include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" | 
|  | #include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h" | 
|  | #include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" | 
|  | #include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" | 
|  | #include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" | 
|  | #include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h" | 
|  | #include "llvm/DebugInfo/PDB/PDBTypes.h" | 
|  | #include "llvm/Support/Casting.h" | 
|  | #include <algorithm> | 
|  | #include <cassert> | 
|  | #include <cstdint> | 
|  | #include <memory> | 
|  |  | 
|  | 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(); | 
|  | } | 
|  |  | 
|  | LayoutItemBase::LayoutItemBase(const UDTLayoutBase *Parent, | 
|  | const PDBSymbol *Symbol, const std::string &Name, | 
|  | uint32_t OffsetInParent, uint32_t Size, | 
|  | bool IsElided) | 
|  | : Symbol(Symbol), Parent(Parent), Name(Name), | 
|  | OffsetInParent(OffsetInParent), SizeOf(Size), LayoutSize(Size), | 
|  | IsElided(IsElided) { | 
|  | UsedBytes.resize(SizeOf, true); | 
|  | } | 
|  |  | 
|  | uint32_t LayoutItemBase::deepPaddingSize() const { | 
|  | return UsedBytes.size() - UsedBytes.count(); | 
|  | } | 
|  |  | 
|  | uint32_t LayoutItemBase::tailPadding() const { | 
|  | int Last = UsedBytes.find_last(); | 
|  |  | 
|  | return UsedBytes.size() - (Last + 1); | 
|  | } | 
|  |  | 
|  | DataMemberLayoutItem::DataMemberLayoutItem( | 
|  | const UDTLayoutBase &Parent, std::unique_ptr<PDBSymbolData> Member) | 
|  | : LayoutItemBase(&Parent, Member.get(), Member->getName(), | 
|  | Member->getOffset(), getTypeLength(*Member), false), | 
|  | DataMember(std::move(Member)) { | 
|  | auto Type = DataMember->getType(); | 
|  | if (auto UDT = unique_dyn_cast<PDBSymbolTypeUDT>(Type)) { | 
|  | UdtLayout = std::make_unique<ClassLayout>(std::move(UDT)); | 
|  | UsedBytes = UdtLayout->usedBytes(); | 
|  | } | 
|  | } | 
|  |  | 
|  | VBPtrLayoutItem::VBPtrLayoutItem(const UDTLayoutBase &Parent, | 
|  | std::unique_ptr<PDBSymbolTypeBuiltin> Sym, | 
|  | uint32_t Offset, uint32_t Size) | 
|  | : LayoutItemBase(&Parent, Sym.get(), "<vbptr>", Offset, Size, false), | 
|  | Type(std::move(Sym)) { | 
|  | } | 
|  |  | 
|  | const PDBSymbolData &DataMemberLayoutItem::getDataMember() { | 
|  | return *cast<PDBSymbolData>(Symbol); | 
|  | } | 
|  |  | 
|  | bool DataMemberLayoutItem::hasUDTLayout() const { return UdtLayout != nullptr; } | 
|  |  | 
|  | const ClassLayout &DataMemberLayoutItem::getUDTLayout() const { | 
|  | return *UdtLayout; | 
|  | } | 
|  |  | 
|  | VTableLayoutItem::VTableLayoutItem(const UDTLayoutBase &Parent, | 
|  | std::unique_ptr<PDBSymbolTypeVTable> VT) | 
|  | : LayoutItemBase(&Parent, VT.get(), "<vtbl>", 0, getTypeLength(*VT), false), | 
|  | VTable(std::move(VT)) { | 
|  | auto VTableType = cast<PDBSymbolTypePointer>(VTable->getType()); | 
|  | ElementSize = VTableType->getLength(); | 
|  | } | 
|  |  | 
|  | UDTLayoutBase::UDTLayoutBase(const UDTLayoutBase *Parent, const PDBSymbol &Sym, | 
|  | const std::string &Name, uint32_t OffsetInParent, | 
|  | uint32_t Size, bool IsElided) | 
|  | : LayoutItemBase(Parent, &Sym, Name, OffsetInParent, Size, IsElided) { | 
|  | // UDT storage comes from a union of all the children's storage, so start out | 
|  | // uninitialized. | 
|  | UsedBytes.reset(0, Size); | 
|  |  | 
|  | initializeChildren(Sym); | 
|  | if (LayoutSize < Size) | 
|  | UsedBytes.resize(LayoutSize); | 
|  | } | 
|  |  | 
|  | uint32_t UDTLayoutBase::tailPadding() const { | 
|  | uint32_t Abs = LayoutItemBase::tailPadding(); | 
|  | if (!LayoutItems.empty()) { | 
|  | const LayoutItemBase *Back = LayoutItems.back(); | 
|  | uint32_t ChildPadding = Back->LayoutItemBase::tailPadding(); | 
|  | if (Abs < ChildPadding) | 
|  | Abs = 0; | 
|  | else | 
|  | Abs -= ChildPadding; | 
|  | } | 
|  | return Abs; | 
|  | } | 
|  |  | 
|  | ClassLayout::ClassLayout(const PDBSymbolTypeUDT &UDT) | 
|  | : UDTLayoutBase(nullptr, UDT, UDT.getName(), 0, UDT.getLength(), false), | 
|  | UDT(UDT) { | 
|  | ImmediateUsedBytes.resize(SizeOf, false); | 
|  | for (auto &LI : LayoutItems) { | 
|  | uint32_t Begin = LI->getOffsetInParent(); | 
|  | uint32_t End = Begin + LI->getLayoutSize(); | 
|  | End = std::min(SizeOf, End); | 
|  | ImmediateUsedBytes.set(Begin, End); | 
|  | } | 
|  | } | 
|  |  | 
|  | ClassLayout::ClassLayout(std::unique_ptr<PDBSymbolTypeUDT> UDT) | 
|  | : ClassLayout(*UDT) { | 
|  | OwnedStorage = std::move(UDT); | 
|  | } | 
|  |  | 
|  | uint32_t ClassLayout::immediatePadding() const { | 
|  | return SizeOf - ImmediateUsedBytes.count(); | 
|  | } | 
|  |  | 
|  | BaseClassLayout::BaseClassLayout(const UDTLayoutBase &Parent, | 
|  | uint32_t OffsetInParent, bool Elide, | 
|  | std::unique_ptr<PDBSymbolTypeBaseClass> B) | 
|  | : UDTLayoutBase(&Parent, *B, B->getName(), OffsetInParent, B->getLength(), | 
|  | Elide), | 
|  | Base(std::move(B)) { | 
|  | if (isEmptyBase()) { | 
|  | // Special case an empty base so that it doesn't get treated as padding. | 
|  | UsedBytes.resize(1); | 
|  | UsedBytes.set(0); | 
|  | } | 
|  | IsVirtualBase = Base->isVirtualBaseClass(); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | UniquePtrVector<PDBSymbolTypeBaseClass> VirtualBaseSyms; | 
|  |  | 
|  | auto Children = Sym.findAllChildren(); | 
|  | while (auto Child = Children->getNext()) { | 
|  | if (auto Base = unique_dyn_cast<PDBSymbolTypeBaseClass>(Child)) { | 
|  | if (Base->isVirtualBaseClass()) | 
|  | VirtualBaseSyms.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(Data)); | 
|  | } 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)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // We don't want to have any re-allocations in the list of bases, so make | 
|  | // sure to reserve enough space so that our ArrayRefs don't get invalidated. | 
|  | AllBases.reserve(Bases.size() + VirtualBaseSyms.size()); | 
|  |  | 
|  | // Only add non-virtual bases to the class first.  Only at the end of the | 
|  | // class, after all non-virtual bases and data members have been added do we | 
|  | // add virtual bases.  This way the offsets are correctly aligned when we go | 
|  | // to lay out virtual bases. | 
|  | for (auto &Base : Bases) { | 
|  | uint32_t Offset = Base->getOffset(); | 
|  | // Non-virtual bases never get elided. | 
|  | auto BL = std::make_unique<BaseClassLayout>(*this, Offset, false, | 
|  | std::move(Base)); | 
|  |  | 
|  | AllBases.push_back(BL.get()); | 
|  | addChildToLayout(std::move(BL)); | 
|  | } | 
|  | NonVirtualBases = AllBases; | 
|  |  | 
|  | assert(VTables.size() <= 1); | 
|  | if (!VTables.empty()) { | 
|  | auto VTLayout = | 
|  | std::make_unique<VTableLayoutItem>(*this, std::move(VTables[0])); | 
|  |  | 
|  | VTable = VTLayout.get(); | 
|  |  | 
|  | addChildToLayout(std::move(VTLayout)); | 
|  | } | 
|  |  | 
|  | for (auto &Data : Members) { | 
|  | auto DM = std::make_unique<DataMemberLayoutItem>(*this, std::move(Data)); | 
|  |  | 
|  | addChildToLayout(std::move(DM)); | 
|  | } | 
|  |  | 
|  | // Make sure add virtual bases before adding functions, since functions may be | 
|  | // overrides of virtual functions declared in a virtual base, so the VTables | 
|  | // and virtual intros need to be correctly initialized. | 
|  | for (auto &VB : VirtualBaseSyms) { | 
|  | int VBPO = VB->getVirtualBasePointerOffset(); | 
|  | if (!hasVBPtrAtOffset(VBPO)) { | 
|  | if (auto VBP = VB->getRawSymbol().getVirtualBaseTableType()) { | 
|  | auto VBPL = std::make_unique<VBPtrLayoutItem>(*this, std::move(VBP), | 
|  | VBPO, VBP->getLength()); | 
|  | VBPtr = VBPL.get(); | 
|  | addChildToLayout(std::move(VBPL)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Virtual bases always go at the end.  So just look for the last place we | 
|  | // ended when writing something, and put our virtual base there. | 
|  | // Note that virtual bases get elided unless this is a top-most derived | 
|  | // class. | 
|  | uint32_t Offset = UsedBytes.find_last() + 1; | 
|  | bool Elide = (Parent != nullptr); | 
|  | auto BL = | 
|  | std::make_unique<BaseClassLayout>(*this, Offset, Elide, std::move(VB)); | 
|  | AllBases.push_back(BL.get()); | 
|  |  | 
|  | // Only lay this virtual base out directly inside of *this* class if this | 
|  | // is a top-most derived class.  Keep track of it regardless, but only | 
|  | // physically lay it out if it's a topmost derived class. | 
|  | addChildToLayout(std::move(BL)); | 
|  | } | 
|  | VirtualBases = makeArrayRef(AllBases).drop_front(NonVirtualBases.size()); | 
|  |  | 
|  | if (Parent != nullptr) | 
|  | LayoutSize = UsedBytes.find_last() + 1; | 
|  | } | 
|  |  | 
|  | bool UDTLayoutBase::hasVBPtrAtOffset(uint32_t Off) const { | 
|  | if (VBPtr && VBPtr->getOffsetInParent() == Off) | 
|  | return true; | 
|  | for (BaseClassLayout *BL : AllBases) { | 
|  | if (BL->hasVBPtrAtOffset(Off - BL->getOffsetInParent())) | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void UDTLayoutBase::addChildToLayout(std::unique_ptr<LayoutItemBase> Child) { | 
|  | uint32_t Begin = Child->getOffsetInParent(); | 
|  |  | 
|  | if (!Child->isElided()) { | 
|  | BitVector ChildBytes = Child->usedBytes(); | 
|  |  | 
|  | // Suppose the child occupies 4 bytes starting at offset 12 in a 32 byte | 
|  | // class.  When we call ChildBytes.resize(32), the Child's storage will | 
|  | // still begin at offset 0, so we need to shift it left by offset bytes | 
|  | // to get it into the right position. | 
|  | ChildBytes.resize(UsedBytes.size()); | 
|  | ChildBytes <<= Child->getOffsetInParent(); | 
|  | UsedBytes |= ChildBytes; | 
|  |  | 
|  | if (ChildBytes.count() > 0) { | 
|  | auto Loc = std::upper_bound(LayoutItems.begin(), LayoutItems.end(), Begin, | 
|  | [](uint32_t Off, const LayoutItemBase *Item) { | 
|  | return (Off < Item->getOffsetInParent()); | 
|  | }); | 
|  |  | 
|  | LayoutItems.insert(Loc, Child.get()); | 
|  | } | 
|  | } | 
|  |  | 
|  | ChildStorage.push_back(std::move(Child)); | 
|  | } |