summaryrefslogtreecommitdiff
path: root/plugins/rtti/itanium.cpp
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2025-03-01 00:34:12 -0500
committerMason Reed <mason@vector35.com>2025-03-19 21:17:34 -0400
commitf719d1d5565f7b7b1237b8e183c2985f006fa3e4 (patch)
tree0fbb2c1af1254add49049abdec3f935669d658de /plugins/rtti/itanium.cpp
parent93e92844d77b72f07bd7563211d60dc3595cc2e0 (diff)
Refactor and fixup MSVC and Itanium RTTI
Bunch of misc fixes and performance improvements
Diffstat (limited to 'plugins/rtti/itanium.cpp')
-rw-r--r--plugins/rtti/itanium.cpp340
1 files changed, 272 insertions, 68 deletions
diff --git a/plugins/rtti/itanium.cpp b/plugins/rtti/itanium.cpp
index e1af724b..0714d17d 100644
--- a/plugins/rtti/itanium.cpp
+++ b/plugins/rtti/itanium.cpp
@@ -16,6 +16,8 @@ TypeInfo::TypeInfo(BinaryView *view, uint64_t address)
reader.Seek(address);
base = reader.ReadPointer();
auto typeNameAddr = reader.ReadPointer();
+ if (!view->IsValidOffset(typeNameAddr))
+ return;
reader.Seek(typeNameAddr);
type_name = reader.ReadCString(512);
}
@@ -36,8 +38,7 @@ BaseClassTypeInfo::BaseClassTypeInfo(BinaryView *view, uint64_t address)
reader.Seek(address);
base_type = reader.ReadPointer();
offset_flags = reader.Read32();
- // TODO: Test this...
- offset_flags_masks = static_cast<OffsetFlagsMasks>(reader.Read32());
+ offset_flags_masks = reader.Read32();
}
@@ -49,12 +50,11 @@ VMIClassTypeInfo::VMIClassTypeInfo(BinaryView *view, uint64_t address) : ClassTy
flags = reader.Read32();
base_count = reader.Read32();
base_info = {};
- for (size_t i = 1; i < base_count; i++)
+ for (size_t i = 0; i < base_count; i++)
{
- // TODO: Verify this is correct.
uint64_t currentBaseAddr = reader.GetOffset();
- base_info.emplace_back(view, reader.GetOffset());
- reader.Seek(currentBaseAddr + 12);
+ base_info.emplace_back(view, currentBaseAddr);
+ reader.Seek(currentBaseAddr + 0x10);
}
}
@@ -183,13 +183,37 @@ Ref<Type> BaseClassTypeInfoType(BinaryView *view)
}
-Ref<Type> VMIClassTypeInfoType(BinaryView *view, int baseCount)
+Ref<Type> VMIFlagsMasksType(BinaryView *view)
+{
+ auto typeId = Type::GenerateAutoTypeId(TYPE_SOURCE_ITANIUM, QualifiedName("VMIFlagsMasks"));
+ Ref<Type> typeCache = view->GetTypeById(typeId);
+
+ if (typeCache == nullptr)
+ {
+ Ref<Architecture> arch = view->GetDefaultArchitecture();
+ Ref<Type> uintType = Type::IntegerType(4, false);
+
+ EnumerationBuilder enumerationBuilder;
+ enumerationBuilder.AddMemberWithValue("__non_diamond_repeat_mask", 0x1);
+ enumerationBuilder.AddMemberWithValue("__diamond_shaped_mask", 0x2);
+
+ Ref<Type> enumerationType = TypeBuilder::EnumerationType(arch, enumerationBuilder.Finalize()).Finalize();
+ view->DefineType(typeId, QualifiedName("__cxxabiv1::__flags_masks"), enumerationType);
+
+ typeCache = view->GetTypeById(typeId);
+ }
+
+ return typeCache;
+}
+
+
+Ref<Type> VMIClassTypeInfoType(BinaryView *view, uint64_t baseCount)
{
Ref<Architecture> arch = view->GetDefaultArchitecture();
Ref<Type> uintType = Type::IntegerType(4, false);
StructureBuilder structureBuilder;
- structureBuilder.AddMemberAtOffset(uintType, "__flags", 0x10);
+ structureBuilder.AddMemberAtOffset(VMIFlagsMasksType(view), "__flags", 0x10);
structureBuilder.AddMemberAtOffset(uintType, "__base_count", 0x14);
Ref<Type> baseInfoType = Type::ArrayType(BaseClassTypeInfoType(view), baseCount);
structureBuilder.AddMemberAtOffset(baseInfoType, "__base_info", 0x18);
@@ -238,9 +262,45 @@ std::optional<TypeInfoVariant> ReadTypeInfoVariant(BinaryView *view, uint64_t ob
}
+std::optional<BaseClassInfo> ItaniumRTTIProcessor::ProcessVFTBaseClassInfo(uint64_t vftAddr, ClassInfo &classInfo)
+{
+ BinaryReader reader = BinaryReader(m_view);
+ // Because we have this we _need_ to have the adjustment stuff.
+ // NOTE: We assume two 0x4 ints with the first being what we want.
+ reader.Seek(vftAddr - 0x10);
+
+ auto adjustmentOffset = static_cast<int32_t>(reader.Read32());
+ auto baseIdx = static_cast<int32_t>(reader.Read32());
+ uint64_t classOffset = std::abs(adjustmentOffset);
+
+ std::optional<BaseClassInfo> selectedBaseClassInfo = std::nullopt;
+ // Assuming we do not have a baseClassInfo already passed we can deduce it here.
+ for (auto& baseClass : classInfo.baseClasses)
+ {
+ // if (baseClass.offset == 0)
+ // {
+ // // If the base class is at offset 0 that means it has yet to be adjusted.
+ // // NOTE: This should only happen for `TIVSIClass`. If this assigns more than
+ // // one base class to this offset we are screwed.
+ // baseClass.offset = classOffset;
+ // LogInfo("Adjusting base class offset for %llx to %llx", vftAddr, classOffset);
+ // }
+
+ if (baseClass.offset == classOffset)
+ {
+ // Found the appropriate base class for this vtable.
+ selectedBaseClassInfo = baseClass;
+ }
+ }
+
+ // Return the selected base class for use in later processing such as `ProcessVFT`.
+ return selectedBaseClassInfo;
+}
+
+
std::optional<ClassInfo> ItaniumRTTIProcessor::ProcessRTTI(uint64_t objectAddr)
{
- // TODO: You cant get subobject offsets from rtti, its stored above this ptr in vtable.
+ // TODO: You cant get sub-object offsets from rtti, its stored above this ptr in vtable.
// Get object as type info then check to see if it's valid.
auto typeInfoVariant = ReadTypeInfoVariant(m_view, objectAddr);
if (!typeInfoVariant.has_value())
@@ -258,35 +318,89 @@ std::optional<ClassInfo> ItaniumRTTIProcessor::ProcessRTTI(uint64_t objectAddr)
m_view->UndefineAutoSymbol(typeInfoSymbol);
m_view->DefineAutoSymbol(new Symbol{DataSymbol, typeInfoName, objectAddr});
+ auto nameFromTypeInfoSymbol = [&](uint64_t addr) -> std::optional<std::string> {
+ auto sym = m_view->GetSymbolByAddress(addr);
+ if (sym == nullptr || sym->GetType() != ExternalSymbol)
+ return std::nullopt;
+ auto symName = sym->GetShortName();
+ // Remove type info prefix.
+ if (symName.rfind("_typeinfo_for_", 0) != 0)
+ return std::nullopt;
+ return symName.substr(14);
+ };
+
if (typeInfoVariant == TIVSIClass)
{
// Read the base class.
auto siClassTypeInfo = SIClassTypeInfo(m_view, objectAddr);
auto subTypeInfoVariant = ReadTypeInfoVariant(m_view, siClassTypeInfo.base_type);
+ std::string subTypeName;
if (!subTypeInfoVariant.has_value())
- return std::nullopt;
- auto subTypeInfo = TypeInfo(m_view, siClassTypeInfo.base_type);
+ {
+ // Allow externals to be used in place of a backed subtype.
+ // TODO: We should probably warn that vtables will likely be inaccurate.
+ // TODO: Because we wont know what offsets are valid.
+ auto externTypeName = nameFromTypeInfoSymbol(siClassTypeInfo.base_type);
+ if (!externTypeName.has_value())
+ return std::nullopt;
+ m_logger->LogDebug("Non-backed external subtype for %llx", objectAddr);
+ subTypeName = externTypeName.value();
+ }
+ else
+ {
+ auto subTypeInfo = TypeInfo(m_view, siClassTypeInfo.base_type);
+ subTypeName = subTypeInfo.type_name;
+ }
// Demangle base class name and set
- auto baseClassName = DemangleNameItanium(m_view, allowMangledClassNames, subTypeInfo.type_name);
+ auto baseClassName = DemangleNameItanium(m_view, allowMangledClassNames, subTypeName);
if (!baseClassName.has_value())
{
m_logger->LogWarn("Skipping base class with mangled name %llx", siClassTypeInfo.base_type);
return std::nullopt;
}
- classInfo.baseClassName = baseClassName;
// NOTE: The base class offset is not able to be resolved here.
// NOTE: To resolve the base class offset you must go to the vtable.
+ uint64_t baseClassOffset = 0;
+ auto subBaseClassInfo = BaseClassInfo {baseClassName.value(), baseClassOffset};
+ classInfo.baseClasses.emplace_back(subBaseClassInfo);
m_view->DefineDataVariable(objectAddr, Confidence(SIClassTypeInfoType(m_view), 255));
}
else if (typeInfoVariant == TIVVMIClass)
{
- // TODO: Read multiple base classes.
auto vmiClassTypeInfo = VMIClassTypeInfo(m_view, objectAddr);
m_view->DefineDataVariable(objectAddr, Confidence(VMIClassTypeInfoType(m_view, vmiClassTypeInfo.base_count), 255));
+ for (const auto& baseInfo : vmiClassTypeInfo.base_info)
+ {
+ // Remove the flags and just get the offset
+ auto baseTypeInfoVariant = ReadTypeInfoVariant(m_view, baseInfo.base_type);
+ std::string subTypeName;
+ if (!baseTypeInfoVariant.has_value())
+ {
+ // Allow externals to be used in place of a backed base type.
+ auto externTypeName = nameFromTypeInfoSymbol(baseInfo.base_type);
+ if (!externTypeName.has_value())
+ return std::nullopt;
+ m_logger->LogDebug("Non-backed external subtype for %llx", objectAddr);
+ subTypeName = externTypeName.value();
+ } else
+ {
+ auto baseTypeInfo = TypeInfo(m_view, baseInfo.base_type);
+ subTypeName = baseTypeInfo.type_name;
+ }
+ auto baseClassName = DemangleNameItanium(m_view, allowMangledClassNames, subTypeName);
+ if (!baseClassName.has_value())
+ {
+ m_logger->LogWarn("Skipping base class with mangled name %llx", baseInfo.base_type);
+ continue;
+ }
+ // Shift off the flag bits.
+ uint64_t offset = baseInfo.offset_flags >> 8;
+ auto baseClassInfo = BaseClassInfo {baseClassName.value(), offset};
+ classInfo.baseClasses.emplace_back(baseClassInfo);
+ }
}
else
{
- // auto classTypeInfo = ClassTypeInfo(m_view, objectAddr);
m_view->DefineDataVariable(objectAddr, Confidence(ClassTypeInfoType(m_view), 255));
}
@@ -294,13 +408,13 @@ std::optional<ClassInfo> ItaniumRTTIProcessor::ProcessRTTI(uint64_t objectAddr)
}
-std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVFT(uint64_t vftAddr, ClassInfo &classInfo)
+std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVFT(uint64_t vftAddr, ClassInfo &classInfo, std::optional<BaseClassInfo> baseClassInfo)
{
VirtualFunctionTableInfo vftInfo = {vftAddr};
BinaryReader reader = BinaryReader(m_view);
reader.Seek(vftAddr);
// Gather all virtual functions
- std::vector<Ref<Function> > virtualFunctions = {};
+ std::vector<VirtualFunctionInfo> virtualFunctions = {};
while (true)
{
uint64_t vFuncAddr = reader.ReadPointer();
@@ -310,17 +424,30 @@ std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVFT(uint64_
Ref<Segment> segment = m_view->GetSegmentAt(vFuncAddr);
if (segment == nullptr || !(segment->GetFlags() & (SegmentExecutable | SegmentDenyWrite)))
{
- // Last CompleteObjectLocator or hit the next CompleteObjectLocator
- break;
+ // TODO: Sometimes vFunc idx will be zeroed iirc.
+ // We allow vfuncs to point to extern functions.
+ auto vFuncSym = m_view->GetSymbolByAddress(vFuncAddr);
+ if (!vFuncSym)
+ break;
+ DataVariable dv;
+ bool foundDv = m_view->GetDataVariableAtAddress(vFuncAddr, dv);
+ // Last virtual function, or hit the next vtable.
+ if (!foundDv || !dv.type->m_object)
+ break;
+ // Void externs are very likely to be a func.
+ // TODO: Add some sanity checks for this!
+ if (!dv.type->IsFunction() && !(dv.type->IsVoid() && vFuncSym->GetType() == ExternalSymbol))
+ break;
+ }
+ else
+ {
+ // TODO: Is likely a function check here?
+ m_logger->LogDebug("Discovered function from virtual function table... %llx", vFuncAddr);
+ m_view->AddFunctionForAnalysis(m_view->GetDefaultPlatform(), vFuncAddr, true);
}
- // TODO: Sometimes vFunc idx will be zeroed.
- // TODO: Is likely a function check here?
- m_logger->LogDebug("Discovered function from virtual function table... %llx", vFuncAddr);
- auto vFunc = m_view->AddFunctionForAnalysis(m_view->GetDefaultPlatform(), vFuncAddr, true);
- funcs.emplace_back(vFunc);
}
// Only ever add one function.
- virtualFunctions.emplace_back(funcs.front());
+ virtualFunctions.emplace_back(VirtualFunctionInfo{vFuncAddr});
}
if (virtualFunctions.empty())
@@ -329,29 +456,12 @@ std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVFT(uint64_
return std::nullopt;
}
- // All vft verification has been done, we can write the classOffset now.
- if (classInfo.baseClassName.has_value() && !classInfo.classOffset.has_value())
- {
- // Because we have this we _need_ to have the adjustment stuff.
- // NOTE: We assume two 0x4 ints with the first being what we want.
- // NOTE: This is where we actually classOffset is pulled.
- reader.Seek(vftAddr - 0x10);
- auto adjustmentOffset = static_cast<int32_t>(reader.Read32());
- auto _what = static_cast<int32_t>(reader.Read32());
- uint64_t classOffset = std::abs(adjustmentOffset);
- classInfo.classOffset = classOffset;
- }
-
-
- for (auto &func: virtualFunctions)
- vftInfo.virtualFunctions.emplace_back(VirtualFunctionInfo{func->GetStart()});
-
// Create virtual function table type
auto vftTypeName = fmt::format("{}::VTable", classInfo.className);
- if (classInfo.baseClassName.has_value())
+ if (baseClassInfo.has_value())
{
// TODO: What is the correct form for the name?
- vftTypeName = fmt::format("{}::{}", classInfo.baseClassName.value(), vftTypeName);
+ vftTypeName = fmt::format("{}::{}", baseClassInfo->className, vftTypeName);
}
// TODO: Hack the debug type id is used here to allow the PDB type (debug info) to overwrite the RTTI vtable type.
auto typeId = Type::GenerateAutoDebugTypeId(vftTypeName);
@@ -368,17 +478,18 @@ std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVFT(uint64_
auto vftSize = virtualFunctions.size() * addrSize;
vftBuilder.SetWidth(vftSize);
- if (auto baseVft = classInfo.baseVft)
+ if (baseClassInfo.has_value() && baseClassInfo->vft.has_value())
{
- if (baseVft->virtualFunctions.size() <= virtualFunctions.size())
+ if (baseClassInfo->vft->virtualFunctions.size() <= virtualFunctions.size())
{
// Adjust the current vFunc index to the end of the shared vFuncs.
- vFuncIdx = baseVft->virtualFunctions.size();
+ vFuncIdx = baseClassInfo->vft->virtualFunctions.size();
virtualFunctions.erase(virtualFunctions.begin(), virtualFunctions.begin() + vFuncIdx);
// We should set the vtable as a base class so that xrefs are propagated (among other things).
// NOTE: this means that `this` params will be assumed pre-adjusted, this is normally fine assuming type propagation
// NOTE: never occurs on the vft types. Other-wise we need to change this.
- auto baseVftTypeName = fmt::format("{}::VTable", classInfo.baseClassName.value());
+ // TODO: Different type name please lol
+ auto baseVftTypeName = fmt::format("{}::VTable", baseClassInfo->className);
NamedTypeReferenceBuilder baseVftNTR;
baseVftNTR.SetName(baseVftTypeName);
// Width is unresolved here so that we can keep non-base vfuncs un-inherited.
@@ -393,20 +504,46 @@ std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVFT(uint64_
for (auto &&vFunc: virtualFunctions)
{
+ // NOTE: The analyzed function type might not be available here.
+ auto vFuncAnalysis = m_view->GetAnalysisFunctionsForAddress(vFunc.funcAddr);
+ Ref<Type> vFuncType = nullptr;
+ Ref<Symbol> vFuncSym = nullptr;
+ if (!vFuncAnalysis.empty())
+ {
+ vFuncType = vFuncAnalysis[0]->GetType();
+ vFuncSym = vFuncAnalysis[0]->GetSymbol();
+ } else
+ {
+ DataVariable dv;
+ bool foundDv = m_view->GetDataVariableAtAddress(vFunc.funcAddr, dv);
+ if (!foundDv)
+ {
+ m_logger->LogWarn("Skipping vfunc with no type... %llx", vFunc.funcAddr);
+ return std::nullopt;
+ }
+ vFuncType = dv.type.GetValue();
+
+ vFuncSym = m_view->GetSymbolByAddress(vFunc.funcAddr);
+ if (vFuncSym == nullptr)
+ {
+ m_logger->LogWarn("Skipping vfunc with no symbol... %llx", vFunc.funcAddr);
+ return std::nullopt;
+ }
+ }
+
auto vFuncName = fmt::format("vFunc_{}", vFuncIdx);
// If we have a better name, use it.
- auto vFuncSymName = vFunc->GetSymbol()->GetShortName();
+ auto vFuncSymName = vFuncSym->GetShortName();
if (vFuncSymName.compare(0, 4, "sub_") != 0)
- vFuncName = vFunc->GetSymbol()->GetShortName();
+ vFuncName = vFuncSym->GetShortName();
// MyClass::func -> func
std::size_t pos = vFuncName.rfind("::");
if (pos != std::string::npos)
vFuncName = vFuncName.substr(pos + 2);
- // NOTE: The analyzed function type might not be available here.
auto vFuncOffset = vFuncIdx * addrSize;
vftBuilder.AddMemberAtOffset(
- Type::PointerType(addrSize, vFunc->GetType(), true), vFuncName, vFuncOffset);
+ Type::PointerType(addrSize, vFuncType, true), vFuncName, vFuncOffset);
vFuncIdx++;
}
m_view->DefineType(typeId, vftTypeName,
@@ -415,8 +552,8 @@ std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVFT(uint64_
auto vftName = fmt::format("_vtable_for_{}", classInfo.className);
// TODO: How to display base classes?
- if (classInfo.baseClassName.has_value())
- vftName += fmt::format("{{for `{}'}}", classInfo.baseClassName.value());
+ if (baseClassInfo.has_value())
+ vftName += fmt::format("{{for `{}'}}", baseClassInfo->className);
auto vftSymbol = m_view->GetSymbolByAddress(vftAddr);
if (vftSymbol != nullptr)
m_view->UndefineAutoSymbol(vftSymbol);
@@ -459,6 +596,7 @@ void ItaniumRTTIProcessor::ProcessRTTI()
}
};
+ m_view->BeginBulkModifySymbols();
// Scan data sections for rtti.
for (const Ref<Section> &section: m_view->GetSections())
{
@@ -468,6 +606,49 @@ void ItaniumRTTIProcessor::ProcessRTTI()
scan(section);
}
}
+ m_view->EndBulkModifySymbols();
+
+ // Go through all classes and recurse into the base classes using the base class name
+ for (auto &[classAddr, classInfo]: m_classInfo)
+ {
+ std::set<std::string> visitedBases;
+ std::deque<BaseClassInfo> baseQueue(classInfo.baseClasses.begin(), classInfo.baseClasses.end());
+
+ while (!baseQueue.empty())
+ {
+ BaseClassInfo baseClass = baseQueue.front();
+ baseQueue.pop_front();
+
+ if (visitedBases.find(baseClass.className) != visitedBases.end())
+ continue;
+
+ visitedBases.insert(baseClass.className);
+
+ auto baseClassIt = std::find_if(m_classInfo.begin(), m_classInfo.end(),
+ [&](const auto &item) {
+ return item.second.className == baseClass.className;
+ });
+
+ if (baseClassIt != m_classInfo.end())
+ {
+ const ClassInfo &nestedBaseClassInfo = baseClassIt->second;
+ baseQueue.insert(baseQueue.end(), nestedBaseClassInfo.baseClasses.begin(),
+ nestedBaseClassInfo.baseClasses.end());
+ }
+
+ classInfo.baseClasses.push_back(baseClass);
+ }
+
+ // Remove duplicates in the baseClasses vector while preserving order
+ std::sort(classInfo.baseClasses.begin(), classInfo.baseClasses.end(),
+ [](const BaseClassInfo &a, const BaseClassInfo &b) { return a.className < b.className; });
+
+ classInfo.baseClasses.erase(
+ std::unique(classInfo.baseClasses.begin(), classInfo.baseClasses.end(),
+ [](const BaseClassInfo &a, const BaseClassInfo &b) { return a.className == b.className; }),
+ classInfo.baseClasses.end()
+ );
+ }
auto end_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed_time = end_time - start_time;
@@ -477,7 +658,8 @@ void ItaniumRTTIProcessor::ProcessRTTI()
void ItaniumRTTIProcessor::ProcessVFT()
{
- std::map<uint64_t, uint64_t> vftMap = {};
+ BinaryReader optReader = BinaryReader(m_view);
+ std::map<uint64_t, std::set<uint64_t>> vftMap = {};
std::map<uint64_t, std::optional<VirtualFunctionTableInfo>> vftFinishedMap = {};
auto start_time = std::chrono::high_resolution_clock::now();
for (auto &[coLocatorAddr, classInfo]: m_classInfo)
@@ -488,15 +670,21 @@ void ItaniumRTTIProcessor::ProcessVFT()
DataVariable dv;
if (m_view->GetDataVariableAtAddress(ref, dv) && m_classInfo.find(dv.address) != m_classInfo.end())
continue;
+ // Verify that there is two 4 byte values above the type info pointer
+ optReader.Seek(ref - 8);
+ auto beforeTypeInfoRef = optReader.ReadPointer();
+ if (m_view->IsValidOffset(beforeTypeInfoRef))
+ continue;
// TODO: This is not pointing at where it should, remember that the vtable will be inside another structure.
auto vftAddr = ref + m_view->GetAddressSize();
- vftMap[coLocatorAddr] = vftAddr;
+ // Found a vtable reference to colocator
+ // TODO: Access check here.
+ vftMap[coLocatorAddr].insert(vftAddr);
}
}
if (virtualFunctionTableSweep)
{
- BinaryReader optReader = BinaryReader(m_view);
auto addrSize = m_view->GetAddressSize();
auto scan = [&](const Ref<Segment> &segment) {
uint64_t startAddr = segment->GetStart();
@@ -509,7 +697,7 @@ void ItaniumRTTIProcessor::ProcessVFT()
if (coLocator == m_classInfo.end())
continue;
// Found a vtable reference to colocator.
- vftMap[coLocatorAddr] = vtableAddr + addrSize;
+ // vftMap[coLocatorAddr] = vtableAddr + addrSize;
}
};
@@ -536,27 +724,35 @@ void ItaniumRTTIProcessor::ProcessVFT()
auto cachedVftInfo = vftFinishedMap.find(vftAddr);
if (cachedVftInfo != vftFinishedMap.end())
return cachedVftInfo->second;
- auto vftInfo = ProcessVFT(vftAddr, classInfo);
+ // We need to have base class info available here.
+ // This works by reading off the adjustment and keying into the bases.
+ // If there is a base at that adjustment we assume this vtable we are creating is for that.
+ auto selectedBaseClass = ProcessVFTBaseClassInfo(vftAddr, classInfo);
+ auto vftInfo = ProcessVFT(vftAddr, classInfo, selectedBaseClass);
vftFinishedMap[vftAddr] = vftInfo;
return vftInfo;
};
- for (const auto &[coLocatorAddr, vftAddr]: vftMap)
- {
+ // Adds the VFT entries in class info and base class info.
+ // TODO: This is so cursed.
+ auto populateVftEntries = [&](uint64_t coLocatorAddr, uint64_t vftAddr) {
auto classInfo = m_classInfo.find(coLocatorAddr)->second;
- if (classInfo.baseClassName.has_value())
+ for (auto& baseClassInfo : classInfo.baseClasses)
{
// Process base vtable and add it to the class info.
- for (auto [baseCoLocAddr, baseClassInfo] : m_classInfo)
+ for (auto& [baseCoLocAddr, bClassInfo] : m_classInfo)
{
- if (baseClassInfo.className == classInfo.baseClassName.value())
+ if (bClassInfo.className == baseClassInfo.className)
{
- uint64_t baseVftAddr = vftMap[baseCoLocAddr];
- if (auto baseVftInfo = GetCachedVFTInfo(baseVftAddr, baseClassInfo))
+ // Recurse into base class and populate all of its vtables.
+ for (auto& baseVftAddr : vftMap[baseCoLocAddr])
{
- classInfo.baseVft = baseVftInfo.value();
- break;
+ if (auto vftInfo = GetCachedVFTInfo(baseVftAddr, bClassInfo))
+ bClassInfo.vft = vftInfo.value();
}
+ // Now that we have populated all the vtables for the base class, we can assign its
+ // root vtable to the base vft.
+ baseClassInfo.vft = bClassInfo.vft;
}
}
}
@@ -565,6 +761,14 @@ void ItaniumRTTIProcessor::ProcessVFT()
classInfo.vft = vftInfo.value();
m_classInfo[coLocatorAddr] = classInfo;
+ };
+
+ for (const auto &[coLocatorAddr, vftAddrs]: vftMap)
+ {
+ for (const auto& vftAddr: vftAddrs)
+ {
+ populateVftEntries(coLocatorAddr, vftAddr);
+ }
}
auto end_time = std::chrono::high_resolution_clock::now();