tools/trace-malloc/lib/nsTypeInfo.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  *
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /*
     8   typeinfo.cpp
    10   Speculatively use RTTI on a random object. If it contains a pointer at offset 0
    11   that is in the current process' address space, and that so on, then attempt to
    12   use C++ RTTI's typeid operation to obtain the name of the type.
    14   by Patrick C. Beard.
    15  */
    17 #include <typeinfo>
    18 #include <ctype.h>
    20 #include "nsTypeInfo.h"
    22 extern "C" void NS_TraceMallocShutdown();
    24 struct TraceMallocShutdown {
    25     TraceMallocShutdown() {}
    26     ~TraceMallocShutdown() {
    27         NS_TraceMallocShutdown();
    28     }
    29 };
    31 extern "C" void RegisterTraceMallocShutdown() {
    32     // This instanciates the dummy class above, and will trigger the class
    33     // destructor when libxul is unloaded. This is equivalent to atexit(),
    34     // but gracefully handles dlclose().
    35     static TraceMallocShutdown t;
    36 }
    38 class IUnknown {
    39 public:
    40     virtual long QueryInterface() = 0;
    41     virtual long AddRef() = 0;
    42     virtual long Release() = 0;
    43 };
    45 #if defined(MACOS)
    47 #include <Processes.h>
    49 class AddressSpace {
    50 public:
    51     AddressSpace();
    52     Boolean contains(void* ptr);
    53 private:
    54     ProcessInfoRec mInfo;
    55 };
    57 AddressSpace::AddressSpace()
    58 {
    59     ProcessSerialNumber psn = { 0, kCurrentProcess };
    60     mInfo.processInfoLength = sizeof(mInfo);
    61     ::GetProcessInformation(&psn, &mInfo);
    62 }
    64 Boolean AddressSpace::contains(void* ptr)
    65 {
    66     UInt32 start = UInt32(mInfo.processLocation);
    67     return (UInt32(ptr) >= start && UInt32(ptr) < (start + mInfo.processSize));
    68 }
    70 const char* nsGetTypeName(void* ptr)
    71 {
    72     // construct only one of these per process.
    73     static AddressSpace space;
    75     // sanity check the vtable pointer, before trying to use RTTI on the object.
    76     void** vt = *(void***)ptr;
    77     if (vt && !(unsigned(vt) & 0x3) && space.contains(vt) && space.contains(*vt)) {
    78 	IUnknown* u = static_cast<IUnknown*>(ptr);
    79 	const char* type = typeid(*u).name();
    80         // make sure it looks like a C++ identifier.
    81 	if (type && (isalnum(type[0]) || type[0] == '_'))
    82 	    return type;
    83     }
    84     return "void*";
    85 }
    87 #endif
    89 // New, more "portable" Linux code is below, but this might be a useful
    90 // model for other platforms, so keeping.
    91 //#if defined(linux)
    92 #if 0
    94 #include <signal.h>
    95 #include <setjmp.h>
    97 static jmp_buf context;
    99 static void handler(int signum)
   100 {
   101     longjmp(context, signum);
   102 }
   104 #define attempt() setjmp(context)
   106 class Signaller {
   107 public:
   108     Signaller(int signum);
   109     ~Signaller();
   111 private:
   112     typedef void (*handler_t) (int signum);
   113     int mSignal;
   114     handler_t mOldHandler;
   115 };
   117 Signaller::Signaller(int signum)
   118     : mSignal(signum), mOldHandler(signal(signum, &handler))
   119 {
   120 }
   122 Signaller::~Signaller()
   123 {
   124     signal(mSignal, mOldHandler);
   125 }
   127 // The following are pointers that bamboozle our otherwise feeble
   128 // attempts to "safely" collect type names.
   129 //
   130 // XXX this kind of sucks because it means that anyone trying to use
   131 // this without NSPR will get unresolved symbols when this library
   132 // loads. It's also not very extensible. Oh well: FIX ME!
   133 extern "C" {
   134     // from nsprpub/pr/src/io/priometh.c (libnspr4.so)
   135     extern void* _pr_faulty_methods;
   136 };
   138 static inline int
   139 sanity_check_vtable_i386(void** vt)
   140 {
   141     // Now that we're "safe" inside the signal handler, we can
   142     // start poking around. If we're really an object with
   143     // RTTI, then the second entry in the vtable should point
   144     // to a function.
   145     //
   146     // Let's see if the second entry:
   147     //
   148     // 1) looks like a 4-byte aligned pointer
   149     //
   150     // 2) points to something that looks like the following
   151     //    i386 instructions:
   152     //
   153     //    55     push %ebp
   154     //    89e5   mov  %esp,%ebp
   155     //    53     push %ebx
   156     //
   157     //    or
   158     //
   159     //    55     push %ebp
   160     //    89e5   mov  %esp,%ebp
   161     //    56     push %esi
   162     //
   163     //    (which is the standard function prologue generated
   164     //    by egcs, plus a ``signature'' instruction that appears
   165     //    in the typeid() function's implementation).
   166     unsigned char** fp1 = reinterpret_cast<unsigned char**>(vt) + 1;
   168     // Does it look like an address?
   169     unsigned char* ip = *fp1;
   170     if ((unsigned(ip) & 3) != 0)
   171         return 0;
   173     // Does it look like it refers to the standard prologue?
   174     static unsigned char prologue[] = { 0x55, 0x89, 0xE5 };
   175     for (unsigned i = 0; i < sizeof(prologue); ++i)
   176         if (*ip++ != prologue[i])
   177             return 0;
   179     // Is the next instruction a `push %ebx' or `push %esi'?
   180     if (*ip == 0x53 || *ip == 0x56) {
   181         return 1;
   182     }
   184     // Nope.  There's another variant that has a `sub' instruction,
   185     // followed by a `cmpl' and a `jne'.  Check for that.
   186     if (ip[0] == 0x83 && ip[1] == 0xec     // sub
   187         && ip[3] == 0x83 && ip[4] == 0x3d  // cmpl
   188         && ip[10] == 0x75                  // jne
   189         ) {
   190         return 1;
   191     }
   193     return 0;
   194 }
   196 static inline int
   197 sanity_check_vtable_ppc(void** vt)
   198 {
   199     // XXX write me!
   200     return 1;
   201 }
   203 #if defined(__i386)
   204 #  define SANITY_CHECK_VTABLE(vt) (sanity_check_vtable_i386(vt))
   205 #elif defined(PPC)
   206 #  define SANITY_CHECK_VTABLE(vt) (sanity_check_vtable_ppc(vt))
   207 #else
   208 #  define SANITY_CHECK_VTABLE(vt) (1)
   209 #endif
   211 const char* nsGetTypeName(void* ptr)
   212 {
   213     // sanity check the vtable pointer, before trying to use RTTI on the object.
   214     void** vt = *(void***)ptr;
   215     if (vt && !(unsigned(vt) & 3) && (vt != &_pr_faulty_methods)) {
   216         Signaller s1(SIGSEGV);
   217         if (attempt() == 0) {
   218             if (SANITY_CHECK_VTABLE(vt)) {
   219                 // Looks like a function: what the hell, let's call it.
   220                 IUnknown* u = static_cast<IUnknown*>(ptr);
   221                 const char* type = typeid(*u).name();
   222                 // EGCS seems to prefix a length string.
   223                 while (isdigit(*type)) ++type;
   224                 return type;
   225             }
   226         }
   227     }
   228     return "void*";
   229 }
   231 #endif
   233 #if defined(linux) || defined(XP_MACOSX)
   235 #define __USE_GNU
   236 #include <dlfcn.h>
   237 #include <ctype.h>
   238 #include <string.h>
   240 const char* nsGetTypeName(void* ptr)
   241 {
   242 #if defined(__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100 /* G++ V3 ABI */
   243     const int expected_offset = 8;
   244     const char vtable_sym_start[] = "_ZTV";
   245     const int vtable_sym_start_length = sizeof(vtable_sym_start) - 1;
   246 #else
   247     const int expected_offset = 0;
   248     const char vtable_sym_start[] = "__vt_";
   249     const int vtable_sym_start_length = sizeof(vtable_sym_start) - 1;
   250 #endif
   251     void* vt = *(void**)ptr;
   252     Dl_info info;
   253     // If dladdr fails, if we're not at the expected offset in the vtable,
   254     // or if the symbol name isn't a vtable symbol name, return "void*".
   255     if ( !dladdr(vt, &info) ||
   256          ((char*)info.dli_saddr) + expected_offset != vt ||
   257          !info.dli_sname ||
   258          strncmp(info.dli_sname, vtable_sym_start, vtable_sym_start_length))
   259         return "void*";
   261     // skip the garbage at the beginning of things like
   262     // __vt_14nsRootBoxFrame (gcc 2.96) or _ZTV14nsRootBoxFrame (gcc 3.0)
   263     const char* rv = info.dli_sname + vtable_sym_start_length;
   264     while (*rv && isdigit(*rv))
   265         ++rv;
   266     return rv;
   267 }
   269 #endif
   271 #ifdef XP_WIN32
   272 const char* nsGetTypeName(void* ptr)
   273 {
   274   //TODO: COMPLETE THIS
   275     return "void*";
   276 }
   278 #endif //XP_WIN32

mercurial