]> git.saurik.com Git - apple/objc4.git/blobdiff - runtime/objc-runtime.mm
objc4-818.2.tar.gz
[apple/objc4.git] / runtime / objc-runtime.mm
index ba1b38d7033b950e957fd02172e40656b07c40b7..e38b274f81476cb97894022480f44678c7750515 100644 (file)
 * Imports.
 **********************************************************************/
 
+#include <os/feature_private.h> // os_feature_enabled_simple()
 #include "objc-private.h"
 #include "objc-loadmethod.h"
+#include "objc-file.h"
 #include "message.h"
 
-OBJC_EXPORT Class getOriginalClassForPosingClass(Class);
-
-
 /***********************************************************************
 * Exports.
 **********************************************************************/
 
+/* Linker metadata symbols */
+
+// NSObject was in Foundation/CF on macOS < 10.8.
+#if TARGET_OS_OSX
+#if __OBJC2__
+
+const char __objc_nsobject_class_10_5 = 0;
+const char __objc_nsobject_class_10_6 = 0;
+const char __objc_nsobject_class_10_7 = 0;
+
+const char __objc_nsobject_metaclass_10_5 = 0;
+const char __objc_nsobject_metaclass_10_6 = 0;
+const char __objc_nsobject_metaclass_10_7 = 0;
+
+const char __objc_nsobject_isa_10_5 = 0;
+const char __objc_nsobject_isa_10_6 = 0;
+const char __objc_nsobject_isa_10_7 = 0;
+
+#else
+
+const char __objc_nsobject_class_10_5 = 0;
+const char __objc_nsobject_class_10_6 = 0;
+const char __objc_nsobject_class_10_7 = 0;
+
+#endif
+#endif
+
 // Settings from environment variables
 #define OPTION(var, env, help) bool var = false;
 #include "objc-env.h"
@@ -62,37 +88,24 @@ const option_t Settings[] = {
 #undef OPTION
 };
 
+namespace objc {
+    int PageCountWarning = 50;  // Default value if the environment variable is not set
+}
 
 // objc's key for pthread_getspecific
+#if SUPPORT_DIRECT_THREAD_KEYS
+#define _objc_pthread_key TLS_DIRECT_KEY
+#else
 static tls_key_t _objc_pthread_key;
+#endif
 
 // 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_dealloc = NULL;
-SEL SEL_copy = NULL;
-SEL SEL_new = NULL;
-SEL SEL_forwardInvocation = NULL;
-SEL SEL_tryRetain = NULL;
-SEL SEL_isDeallocating = NULL;
-SEL SEL_retainWeakReference = NULL;
-SEL SEL_allowsWeakReference = NULL;
-
 
+struct objc::SafeRanges objc::dataSegmentsRanges;
 header_info *FirstHeader = 0;  // NULL means empty list
 header_info *LastHeader  = 0;  // NULL means invalid; recompute it
-int HeaderCount = 0;
-
 
 // Set to true on the child side of fork() 
 // if the parent process was multithreaded when fork() was called.
@@ -107,6 +120,22 @@ id objc_noop_imp(id self, SEL _cmd __unused) {
 }
 
 
+/***********************************************************************
+* _objc_isDebugBuild. Defined in debug builds only.
+* Some test code looks for the presence of this symbol.
+**********************************************************************/
+#if DEBUG != OBJC_IS_DEBUG_BUILD
+#error mismatch in debug-ness macros
+// DEBUG is used in our code. OBJC_IS_DEBUG_BUILD is used in the
+// header declaration of _objc_isDebugBuild() because that header
+// is visible to other clients who might have their own DEBUG macro.
+#endif
+
+#if OBJC_IS_DEBUG_BUILD
+void _objc_isDebugBuild(void) { }
+#endif
+
+
 /***********************************************************************
 * objc_getClass.  Return the id of the named class.  If the class does
 * not exist, call _objc_classLoader and then objc_classHandler, either of 
@@ -172,6 +201,72 @@ Class objc_getMetaClass(const char *aClassName)
     return cls->ISA();
 }
 
+/***********************************************************************
+ * objc::SafeRanges::find.  Find an image data segment that contains address
+ **********************************************************************/
+bool
+objc::SafeRanges::find(uintptr_t ptr, uint32_t &pos)
+{
+    if (!sorted) {
+        std::sort(ranges, ranges + count, [](const Range &s1, const Range &s2){
+            return s1.start < s2.start;
+        });
+        sorted = true;
+    }
+
+    uint32_t l = 0, r = count;
+    while (l < r) {
+        uint32_t i = (l + r) / 2;
+
+        if (ptr < ranges[i].start) {
+            r = i;
+        } else if (ptr >= ranges[i].end) {
+            l = i + 1;
+        } else {
+            pos = i;
+            return true;
+        }
+    }
+
+    pos = UINT32_MAX;
+    return false;
+}
+
+/***********************************************************************
+ * objc::SafeRanges::add.  Register a new well known data segment.
+ **********************************************************************/
+void
+objc::SafeRanges::add(uintptr_t start, uintptr_t end)
+{
+    if (count == size) {
+        // Have a typical malloc growth:
+        // - size <= 32:  grow by  4
+        // - size <= 64:  grow by  8
+        // - size <= 128: grow by 16
+        // ... etc
+        size += size < 16 ? 4 : 1 << (fls(size) - 3);
+        ranges = (Range *)realloc(ranges, sizeof(Range) * size);
+    }
+    ranges[count++] = Range{ start, end };
+    sorted = false;
+}
+
+/***********************************************************************
+ * objc::SafeRanges::remove.  Remove a previously known data segment.
+ **********************************************************************/
+void
+objc::SafeRanges::remove(uintptr_t start, uintptr_t end)
+{
+    uint32_t pos;
+
+    if (!find(start, pos) || ranges[pos].end != end) {
+        _objc_fatal("Cannot find range %#lx..%#lx", start, end);
+    }
+    if (pos < --count) {
+        ranges[pos] = ranges[count];
+        sorted = false;
+    }
+}
 
 /***********************************************************************
 * appendHeader.  Add a newly-constructed header_info to the list. 
@@ -180,7 +275,6 @@ 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->setNext(NULL);
     if (!FirstHeader) {
         // list is empty
@@ -195,6 +289,15 @@ void appendHeader(header_info *hi)
         LastHeader->setNext(hi);
         LastHeader = hi;
     }
+
+#if __OBJC2__
+    if ((hi->mhdr()->flags & MH_DYLIB_IN_CACHE) == 0) {
+        foreach_data_segment(hi->mhdr(), [](const segmentType *seg, intptr_t slide) {
+            uintptr_t start = (uintptr_t)seg->vmaddr + slide;
+            objc::dataSegmentsRanges.add(start, start + seg->vmsize);
+        });
+    }
+#endif
 }
 
 
@@ -224,14 +327,37 @@ void removeHeader(header_info *hi)
             if (LastHeader == deadHead) {
                 LastHeader = NULL;  // will be recomputed next time it's used
             }
-
-            HeaderCount--;
             break;
         }
         prev = current;
     }
+
+#if __OBJC2__
+    if ((hi->mhdr()->flags & MH_DYLIB_IN_CACHE) == 0) {
+        foreach_data_segment(hi->mhdr(), [](const segmentType *seg, intptr_t slide) {
+            uintptr_t start = (uintptr_t)seg->vmaddr + slide;
+            objc::dataSegmentsRanges.remove(start, start + seg->vmsize);
+        });
+    }
+#endif
 }
 
+/***********************************************************************
+* SetPageCountWarning
+* Convert environment variable value to integer value.
+* If the value is valid, set the global PageCountWarning value.
+**********************************************************************/
+void SetPageCountWarning(const char* envvar) {
+    if (envvar) {
+        long result = strtol(envvar, NULL, 10);
+        if (result <= INT_MAX && result >= -1) {
+            int32_t var = (int32_t)result;
+            if (var != 0) {  // 0 is not a valid value for the env var
+                objc::PageCountWarning = var;
+            }
+        }
+    }
+}
 
 /***********************************************************************
 * environ_init
@@ -246,6 +372,13 @@ void environ_init(void)
         return;
     } 
 
+    // Turn off autorelease LRU coalescing by default for apps linked against
+    // older SDKs. LRU coalescing can reorder releases and certain older apps
+    // are accidentally relying on the ordering.
+    // rdar://problem/63886091
+    if (!dyld_program_sdk_at_least(dyld_fall_2020_os_versions))
+        DisableAutoreleaseCoalescingLRU = true;
+
     bool PrintHelp = false;
     bool PrintOptions = false;
     bool maybeMallocDebugging = false;
@@ -270,6 +403,11 @@ void environ_init(void)
             continue;
         }
         
+        if (0 == strncmp(*p, "OBJC_DEBUG_POOL_DEPTH=", 22)) {
+            SetPageCountWarning(*p + 22);
+            continue;
+        }
+
         const char *value = strchr(*p, '=');
         if (!*value) continue;
         value++;
@@ -282,10 +420,10 @@ void environ_init(void)
                 *opt->var = (0 == strcmp(value, "YES"));
                 break;
             }
-        }            
+        }
     }
 
-    // Special case: enable some autorelease pool debugging 
+    // Special case: enable some autorelease pool debugging
     // when some malloc debugging is enabled 
     // and OBJC_DEBUG_POOL_ALLOCATION is not set to something other than NO.
     if (maybeMallocDebugging) {
@@ -303,6 +441,10 @@ void environ_init(void)
         }
     }
 
+    if (!os_feature_enabled_simple(objc4, preoptimizedCaches, true)) {
+        DisablePreoptCaches = true;
+    }
+
     // Print OBJC_HELP and OBJC_PRINT_OPTIONS output.
     if (PrintHelp  ||  PrintOptions) {
         if (PrintHelp) {
@@ -339,7 +481,7 @@ logReplacedMethod(const char *className, SEL s,
     const char *newImage = "??";
 
     // Silently ignore +load replacement because category +load is special
-    if (s == SEL_load) return;
+    if (s == @selector(load)) return;
 
 #if TARGET_OS_WIN32
     // don't know dladdr()/dli_fname equivalent
@@ -357,18 +499,6 @@ logReplacedMethod(const char *className, SEL s,
 }
 
 
-
-/***********************************************************************
-* 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.
@@ -408,6 +538,7 @@ void _objc_pthread_destroyspecific(void *arg)
                 free(data->printableNames[i]);  
             }
         }
+        free(data->classNameLookups);
 
         // add further cleanup here...
 
@@ -419,7 +550,6 @@ void _objc_pthread_destroyspecific(void *arg)
 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);
@@ -452,7 +582,7 @@ void *_objc_forward_stret_handler = nil;
 #else
 
 // Default forward handler halts the process.
-__attribute__((noreturn)) void 
+__attribute__((noreturn, cold)) void
 objc_defaultForwardHandler(id self, SEL sel)
 {
     _objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
@@ -464,7 +594,7 @@ void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
 
 #if SUPPORT_STRET
 struct stret { int i[100]; };
-__attribute__((noreturn)) struct stret 
+__attribute__((noreturn, cold)) struct stret
 objc_defaultForwardStretHandler(id self, SEL sel)
 {
     objc_defaultForwardHandler(self, sel);
@@ -487,109 +617,36 @@ void objc_setForwardHandler(void *fwd, void *fwd_stret)
 // GrP fixme
 extern "C" 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;
 
+static BOOL internal_class_getImageName(Class cls, const char **outName)
+{
 #if !__OBJC2__
     cls = _objc_getOrigClass(cls->demangledName());
 #endif
-#if TARGET_OS_WIN32
-    charactersCopied = 0;
-    szFileName = malloc(MAX_PATH * sizeof(TCHAR));
-    
-    origCls = objc_getOrigClass(cls->demangledName());
-    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
+    auto result = dyld_image_path_containing_address(cls);
+    *outName = result;
+    return (result != nil);
 }
 
 
-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->getNext()) {
-#if TARGET_OS_WIN32
-        if (hi->moduleName) {
-            names[count++] = hi->moduleName;
-        }
-#else
-        const char *fname = hi->fname();
-        if (fname) {
-            names[count++] = fname;
-        }
-#endif
-    }
-    names[count] = NULL;
-    
-    if (count == 0) {
-        // Return NULL instead of empty list if there are no images
-        free((void *)names);
-        names = NULL;
-    }
+static ChainedHookFunction<objc_hook_getImageName>
+GetImageNameHook{internal_class_getImageName};
 
-    if (outCount) *outCount = count;
-    return names;
+void objc_setHook_getImageName(objc_hook_getImageName newValue,
+                               objc_hook_getImageName *outOldValue)
+{
+    GetImageNameHook.set(newValue, outOldValue);
 }
 
-
-/**********************************************************************
-*
-**********************************************************************/
-const char ** 
-objc_copyClassNamesForImage(const char *image, unsigned int *outCount)
+const char *class_getImageName(Class cls)
 {
-    header_info *hi;
-
-    if (!image) {
-        if (outCount) *outCount = 0;
-        return NULL;
-    }
-
-    // Find the image.
-    for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
-#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;
-    }
+    if (!cls) return nil;
 
-    return _objc_copyClassNamesForImage(hi, outCount);
+    const char *name;
+    if (GetImageNameHook.get()(cls, &name)) return name;
+    else return nil;
 }
-       
+
 
 /**********************************************************************
 * Fast Enumeration Support
@@ -622,20 +679,31 @@ void objc_setEnumerationMutationHandler(void (*handler)(id)) {
 * Associative Reference Support
 **********************************************************************/
 
-id objc_getAssociatedObject(id object, const void *key) {
-    return _object_get_associative_reference(object, (void *)key);
+id
+objc_getAssociatedObject(id object, const void *key)
+{
+    return _object_get_associative_reference(object, key);
 }
 
+typedef void (*objc_hook_setAssociatedObject)(id _Nonnull object, const void * _Nonnull key,
+                                              id _Nullable value, objc_AssociationPolicy policy);
 
-void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
-    _object_set_associative_reference(object, (void *)key, value, policy);
+void
+objc_setHook_setAssociatedObject(objc_hook_setAssociatedObject _Nonnull newValue,
+                                 objc_hook_setAssociatedObject _Nullable * _Nonnull outOldValue) {
+  // See objc_object::setHasAssociatedObjects() for a replacement
 }
 
+void
+objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
+{
+    _object_set_associative_reference(object, key, value, policy);
+}
 
 void objc_removeAssociatedObjects(id object) 
 {
     if (object && object->hasAssociatedObjects()) {
-        _object_remove_assocations(object);
+        _object_remove_assocations(object, /*deallocating*/false);
     }
 }