tools/trace-malloc/lib/nsTypeInfo.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial