Skip to content

Commit 7a2f7a0

Browse files
committed
Handle base class VFT processing better for MSVC RTTI
Still need to allow NTR's to work in the base structure attribute. There is still a bit of extra work that needs to be done to polish this up but there are no regressions using this.
1 parent f719d1d commit 7a2f7a0

File tree

1 file changed

+62
-16
lines changed

1 file changed

+62
-16
lines changed

plugins/rtti/microsoft.cpp

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -429,12 +429,12 @@ std::optional<ClassInfo> MicrosoftRTTIProcessor::ProcessRTTI(uint64_t coLocatorA
429429

430430
// Locate the current base class if we are in one.
431431
std::optional<BaseClassInfo> currentBaseClass;
432-
if (coLocator->offset >= 0)
432+
if (coLocator->offset > 0)
433433
{
434434
for (const auto &baseClassInfo: classInfo.baseClasses)
435435
{
436436
if (baseClassInfo.className != classInfo.className
437-
|| baseClassInfo.offset == coLocator->offset)
437+
&& baseClassInfo.offset == coLocator->offset)
438438
{
439439
currentBaseClass = baseClassInfo;
440440
break;
@@ -571,6 +571,22 @@ std::optional<VirtualFunctionTableInfo> MicrosoftRTTIProcessor::ProcessVFT(uint6
571571
Type::PointerType(addrSize, vFuncType, true), vFuncName, vFuncOffset);
572572
vFuncIdx++;
573573
}
574+
575+
// TODO: Hack, we have a base class at 0 we are defining, meaning we never get the MyClass::VTable
576+
// TODO: Only the MyBase::MyClass::VTable, and people who reference this type dont know that the appropriate type is the
577+
// TODO: MyBase::MyClass::VTable, we should create a fake vtable for this type to redirect to the correct 0 offset base class type.
578+
if (baseClassInfo.has_value() && baseClassInfo->offset == 0)
579+
{
580+
NamedTypeReferenceBuilder namedTypeRef;
581+
namedTypeRef.SetName(vftTypeName);
582+
namedTypeRef.SetTypeId(typeId);
583+
auto rootRedirectType = Confidence(Type::NamedType(namedTypeRef.Finalize()), RTTI_CONFIDENCE);
584+
auto rootRedirectName = fmt::format("{}::VTable", classInfo.className);
585+
auto redirectTypeId = Type::GenerateAutoDebugTypeId(rootRedirectName);
586+
// This will now create the redirect type MyClass::VTable for uninformed analysis to use.
587+
// MyClass -> MyBase::MyClass::VTable (when MyBase offset is 0).
588+
m_view->DefineType(redirectTypeId, rootRedirectName, rootRedirectType);
589+
}
574590
m_view->DefineType(typeId, vftTypeName,
575591
Confidence(TypeBuilder::StructureType(vftBuilder.Finalize()).Finalize(), RTTI_CONFIDENCE));
576592
}
@@ -724,41 +740,71 @@ void MicrosoftRTTIProcessor::ProcessVFT()
724740
}
725741
}
726742

727-
auto GetCachedVFTInfo = [&](uint64_t vftAddr, ClassInfo& classInfo, const std::optional<BaseClassInfo>& baseClassInfo = std::nullopt) -> std::optional<VirtualFunctionTableInfo> {
743+
auto GetCachedVFTInfo = [&](uint64_t vftAddr, ClassInfo& classInfo) -> std::optional<VirtualFunctionTableInfo> {
728744
// Check in the cache so that we don't process vfts more than once.
729745
auto cachedVftInfo = vftFinishedMap.find(vftAddr);
730746
if (cachedVftInfo != vftFinishedMap.end())
731747
return cachedVftInfo->second;
748+
749+
// Get the appropriate base class if there is one by reading the colocator.
750+
BinaryReader reader = BinaryReader(m_view);
751+
reader.Seek(vftAddr - m_view->GetAddressSize());
752+
auto coLocatorAddr = reader.ReadPointer();
753+
auto coLocator = ReadCompleteObjectorLocator(m_view, coLocatorAddr);
754+
// TODO: This should always be valid!
755+
if (!coLocator.has_value())
756+
return std::nullopt;
757+
758+
std::optional<BaseClassInfo> baseClassInfo;
759+
for (const auto& base: classInfo.baseClasses)
760+
{
761+
if (base.offset == coLocator->offset && base.className != classInfo.className)
762+
{
763+
// Take the first to match the offset with a different name from the class.
764+
baseClassInfo = base;
765+
break;
766+
}
767+
}
768+
732769
auto vftInfo = ProcessVFT(vftAddr, classInfo, baseClassInfo);
733770
vftFinishedMap[vftAddr] = vftInfo;
734771
return vftInfo;
735772
};
736-
737-
for (const auto &[coLocatorAddr, vftAddr]: vftMap)
738-
{
739-
auto classInfo = m_classInfo.find(coLocatorAddr)->second;
740-
for (auto& baseClassInfo : classInfo.baseClasses)
773+
774+
std::function<void(uint64_t)> ProcessClassAndBases = [&](uint64_t coLocatorAddr) -> void {
775+
auto& classInfo = m_classInfo[coLocatorAddr];
776+
777+
// Process all relevant base classes first.
778+
// Otherwise, when we process this class we won't have the base vft available if needed.
779+
for (auto& baseInfo : classInfo.baseClasses)
741780
{
742-
// Process base vtable and add it to the class info.
743-
for (auto& [baseCoLocAddr, classInfo] : m_classInfo)
781+
for (auto& [baseCoLocAddr, baseClassInfo] : m_classInfo)
744782
{
745-
if (classInfo.className == baseClassInfo.className)
783+
if (baseClassInfo.className == baseInfo.className)
746784
{
747785
uint64_t baseVftAddr = vftMap[baseCoLocAddr];
748-
if (auto baseVftInfo = GetCachedVFTInfo(baseVftAddr, classInfo, baseClassInfo))
786+
// Recurse into the bases bases.
787+
ProcessClassAndBases(baseCoLocAddr);
788+
789+
// Fetch the VFT info for the base class and store it.
790+
if (auto baseVftInfo = GetCachedVFTInfo(baseVftAddr, baseClassInfo))
749791
{
750-
baseClassInfo.vft = baseVftInfo.value();
792+
baseInfo.vft = baseVftInfo.value();
751793
break;
752794
}
753795
}
754796
}
755797
}
756798

799+
// Process the vtable for the current class.
800+
// By this point all base classes should already exist, along with their type.
801+
uint64_t vftAddr = vftMap[coLocatorAddr];
757802
if (auto vftInfo = GetCachedVFTInfo(vftAddr, classInfo))
758803
classInfo.vft = vftInfo.value();
759-
760-
m_classInfo[coLocatorAddr] = classInfo;
761-
}
804+
};
805+
806+
for (const auto &[coLocatorAddr, _]: vftMap)
807+
ProcessClassAndBases(coLocatorAddr);
762808

763809
auto end_time = std::chrono::high_resolution_clock::now();
764810
std::chrono::duration<double> elapsed_time = end_time - start_time;

0 commit comments

Comments
 (0)