summaryrefslogtreecommitdiff
path: root/plugins/rtti
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2025-03-19 21:07:53 -0400
committerMason Reed <mason@vector35.com>2025-03-19 21:17:34 -0400
commit7a2f7a0526ad8bc444f04f7256fe67b9f2abc6cc (patch)
tree76fcd3f0ee7285ce66270b0f81f05f1d35140dde /plugins/rtti
parentf719d1d5565f7b7b1237b8e183c2985f006fa3e4 (diff)
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.
Diffstat (limited to 'plugins/rtti')
-rw-r--r--plugins/rtti/microsoft.cpp78
1 files changed, 62 insertions, 16 deletions
diff --git a/plugins/rtti/microsoft.cpp b/plugins/rtti/microsoft.cpp
index 1c600c9d..29a909b3 100644
--- a/plugins/rtti/microsoft.cpp
+++ b/plugins/rtti/microsoft.cpp
@@ -429,12 +429,12 @@ std::optional<ClassInfo> MicrosoftRTTIProcessor::ProcessRTTI(uint64_t coLocatorA
// Locate the current base class if we are in one.
std::optional<BaseClassInfo> currentBaseClass;
- if (coLocator->offset >= 0)
+ if (coLocator->offset > 0)
{
for (const auto &baseClassInfo: classInfo.baseClasses)
{
if (baseClassInfo.className != classInfo.className
- || baseClassInfo.offset == coLocator->offset)
+ && baseClassInfo.offset == coLocator->offset)
{
currentBaseClass = baseClassInfo;
break;
@@ -571,6 +571,22 @@ std::optional<VirtualFunctionTableInfo> MicrosoftRTTIProcessor::ProcessVFT(uint6
Type::PointerType(addrSize, vFuncType, true), vFuncName, vFuncOffset);
vFuncIdx++;
}
+
+ // TODO: Hack, we have a base class at 0 we are defining, meaning we never get the MyClass::VTable
+ // TODO: Only the MyBase::MyClass::VTable, and people who reference this type dont know that the appropriate type is the
+ // TODO: MyBase::MyClass::VTable, we should create a fake vtable for this type to redirect to the correct 0 offset base class type.
+ if (baseClassInfo.has_value() && baseClassInfo->offset == 0)
+ {
+ NamedTypeReferenceBuilder namedTypeRef;
+ namedTypeRef.SetName(vftTypeName);
+ namedTypeRef.SetTypeId(typeId);
+ auto rootRedirectType = Confidence(Type::NamedType(namedTypeRef.Finalize()), RTTI_CONFIDENCE);
+ auto rootRedirectName = fmt::format("{}::VTable", classInfo.className);
+ auto redirectTypeId = Type::GenerateAutoDebugTypeId(rootRedirectName);
+ // This will now create the redirect type MyClass::VTable for uninformed analysis to use.
+ // MyClass -> MyBase::MyClass::VTable (when MyBase offset is 0).
+ m_view->DefineType(redirectTypeId, rootRedirectName, rootRedirectType);
+ }
m_view->DefineType(typeId, vftTypeName,
Confidence(TypeBuilder::StructureType(vftBuilder.Finalize()).Finalize(), RTTI_CONFIDENCE));
}
@@ -724,41 +740,71 @@ void MicrosoftRTTIProcessor::ProcessVFT()
}
}
- auto GetCachedVFTInfo = [&](uint64_t vftAddr, ClassInfo& classInfo, const std::optional<BaseClassInfo>& baseClassInfo = std::nullopt) -> std::optional<VirtualFunctionTableInfo> {
+ auto GetCachedVFTInfo = [&](uint64_t vftAddr, ClassInfo& classInfo) -> std::optional<VirtualFunctionTableInfo> {
// Check in the cache so that we don't process vfts more than once.
auto cachedVftInfo = vftFinishedMap.find(vftAddr);
if (cachedVftInfo != vftFinishedMap.end())
return cachedVftInfo->second;
+
+ // Get the appropriate base class if there is one by reading the colocator.
+ BinaryReader reader = BinaryReader(m_view);
+ reader.Seek(vftAddr - m_view->GetAddressSize());
+ auto coLocatorAddr = reader.ReadPointer();
+ auto coLocator = ReadCompleteObjectorLocator(m_view, coLocatorAddr);
+ // TODO: This should always be valid!
+ if (!coLocator.has_value())
+ return std::nullopt;
+
+ std::optional<BaseClassInfo> baseClassInfo;
+ for (const auto& base: classInfo.baseClasses)
+ {
+ if (base.offset == coLocator->offset && base.className != classInfo.className)
+ {
+ // Take the first to match the offset with a different name from the class.
+ baseClassInfo = base;
+ break;
+ }
+ }
+
auto vftInfo = ProcessVFT(vftAddr, classInfo, baseClassInfo);
vftFinishedMap[vftAddr] = vftInfo;
return vftInfo;
};
-
- for (const auto &[coLocatorAddr, vftAddr]: vftMap)
- {
- auto classInfo = m_classInfo.find(coLocatorAddr)->second;
- for (auto& baseClassInfo : classInfo.baseClasses)
+
+ std::function<void(uint64_t)> ProcessClassAndBases = [&](uint64_t coLocatorAddr) -> void {
+ auto& classInfo = m_classInfo[coLocatorAddr];
+
+ // Process all relevant base classes first.
+ // Otherwise, when we process this class we won't have the base vft available if needed.
+ for (auto& baseInfo : classInfo.baseClasses)
{
- // Process base vtable and add it to the class info.
- for (auto& [baseCoLocAddr, classInfo] : m_classInfo)
+ for (auto& [baseCoLocAddr, baseClassInfo] : m_classInfo)
{
- if (classInfo.className == baseClassInfo.className)
+ if (baseClassInfo.className == baseInfo.className)
{
uint64_t baseVftAddr = vftMap[baseCoLocAddr];
- if (auto baseVftInfo = GetCachedVFTInfo(baseVftAddr, classInfo, baseClassInfo))
+ // Recurse into the bases bases.
+ ProcessClassAndBases(baseCoLocAddr);
+
+ // Fetch the VFT info for the base class and store it.
+ if (auto baseVftInfo = GetCachedVFTInfo(baseVftAddr, baseClassInfo))
{
- baseClassInfo.vft = baseVftInfo.value();
+ baseInfo.vft = baseVftInfo.value();
break;
}
}
}
}
+ // Process the vtable for the current class.
+ // By this point all base classes should already exist, along with their type.
+ uint64_t vftAddr = vftMap[coLocatorAddr];
if (auto vftInfo = GetCachedVFTInfo(vftAddr, classInfo))
classInfo.vft = vftInfo.value();
-
- m_classInfo[coLocatorAddr] = classInfo;
- }
+ };
+
+ for (const auto &[coLocatorAddr, _]: vftMap)
+ ProcessClassAndBases(coLocatorAddr);
auto end_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed_time = end_time - start_time;