X-Git-Url: https://git.saurik.com/apple/objc4.git/blobdiff_plain/4a109af391a6bf6552c01b3e7ae2da858a151c18..refs/heads/master:/runtime/objc-runtime.mm?ds=sidebyside diff --git a/runtime/objc-runtime.mm b/runtime/objc-runtime.mm index ba1b38d..e38b274 100644 --- a/runtime/objc-runtime.mm +++ b/runtime/objc-runtime.mm @@ -33,17 +33,43 @@ * Imports. **********************************************************************/ +#include // 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 +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); } }