Back to index

lightning-sunbird  0.9+nobinonly
typeinfo.cpp
Go to the documentation of this file.
00001 /*
00002   typeinfo.cpp
00003        
00004   Speculatively use RTTI on a random object. If it contains a pointer at offset 0
00005   that is in the current process' address space, and that so on, then attempt to
00006   use C++ RTTI's typeid operation to obtain the name of the type.
00007   
00008   by Patrick C. Beard.
00009  */
00010 
00011 #include <typeinfo>
00012 #include <ctype.h>
00013 
00014 #include "gcconfig.h"
00015 
00016 extern "C" const char* getTypeName(void* ptr);
00017 
00018 class IUnknown {
00019 public:
00020     virtual long QueryInterface() = 0;
00021     virtual long AddRef() = 0;
00022     virtual long Release() = 0;
00023 };
00024 
00025 #if defined(MACOS)
00026 
00027 #include <Processes.h>
00028 
00029 class AddressSpace {
00030 public:
00031     AddressSpace();
00032     Boolean contains(void* ptr);
00033 private:
00034     ProcessInfoRec mInfo;
00035 };
00036 
00037 AddressSpace::AddressSpace()
00038 {
00039     ProcessSerialNumber psn = { 0, kCurrentProcess };
00040     mInfo.processInfoLength = sizeof(mInfo);
00041     ::GetProcessInformation(&psn, &mInfo);
00042 }
00043 
00044 Boolean AddressSpace::contains(void* ptr)
00045 {
00046     UInt32 start = UInt32(mInfo.processLocation);
00047     return (UInt32(ptr) >= start && UInt32(ptr) < (start + mInfo.processSize));
00048 }
00049 
00050 const char* getTypeName(void* ptr)
00051 {
00052     // construct only one of these per process.
00053     static AddressSpace space;
00054        
00055     // sanity check the vtable pointer, before trying to use RTTI on the object.
00056     void** vt = *(void***)ptr;
00057     if (vt && !(unsigned(vt) & 0x3) && space.contains(vt) && space.contains(*vt)) {
00058        IUnknown* u = static_cast<IUnknown*>(ptr);
00059        const char* type = typeid(*u).name();
00060         // make sure it looks like a C++ identifier.
00061        if (type && (isalnum(type[0]) || type[0] == '_'))
00062            return type;
00063     }
00064     return "void*";
00065 }
00066 
00067 #endif
00068 
00069 #if defined(LINUX)
00070 
00071 #include <signal.h>
00072 #include <setjmp.h>
00073 
00074 static jmp_buf context;
00075 
00076 static void handler(int signum)
00077 {
00078     longjmp(context, signum);
00079 }
00080 
00081 #define attempt() setjmp(context)
00082 
00083 class Signaller {
00084 public:
00085     Signaller(int signum);
00086     ~Signaller();
00087 
00088 private:
00089     typedef void (*handler_t) (int signum);
00090     int mSignal;
00091     handler_t mOldHandler;
00092 };
00093 
00094 Signaller::Signaller(int signum)
00095     : mSignal(signum), mOldHandler(signal(signum, &handler))
00096 {
00097 }
00098 
00099 Signaller::~Signaller()
00100 {
00101     signal(mSignal, mOldHandler);
00102 }
00103 
00104 // The following are pointers that bamboozle our otherwise feeble
00105 // attempts to "safely" collect type names.
00106 //
00107 // XXX this kind of sucks because it means that anyone trying to use
00108 // this without NSPR will get unresolved symbols when this library
00109 // loads. It's also not very extensible. Oh well: FIX ME!
00110 extern "C" {
00111     // from nsprpub/pr/src/io/priometh.c (libnspr4.so)
00112     extern void* _pr_faulty_methods;
00113 };
00114 
00115 static inline int
00116 sanity_check_vtable_i386(void** vt)
00117 {
00118     // Now that we're "safe" inside the signal handler, we can
00119     // start poking around. If we're really an object with
00120     // RTTI, then the second entry in the vtable should point
00121     // to a function.
00122     //
00123     // Let's see if the second entry:
00124     //
00125     // 1) looks like a 4-byte aligned pointer
00126     //
00127     // 2) points to something that looks like the following
00128     //    i386 instructions:
00129     //
00130     //    55     push %ebp
00131     //    89e5   mov  %esp,%ebp
00132     //    53     push %ebx
00133     //
00134     //    or
00135     //
00136     //    55     push %ebp
00137     //    89e5   mov  %esp,%ebp
00138     //    56     push %esi
00139     //
00140     //    (which is the standard function prologue generated
00141     //    by egcs, plus a ``signature'' instruction that appears
00142     //    in the typeid() function's implementation).
00143     unsigned char** fp1 = reinterpret_cast<unsigned char**>(vt) + 1;
00144 
00145     // Does it look like an address?
00146     unsigned char* ip = *fp1;
00147     if ((unsigned(ip) & 3) != 0)
00148         return 0;
00149 
00150     // Does it look like it refers to the standard prologue?
00151     static unsigned char prologue[] = { 0x55, 0x89, 0xE5 };
00152     for (unsigned i = 0; i < sizeof(prologue); ++i)
00153        if (*ip++ != prologue[i])
00154            return 0;
00155 
00156     // Is the next instruction a `push %ebx' or `push %esi'?
00157     return (*ip == 0x53 || *ip == 0x56);
00158 }
00159 
00160 static inline int
00161 sanity_check_vtable_ppc(void** vt)
00162 {
00163     // XXX write me!
00164     return 1;
00165 }
00166 
00167 #if defined(__i386)
00168 #  define SANITY_CHECK_VTABLE(vt) (sanity_check_vtable_i386(vt))
00169 #elif defined(PPC)
00170 #  define SANITY_CHECK_VTABLE(vt) (sanity_check_vtable_ppc(vt))
00171 #else
00172 #  define SANITY_CHECK_VTABLE(vt) (1)
00173 #endif
00174 
00175 const char* getTypeName(void* ptr)
00176 {
00177     // sanity check the vtable pointer, before trying to use RTTI on the object.
00178     void** vt = *(void***)ptr;
00179     if (vt && !(unsigned(vt) & 3) && (vt != &_pr_faulty_methods)) {
00180        Signaller s1(SIGSEGV);
00181        if (attempt() == 0) {
00182             if (SANITY_CHECK_VTABLE(vt)) {
00183                 // Looks like a function: what the hell, let's call it.
00184                 IUnknown* u = static_cast<IUnknown*>(ptr);
00185                 const char* type = typeid(*u).name();
00186                 // EGCS seems to prefix a length string.
00187                 while (isdigit(*type)) ++type;
00188                 return type;
00189            }
00190        }
00191     }
00192     return "void*";
00193 }
00194 
00195 #endif