Index: chrome/common/mac/objc_zombie.mm |
diff --git a/chrome/common/mac/objc_zombie.mm b/chrome/common/mac/objc_zombie.mm |
index a9343594e25786e04a75f3a5321a6782379660be..afb532830c9df161cc2d8363e5d84f11fcc8b5da 100644 |
--- a/chrome/common/mac/objc_zombie.mm |
+++ b/chrome/common/mac/objc_zombie.mm |
@@ -4,12 +4,15 @@ |
#import "chrome/common/mac/objc_zombie.h" |
+#include <AvailabilityMacros.h> |
+ |
#include <dlfcn.h> |
#include <execinfo.h> |
#include <mach-o/dyld.h> |
#include <mach-o/nlist.h> |
#import <objc/objc-class.h> |
+#import <objc/runtime.h> |
#include <algorithm> |
@@ -22,6 +25,12 @@ |
#include "base/synchronization/lock.h" |
#import "chrome/common/mac/objc_method_swizzle.h" |
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_6 |
+// Apparently objc/runtime.h doesn't define this with the 10.6 SDK yet. |
+// The docs say it exists since 10.6 however. |
+OBJC_EXPORT void *objc_destructInstance(id obj); |
+#endif |
+ |
// Deallocated objects are re-classed as |CrZombie|. No superclass |
// because then the class would have to override many/most of the |
// inherited methods (|NSObject| is like a category magnet!). |
@@ -52,16 +61,6 @@ namespace { |
// the maximum number of 32-bit items which can be encoded is 23. |
const size_t kBacktraceDepth = 20; |
-// 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*| but pretending it has no |
-// return value makes the code simpler. |
-typedef void DestructFn(id obj); |
-DestructFn* g_objectDestruct = NULL; |
- |
// The original implementation for |-[NSObject dealloc]|. |
IMP g_originalDeallocIMP = NULL; |
@@ -105,60 +104,6 @@ const char* LookupObjcRuntimePath() { |
return NULL; |
} |
-// Lookup |objc_destructInstance()| dynamically because it isn't |
-// available on 10.5, but we link with the 10.5 SDK. |
-DestructFn* LookupObjectDestruct_10_6() { |
- const char* objc_runtime_path = LookupObjcRuntimePath(); |
- if (!objc_runtime_path) |
- return NULL; |
- |
- void* handle = dlopen(objc_runtime_path, RTLD_LAZY | RTLD_LOCAL); |
- if (!handle) |
- return NULL; |
- |
- void* fn = dlsym(handle, "objc_destructInstance"); |
- |
- // |fn| would normally be expected to become invalid after this |
- // |dlclose()|, but since the |dlopen()| was on a library |
- // containing an already-mapped symbol, it will remain valid. |
- dlclose(handle); |
- return reinterpret_cast<DestructFn*>(fn); |
-} |
- |
-// Under 10.5 |object_cxxDestruct()| is used but unfortunately it is |
-// |__private_extern__| in the runtime, meaning |dlsym()| cannot reach it. |
-DestructFn* LookupObjectDestruct_10_5() { |
- // |nlist()| is only present for 32-bit. |
-#if ARCH_CPU_32_BITS |
- const char* objc_runtime_path = LookupObjcRuntimePath(); |
- if (!objc_runtime_path) |
- return NULL; |
- |
- struct nlist nl[3]; |
- bzero(&nl, sizeof(nl)); |
- |
- 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 = const_cast<char*>("_class_addIvar"); |
- |
- if (nlist(objc_runtime_path, nl) < 0 || |
Mark Mentovai
2012/08/09 16:03:43
I’m very happy to see this code go.
|
- nl[0].n_type == N_UNDF || nl[1].n_type == N_UNDF) |
- return NULL; |
- |
- // Back out offset to |class_addIvar()| to get the baseline, then |
- // add back offset to |object_cxxDestruct()|. |
- uintptr_t reference_addr = reinterpret_cast<uintptr_t>(&class_addIvar); |
- reference_addr -= nl[1].n_value; |
- reference_addr += nl[0].n_value; |
- |
- return reinterpret_cast<DestructFn*>(reference_addr); |
-#endif |
- |
- return NULL; |
-} |
- |
// Replacement |-dealloc| which turns objects into zombies and places |
// them into |g_zombies| to be freed later. |
void ZombieDealloc(id self, SEL _cmd) { |
@@ -172,16 +117,6 @@ void ZombieDealloc(id self, SEL _cmd) { |
return; |
} |
- // 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_objectDestruct); |
- if (!g_objectDestruct) { |
- g_originalDeallocIMP(self, _cmd); |
- return; |
- } |
- |
Class wasa = object_getClass(self); |
const size_t size = class_getInstanceSize(wasa); |
@@ -191,7 +126,7 @@ void ZombieDealloc(id self, SEL _cmd) { |
// 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); |
+ objc_destructInstance(self); |
memset(self, '!', size); |
// If the instance is big enough, make it into a fat zombie and have |
@@ -313,54 +248,12 @@ void ZombieObjectCrash(id object, SEL aSelector, SEL viaSelector) { |
*zero = 0; |
} |
-// For monitoring failures in |ZombieInit()|. |
-enum ZombieFailure { |
- FAILED_10_5, |
- FAILED_10_6, |
- |
- // Add new versions before here. |
- FAILED_MAX, |
-}; |
- |
-void RecordZombieFailure(ZombieFailure failure) { |
- UMA_HISTOGRAM_ENUMERATION("OSX.ZombieInitFailure", failure, FAILED_MAX); |
-} |
- |
// Initialize our globals, returning YES on success. |
BOOL ZombieInit() { |
static BOOL initialized = NO; |
if (initialized) |
return YES; |
- // Whitelist releases that are compatible with objc zombies. |
- if (base::mac::IsOSLeopard()) { |
- g_objectDestruct = LookupObjectDestruct_10_5(); |
- if (!g_objectDestruct) { |
- RecordZombieFailure(FAILED_10_5); |
- return NO; |
- } |
- } else if (base::mac::IsOSSnowLeopard()) { |
- g_objectDestruct = LookupObjectDestruct_10_6(); |
- if (!g_objectDestruct) { |
- RecordZombieFailure(FAILED_10_6); |
- return NO; |
- } |
- } else if (base::mac::IsOSLionOrLater()) { |
- // Assume the future looks like the present. |
- g_objectDestruct = LookupObjectDestruct_10_6(); |
- |
- // Put all future failures into the MAX bin. New OS releases come |
- // out infrequently enough that this should always correspond to |
- // "Next release", and once the next release happens that bin will |
- // get an official name. |
- if (!g_objectDestruct) { |
- RecordZombieFailure(FAILED_MAX); |
- return NO; |
- } |
- } else { |
- return NO; |
- } |
- |
Class rootClass = [NSObject class]; |
g_originalDeallocIMP = |
class_getMethodImplementation(rootClass, @selector(dealloc)); |
@@ -369,8 +262,7 @@ BOOL ZombieInit() { |
g_fatZombieClass = objc_getClass("CrFatZombie"); |
g_fatZombieSize = class_getInstanceSize(g_fatZombieClass); |
- if (!g_objectDestruct || !g_originalDeallocIMP || |
- !g_zombieClass || !g_fatZombieClass) |
+ if (!g_originalDeallocIMP || !g_zombieClass || !g_fatZombieClass) |
return NO; |
initialized = YES; |