📄 vt_test.cpp
字号:
// Copyright (c) 2007, Arne Steinarson// Licensing for DynObj project - see DynObj-license.txt in this folder#define DO_IMPLEMENT_VTTEST#define DO_IMPLEMENTING #include <stdio.h>#include "vt_test.h"//#include "DynObj.h"#include "DoVTableInfo.hpp"#include "pi/visibility.h"#define docall // Packed version describing characteristics of VTable usage.// It allows for quickly testing for compatibility with a module.static int g_do_traits;// Globals used by doVTableInfo.hppvoid *g_do_TFA; // Address of test functionvoid *g_do_RA1; // Return address (inside function)// Globals descrining VTable layout (after performing tests)static int g_do_vtbl_es; // Entry size, (4/8/16)static int g_do_vtbl_off_1st; // Offset (in nr of void*) to 1st valid entry (VFunc0 above) // Usually 0 static int g_do_vtbl_off; // Inside an entry, offset to the function pointer // Usually 0int VTableIndex( int ix ){_CFE_; if( !g_do_vtbl_es ){ int of; if( VTableEntrySize(of)<0 ) return -1; } // This is a complex calculation for a simple thing... return g_do_vtbl_off_1st+ix*g_do_vtbl_es/sizeof(void*)+g_do_vtbl_off;}int VTableCompatible( int traits ){_CFE_; // If not initialized yet if( !g_do_traits ) doGetDoTraits(); if( (g_do_traits|traits)&DO_TRAITS_VT_ERROR ) return -1; int diff = g_do_traits^traits; // Entry size must match if( diff&(DO_TRAITS_MASK<<DO_TRAITS_VT_ES) ) return -2; // Offset to first entry must match if( diff&(DO_TRAITS_MASK<<DO_TRAITS_VT_OFF_1ST) ) return -2; // Offset within one entry must match if( diff&(DO_TRAITS_MASK<<DO_TRAITS_VT_OFF) ) return -3; // Inheritance padding must match if( diff&(DO_TRAITS_MASK<<DO_TRAITS_VT_IHP) ) return -4; // We can live with different entry count for destructo if( diff&(DO_TRAITS_MASK<<DO_TRAITS_VT_DEC) ) return 1; return 0;}// If compiled with DynObj.cpp the VTables are there#include "utils/ExpArr.hpp"ExpArrPOwn<VTableInfo*> g_do_test_vtbls;// This will restore an entry of the VTable to one of the ancestors// member functions.template <class C, class MIB>int MiSideBaseRestoreVTableEntry( C *pc, MIB *psb, int eix ){_CFE_; VTableInfo *vtp = g_do_test_vtbls.Find(*(void***)psb); if( !vtp ){ MIB sb; // Side base object, have to declare to get its VTable int ix = GetVTableSizeOf<MIB,void>(); if( ix<1 ) return -1; vtp = new VTableInfo(*(void***)psb,*(void***)&sb,ix); if( !vtp->AllocSoftVTbl() ){ delete vtp; return -2; } g_do_test_vtbls.Push(vtp); } if( eix>=vtp->size || eix<=-VTP_N_ENTRIES_BEFORE ) return -3; // Set entry from original VTable in RAM VTable vtp->vtbl_r[eix] = vtp->vtbl_o[eix]; return 0;}// Set the Vtable to the RAM (soft) vtabletemplate <class MIB>bool MiSideBaseSetRamVTable( MIB *psb ){_CFE_; VTableInfo *vtp = g_do_test_vtbls.Find(*(void***)psb); if( !vtp ) return false; *(void***)psb = vtp->vtbl_r; return true;} class VTblEntSizeTest { public: // These functions are packed into a VTable. // We find out which is which by calling two of them. // Careful with calling convention here, we're basically // converting to an ordinary function pointer below. virtual int docall VFunc0() {_CFE_; return 100; } virtual int docall VFunc1() {_CFE_; return 101; } virtual int docall VFunc2() {_CFE_; return 102; } virtual int docall VFunc3() {_CFE_; return 103; } virtual int docall VFunc4() {_CFE_; return 104; } virtual int docall VFunc5() {_CFE_; return 105; } virtual int docall VFunc6() {_CFE_; return 106; } virtual int docall VFunc7() {_CFE_; return 107; }};// Used to hold member function pointer as ordinary function pointertypedef int (docall *Vp2IntFn)( void* );// Testing VTable entry size.// (we basically accept 1 or 2 void* per entry// and a small offset before the first virtual function// in the VTable)// Usually int VTableEntrySize( int &off_first ){_CFE_; VTblEntSizeTest vtest; void **vtbl = *(void***)&vtest; for( int off=0; off<2; off++ ){ try { // Try even entries, 2 and 4 (+offset 0/1) Vp2IntFn tfn = (Vp2IntFn)vtbl[2+off]; int r1 = tfn ? tfn((void*)&vtest) : -1; if( r1<100 || r1>107 ) continue; tfn = (Vp2IntFn)vtbl[4+off]; int r2 = tfn ? tfn((void*)&vtest) : -1; if( r2<100 || r2>107 ) continue; if( r2==r1 ) continue; // It went fine. Look at result to count offset/size per entry int es = 2/(r2-r1); // Really only es=1 or 2 possible off_first = -((r1-100) - (2+off)/es); g_do_vtbl_es = es*sizeof(void*); g_do_vtbl_off_1st = off_first; g_do_vtbl_off = off; g_do_traits |= ((es&0xF)<<DO_TRAITS_VT_ES); g_do_traits |= ((off_first&0xF)<<DO_TRAITS_VT_OFF_1ST); g_do_traits |= ((off&0xF)<<DO_TRAITS_VT_OFF); return g_do_vtbl_es; } catch(...){ printf("Catch in VTableEntrySize\n"); } } return -1;}class BC1{public: virtual int A() {_CFE_; return (int)'A'; } int m_i;};int g_vt_dummy;class BC1d{public: virtual int A() {_CFE_; return (int)'A'; } virtual ~BC1d(){ g_vt_dummy++; } // Add destructor int m_i;};// This usually returns 1, since we have 1 virtual func. // However, it can be OK to return >1, maybe some additional// info is put into the VTable.int Bc1VTableSize(){_CFE_; return GetVTableSizeOf<BC1,void>();}class BC3{public: virtual int B() {_CFE_; return (int)'B'; } virtual int C() {_CFE_; return (int)'C'; } virtual int D() {_CFE_; return (int)'C'; } int m_i;};class BC3d : public BC3{public: virtual ~BC3d(){ g_vt_dummy<<=1; } // Destructor in derived class};class BC3dd : public BC3{public: virtual int D() {_CFE_; return (int)'C'; } virtual int E() {_CFE_; return (int)'C'; } virtual int F() {_CFE_; return (int)'C'; } virtual ~BC3dd(){ g_vt_dummy<<=1; } // Destructor in derived class};// This usually returns 3, since we have 3 virtual funcs. // It is correct to return 2 more than for BC1int Bc3VTableSize(){_CFE_; return GetVTableSizeOf<BC3,void>();}class DC1 : public BC1 {public: //virtual int A() { return (int)'a'; } virtual int B() {_CFE_; return (int)'b'; } int m_i;};// Test for VTable at beginning of objects. Some compilers may put it// elsewhere, but that should be unusual.int VTablePos( ){_CFE_; DC1 d1, d2; d1.m_i = 1; d2.m_i = 2; // Assumed VTable at first position should be same for both objects if( *(void***)&d1!=*(void***)&d2 ) return -1; // In the Vtable for dc2, we should have something from the Vtable of BC1 // because they share virtual functions BC1 bc; void **vtdc1 = *(void***)&d1; void **vtbc1 = *(void***)&bc; if( !vtdc1 || !vtbc1 ) return -2; try{ for( int ix=0; ix<4; ix++ ) for( int jx=0; jx<4; jx++ ) if( vtbc1[jx] && vtdc1[ix]==vtbc1[jx] ) return 0; }catch(...){ return -3; } return -4;}// This usually returns 2, since we have 2 virtual funcs. // Theoretically, some padding could be put between base class// virt funcs and those of the derived one.int Dc1VTableSize(){_CFE_; return GetVTableSizeOf<DC1,void>();}class DC3 : public BC3 {public: virtual int A() {_CFE_; return (int)'a'; } // Override virtual int C() {_CFE_; return (int)'c'; } // Override virtual int D() {_CFE_; return (int)'d'; } virtual int E() {_CFE_; return (int)'e'; } int m_i;};// This usually returns 5, since we have 3+2 virtual funcs. // Theoretically, some padding could be put between base class// virt funcs and those of the derived one.int Dc3VTableSize(){_CFE_; return GetVTableSizeOf<DC3,void>();}int InheritPadding(){_CFE_; int bc1_vts = Bc1VTableSize(); // 1 int bc3_vts = Bc3VTableSize(); // 3 int dc1_vts = Dc1VTableSize(); // 2 int dc3_vts = Dc3VTableSize(); // 5 int ihp1 = dc1_vts-bc1_vts-1; int ihp2 = dc3_vts-bc3_vts-2; if( ihp1!=ihp2 ) return -1; g_do_traits |= (ihp1<<DO_TRAITS_VT_IHP); return ihp1;}// Number of entries used for virtual destructor. DynObj framework// does not depend on that or use it, so it is not necessary for // main and module to have same result here.// Usually 1/2 entries.int DtorEntries(){_CFE_; int ne_bc1d = GetVTableSizeOf<BC1d,void>() - GetVTableSizeOf<BC1,void>(); int ne_dc3d = GetVTableSizeOf<BC3d,void>() - GetVTableSizeOf<BC3,void>(); if( ne_bc1d!=ne_dc3d ){ printf( "Found two different entry counts for Virt Dtor: %d %d\n", ne_bc1d, ne_dc3d ); return -1; } g_do_traits |= ((ne_bc1d&0xF)<<DO_TRAITS_VT_DEC); return ne_bc1d;}// Positioning of destructor in derived object// First or lastint DtorPosition(){_CFE_; BC1d bc1d; int ne_bc1d = GetVTableSizeOf<BC1d,void>(); int a_ix = GetVIndex<BC1d>(&BC1d::A,*(void***)(&bc1d)); //printf( "DtorPos - Size: %d Pos:%d (3,0)\n", ne_bc1d, a_ix ); BC3dd bc3dd; int ne_bc3dd = GetVTableSizeOf<BC3dd,void>(); int e_ix = GetVIndex<BC3dd>(&BC3dd::E,*(void***)(&bc3dd)); //printf( "DtorPos - Size: %d Pos:%d (7,3)\n", ne_bc3dd, e_ix ); bool first = false; bool at_end = false; if( a_ix==0 && e_ix==3 ) at_end = true; else if( a_ix>0 && e_ix>3 ) first = true; if( !first && !at_end ){ printf( "Could not determine destructor position: S1-%d P1-%d S2-%d P2-%d\n", ne_bc1d, a_ix, ne_bc3dd, e_ix ); return -1; } if( at_end ) g_do_traits |= (1<<DO_TRAITS_VT_DTOR_POS); return at_end;}// All tests below are excluded usually #ifdef VT_ALL_TESTSclass DC {public: int m_d1;};// See if we can detect VTable size correctly for POD// (data structure without VTable)// Return value 0 is correctint PodVTableSize(){_CFE_; return GetVTableSizeOf<DC,void>();}// Member function pointerstypedef int (DC3::*DC3PMF)();typedef int (DC1::*DC1PMF)();// Test what pointer to member func does// Can be used for testing how virtual functions are accessed with a certain// compiler. Stepping through the assembler language level will show how it works. // Return 0 if it works as expected.// Return -1 if it fails.int TestMembFuncPtr(){_CFE_; DC3PMF dc3pmf; DC1PMF dc1pmf; DC1 dc1, *pdc1=&dc1; DC3 dc3, *pdc3=&dc3; int n_ok=0; dc3pmf = &DC3::A; if( (dc3.*dc3pmf)()==(int)'a' ) n_ok++; dc3pmf = &DC3::B; if( (dc3.*dc3pmf)()==(int)'B' ) n_ok++; dc3pmf = &DC3::D; if( (dc3.*dc3pmf)()==(int)'d' ) n_ok++; dc1pmf = &DC1::A;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -