@@ -11,19 +11,49 @@ using namespace BinaryNinja::RTTI::Itanium;
1111constexpr const char *TYPE_SOURCE_ITANIUM = " rtti_itanium" ;
1212
1313
14+ Ref<Symbol> GetRealSymbol (BinaryView *view, uint64_t relocAddr, uint64_t symAddr)
15+ {
16+ if (view->IsOffsetExternSemantics (symAddr))
17+ {
18+ // Because bases in the extern section are not 8 byte width only they will
19+ // overlap with other externs, until https://github.com/Vector35/binaryninja-api/issues/6387 is fixed.
20+ // Check relocation at objectAddr for symbol
21+ for (const auto & r : view->GetRelocationsAt (relocAddr))
22+ if (auto relocSym = r->GetSymbol ())
23+ return relocSym;
24+ }
25+
26+ return view->GetSymbolByAddress (symAddr);
27+ }
28+
29+
30+ // Some fields are not always u32, use this if it goes from u32 -> u16 on 32bit
31+ uint64_t ArchFieldSize (BinaryView *view)
32+ {
33+ return view->GetAddressSize () / 2 ;
34+ }
35+
36+
37+
38+ uint64_t TypeInfoSize (BinaryView *view)
39+ {
40+ return view->GetAddressSize () * 2 ;
41+ }
42+
43+
1444std::optional<TypeInfo> GetTypeInfo (BinaryView* view, uint64_t address)
1545{
1646 // TODO: We really need a valid offset range thing.
17- const auto typeInfoSize = view-> GetAddressSize () * 2 ;
47+ const auto typeInfoSize = TypeInfoSize (view) ;
1848 if (!view->IsValidOffset (address) || !view->IsValidOffset (address + typeInfoSize))
1949 return std::nullopt ;
2050 BinaryReader reader = BinaryReader (view);
2151 reader.Seek (address);
2252 auto base = reader.ReadPointer ();
23- if (!view->IsValidOffset (base))
53+ if (!view->IsValidOffset (base) || view-> IsOffsetCodeSemantics (base) )
2454 return std::nullopt ;
2555 auto typeNameAddr = reader.ReadPointer ();
26- if (!view->IsValidOffset (typeNameAddr))
56+ if (!view->IsValidOffset (typeNameAddr) || view-> IsOffsetCodeSemantics (typeNameAddr) )
2757 return std::nullopt ;
2858 reader.Seek (typeNameAddr);
2959 auto type_name = reader.ReadCString (512 );
@@ -46,7 +76,7 @@ SIClassTypeInfo::SIClassTypeInfo(BinaryView *view, uint64_t address) : ClassType
4676{
4777 BinaryReader reader = BinaryReader (view);
4878 // TODO: Manually seeking to the offset is ugly.
49- reader.Seek (address + 0x10 );
79+ reader.Seek (address + TypeInfoSize (view) );
5080 base_type = reader.ReadPointer ();
5181}
5282
@@ -56,24 +86,38 @@ BaseClassTypeInfo::BaseClassTypeInfo(BinaryView *view, uint64_t address)
5686 BinaryReader reader = BinaryReader (view);
5787 reader.Seek (address);
5888 base_type = reader.ReadPointer ();
59- offset_flags = reader.Read32 ();
60- offset_flags_masks = reader.Read32 ();
89+ // I thought the spec was pretty clear about these being u32
90+ // but apparently not? Or atleast on the gcc compiler I have these get turned to u16, disappointing.
91+ if (view->GetAddressSize () == 8 )
92+ {
93+ offset_flags = reader.Read32 ();
94+ offset_flags_masks = reader.Read32 ();
95+ }
96+ else
97+ {
98+ offset_flags = reader.Read16 ();
99+ offset_flags_masks = reader.Read16 ();
100+ }
61101}
62102
103+ uint64_t BaseClassTypeSize (BinaryView *view)
104+ {
105+ return view->GetAddressSize () + 0x8 ;
106+ }
63107
64108VMIClassTypeInfo::VMIClassTypeInfo (BinaryView *view, uint64_t address) : ClassTypeInfo(view, address)
65109{
66110 BinaryReader reader = BinaryReader (view);
67111 // TODO: Manually seeking to the offset is ugly.
68- reader.Seek (address + 0x10 );
112+ reader.Seek (address + TypeInfoSize (view) );
69113 flags = reader.Read32 ();
70114 base_count = reader.Read32 ();
71115 base_info = {};
72116 for (size_t i = 0 ; i < base_count; i++)
73117 {
74118 uint64_t currentBaseAddr = reader.GetOffset ();
75119 base_info.emplace_back (view, currentBaseAddr);
76- reader.Seek (currentBaseAddr + 0x10 );
120+ reader.Seek (currentBaseAddr + BaseClassTypeSize (view) );
77121 }
78122}
79123
@@ -115,7 +159,7 @@ Ref<Type> ClassTypeInfoType(BinaryView *view)
115159 BaseStructure typeInfoBase = BaseStructure (TypeInfoType (view), 0 );
116160 structureBuilder.SetBaseStructures ({typeInfoBase});
117161 // TODO: This exists because if you have no members but a base struct things get screwy.
118- structureBuilder.SetWidth (0x10 );
162+ structureBuilder.SetWidth (TypeInfoSize (view) );
119163
120164 Ref<Type> structureType = TypeBuilder::StructureType (structureBuilder.Finalize ()).Finalize ();
121165 view->DefineType (typeId, QualifiedName (" __cxxabiv1::__class_type_info" ), structureType);
@@ -134,10 +178,11 @@ Ref<Type> SIClassTypeInfoType(BinaryView *view)
134178 if (typeCache == nullptr )
135179 {
136180 Ref<Architecture> arch = view->GetDefaultArchitecture ();
181+ uint64_t baseOffset = TypeInfoSize (view);
137182
138183 StructureBuilder structureBuilder;
139184 Ref<Type> pBaseType = Type::PointerType (arch, Type::VoidType ());
140- structureBuilder.AddMemberAtOffset (pBaseType, " __base_type" , 0x10 );
185+ structureBuilder.AddMemberAtOffset (pBaseType, " __base_type" , baseOffset );
141186 BaseStructure classTypeInfoBase = BaseStructure (ClassTypeInfoType (view), 0 );
142187 structureBuilder.SetBaseStructures ({classTypeInfoBase});
143188
@@ -159,14 +204,15 @@ Ref<Type> OffsetFlagsMasksType(BinaryView *view)
159204 if (typeCache == nullptr )
160205 {
161206 Ref<Architecture> arch = view->GetDefaultArchitecture ();
162- Ref<Type> uintType = Type::IntegerType (4 , false );
207+ // u32 on 64 and u16 on 32, see comment in BaseClassTypeInfo::BaseClassTypeInfo
208+ uint64_t enumSize = ArchFieldSize (view);
163209
164210 EnumerationBuilder enumerationBuilder;
165211 enumerationBuilder.AddMemberWithValue (" __virtual_mask" , 0x1 );
166212 enumerationBuilder.AddMemberWithValue (" __public_mask" , 0x2 );
167213 enumerationBuilder.AddMemberWithValue (" __offset_shift" , 0x8 );
168214
169- Ref<Type> enumerationType = TypeBuilder::EnumerationType (arch, enumerationBuilder.Finalize ()).Finalize ();
215+ Ref<Type> enumerationType = TypeBuilder::EnumerationType (arch, enumerationBuilder.Finalize (), enumSize ).Finalize ();
170216 view->DefineType (typeId, QualifiedName (" __cxxabiv1::__offset_flags_masks" ), enumerationType);
171217
172218 typeCache = view->GetTypeById (typeId);
@@ -184,7 +230,9 @@ Ref<Type> BaseClassTypeInfoType(BinaryView *view)
184230 if (typeCache == nullptr )
185231 {
186232 Ref<Architecture> arch = view->GetDefaultArchitecture ();
187- Ref<Type> uintType = Type::IntegerType (4 , false );
233+ // u32 on 64 and u16 on 32, see comment in BaseClassTypeInfo::BaseClassTypeInfo
234+ uint64_t fieldSize = ArchFieldSize (view);
235+ Ref<Type> uintType = Type::IntegerType (fieldSize, false );
188236
189237 StructureBuilder structureBuilder;
190238 Ref<Type> pBaseType = Type::PointerType (arch, Type::VoidType ());
@@ -230,12 +278,13 @@ Ref<Type> VMIClassTypeInfoType(BinaryView *view, uint64_t baseCount)
230278{
231279 Ref<Architecture> arch = view->GetDefaultArchitecture ();
232280 Ref<Type> uintType = Type::IntegerType (4 , false );
281+ uint64_t baseOffset = TypeInfoSize (view);
233282
234283 StructureBuilder structureBuilder;
235- structureBuilder.AddMemberAtOffset (VMIFlagsMasksType (view), " __flags" , 0x10 );
236- structureBuilder.AddMemberAtOffset (uintType, " __base_count" , 0x14 );
284+ structureBuilder.AddMemberAtOffset (VMIFlagsMasksType (view), " __flags" , baseOffset );
285+ structureBuilder.AddMemberAtOffset (uintType, " __base_count" , baseOffset + 0x4 );
237286 Ref<Type> baseInfoType = Type::ArrayType (BaseClassTypeInfoType (view), baseCount);
238- structureBuilder.AddMemberAtOffset (baseInfoType, " __base_info" , 0x18 );
287+ structureBuilder.AddMemberAtOffset (baseInfoType, " __base_info" , baseOffset + 0x8 );
239288 BaseStructure classTypeInfoBase = BaseStructure (ClassTypeInfoType (view), 0 );
240289 structureBuilder.SetBaseStructures ({classTypeInfoBase});
241290
@@ -248,43 +297,32 @@ std::optional<TypeInfoVariant> ReadTypeInfoVariant(BinaryView *view, uint64_t ob
248297 auto typeInfo = GetTypeInfo (view, objectAddr);
249298 if (!typeInfo.has_value ())
250299 return std::nullopt ;
251-
252- // TODO: What if there is no symbol?
253- // If there is a symbol at objectAddr pointing to a symbol starting with "vtable for __cxxabiv1"
254- auto baseSym = view->GetSymbolByAddress (typeInfo->base );
255- if (baseSym == nullptr )
256- {
257- // Check relocation at objectAddr for symbol
258- for (const auto & r : view->GetRelocationsAt (objectAddr))
259- if (auto relocSym = r->GetSymbol ())
260- baseSym = relocSym;
261- }
262300
263- if (baseSym != nullptr && baseSym->GetType () != ExternalSymbol)
264- {
265- // The base did have a symbol, but it wasn't external.
266- // NOTE: We only require it to be external for symbol available bases.
267- return std::nullopt ;
268- }
301+ // If there is a symbol at objectAddr pointing to a symbol starting with "vtable for __cxxabiv1"
302+ Ref<Symbol> baseSym = GetRealSymbol (view, objectAddr, typeInfo->base );
269303
270304 if (baseSym == nullptr )
271305 {
272306 // Verify first that we can even read a pointer sized value at the base.
273- if (!view->IsValidOffset (typeInfo->base + view->GetAddressSize ()))
307+ if (!view->IsValidOffset (typeInfo->base - view->GetAddressSize ()))
274308 return std::nullopt ;
275309 // We did not find a symbol for the base.
276310 // Last resort, try and deref to check for static linked c++ rt.
277311 // to get the c++ variant assume we are in a vtable like this
278312 // void* data_102bb4ca0 = _typeinfo_for___cxxabiv1::__class_type_info
279313 // void *data_102bb4ca8 = __cxxabiv1::__class_type_info::~__class_type_info() <--- typeInfo.base points to this.
280314 // void *data_102bb4cb0 = __cxxabiv1::__class_type_info::~__class_type_info()
281- // Because we are pointing at the start of the vtable, we can just deref again to get the symbol.
315+ // While we could just deref to get the symbol of the vfunc, the symbol might not actually be present for that function yet.
316+ // So instead we will deref to the type info above the supposed vtable.
317+ // TODO: We should really just deref into the type info to get the name string.
282318 BinaryReader reader = BinaryReader (view);
283- reader.Seek (typeInfo->base );
284- uint64_t vftAddr = reader.ReadPointer ();
285- if (!view->IsValidOffset (vftAddr))
319+ reader.Seek (typeInfo->base - view->GetAddressSize ());
320+ // Read the type info pointer above the vft
321+ uint64_t typeInfoAddr = reader.ReadPointer ();
322+ if (!view->IsValidOffset (typeInfoAddr))
286323 return std::nullopt ;
287- auto vftSym = view->GetSymbolByAddress (vftAddr);
324+ LogInfo (" Assuming base type info for %llx is at %llx" , objectAddr, typeInfoAddr);
325+ auto vftSym = view->GetSymbolByAddress (typeInfoAddr);
288326 if (vftSym == nullptr )
289327 return std::nullopt ;
290328 baseSym = vftSym;
@@ -294,21 +332,12 @@ std::optional<TypeInfoVariant> ReadTypeInfoVariant(BinaryView *view, uint64_t ob
294332 if (baseSymName.find (" __cxxabiv1" ) != std::string::npos)
295333 {
296334 // symbol takes the form of `abi::base_name::addend`
297- // Remove the `abi::`
298- auto baseTyStartPos = baseSymName.find (" ::" );
299- if (baseTyStartPos != std::string::npos)
300- baseSymName = baseSymName.substr (baseTyStartPos + 2 );
301- // Remove the `::addend`
302- auto baseTyEndPos = baseSymName.find (" ::" );
303- if (baseTyEndPos != std::string::npos)
304- baseSymName = baseSymName.substr (0 , baseTyEndPos);
305-
306- if (baseSymName == " __class_type_info" )
307- return TIVClass;
308- if (baseSymName == " __si_class_type_info" )
335+ if (baseSymName.find (" si_class_type_info" ) != std::string::npos)
309336 return TIVSIClass;
310- if (baseSymName == " __vmi_class_type_info " )
337+ if (baseSymName. find ( " vmi_class_type_info " ) != std::string::npos )
311338 return TIVVMIClass;
339+ if (baseSymName.find (" class_type_info" ) != std::string::npos)
340+ return TIVClass;
312341 }
313342
314343 return std::nullopt ;
@@ -319,8 +348,9 @@ std::optional<BaseClassInfo> ItaniumRTTIProcessor::ProcessVFTBaseClassInfo(uint6
319348{
320349 BinaryReader reader = BinaryReader (m_view);
321350 // Because we have this we _need_ to have the adjustment stuff.
322- // NOTE: We assume two 0x4 ints with the first being what we want.
323- reader.Seek (vftAddr - 0x10 );
351+ // NOTE: We assume two field sized ints with the first being what we want.
352+ // Two 0x4 ints and the pointer to the type info.
353+ reader.Seek (vftAddr - ((ArchFieldSize (m_view) * 2 ) + m_view->GetAddressSize ()));
324354
325355 auto adjustmentOffset = static_cast <int32_t >(reader.Read32 ());
326356 [[maybe_unused]]
@@ -331,15 +361,6 @@ std::optional<BaseClassInfo> ItaniumRTTIProcessor::ProcessVFTBaseClassInfo(uint6
331361 // Assuming we do not have a baseClassInfo already passed we can deduce it here.
332362 for (auto & baseClass : classInfo.baseClasses )
333363 {
334- // if (baseClass.offset == 0)
335- // {
336- // // If the base class is at offset 0 that means it has yet to be adjusted.
337- // // NOTE: This should only happen for `TIVSIClass`. If this assigns more than
338- // // one base class to this offset we are screwed.
339- // baseClass.offset = classOffset;
340- // LogInfo("Adjusting base class offset for %llx to %llx", vftAddr, classOffset);
341- // }
342-
343364 if (baseClass.offset == classOffset)
344365 {
345366 // Found the appropriate base class for this vtable.
@@ -436,7 +457,8 @@ std::optional<ClassInfo> ItaniumRTTIProcessor::ProcessRTTI(uint64_t objectAddr)
436457 return std::nullopt ;
437458 m_logger->LogDebug (" Non-backed external subtype for %llx" , objectAddr);
438459 subTypeName = externTypeName.value ();
439- } else
460+ }
461+ else
440462 {
441463 auto baseTypeInfo = TypeInfo (m_view, baseInfo.base_type );
442464 subTypeName = baseTypeInfo.type_name ;
@@ -480,7 +502,8 @@ std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVFT(uint64_
480502 {
481503 // TODO: Sometimes vFunc idx will be zeroed iirc.
482504 // We allow vfuncs to point to extern functions.
483- auto vFuncSym = m_view->GetSymbolByAddress (vFuncAddr);
505+ // TODO: Until https://github.com/Vector35/binaryninja-api/issues/5982 is fixed we need to check extern sym relocs instead of the symbol directly
506+ auto vFuncSym = GetRealSymbol (m_view, reader.GetOffset (), vFuncAddr);
484507 if (!vFuncSym)
485508 break ;
486509 DataVariable dv;
@@ -639,14 +662,20 @@ void ItaniumRTTIProcessor::ProcessRTTI()
639662{
640663 auto start_time = std::chrono::high_resolution_clock::now ();
641664 auto addrSize = m_view->GetAddressSize ();
642- // TODO: This probably needs to change
643- uint64_t maxTypeInfoSize = 0x10 ;
665+ uint64_t maxTypeInfoSize = TypeInfoSize (m_view);
644666
645667 auto scan = [&](const Ref<Section> §ion) {
646668 for (uint64_t currAddr = section->GetStart (); currAddr <= section->GetEnd () - maxTypeInfoSize; currAddr += addrSize)
647669 {
648- if (auto classInfo = ProcessRTTI (currAddr))
649- m_classInfo[currAddr] = classInfo.value ();
670+ try
671+ {
672+ if (auto classInfo = ProcessRTTI (currAddr))
673+ m_classInfo[currAddr] = classInfo.value ();
674+ }
675+ catch (std::exception& e)
676+ {
677+ m_logger->LogWarn (" Failed to process object at %llx... skipping" , currAddr);
678+ }
650679 }
651680 };
652681
@@ -724,8 +753,8 @@ void ItaniumRTTIProcessor::ProcessVFT()
724753 DataVariable dv;
725754 if (m_view->GetDataVariableAtAddress (ref, dv) && m_classInfo.find (dv.address ) != m_classInfo.end ())
726755 continue ;
727- // Verify that there is two 4 byte values above the type info pointer
728- optReader.Seek (ref - 8 );
756+ // Verify that there is two field sized values above the type info pointer
757+ optReader.Seek (ref - ArchFieldSize (m_view) * 2 );
729758 auto beforeTypeInfoRef = optReader.ReadPointer ();
730759 if (m_view->IsValidOffset (beforeTypeInfoRef))
731760 continue ;
@@ -737,42 +766,6 @@ void ItaniumRTTIProcessor::ProcessVFT()
737766 }
738767 }
739768
740- if (virtualFunctionTableSweep)
741- {
742- auto addrSize = m_view->GetAddressSize ();
743- auto scan = [&](const Ref<Segment> &segment) {
744- uint64_t startAddr = segment->GetStart ();
745- uint64_t endAddr = segment->GetEnd ();
746- for (uint64_t vtableAddr = startAddr; vtableAddr < endAddr - 0x10 ; vtableAddr += addrSize)
747- {
748- optReader.Seek (vtableAddr);
749- uint64_t coLocatorAddr = optReader.ReadPointer ();
750- auto coLocator = m_classInfo.find (coLocatorAddr);
751- if (coLocator == m_classInfo.end ())
752- continue ;
753- // Found a vtable reference to colocator.
754- // vftMap[coLocatorAddr] = vtableAddr + addrSize;
755- }
756- };
757-
758- // Scan data sections for virtual function tables.
759- auto rdataSection = m_view->GetSectionByName (" .rdata" );
760- for (const Ref<Segment> &segment: m_view->GetSegments ())
761- {
762- if (segment->GetFlags () == (SegmentReadable | SegmentContainsData))
763- {
764- m_logger->LogDebug (" Attempting to find VirtualFunctionTables in segment %llx" , segment->GetStart ());
765- scan (segment);
766- }
767- else if (checkWritableRData && rdataSection && rdataSection->GetStart () == segment->GetStart ())
768- {
769- m_logger->LogDebug (" Attempting to find VirtualFunctionTables in writable rdata segment %llx" ,
770- segment->GetStart ());
771- scan (segment);
772- }
773- }
774- }
775-
776769 auto GetCachedVFTInfo = [&](uint64_t vftAddr, ClassInfo& classInfo) {
777770 // Check in the cache so that we don't process vfts more than once.
778771 auto cachedVftInfo = vftFinishedMap.find (vftAddr);
0 commit comments