#include #include #include class AbstractClass { public: virtual int f(); virtual int g(); }; class Derived : public AbstractClass { int f(); int g(); }; int Derived::f(){ return 1; } int Derived::g(){ return 2; } /* Return the address of the `f' function of `A' that would be called for the expression: A->f(); regardless of the concrete type of `A'. It is left as an exercise for the reader to templatize this function for arbitrary `f'. */ void* find_addr_f(AbstractClass* A){ /* * The virtual function table is stored * at the beginning of the object. */ int* vptr = *(int**)obj; void** vtable = *(void***)A; /* * This structure is described in the cross-platform "Itanium" C++ ABI: * * http://mentorembedded.github.io/cxx-abi/abi.html * * The particular layout replicated here is described in: * * http://mentorembedded.github.io/cxx-abi/abi.html#member-pointers */ struct pointerToMember { /* * This field has separate representations for non-virtual and * virtual functions. For non-virtual functions, this field is * simply the address of the function. For our case, virtual * functions, this field is 1 plus the virtual table offset (in * bytes) of the function in question. The least-significant * bit therefore discriminates between virtual and non-virtual * functions. * * "Ah," you say, "what about architectures where function * pointers do not necessarily have even addresses?" (ARM, * MIPS, and AArch64 are the major ones.) Excellent point. * Please see below. */ size_t pointerOrOffset; /* * This field is only interesting for calling the function; it * describes the amount that the `this' pointer must be * adjusted prior to the call. However, on architectures where * function pointers do not necessarily have even addresses, * this field has the representation: * * 2 * adjustment + (virtual_function_p ? 1 : 0) */ ptrdiff_t thisAdjustment; }; /* * Translate from the opaque pointer-to-member type * representation to the representation given above. */ pointerToMember p; int ((AbstractClass::*m)()) = &AbstractClass::f; memcpy(&p, &m, sizeof(p)); /* * Compute the actual offset into the vtable. Given the * differing meaing of the fields between architectures, as * described above, and that there's no convenient preprocessor * macro, we have to do this ourselves. * */ #if defined(__arm__) || defined(__mips__) || defined(__aarch64__) /* No adjustment required to `pointerOrOffset'. */ static const size_t pfnAdjustment = 0; #else /* Strip off the lowest bit of `pointerOrOffset'. */ static const size_t pfnAdjustment = 1; #endif size_t offset = (p.pointerOrOffset - pfnAdjustment) / sizeof(void*); /* Now grab the address out of the vtable and return it. */ return vtable[offset]; } int main(){ Derived A; void *v = find_addr_f(&A); printf("0x%08x\n", v); }