Chromium Code Reviews| Index: chrome/browser/ui/cocoa/objc_zombie.mm |
| diff --git a/chrome/browser/ui/cocoa/objc_zombie.mm b/chrome/browser/ui/cocoa/objc_zombie.mm |
| index 7dcbfc6b6b6bb79d6ebd63b65b782fe3bf860507..1e82f31bbc3ca8a1d89dd4b8c0d9f411eaa10bd2 100644 |
| --- a/chrome/browser/ui/cocoa/objc_zombie.mm |
| +++ b/chrome/browser/ui/cocoa/objc_zombie.mm |
| @@ -33,12 +33,15 @@ |
| namespace { |
| -// |object_cxxDestruct()| is an Objective-C runtime function which |
| -// traverses the object's class tree for ".cxxdestruct" methods which |
| -// are run to call C++ destructors as part of |-dealloc|. The |
| -// function is not public, so must be looked up using nlist. |
| +// Function which destroys the contents of an object without freeing |
| +// the object. On 10.5 this is |object_cxxDestruct()|, which |
| +// traverses the class hierarchy to run the C++ destructors. On 10.6 |
| +// this is |objc_destructInstance()| which calls |
| +// |object_cxxDestruct()| and removes associative references. |
| +// |objc_destructInstance()| returns |void*|, pretending it has no |
| +// return value makes the code simpler. |
| typedef void DestructFn(id obj); |
| -DestructFn* g_object_cxxDestruct = NULL; |
| +DestructFn* g_objectDestruct = NULL; |
| // The original implementation for |-[NSObject dealloc]|. |
| IMP g_originalDeallocIMP = NULL; |
| @@ -70,9 +73,9 @@ typedef struct { |
| ZombieRecord* g_zombies = NULL; |
| -// Lookup the private |object_cxxDestruct| function and return a |
| -// pointer to it. Returns |NULL| on failure. |
| -DestructFn* LookupObjectCxxDestruct() { |
| +// Lookup the private object destruction function and return a pointer |
| +// to it. Returns |NULL| on failure. |
| +DestructFn* LookupObjectDestruct() { |
| #if ARCH_CPU_64_BITS |
| // TODO(shess): Port to 64-bit. I believe using struct nlist_64 |
| // will suffice. http://crbug.com/44021 . |
| @@ -80,20 +83,37 @@ DestructFn* LookupObjectCxxDestruct() { |
| return NULL; |
| #endif |
| + // Lookup |objc_destructInstance()| dynamically because it isn't |
| + // available on 10.5. |
| + const void* addr = reinterpret_cast<void*>(&object_getClass); |
| + Dl_info info; |
| + if (dladdr(addr, &info)) { |
| + void* handle = dlopen(info.dli_fname, RTLD_LAZY|RTLD_LOCAL|RTLD_FIRST); |
|
Mark Mentovai
2011/05/31 21:00:57
Not RTLD_FIRST. RTLD_LAZY | RTLD_LOCAL is fine. No
Scott Hess - ex-Googler
2011/06/07 21:24:51
Done.
|
| + if (handle) { |
| + void* fn = dlsym(handle, "objc_destructInstance"); |
| + dlclose(handle); |
|
Mark Mentovai
2011/05/31 21:00:57
This is normally dangerous. If you get a pointer f
Scott Hess - ex-Googler
2011/06/07 21:24:51
Done.
You're making me think fondly about dlsym(R
|
| + if (fn) |
| + return reinterpret_cast<DestructFn*>(fn); |
|
Mark Mentovai
2011/05/31 21:00:57
This cast is fine.
Scott Hess - ex-Googler
2011/06/07 21:24:51
OK.
|
| + } |
| + } |
| + |
| + // |object_cxxDestruct()| is |__private_extern__| in the runtime, so |
| + // there isn't a straight-forward way to find it. |
| struct nlist nl[3]; |
| bzero(&nl, sizeof(nl)); |
| - nl[0].n_un.n_name = (char*)"_object_cxxDestruct"; |
| + nl[0].n_un.n_name = const_cast<char*>("_object_cxxDestruct"); |
| // My ability to calculate the base for offsets is apparently poor. |
| // Use |class_addIvar| as a known reference point. |
| - nl[1].n_un.n_name = (char*)"_class_addIvar"; |
| + nl[1].n_un.n_name = const_cast<char*>("_class_addIvar"); |
| if (nlist("/usr/lib/libobjc.dylib", nl) < 0 || |
|
Mark Mentovai
2011/05/31 21:00:57
You could maybe use info.dli_fname here too, since
Scott Hess - ex-Googler
2011/06/07 21:24:51
I did break the function across the two code paths
|
| nl[0].n_type == N_UNDF || nl[1].n_type == N_UNDF) |
| return NULL; |
| - return (DestructFn*)((char*)&class_addIvar - nl[1].n_value + nl[0].n_value); |
| + return reinterpret_cast<DestructFn*>( |
| + reinterpret_cast<char*>(&class_addIvar) - nl[1].n_value + nl[0].n_value); |
|
Mark Mentovai
2011/05/31 21:05:05
Maybe intptr_t is a little more meaningful than ch
Scott Hess - ex-Googler
2011/06/07 21:24:51
Done.
|
| } |
| // Replacement |-dealloc| which turns objects into zombies and places |
| @@ -109,12 +129,12 @@ void ZombieDealloc(id self, SEL _cmd) { |
| return; |
| } |
| - // Use the original |-dealloc| if |object_cxxDestruct| was never |
| + // Use the original |-dealloc| if |g_objectDestruct| was never |
| // initialized, because otherwise C++ destructors won't be called. |
| // This case should be impossible, but doing it wrong would cause |
| // terrible problems. |
| - DCHECK(g_object_cxxDestruct); |
| - if (!g_object_cxxDestruct) { |
| + DCHECK(g_objectDestruct); |
| + if (!g_objectDestruct) { |
| g_originalDeallocIMP(self, _cmd); |
| return; |
| } |
| @@ -124,10 +144,11 @@ void ZombieDealloc(id self, SEL _cmd) { |
| // Destroy the instance by calling C++ destructors and clearing it |
| // to something unlikely to work well if someone references it. |
| - // NOTE(shess): |object_dispose()| will call |object_cxxDestruct()| |
| - // again when the zombie falls off the treadmill! But by then |isa| |
| - // will be something without destructors, so it won't hurt anything. |
| - (*g_object_cxxDestruct)(self); |
| + // NOTE(shess): |object_dispose()| will call this again when the |
| + // zombie falls off the treadmill! But by then |isa| will be a |
| + // class without C++ destructors or associative references, so it |
| + // won't hurt anything. |
| + (*g_objectDestruct)(self); |
| memset(self, '!', size); |
| // If the instance is big enough, make it into a fat zombie and have |
| @@ -222,7 +243,7 @@ BOOL ZombieInit() { |
| Class rootClass = [NSObject class]; |
| - g_object_cxxDestruct = LookupObjectCxxDestruct(); |
| + g_objectDestruct = LookupObjectDestruct(); |
| g_originalDeallocIMP = |
| class_getMethodImplementation(rootClass, @selector(dealloc)); |
| // objc_getClass() so CrZombie doesn't need +class. |
| @@ -230,7 +251,7 @@ BOOL ZombieInit() { |
| g_fatZombieClass = objc_getClass("CrFatZombie"); |
| g_fatZombieSize = class_getInstanceSize(g_fatZombieClass); |
| - if (!g_object_cxxDestruct || !g_originalDeallocIMP || |
| + if (!g_objectDestruct || !g_originalDeallocIMP || |
| !g_zombieClass || !g_fatZombieClass) |
| return NO; |