/* * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /*********************************************************************** * objc-runtime.m * Copyright 1988-1996, NeXT Software, Inc. * Author: s. naroff * **********************************************************************/ /*********************************************************************** * Imports. **********************************************************************/ #include "objc-private.h" #include "objc-loadmethod.h" #include "message.h" OBJC_EXPORT Class getOriginalClassForPosingClass(Class); /*********************************************************************** * Exports. **********************************************************************/ // Settings from environment variables #if SUPPORT_ENVIRON int PrintImages = -1; // env OBJC_PRINT_IMAGES int PrintLoading = -1; // env OBJC_PRINT_LOAD_METHODS int PrintInitializing = -1; // env OBJC_PRINT_INITIALIZE_METHODS int PrintResolving = -1; // env OBJC_PRINT_RESOLVED_METHODS int PrintConnecting = -1; // env OBJC_PRINT_CLASS_SETUP int PrintProtocols = -1; // env OBJC_PRINT_PROTOCOL_SETUP int PrintIvars = -1; // env OBJC_PRINT_IVAR_SETUP int PrintVtables = -1; // env OBJC_PRINT_VTABLE_SETUP int PrintVtableImages = -1;//env OBJC_PRINT_VTABLE_IMAGES int PrintFuture = -1; // env OBJC_PRINT_FUTURE_CLASSES int PrintRTP = -1; // env OBJC_PRINT_RTP int PrintGC = -1; // env OBJC_PRINT_GC int PrintPreopt = -1; // env OBJC_PRINT_PREOPTIMIZATION int PrintCxxCtors = -1; // env OBJC_PRINT_CXX_CTORS int PrintExceptions = -1; // env OBJC_PRINT_EXCEPTIONS int PrintExceptionThrow = -1; // env OBJC_PRINT_EXCEPTION_THROW int PrintAltHandlers = -1; // env OBJC_PRINT_ALT_HANDLERS int PrintDeprecation = -1;// env OBJC_PRINT_DEPRECATION_WARNINGS int PrintReplacedMethods = -1; // env OBJC_PRINT_REPLACED_METHODS int PrintCaches = -1; // env OBJC_PRINT_CACHE_SETUP int PrintPoolHiwat = -1; // env OBJC_PRINT_POOL_HIGHWATER int PrintCustomRR = -1; // env OBJC_PRINT_CUSTOM_RR int PrintCustomAWZ = -1; // env OBJC_PRINT_CUSTOM_AWZ int UseInternalZone = -1; // env OBJC_USE_INTERNAL_ZONE int DebugUnload = -1; // env OBJC_DEBUG_UNLOAD int DebugFragileSuperclasses = -1; // env OBJC_DEBUG_FRAGILE_SUPERCLASSES int DebugNilSync = -1; // env OBJC_DEBUG_NIL_SYNC int DebugNonFragileIvars = -1; // env OBJC_DEBUG_NONFRAGILE_IVARS int DebugAltHandlers = -1;// env OBJC_DEBUG_ALT_HANDLERS int DisableGC = -1; // env OBJC_DISABLE_GC int DisableVtables = -1; // env OBJC_DISABLE_VTABLES int DisablePreopt = -1; // env OBJC_DISABLE_PREOPTIMIZATION int DebugFinalizers = -1; // env OBJC_DEBUG_FINALIZERS #endif // objc's key for pthread_getspecific static tls_key_t _objc_pthread_key; // Selectors SEL SEL_load = NULL; SEL SEL_initialize = NULL; SEL SEL_resolveInstanceMethod = NULL; SEL SEL_resolveClassMethod = NULL; SEL SEL_cxx_construct = NULL; SEL SEL_cxx_destruct = NULL; SEL SEL_retain = NULL; SEL SEL_release = NULL; SEL SEL_autorelease = NULL; SEL SEL_retainCount = NULL; SEL SEL_alloc = NULL; SEL SEL_allocWithZone = NULL; SEL SEL_copy = NULL; SEL SEL_new = NULL; SEL SEL_finalize = NULL; SEL SEL_forwardInvocation = NULL; header_info *FirstHeader = 0; // NULL means empty list header_info *LastHeader = 0; // NULL means invalid; recompute it int HeaderCount = 0; /*********************************************************************** * objc_getClass. Return the id of the named class. If the class does * not exist, call _objc_classLoader and then objc_classHandler, either of * which may create a new class. * Warning: doesn't work if aClassName is the name of a posed-for class's isa! **********************************************************************/ id objc_getClass(const char *aClassName) { if (!aClassName) return Nil; // NO unconnected, YES class handler return look_up_class(aClassName, NO, YES); } /*********************************************************************** * objc_getRequiredClass. * Same as objc_getClass, but kills the process if the class is not found. * This is used by ZeroLink, where failing to find a class would be a * compile-time link error without ZeroLink. **********************************************************************/ id objc_getRequiredClass(const char *aClassName) { id cls = objc_getClass(aClassName); if (!cls) _objc_fatal("link error: class '%s' not found.", aClassName); return cls; } /*********************************************************************** * objc_lookUpClass. Return the id of the named class. * If the class does not exist, call _objc_classLoader, which may create * a new class. * * Formerly objc_getClassWithoutWarning () **********************************************************************/ id objc_lookUpClass(const char *aClassName) { if (!aClassName) return Nil; // NO unconnected, NO class handler return look_up_class(aClassName, NO, NO); } /*********************************************************************** * objc_getFutureClass. Return the id of the named class. * If the class does not exist, return an uninitialized class * structure that will be used for the class when and if it * does get loaded. * Not thread safe. **********************************************************************/ Class objc_getFutureClass(const char *name) { Class cls; // YES unconnected, NO class handler // (unconnected is OK because it will someday be the real class) cls = (Class)look_up_class(name, YES, NO); if (cls) { if (PrintFuture) { _objc_inform("FUTURE: found %p already in use for %s", cls, name); } return cls; } // No class or future class with that name yet. Make one. // fixme not thread-safe with respect to // simultaneous library load or getFutureClass. return _objc_allocateFutureClass(name); } /*********************************************************************** * objc_getMetaClass. Return the id of the meta class the named class. * Warning: doesn't work if aClassName is the name of a posed-for class's isa! **********************************************************************/ id objc_getMetaClass(const char *aClassName) { Class cls; if (!aClassName) return Nil; cls = (Class)objc_getClass (aClassName); if (!cls) { _objc_inform ("class `%s' not linked into application", aClassName); return Nil; } return (id)cls->isa; } /*********************************************************************** * appendHeader. Add a newly-constructed header_info to the list. **********************************************************************/ void appendHeader(header_info *hi) { // Add the header to the header list. // The header is appended to the list, to preserve the bottom-up order. HeaderCount++; hi->next = NULL; if (!FirstHeader) { // list is empty FirstHeader = LastHeader = hi; } else { if (!LastHeader) { // list is not empty, but LastHeader is invalid - recompute it LastHeader = FirstHeader; while (LastHeader->next) LastHeader = LastHeader->next; } // LastHeader is now valid LastHeader->next = hi; LastHeader = hi; } } /*********************************************************************** * removeHeader * Remove the given header from the header list. * FirstHeader is updated. * LastHeader is set to NULL. Any code that uses LastHeader must * detect this NULL and recompute LastHeader by traversing the list. **********************************************************************/ void removeHeader(header_info *hi) { header_info **hiP; for (hiP = &FirstHeader; *hiP != NULL; hiP = &(**hiP).next) { if (*hiP == hi) { header_info *deadHead = *hiP; // Remove from the linked list (updating FirstHeader if necessary). *hiP = (**hiP).next; // Update LastHeader if necessary. if (LastHeader == deadHead) { LastHeader = NULL; // will be recomputed next time it's used } HeaderCount--; break; } } } /*********************************************************************** * environ_init * Read environment variables that affect the runtime. * Also print environment variable help, if requested. **********************************************************************/ void environ_init(void) { #if SUPPORT_ENVIRON int PrintHelp = (getenv("OBJC_HELP") != NULL); int PrintOptions = (getenv("OBJC_PRINT_OPTIONS") != NULL); int secure = issetugid(); if (secure) { // All environment variables are ignored when setuid or setgid. // This includes OBJC_HELP and OBJC_PRINT_OPTIONS themselves. } else { if (PrintHelp) { _objc_inform("Objective-C runtime debugging. Set variable=YES to enable."); _objc_inform("OBJC_HELP: describe available environment variables"); if (PrintOptions) { _objc_inform("OBJC_HELP is set"); } _objc_inform("OBJC_PRINT_OPTIONS: list which options are set"); } if (PrintOptions) { _objc_inform("OBJC_PRINT_OPTIONS is set"); } } #define OPTION(var, env, help) \ if ( var == -1 ) { \ char *value = getenv(#env); \ var = value != NULL && !strcmp("YES", value); \ if (secure) { \ if (var) _objc_inform(#env " ignored when running setuid or setgid"); \ var = 0; \ } else { \ if (PrintHelp) _objc_inform(#env ": " help); \ if (PrintOptions && var) _objc_inform(#env " is set"); \ } \ } OPTION(PrintImages, OBJC_PRINT_IMAGES, "log image and library names as they are loaded"); OPTION(PrintLoading, OBJC_PRINT_LOAD_METHODS, "log calls to class and category +load methods"); OPTION(PrintInitializing, OBJC_PRINT_INITIALIZE_METHODS, "log calls to class +initialize methods"); OPTION(PrintResolving, OBJC_PRINT_RESOLVED_METHODS, "log methods created by +resolveClassMethod: and +resolveInstanceMethod:"); OPTION(PrintConnecting, OBJC_PRINT_CLASS_SETUP, "log progress of class and category setup"); OPTION(PrintProtocols, OBJC_PRINT_PROTOCOL_SETUP, "log progress of protocol setup"); OPTION(PrintIvars, OBJC_PRINT_IVAR_SETUP, "log processing of non-fragile ivars"); OPTION(PrintVtables, OBJC_PRINT_VTABLE_SETUP, "log processing of class vtables"); OPTION(PrintVtableImages, OBJC_PRINT_VTABLE_IMAGES, "print vtable images showing overridden methods"); OPTION(PrintCaches, OBJC_PRINT_CACHE_SETUP, "log processing of method caches"); OPTION(PrintFuture, OBJC_PRINT_FUTURE_CLASSES, "log use of future classes for toll-free bridging"); OPTION(PrintRTP, OBJC_PRINT_RTP, "log initialization of the Objective-C runtime pages"); OPTION(PrintGC, OBJC_PRINT_GC, "log some GC operations"); OPTION(PrintPreopt, OBJC_PRINT_PREOPTIMIZATION, "log preoptimization courtesy of dyld shared cache"); OPTION(PrintCxxCtors, OBJC_PRINT_CXX_CTORS, "log calls to C++ ctors and dtors for instance variables"); OPTION(PrintExceptions, OBJC_PRINT_EXCEPTIONS, "log exception handling"); OPTION(PrintExceptionThrow, OBJC_PRINT_EXCEPTION_THROW, "log backtrace of every objc_exception_throw()"); OPTION(PrintAltHandlers, OBJC_PRINT_ALT_HANDLERS, "log processing of exception alt handlers"); OPTION(PrintReplacedMethods, OBJC_PRINT_REPLACED_METHODS, "log methods replaced by category implementations"); OPTION(PrintDeprecation, OBJC_PRINT_DEPRECATION_WARNINGS, "warn about calls to deprecated runtime functions"); OPTION(PrintPoolHiwat, OBJC_PRINT_POOL_HIGHWATER, "log high-water marks for autorelease pools"); OPTION(PrintCustomRR, OBJC_PRINT_CUSTOM_RR, "log classes with un-optimized custom retain/release methods"); OPTION(PrintCustomAWZ, OBJC_PRINT_CUSTOM_AWZ, "log classes with un-optimized custom allocWithZone methods"); OPTION(DebugUnload, OBJC_DEBUG_UNLOAD, "warn about poorly-behaving bundles when unloaded"); OPTION(DebugFragileSuperclasses, OBJC_DEBUG_FRAGILE_SUPERCLASSES, "warn about subclasses that may have been broken by subsequent changes to superclasses"); OPTION(DebugFinalizers, OBJC_DEBUG_FINALIZERS, "warn about classes that implement -dealloc but not -finalize"); OPTION(DebugNilSync, OBJC_DEBUG_NIL_SYNC, "warn about @synchronized(nil), which does no synchronization"); OPTION(DebugNonFragileIvars, OBJC_DEBUG_NONFRAGILE_IVARS, "capriciously rearrange non-fragile ivars"); OPTION(DebugAltHandlers, OBJC_DEBUG_ALT_HANDLERS, "record more info about bad alt handler use"); OPTION(UseInternalZone, OBJC_USE_INTERNAL_ZONE, "allocate runtime data in a dedicated malloc zone"); OPTION(DisableGC, OBJC_DISABLE_GC, "force GC OFF, even if the executable wants it on"); OPTION(DisableVtables, OBJC_DISABLE_VTABLES, "disable vtable dispatch"); OPTION(DisablePreopt, OBJC_DISABLE_PREOPTIMIZATION, "disable preoptimization courtesy of dyld shared cache"); #undef OPTION #endif } /*********************************************************************** * logReplacedMethod * OBJC_PRINT_REPLACED_METHODS implementation **********************************************************************/ void logReplacedMethod(const char *className, SEL s, BOOL isMeta, const char *catName, IMP oldImp, IMP newImp) { const char *oldImage = "??"; const char *newImage = "??"; // Silently ignore +load replacement because category +load is special if (s == SEL_load) return; #if TARGET_OS_WIN32 // don't know dladdr()/dli_fname equivalent #else Dl_info dl; if (dladdr((void*)oldImp, &dl) && dl.dli_fname) oldImage = dl.dli_fname; if (dladdr((void*)newImp, &dl) && dl.dli_fname) newImage = dl.dli_fname; #endif _objc_inform("REPLACED: %c[%s %s] %s%s (IMP was %p (%s), now %p (%s))", isMeta ? '+' : '-', className, sel_getName(s), catName ? "by category " : "", catName ? catName : "", oldImp, oldImage, newImp, newImage); } /*********************************************************************** * objc_setMultithreaded. **********************************************************************/ void objc_setMultithreaded (BOOL flag) { OBJC_WARN_DEPRECATED; // Nothing here. Thread synchronization in the runtime is always active. } /*********************************************************************** * _objc_fetch_pthread_data * Fetch objc's pthread data for this thread. * If the data doesn't exist yet and create is NO, return NULL. * If the data doesn't exist yet and create is YES, allocate and return it. **********************************************************************/ _objc_pthread_data *_objc_fetch_pthread_data(BOOL create) { _objc_pthread_data *data; data = (_objc_pthread_data *)tls_get(_objc_pthread_key); if (!data && create) { data = (_objc_pthread_data *) _calloc_internal(1, sizeof(_objc_pthread_data)); tls_set(_objc_pthread_key, data); } return data; } /*********************************************************************** * _objc_pthread_destroyspecific * Destructor for objc's per-thread data. * arg shouldn't be NULL, but we check anyway. **********************************************************************/ extern void _destroyInitializingClassList(struct _objc_initializing_classes *list); void _objc_pthread_destroyspecific(void *arg) { _objc_pthread_data *data = (_objc_pthread_data *)arg; if (data != NULL) { _destroyInitializingClassList(data->initializingClasses); _destroySyncCache(data->syncCache); _destroyAltHandlerList(data->handlerList); // add further cleanup here... _free_internal(data); } } void tls_init(void) { #if SUPPORT_DIRECT_THREAD_KEYS _objc_pthread_key = TLS_DIRECT_KEY; pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific); #else _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific); #endif } /*********************************************************************** * _objcInit * Former library initializer. This function is now merely a placeholder * for external callers. All runtime initialization has now been moved * to map_images() and _objc_init. **********************************************************************/ void _objcInit(void) { // do nothing } #if !(TARGET_OS_WIN32 || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE) /*********************************************************************** * _objc_setNilReceiver **********************************************************************/ id _objc_setNilReceiver(id newNilReceiver) { id oldNilReceiver; oldNilReceiver = _objc_nilReceiver; _objc_nilReceiver = newNilReceiver; return oldNilReceiver; } /*********************************************************************** * _objc_getNilReceiver **********************************************************************/ id _objc_getNilReceiver(void) { return _objc_nilReceiver; } #endif /*********************************************************************** * objc_setForwardHandler **********************************************************************/ void objc_setForwardHandler(void *fwd, void *fwd_stret) { _objc_forward_handler = fwd; _objc_forward_stret_handler = fwd_stret; } #if defined(__i386__) || defined(__x86_64__) /********************************************************************** * objc_branch_size * Returns the number of BYTES needed * for a branch from entry to target. **********************************************************************/ size_t objc_branch_size(void *entry, void *target) { return objc_cond_branch_size(entry, target, COND_ALWAYS); } size_t objc_cond_branch_size(void *entry, void *target, unsigned cond) { // For simplicity, always use 32-bit relative jumps. if (cond == COND_ALWAYS) return 5; else return 6; } /********************************************************************** * objc_write_branch * Writes at entry an i386 branch instruction sequence that branches to target. * The sequence written will be objc_branch_size(entry, target) BYTES. * Returns the number of BYTES written. **********************************************************************/ size_t objc_write_branch(void *entry, void *target) { return objc_write_cond_branch(entry, target, COND_ALWAYS); } size_t objc_write_cond_branch(void *entry, void *target, unsigned cond) { uint8_t *address = (uint8_t *)entry; // instructions written to here intptr_t destination = (intptr_t)target; // branch dest as absolute address intptr_t displacement = (intptr_t)destination - ((intptr_t)address + objc_cond_branch_size(entry, target, cond)); // branch dest as relative offset // For simplicity, always use 32-bit relative jumps if (cond != COND_ALWAYS) { *address++ = 0x0f; // Jcc prefix } *address++ = cond; *address++ = displacement & 0xff; *address++ = (displacement >> 8) & 0xff; *address++ = (displacement >> 16) & 0xff; *address++ = (displacement >> 24) & 0xff; return address - (uint8_t *)entry; } // defined __i386__ #endif #if !__OBJC2__ // GrP fixme OBJC_EXTERN Class _objc_getOrigClass(const char *name); #endif const char *class_getImageName(Class cls) { #if TARGET_OS_WIN32 TCHAR *szFileName; DWORD charactersCopied; Class origCls; HMODULE classModule; BOOL res; #endif if (!cls) return NULL; #if !__OBJC2__ cls = _objc_getOrigClass(_class_getName(cls)); #endif #if TARGET_OS_WIN32 charactersCopied = 0; szFileName = malloc(MAX_PATH * sizeof(TCHAR)); origCls = objc_getOrigClass(class_getName(cls)); classModule = NULL; res = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)origCls, &classModule); if (res && classModule) { charactersCopied = GetModuleFileName(classModule, szFileName, MAX_PATH * sizeof(TCHAR)); } if (classModule) FreeLibrary(classModule); if (charactersCopied) { return (const char *)szFileName; } else free(szFileName); return NULL; #else return dyld_image_path_containing_address(cls); #endif } const char **objc_copyImageNames(unsigned int *outCount) { header_info *hi; int count = 0; int max = HeaderCount; #if TARGET_OS_WIN32 const TCHAR **names = (const TCHAR **)calloc(max+1, sizeof(TCHAR *)); #else const char **names = (const char **)calloc(max+1, sizeof(char *)); #endif for (hi = FirstHeader; hi != NULL && count < max; hi = hi->next) { #if TARGET_OS_WIN32 if (hi->moduleName) { names[count++] = hi->moduleName; } #else if (hi->fname) { names[count++] = hi->fname; } #endif } names[count] = NULL; if (count == 0) { // Return NULL instead of empty list if there are no images free((void *)names); names = NULL; } if (outCount) *outCount = count; return names; } /********************************************************************** * **********************************************************************/ const char ** objc_copyClassNamesForImage(const char *image, unsigned int *outCount) { header_info *hi; if (!image) { if (outCount) *outCount = 0; return NULL; } // Find the image. for (hi = FirstHeader; hi != NULL; hi = hi->next) { #if TARGET_OS_WIN32 if (0 == wcscmp((TCHAR *)image, hi->moduleName)) break; #else if (0 == strcmp(image, hi->fname)) break; #endif } if (!hi) { if (outCount) *outCount = 0; return NULL; } return _objc_copyClassNamesForImage(hi, outCount); } /********************************************************************** * Fast Enumeration Support **********************************************************************/ static void (*enumerationMutationHandler)(id); /********************************************************************** * objc_enumerationMutation * called by compiler when a mutation is detected during foreach iteration **********************************************************************/ void objc_enumerationMutation(id object) { if (enumerationMutationHandler == nil) { _objc_fatal("mutation detected during 'for(... in ...)' enumeration of object %p.", object); } (*enumerationMutationHandler)(object); } /********************************************************************** * objc_setEnumerationMutationHandler * an entry point to customize mutation error handing **********************************************************************/ void objc_setEnumerationMutationHandler(void (*handler)(id)) { enumerationMutationHandler = handler; } /********************************************************************** * Associative Reference Support **********************************************************************/ #if SUPPORT_GC id objc_getAssociatedObject_gc(id object, const void *key) { return (id)auto_zone_get_associative_ref(gc_zone, object, (void *)key); } #endif id objc_getAssociatedObject_non_gc(id object, const void *key) { return _object_get_associative_reference(object, (void *)key); } id objc_getAssociatedObject(id object, const void *key) { #if SUPPORT_GC if (UseGC) { return (id)auto_zone_get_associative_ref(gc_zone, object, (void *)key); } else #endif { return _object_get_associative_reference(object, (void *)key); } } #if SUPPORT_GC void objc_setAssociatedObject_gc(id object, const void *key, id value, objc_AssociationPolicy policy) { if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) { value = ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy); } auto_zone_set_associative_ref(gc_zone, object, (void *)key, value); } #endif void objc_setAssociatedObject_non_gc(id object, const void *key, id value, objc_AssociationPolicy policy) { _object_set_associative_reference(object, (void *)key, value, policy); } void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) { #if SUPPORT_GC if (UseGC) { if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) { value = ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy); } auto_zone_set_associative_ref(gc_zone, object, (void *)key, value); } else #endif { // Note, creates a retained reference in non-GC. _object_set_associative_reference(object, (void *)key, value, policy); } } void objc_removeAssociatedObjects(id object) { #if SUPPORT_GC if (UseGC) { auto_zone_erase_associative_refs(gc_zone, object); } else #endif { if (_class_instancesHaveAssociatedObjects(_object_getClass(object))) _object_remove_assocations(object); } } BOOL class_instancesHaveAssociatedObjects(Class cls) { return _class_instancesHaveAssociatedObjects(cls); } /********************************************************************** * Debugger mode * * Debugger mode is used when gdb wants to call runtime functions * and other methods while other threads are stopped. The runtime * provides best-effort functionality while avoiding deadlocks * with the stopped threads. gdb is responsible for ensuring that all * threads but one stay stopped. * * When debugger mode starts, the runtime acquires as many locks as * it can. Any locks that can't be acquired are off-limits until * debugger mode ends. The locking functions in objc-os.h check each * operation and halt if a disallowed lock is used; gdb catches that * trap and cleans up. * * Each ABI is responsible for tracking its locks. Any lock not * handled there is a potential gdb deadlock. **********************************************************************/ #if SUPPORT_DEBUGGER_MODE int DebuggerMode = DEBUGGER_OFF; objc_thread_t DebuggerModeThread = 0; static int DebuggerModeCount; /********************************************************************** * gdb_objc_startDebuggerMode * Start debugger mode by taking locks. Return 0 if not enough locks * could be acquired. **********************************************************************/ int gdb_objc_startDebuggerMode(uint32_t flags) { BOOL wantFull = flags & OBJC_DEBUGMODE_FULL; if (! DebuggerMode) { // Start debugger mode int mode = startDebuggerMode(); // Do this FIRST if (mode == DEBUGGER_OFF) { // sorry return 0; } else if (mode == DEBUGGER_PARTIAL && wantFull) { // not good enough endDebuggerMode(); return 0; } else { // w00t DebuggerMode = mode; DebuggerModeCount = 1; DebuggerModeThread = thread_self(); return 1; } } else if (DebuggerMode == DEBUGGER_PARTIAL && wantFull) { // Debugger mode already active, but not as requested - sorry return 0; } else { // Debugger mode already active as requested if (thread_self() == DebuggerModeThread) { DebuggerModeCount++; return 1; } else { _objc_inform("DEBUGGER MODE: debugger is buggy: can't run " "debugger mode from two threads!"); return 0; } } } /********************************************************************** * gdb_objc_endDebuggerMode * Relinquish locks and end debugger mode. **********************************************************************/ void gdb_objc_endDebuggerMode(void) { if (DebuggerMode && thread_self() == DebuggerModeThread) { if (--DebuggerModeCount == 0) { DebuggerMode = NO; DebuggerModeThread = 0; endDebuggerMode(); // Do this LAST } } else { _objc_inform("DEBUGGER MODE: debugger is buggy: debugger mode " "not active for this thread!"); } } /********************************************************************** * gdb_objc_debuggerModeFailure * Breakpoint hook for gdb when debugger mode can't finish something **********************************************************************/ void gdb_objc_debuggerModeFailure(void) { _objc_fatal("DEBUGGER MODE: failed"); } // SUPPORT_DEBUGGER_MODE #endif