1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084 |
- /*
- * 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 "objc-file.h"
- #include "message.h"
- /***********************************************************************
- * 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
- struct option_t {
- bool* var;
- const char *env;
- const char *help;
- size_t envlen;
- };
- const option_t Settings[] = {
- #define OPTION(var, env, help) option_t{&var, #env, help, strlen(#env)},
- #include "objc-env.h"
- #undef OPTION
- };
- // 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_cxx_construct = NULL;
- SEL SEL_cxx_destruct = NULL;
- struct objc::SafeRanges objc::dataSegmentsRanges;
- header_info *FirstHeader = 0; // NULL means empty list
- header_info *LastHeader = 0; // NULL means invalid; recompute it
- // Set to true on the child side of fork()
- // if the parent process was multithreaded when fork() was called.
- bool MultithreadedForkChild = false;
- /***********************************************************************
- * objc_noop_imp. Used when we need to install a do-nothing method somewhere.
- **********************************************************************/
- id objc_noop_imp(id self, SEL _cmd __unused) {
- return self;
- }
- /***********************************************************************
- * _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
- * which may create a new class.
- * Warning: doesn't work if aClassName is the name of a posed-for class's isa!
- **********************************************************************/
- Class 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.
- **********************************************************************/
- Class objc_getRequiredClass(const char *aClassName)
- {
- Class 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 ()
- **********************************************************************/
- Class objc_lookUpClass(const char *aClassName)
- {
- if (!aClassName) return Nil;
- // NO unconnected, NO class handler
- return look_up_class(aClassName, NO, NO);
- }
- /***********************************************************************
- * 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!
- **********************************************************************/
- Class objc_getMetaClass(const char *aClassName)
- {
- Class cls;
- if (!aClassName) return Nil;
- cls = objc_getClass (aClassName);
- if (!cls)
- {
- _objc_inform ("class `%s' not linked into application", aClassName);
- return Nil;
- }
- 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.
- **********************************************************************/
- 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.
- hi->setNext(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->getNext()) LastHeader = LastHeader->getNext();
- }
- // LastHeader is now valid
- 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
- }
- /***********************************************************************
- * 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 *prev = NULL;
- header_info *current = NULL;
- for (current = FirstHeader; current != NULL; current = current->getNext()) {
- if (current == hi) {
- header_info *deadHead = current;
- // Remove from the linked list.
- if (prev)
- prev->setNext(current->getNext());
- else
- FirstHeader = current->getNext(); // no prev so removing head
-
- // Update LastHeader if necessary.
- if (LastHeader == deadHead) {
- LastHeader = NULL; // will be recomputed next time it's used
- }
- 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
- }
- /***********************************************************************
- * environ_init
- * Read environment variables that affect the runtime.
- * Also print environment variable help, if requested.
- **********************************************************************/
- void environ_init(void)
- {
- if (issetugid()) {
- // All environment variables are silently ignored when setuid or setgid
- // This includes OBJC_HELP and OBJC_PRINT_OPTIONS themselves.
- return;
- }
- bool PrintHelp = false;
- bool PrintOptions = false;
- bool maybeMallocDebugging = false;
- // Scan environ[] directly instead of calling getenv() a lot.
- // This optimizes the case where none are set.
- for (char **p = *_NSGetEnviron(); *p != nil; p++) {
- if (0 == strncmp(*p, "Malloc", 6) || 0 == strncmp(*p, "DYLD", 4) ||
- 0 == strncmp(*p, "NSZombiesEnabled", 16))
- {
- maybeMallocDebugging = true;
- }
- if (0 != strncmp(*p, "OBJC_", 5)) continue;
-
- if (0 == strncmp(*p, "OBJC_HELP=", 10)) {
- PrintHelp = true;
- continue;
- }
- if (0 == strncmp(*p, "OBJC_PRINT_OPTIONS=", 19)) {
- PrintOptions = true;
- continue;
- }
-
- const char *value = strchr(*p, '=');
- if (!*value) continue;
- value++;
-
- for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
- const option_t *opt = &Settings[i];
- if ((size_t)(value - *p) == 1+opt->envlen &&
- 0 == strncmp(*p, opt->env, opt->envlen))
- {
- *opt->var = (0 == strcmp(value, "YES"));
- break;
- }
- }
- }
- // 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) {
- const char *insert = getenv("DYLD_INSERT_LIBRARIES");
- const char *zombie = getenv("NSZombiesEnabled");
- const char *pooldebug = getenv("OBJC_DEBUG_POOL_ALLOCATION");
- if ((getenv("MallocStackLogging")
- || getenv("MallocStackLoggingNoCompact")
- || (zombie && (*zombie == 'Y' || *zombie == 'y'))
- || (insert && strstr(insert, "libgmalloc")))
- &&
- (!pooldebug || 0 == strcmp(pooldebug, "YES")))
- {
- DebugPoolAllocation = true;
- }
- }
- // Print OBJC_HELP and OBJC_PRINT_OPTIONS output.
- if (PrintHelp || PrintOptions) {
- 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");
- }
- for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
- const option_t *opt = &Settings[i];
- if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
- if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
- }
- }
- }
- /***********************************************************************
- * 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 == @selector(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_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(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);
- for (int i = 0; i < (int)countof(data->printableNames); i++) {
- if (data->printableNames[i]) {
- free(data->printableNames[i]);
- }
- }
- free(data->classNameLookups);
- // add further cleanup here...
- free(data);
- }
- }
- void tls_init(void)
- {
- #if SUPPORT_DIRECT_THREAD_KEYS
- 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
- }
- /***********************************************************************
- * objc_setForwardHandler
- **********************************************************************/
- #if !__OBJC2__
- // Default forward handler (nil) goes to forward:: dispatch.
- void *_objc_forward_handler = nil;
- void *_objc_forward_stret_handler = nil;
- #else
- // Default forward handler halts the process.
- __attribute__((noreturn, cold)) void
- objc_defaultForwardHandler(id self, SEL sel)
- {
- _objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
- "(no message forward handler is installed)",
- class_isMetaClass(object_getClass(self)) ? '+' : '-',
- object_getClassName(self), sel_getName(sel), self);
- }
- void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
- #if SUPPORT_STRET
- struct stret { int i[100]; };
- __attribute__((noreturn, cold)) struct stret
- objc_defaultForwardStretHandler(id self, SEL sel)
- {
- objc_defaultForwardHandler(self, sel);
- }
- void *_objc_forward_stret_handler = (void*)objc_defaultForwardStretHandler;
- #endif
- #endif
- void objc_setForwardHandler(void *fwd, void *fwd_stret)
- {
- _objc_forward_handler = fwd;
- #if SUPPORT_STRET
- _objc_forward_stret_handler = fwd_stret;
- #endif
- }
- #if !__OBJC2__
- // GrP fixme
- extern "C" Class _objc_getOrigClass(const char *name);
- #endif
- static BOOL internal_class_getImageName(Class cls, const char **outName)
- {
- #if !__OBJC2__
- cls = _objc_getOrigClass(cls->demangledName());
- #endif
- auto result = dyld_image_path_containing_address(cls);
- *outName = result;
- return (result != nil);
- }
- static ChainedHookFunction<objc_hook_getImageName>
- GetImageNameHook{internal_class_getImageName};
- void objc_setHook_getImageName(objc_hook_getImageName newValue,
- objc_hook_getImageName *outOldValue)
- {
- GetImageNameHook.set(newValue, outOldValue);
- }
- const char *class_getImageName(Class cls)
- {
- if (!cls) return nil;
- const char *name;
- if (GetImageNameHook.get()(cls, &name)) return name;
- else return nil;
- }
- /**********************************************************************
- * 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.", (void*)object);
- }
- (*enumerationMutationHandler)(object);
- }
- /**********************************************************************
- * objc_setEnumerationMutationHandler
- * an entry point to customize mutation error handing
- **********************************************************************/
- void objc_setEnumerationMutationHandler(void (*handler)(id)) {
- enumerationMutationHandler = handler;
- }
- /**********************************************************************
- * Associative Reference Support
- **********************************************************************/
- id
- objc_getAssociatedObject(id object, const void *key)
- {
- return _object_get_associative_reference(object, key);
- }
- static void
- _base_objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
- {
- _object_set_associative_reference(object, key, value, policy);
- }
- static ChainedHookFunction<objc_hook_setAssociatedObject> SetAssocHook{_base_objc_setAssociatedObject};
- void
- objc_setHook_setAssociatedObject(objc_hook_setAssociatedObject _Nonnull newValue,
- objc_hook_setAssociatedObject _Nullable * _Nonnull outOldValue) {
- SetAssocHook.set(newValue, outOldValue);
- }
- void
- objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
- {
- SetAssocHook.get()(object, key, value, policy);
- }
- void objc_removeAssociatedObjects(id object)
- {
- if (object && object->hasAssociatedObjects()) {
- _object_remove_assocations(object);
- }
- }
- #if SUPPORT_GC_COMPAT
- #include <mach-o/fat.h>
- // GC preflight for an app executable.
- enum GCness {
- WithGC = 1,
- WithoutGC = 0,
- Error = -1
- };
- // Overloaded template wrappers around clang's overflow-checked arithmetic.
- template <typename T> bool uadd_overflow(T x, T y, T* sum);
- template <typename T> bool usub_overflow(T x, T y, T* diff);
- template <typename T> bool umul_overflow(T x, T y, T* prod);
- template <typename T> bool sadd_overflow(T x, T y, T* sum);
- template <typename T> bool ssub_overflow(T x, T y, T* diff);
- template <typename T> bool smul_overflow(T x, T y, T* prod);
- template <> bool uadd_overflow(unsigned x, unsigned y, unsigned* sum) { return __builtin_uadd_overflow(x, y, sum); }
- template <> bool uadd_overflow(unsigned long x, unsigned long y, unsigned long* sum) { return __builtin_uaddl_overflow(x, y, sum); }
- template <> bool uadd_overflow(unsigned long long x, unsigned long long y, unsigned long long* sum) { return __builtin_uaddll_overflow(x, y, sum); }
- template <> bool usub_overflow(unsigned x, unsigned y, unsigned* diff) { return __builtin_usub_overflow(x, y, diff); }
- template <> bool usub_overflow(unsigned long x, unsigned long y, unsigned long* diff) { return __builtin_usubl_overflow(x, y, diff); }
- template <> bool usub_overflow(unsigned long long x, unsigned long long y, unsigned long long* diff) { return __builtin_usubll_overflow(x, y, diff); }
- template <> bool umul_overflow(unsigned x, unsigned y, unsigned* prod) { return __builtin_umul_overflow(x, y, prod); }
- template <> bool umul_overflow(unsigned long x, unsigned long y, unsigned long* prod) { return __builtin_umull_overflow(x, y, prod); }
- template <> bool umul_overflow(unsigned long long x, unsigned long long y, unsigned long long* prod) { return __builtin_umulll_overflow(x, y, prod); }
- template <> bool sadd_overflow(signed x, signed y, signed* sum) { return __builtin_sadd_overflow(x, y, sum); }
- template <> bool sadd_overflow(signed long x, signed long y, signed long* sum) { return __builtin_saddl_overflow(x, y, sum); }
- template <> bool sadd_overflow(signed long long x, signed long long y, signed long long* sum) { return __builtin_saddll_overflow(x, y, sum); }
- template <> bool ssub_overflow(signed x, signed y, signed* diff) { return __builtin_ssub_overflow(x, y, diff); }
- template <> bool ssub_overflow(signed long x, signed long y, signed long* diff) { return __builtin_ssubl_overflow(x, y, diff); }
- template <> bool ssub_overflow(signed long long x, signed long long y, signed long long* diff) { return __builtin_ssubll_overflow(x, y, diff); }
- template <> bool smul_overflow(signed x, signed y, signed* prod) { return __builtin_smul_overflow(x, y, prod); }
- template <> bool smul_overflow(signed long x, signed long y, signed long* prod) { return __builtin_smull_overflow(x, y, prod); }
- template <> bool smul_overflow(signed long long x, signed long long y, signed long long* prod) { return __builtin_smulll_overflow(x, y, prod); }
- // Range-checking subview of a file.
- class FileSlice {
- int fd;
- uint64_t sliceOffset;
- uint64_t sliceSize;
- public:
- FileSlice() : fd(-1), sliceOffset(0), sliceSize(0) { }
- FileSlice(int newfd, uint64_t newOffset, uint64_t newSize)
- : fd(newfd) , sliceOffset(newOffset) , sliceSize(newSize) { }
- // Read bytes from this slice.
- // Returns YES if all bytes were read successfully.
- bool pread(void *buf, uint64_t readSize, uint64_t readOffset = 0) {
- uint64_t readEnd;
- if (uadd_overflow(readOffset, readSize, &readEnd)) return NO;
- if (readEnd > sliceSize) return NO;
- uint64_t preadOffset;
- if (uadd_overflow(sliceOffset, readOffset, &preadOffset)) return NO;
- int64_t readed = ::pread(fd, buf, (size_t)readSize, preadOffset);
- if (readed < 0 || (uint64_t)readed != readSize) return NO;
- return YES;
- }
- // Create a new slice that is a subset of this slice.
- // Returnes YES if successful.
- bool slice(uint64_t newOffset, uint64_t newSize, FileSlice& result) {
- // fixme arithmetic overflow
- uint64_t newEnd;
- if (uadd_overflow(newOffset, newSize, &newEnd)) return NO;
- if (newEnd > sliceSize) return NO;
- if (uadd_overflow(sliceOffset, newOffset, &result.sliceOffset)) {
- return NO;
- }
- result.sliceSize = newSize;
- result.fd = fd;
- return YES;
- }
- // Shorten this slice in place by removing a range from the start.
- bool advance(uint64_t distance) {
- if (distance > sliceSize) return NO;
- if (uadd_overflow(sliceOffset, distance, &sliceOffset)) return NO;
- if (usub_overflow(sliceSize, distance, &sliceSize)) return NO;
- return YES;
- }
- };
- // Arch32 and Arch64 are used to specialize sliceRequiresGC()
- // to interrogate old-ABI i386 and new-ABI x86_64 files.
- struct Arch32 {
- using mh_t = struct mach_header;
- using segment_command_t = struct segment_command;
- using section_t = struct section;
- enum : cpu_type_t { cputype = CPU_TYPE_X86 };
- enum : int { segment_cmd = LC_SEGMENT };
- static bool isObjCSegment(const char *segname) {
- return segnameEquals(segname, "__OBJC");
- }
- static bool isImageInfoSection(const char *sectname) {
- return sectnameEquals(sectname, "__image_info");
- }
- static bool countClasses(FileSlice file, section_t& sect,
- int& classCount, int& classrefCount)
- {
- if (sectnameEquals(sect.sectname, "__cls_refs")) {
- classrefCount += sect.size / 4;
- }
- else if (sectnameEquals(sect.sectname, "__module_info")) {
- struct module_t {
- uint32_t version;
- uint32_t size;
- uint32_t name; // not bound
- uint32_t symtab; // not bound
- };
- size_t mod_count = sect.size / sizeof(module_t);
- if (mod_count == 0) {
- // no classes defined
- } else if (mod_count > 1) {
- // AppleScriptObjC apps only have one module.
- // Disqualify this app by setting classCount to non-zero.
- // We don't actually need an accurate count.
- classCount = 1;
- } else if (mod_count == 1) {
- FileSlice moduleSlice;
- if (!file.slice(sect.offset, sect.size, moduleSlice)) return NO;
- module_t module;
- if (!moduleSlice.pread(&module, sizeof(module))) return NO;
- if (module.symtab) {
- // AppleScriptObjC apps only have a module with no symtab.
- // Disqualify this app by setting classCount to non-zero.
- // We don't actually need an accurate count.
- classCount = 1;
- }
- }
-
- }
- return YES;
- }
- };
- struct Arch64 {
- using mh_t = struct mach_header_64;
- using segment_command_t = struct segment_command_64;
- using section_t = struct section_64;
- enum : cpu_type_t { cputype = CPU_TYPE_X86_64 };
- enum : int { segment_cmd = LC_SEGMENT_64 };
- static bool isObjCSegment(const char *segname) {
- return
- segnameEquals(segname, "__DATA") ||
- segnameEquals(segname, "__DATA_CONST") ||
- segnameEquals(segname, "__DATA_DIRTY");
- }
- static bool isImageInfoSection(const char *sectname) {
- return sectnameEquals(sectname, "__objc_imageinfo");
- }
- static bool countClasses(FileSlice, section_t& sect,
- int& classCount, int& classrefCount)
- {
- if (sectnameEquals(sect.sectname, "__objc_classlist")) {
- classCount += sect.size / 8;
- }
- else if (sectnameEquals(sect.sectname, "__objc_classrefs")) {
- classrefCount += sect.size / 8;
- }
- return YES;
- }
- };
- #define SANE_HEADER_SIZE (32*1024)
- template <typename Arch>
- static int sliceRequiresGC(typename Arch::mh_t mh, FileSlice file)
- {
- // We assume there is only one arch per pointer size that can support GC.
- // (i386 and x86_64)
- if (mh.cputype != Arch::cputype) return 0;
- // We only check the main executable.
- if (mh.filetype != MH_EXECUTE) return 0;
- // Look for ObjC segment.
- // Look for AppleScriptObjC linkage.
- FileSlice cmds;
- if (!file.slice(sizeof(mh), mh.sizeofcmds, cmds)) return Error;
- // Exception: Some AppleScriptObjC apps built for GC can run without GC.
- // 1. executable defines no classes
- // 2. executable references NSBundle only
- // 3. executable links to AppleScriptObjC.framework
- // Note that shouldRejectGCApp() also knows about this.
- bool wantsGC = NO;
- bool linksToAppleScriptObjC = NO;
- int classCount = 0;
- int classrefCount = 0;
- // Disallow abusively-large executables that could hang this checker.
- // dyld performs similar checks (MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE)
- if (mh.sizeofcmds > SANE_HEADER_SIZE) return Error;
- if (mh.ncmds > mh.sizeofcmds / sizeof(struct load_command)) return Error;
- for (uint32_t cmdindex = 0; cmdindex < mh.ncmds; cmdindex++) {
- struct load_command lc;
- if (!cmds.pread(&lc, sizeof(lc))) return Error;
- // Disallow abusively-small load commands that could hang this checker.
- // dyld performs a similar check.
- if (lc.cmdsize < sizeof(lc)) return Error;
- if (lc.cmd == LC_LOAD_DYLIB || lc.cmd == LC_LOAD_UPWARD_DYLIB ||
- lc.cmd == LC_LOAD_WEAK_DYLIB || lc.cmd == LC_REEXPORT_DYLIB)
- {
- // Look for AppleScriptObjC linkage.
- FileSlice dylibSlice;
- if (!cmds.slice(0, lc.cmdsize, dylibSlice)) return Error;
- struct dylib_command dylib;
- if (!dylibSlice.pread(&dylib, sizeof(dylib))) return Error;
- const char *asoFramework =
- "/System/Library/Frameworks/AppleScriptObjC.framework"
- "/Versions/A/AppleScriptObjC";
- size_t asoLen = strlen(asoFramework);
- FileSlice nameSlice;
- if (dylibSlice.slice(dylib.dylib.name.offset, asoLen, nameSlice)) {
- char name[asoLen];
- if (!nameSlice.pread(name, asoLen)) return Error;
- if (0 == memcmp(name, asoFramework, asoLen)) {
- linksToAppleScriptObjC = YES;
- }
- }
- }
- else if (lc.cmd == Arch::segment_cmd) {
- typename Arch::segment_command_t seg;
- if (!cmds.pread(&seg, sizeof(seg))) return Error;
- if (Arch::isObjCSegment(seg.segname)) {
- // ObjC segment.
- // Look for image info section.
- // Look for class implementations and class references.
- FileSlice sections;
- if (!cmds.slice(0, seg.cmdsize, sections)) return Error;
- if (!sections.advance(sizeof(seg))) return Error;
-
- for (uint32_t segindex = 0; segindex < seg.nsects; segindex++) {
- typename Arch::section_t sect;
- if (!sections.pread(§, sizeof(sect))) return Error;
- if (!Arch::isObjCSegment(sect.segname)) return Error;
- if (!Arch::countClasses(file, sect,
- classCount, classrefCount))
- {
- return Error;
- }
- if ((sect.flags & SECTION_TYPE) == S_REGULAR &&
- Arch::isImageInfoSection(sect.sectname))
- {
- // ObjC image info section.
- // Check its contents.
- FileSlice section;
- if (!file.slice(sect.offset, sect.size, section)) {
- return Error;
- }
- // The subset of objc_image_info that was in use for GC.
- struct {
- uint32_t version;
- uint32_t flags;
- } ii;
- if (!section.pread(&ii, sizeof(ii))) return Error;
- if (ii.flags & (1<<1)) {
- // App wants GC.
- // Don't return yet because we need to
- // check the AppleScriptObjC exception.
- wantsGC = YES;
- }
- }
- if (!sections.advance(sizeof(sect))) return Error;
- }
- }
- }
- if (!cmds.advance(lc.cmdsize)) return Error;
- }
- if (!wantsGC) {
- // No GC bit set.
- return WithoutGC;
- }
- else if (linksToAppleScriptObjC && classCount == 0 && classrefCount == 1) {
- // Has GC bit but falls under the AppleScriptObjC exception.
- return WithoutGC;
- }
- else {
- // Has GC bit and is not AppleScriptObjC.
- return WithGC;
- }
- }
- static int sliceRequiresGC(FileSlice file)
- {
- // Read mach-o header.
- struct mach_header_64 mh;
- if (!file.pread(&mh, sizeof(mh))) return Error;
- // Check header magic. We assume only host-endian slices can support GC.
- switch (mh.magic) {
- case MH_MAGIC:
- return sliceRequiresGC<Arch32>(*(struct mach_header *)&mh, file);
- case MH_MAGIC_64:
- return sliceRequiresGC<Arch64>(mh, file);
- default:
- return WithoutGC;
- }
- }
- // Returns 1 if any slice requires GC.
- // Returns 0 if no slice requires GC.
- // Returns -1 on any I/O or file format error.
- int objc_appRequiresGC(int fd)
- {
- struct stat st;
- if (fstat(fd, &st) < 0) return Error;
- FileSlice file(fd, 0, st.st_size);
- // Read fat header, if any.
- struct fat_header fh;
- if (! file.pread(&fh, sizeof(fh))) return Error;
- int result;
- if (OSSwapBigToHostInt32(fh.magic) == FAT_MAGIC) {
- // Fat header.
- size_t nfat_arch = OSSwapBigToHostInt32(fh.nfat_arch);
- // Disallow abusively-large files that could hang this checker.
- if (nfat_arch > SANE_HEADER_SIZE/sizeof(struct fat_arch)) return Error;
- size_t fat_size;
- if (umul_overflow(nfat_arch, sizeof(struct fat_arch), &fat_size)) {
- return Error;
- }
- FileSlice archlist;
- if (!file.slice(sizeof(fh), fat_size, archlist)) return Error;
- result = WithoutGC;
- for (size_t i = 0; i < nfat_arch; i++) {
- struct fat_arch fa;
- if (!archlist.pread(&fa, sizeof(fa))) return Error;
- if (!archlist.advance(sizeof(fa))) return Error;
- FileSlice thin;
- if (!file.slice(OSSwapBigToHostInt32(fa.offset),
- OSSwapBigToHostInt32(fa.size), thin))
- {
- return Error;
- }
- switch (sliceRequiresGC(thin)) {
- case WithoutGC: break; // no change
- case WithGC: if (result != Error) result = WithGC; break;
- case Error: result = Error; break;
- }
- }
- }
- else {
- // Thin header or not a header.
- result = sliceRequiresGC(file);
- }
-
- return result;
- }
- // SUPPORT_GC_COMPAT
- #endif
|