/** * ============================================================================= * Source Python * Copyright (C) 2012-2015 Source Python Development Team. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 3.0, as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . * * As a special exception, the Source Python Team gives you permission * to link the code of this program (as well as its derivative works) to * "Half-Life 2," the "Source Engine," and any Game MODs that run on software * by the Valve Corporation. You must obey the GNU General Public License in * all respects for all other code used. Additionally, the Source.Python * Development Team grants this exception to all derivative works. */ #include "memory_rtti.h" #include "utilities/call_python.h" #include #include #include #include #include #ifdef WIN32 struct _s_RTTIBaseClassDescriptor; typedef _s_RTTIBaseClassDescriptor ClassDescriptor; #else class __class_type_info; typedef __class_type_info ClassDescriptor; #endif class CBaseType : public IBaseType { public: CBaseType(const ClassDescriptor *descriptor, size_t offset); ~CBaseType() { for (size_t i=0; iGetName()); } void Dump() { Dump(0); } void Dump(int level) { std::string x = ""; for (int i=0; i < level; ++i) x += "-"; PythonLog(-1, "%s%s", x.c_str(), GetName()); for (unsigned int i=0; i < GetNumBaseClasses(); ++i) { GetBaseClass(i)->Dump(level + 1); } } private: ptrdiff_t m_Offset; size_t m_BaseTypes; CBaseType *m_Bases; const std::type_info *m_TypeInfo; }; /* OS Specific Implementations */ const char *GetTypeName(const std::type_info &type) { const char *temp = type.name(); #ifdef WIN32 /* WIN32 uses 'class ' */ while (*temp != ' ') { temp++; } return ++temp; #else /* GCC uses '' */ while (isdigit(*temp)) { temp++; } return temp; #endif } #ifdef WIN32 #include using namespace std; using std::type_info; typedef std::type_info TypeDescriptor; struct PMD { ptrdiff_t mdisp; //vftable offset ptrdiff_t pdisp; //vbtable offset ptrdiff_t vdisp; //vftable offset (for virtual base class) }; struct _s_RTTICompleteObjectLocator; struct _s_RTTIClassHierarchyDescriptor; struct _s_RTTIBaseClassDescriptor; struct _s_RTTICompleteObjectLocator { DWORD signature; DWORD offset; //vftable offset to this DWORD cdOffset; TypeDescriptor *pTypeDescriptor; _s_RTTIClassHierarchyDescriptor *pClassHierarchyDescriptor; }; struct _s_RTTIClassHierarchyDescriptor { DWORD signature; DWORD attributes; //bit 0 multiple inheritance, bit 1 virtual inheritance size_t numBaseClasses; //at least 1 (all base classes, including itself) _s_RTTIBaseClassDescriptor **pBaseClassArray; }; struct _s_RTTIBaseClassDescriptor { TypeDescriptor *pTypeDescriptor; size_t numBaseClasses; //direct base classes PMD pmd; //Len=0xC DWORD attributes; _s_RTTIClassHierarchyDescriptor *pClassHierarchyDescriptor; //of this base class }; template // only ptrdiff_t and size_t allowed R ReadData(size_t ptr) { return *(( const R *)ptr); } template < typename R, typename T> R ReadData( const T *ptr) { return ReadData((size_t)ptr); } //We assume the vftable is at offset 0 //If it is not the case, we assume the vbtable should be at offset 0, and with its first entry equal to 0 (point to itself). //Then its second entry should contain the offset of vftable template < typename T> _s_RTTICompleteObjectLocator *GetCompleteObjectLocator( const T *ptr) //not have vbtable { ptrdiff_t offset=0; if (ReadData(ReadData(ptr))==0) offset=ReadData(ReadData(ptr)+4); return (_s_RTTICompleteObjectLocator *)(ReadData(ReadData((size_t)ptr+offset)-4)); } CBaseType::CBaseType(const ClassDescriptor *descriptor, size_t offset) { m_BaseTypes = 0; int basecount = descriptor->numBaseClasses; int current = 1; while (current <= basecount) { _s_RTTIBaseClassDescriptor *temp = descriptor->pClassHierarchyDescriptor->pBaseClassArray[current]; current+= temp->numBaseClasses; current++; m_BaseTypes++; } m_Offset = descriptor->pmd.mdisp + offset; m_TypeInfo = descriptor->pTypeDescriptor; m_Bases = (CBaseType *)malloc(sizeof(CBaseType) * m_BaseTypes); current = 1; int count = 0; while (current <= basecount) { _s_RTTIBaseClassDescriptor *temp = descriptor->pClassHierarchyDescriptor->pBaseClassArray[current]; current+= temp->numBaseClasses; current++; new (&m_Bases[count]) CBaseType(temp, m_Offset); count++; } } #else class __class_type_info; /* helper class for __vmi_class_type */ class __base_class_info { /* abi defined member variables */ public: const __class_type_info *__base; /* base class type */ long __offset_flags; /* offset and info */ /* implementation defined types */ public: enum __offset_flags_masks { __virtual_mask = 0x1, __public_mask = 0x2, hwm_bit = 2, offset_shift = 8 /* bits to shift offset by */ }; /* implementation defined member functions */ public: bool __is_virtual_p () const { return __offset_flags & __virtual_mask; } bool __is_public_p () const { return __offset_flags & __public_mask; } __PTRDIFF_TYPE__ __offset () const { // This shift, being of a signed type, is implementation defined. GCC // implements such shifts as arithmetic, which is what we want. return static_cast<__PTRDIFF_TYPE__> (__offset_flags) >> offset_shift; } }; class __class_type_info : public std::type_info { }; class __si_class_type_info : public __class_type_info { public: const __class_type_info *__base_type; }; class __vmi_class_type_info : public __class_type_info { /* abi defined member variables */ public: unsigned int __flags; /* details about the class hierarchy */ unsigned int __base_count; /* number of direct bases */ __base_class_info const __base_info[1]; /* array of bases */ }; struct vtable_prefix { ptrdiff_t whole_object; // offset to most derived object const __class_type_info *whole_type; // pointer to most derived type_info const void *origin; // what a class's vptr points to }; template inline const T * adjust_pointer (const void *base, ptrdiff_t offset) { return reinterpret_cast (reinterpret_cast (base) + offset); } const __class_type_info *typeid2(const void *ptr) { const void *pVtable = *(const void **)(ptr); const vtable_prefix *prefix = adjust_pointer (pVtable, -offsetof (vtable_prefix, origin)); return prefix->whole_type; } enum InheritanceType { Inheritance_None, /* Doubles as unknown */ Inheritance_Single, Inheritance_VirtualMultiple, }; InheritanceType GetInheritanceType(const __class_type_info *ptr) { const __class_type_info *typeinfo = typeid2(ptr); if (strcmp(typeinfo->name(), "N10__cxxabiv120__si_class_type_infoE") == 0) { return Inheritance_Single; } else if (strcmp(typeinfo->name(), "N10__cxxabiv121__vmi_class_type_infoE") == 0) { return Inheritance_VirtualMultiple; } return Inheritance_None; } CBaseType::CBaseType(const ClassDescriptor *descriptor, size_t offset) { InheritanceType type = GetInheritanceType(descriptor); switch (type) { case Inheritance_Single: { m_BaseTypes = 1; break; } case Inheritance_VirtualMultiple: { __vmi_class_type_info *vmiObject = (__vmi_class_type_info *)descriptor; m_BaseTypes = vmiObject->__base_count; break; } default: { m_BaseTypes = 0; } } m_Offset = offset; m_TypeInfo = descriptor; if (type == Inheritance_None) { m_Bases = NULL; return; } m_Bases = (CBaseType *)malloc(sizeof(CBaseType) * m_BaseTypes); if (type == Inheritance_Single) { __si_class_type_info *siObject = (__si_class_type_info *)descriptor; new (m_Bases) CBaseType(siObject->__base_type, offset); return; } __vmi_class_type_info *vmiObject = (__vmi_class_type_info *)descriptor; for (size_t i=0; i__base_info[i].__base, offset + vmiObject->__base_info[i].__offset()); } } #endif IBaseType *GetType(const void *ptr) { #ifdef _WIN32 _s_RTTICompleteObjectLocator *locator = GetCompleteObjectLocator(ptr); return new CBaseType(locator->pClassHierarchyDescriptor->pBaseClassArray[0], 0); #elif __linux__ return new CBaseType(typeid2(ptr), 0); #else #error Unsupported platform. #endif }