@@ -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