//===- 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 using namespace llvm; using namespace llvm::pdb; static std::unique_ptr 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 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(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(std::move(UDT)); } } const PDBSymbolData &DataMemberLayoutItem::getDataMember() { return *dyn_cast(&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 VTable) : StorageItemBase(Parent, *VTable, "", 0, getTypeLength(*VTable)), VTable(std::move(VTable)) { auto VTableType = cast(this->VTable->getType()); ElementSize = VTableType->getLength(); Shape = unique_dyn_cast(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 UDT) : ClassLayout(*UDT) { OwnedStorage = std::move(UDT); } BaseClassLayout::BaseClassLayout(const UDTLayoutBase &Parent, std::unique_ptr 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 Bases; UniquePtrVector VTables; UniquePtrVector Members; auto Children = Sym.findAllChildren(); while (auto Child = Children->getNext()) { if (auto Base = unique_dyn_cast(Child)) { if (Base->isVirtualBaseClass()) VirtualBases.push_back(std::move(Base)); else Bases.push_back(std::move(Base)); } else if (auto Data = unique_dyn_cast(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(Child)) VTables.push_back(std::move(VT)); else if (auto Func = unique_dyn_cast(Child)) Funcs.push_back(std::move(Func)); else Other.push_back(std::move(Child)); } for (auto &Base : Bases) { auto BL = llvm::make_unique(*this, std::move(Base)); BaseClasses.push_back(BL.get()); addChildToLayout(std::move(BL)); } for (auto &VT : VTables) { auto VTLayout = llvm::make_unique(*this, std::move(VT)); VTable = VTLayout.get(); addChildToLayout(std::move(VTLayout)); continue; } for (auto &Data : Members) { auto DM = llvm::make_unique(*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 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 &Item) { return Off < Item->getOffsetInParent(); }); ChildStorage.insert(Loc, std::move(Child)); }