* 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"
#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.
}
+/***********************************************************************
+* _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
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.
{
// 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
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
}
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
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;
continue;
}
+ if (0 == strncmp(*p, "OBJC_DEBUG_POOL_DEPTH=", 22)) {
+ SetPageCountWarning(*p + 22);
+ continue;
+ }
+
const char *value = strchr(*p, '=');
if (!*value) continue;
value++;
*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) {
}
}
+ if (!os_feature_enabled_simple(objc4, preoptimizedCaches, true)) {
+ DisablePreoptCaches = true;
+ }
+
// Print OBJC_HELP and OBJC_PRINT_OPTIONS output.
if (PrintHelp || PrintOptions) {
if (PrintHelp) {
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
}
-
-/***********************************************************************
-* 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.
free(data->printableNames[i]);
}
}
+ free(data->classNameLookups);
// add further cleanup here...
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);
#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 "
#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);
// 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
* 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);
}
}