]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-runtime.mm
objc4-781.tar.gz
[apple/objc4.git] / runtime / objc-runtime.mm
1 /*
2 * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /***********************************************************************
24 * objc-runtime.m
25 * Copyright 1988-1996, NeXT Software, Inc.
26 * Author: s. naroff
27 *
28 **********************************************************************/
29
30
31
32 /***********************************************************************
33 * Imports.
34 **********************************************************************/
35
36 #include "objc-private.h"
37 #include "objc-loadmethod.h"
38 #include "objc-file.h"
39 #include "message.h"
40
41 /***********************************************************************
42 * Exports.
43 **********************************************************************/
44
45 /* Linker metadata symbols */
46
47 // NSObject was in Foundation/CF on macOS < 10.8.
48 #if TARGET_OS_OSX
49 #if __OBJC2__
50
51 const char __objc_nsobject_class_10_5 = 0;
52 const char __objc_nsobject_class_10_6 = 0;
53 const char __objc_nsobject_class_10_7 = 0;
54
55 const char __objc_nsobject_metaclass_10_5 = 0;
56 const char __objc_nsobject_metaclass_10_6 = 0;
57 const char __objc_nsobject_metaclass_10_7 = 0;
58
59 const char __objc_nsobject_isa_10_5 = 0;
60 const char __objc_nsobject_isa_10_6 = 0;
61 const char __objc_nsobject_isa_10_7 = 0;
62
63 #else
64
65 const char __objc_nsobject_class_10_5 = 0;
66 const char __objc_nsobject_class_10_6 = 0;
67 const char __objc_nsobject_class_10_7 = 0;
68
69 #endif
70 #endif
71
72 // Settings from environment variables
73 #define OPTION(var, env, help) bool var = false;
74 #include "objc-env.h"
75 #undef OPTION
76
77 struct option_t {
78 bool* var;
79 const char *env;
80 const char *help;
81 size_t envlen;
82 };
83
84 const option_t Settings[] = {
85 #define OPTION(var, env, help) option_t{&var, #env, help, strlen(#env)},
86 #include "objc-env.h"
87 #undef OPTION
88 };
89
90
91 // objc's key for pthread_getspecific
92 #if SUPPORT_DIRECT_THREAD_KEYS
93 #define _objc_pthread_key TLS_DIRECT_KEY
94 #else
95 static tls_key_t _objc_pthread_key;
96 #endif
97
98 // Selectors
99 SEL SEL_cxx_construct = NULL;
100 SEL SEL_cxx_destruct = NULL;
101
102 struct objc::SafeRanges objc::dataSegmentsRanges;
103 header_info *FirstHeader = 0; // NULL means empty list
104 header_info *LastHeader = 0; // NULL means invalid; recompute it
105
106 // Set to true on the child side of fork()
107 // if the parent process was multithreaded when fork() was called.
108 bool MultithreadedForkChild = false;
109
110
111 /***********************************************************************
112 * objc_noop_imp. Used when we need to install a do-nothing method somewhere.
113 **********************************************************************/
114 id objc_noop_imp(id self, SEL _cmd __unused) {
115 return self;
116 }
117
118
119 /***********************************************************************
120 * _objc_isDebugBuild. Defined in debug builds only.
121 * Some test code looks for the presence of this symbol.
122 **********************************************************************/
123 #if DEBUG != OBJC_IS_DEBUG_BUILD
124 #error mismatch in debug-ness macros
125 // DEBUG is used in our code. OBJC_IS_DEBUG_BUILD is used in the
126 // header declaration of _objc_isDebugBuild() because that header
127 // is visible to other clients who might have their own DEBUG macro.
128 #endif
129
130 #if OBJC_IS_DEBUG_BUILD
131 void _objc_isDebugBuild(void) { }
132 #endif
133
134
135 /***********************************************************************
136 * objc_getClass. Return the id of the named class. If the class does
137 * not exist, call _objc_classLoader and then objc_classHandler, either of
138 * which may create a new class.
139 * Warning: doesn't work if aClassName is the name of a posed-for class's isa!
140 **********************************************************************/
141 Class objc_getClass(const char *aClassName)
142 {
143 if (!aClassName) return Nil;
144
145 // NO unconnected, YES class handler
146 return look_up_class(aClassName, NO, YES);
147 }
148
149
150 /***********************************************************************
151 * objc_getRequiredClass.
152 * Same as objc_getClass, but kills the process if the class is not found.
153 * This is used by ZeroLink, where failing to find a class would be a
154 * compile-time link error without ZeroLink.
155 **********************************************************************/
156 Class objc_getRequiredClass(const char *aClassName)
157 {
158 Class cls = objc_getClass(aClassName);
159 if (!cls) _objc_fatal("link error: class '%s' not found.", aClassName);
160 return cls;
161 }
162
163
164 /***********************************************************************
165 * objc_lookUpClass. Return the id of the named class.
166 * If the class does not exist, call _objc_classLoader, which may create
167 * a new class.
168 *
169 * Formerly objc_getClassWithoutWarning ()
170 **********************************************************************/
171 Class objc_lookUpClass(const char *aClassName)
172 {
173 if (!aClassName) return Nil;
174
175 // NO unconnected, NO class handler
176 return look_up_class(aClassName, NO, NO);
177 }
178
179
180 /***********************************************************************
181 * objc_getMetaClass. Return the id of the meta class the named class.
182 * Warning: doesn't work if aClassName is the name of a posed-for class's isa!
183 **********************************************************************/
184 Class objc_getMetaClass(const char *aClassName)
185 {
186 Class cls;
187
188 if (!aClassName) return Nil;
189
190 cls = objc_getClass (aClassName);
191 if (!cls)
192 {
193 _objc_inform ("class `%s' not linked into application", aClassName);
194 return Nil;
195 }
196
197 return cls->ISA();
198 }
199
200 /***********************************************************************
201 * objc::SafeRanges::find. Find an image data segment that contains address
202 **********************************************************************/
203 bool
204 objc::SafeRanges::find(uintptr_t ptr, uint32_t &pos)
205 {
206 if (!sorted) {
207 std::sort(ranges, ranges + count, [](const Range &s1, const Range &s2){
208 return s1.start < s2.start;
209 });
210 sorted = true;
211 }
212
213 uint32_t l = 0, r = count;
214 while (l < r) {
215 uint32_t i = (l + r) / 2;
216
217 if (ptr < ranges[i].start) {
218 r = i;
219 } else if (ptr >= ranges[i].end) {
220 l = i + 1;
221 } else {
222 pos = i;
223 return true;
224 }
225 }
226
227 pos = UINT32_MAX;
228 return false;
229 }
230
231 /***********************************************************************
232 * objc::SafeRanges::add. Register a new well known data segment.
233 **********************************************************************/
234 void
235 objc::SafeRanges::add(uintptr_t start, uintptr_t end)
236 {
237 if (count == size) {
238 // Have a typical malloc growth:
239 // - size <= 32: grow by 4
240 // - size <= 64: grow by 8
241 // - size <= 128: grow by 16
242 // ... etc
243 size += size < 16 ? 4 : 1 << (fls(size) - 3);
244 ranges = (Range *)realloc(ranges, sizeof(Range) * size);
245 }
246 ranges[count++] = Range{ start, end };
247 sorted = false;
248 }
249
250 /***********************************************************************
251 * objc::SafeRanges::remove. Remove a previously known data segment.
252 **********************************************************************/
253 void
254 objc::SafeRanges::remove(uintptr_t start, uintptr_t end)
255 {
256 uint32_t pos;
257
258 if (!find(start, pos) || ranges[pos].end != end) {
259 _objc_fatal("Cannot find range %#lx..%#lx", start, end);
260 }
261 if (pos < --count) {
262 ranges[pos] = ranges[count];
263 sorted = false;
264 }
265 }
266
267 /***********************************************************************
268 * appendHeader. Add a newly-constructed header_info to the list.
269 **********************************************************************/
270 void appendHeader(header_info *hi)
271 {
272 // Add the header to the header list.
273 // The header is appended to the list, to preserve the bottom-up order.
274 hi->setNext(NULL);
275 if (!FirstHeader) {
276 // list is empty
277 FirstHeader = LastHeader = hi;
278 } else {
279 if (!LastHeader) {
280 // list is not empty, but LastHeader is invalid - recompute it
281 LastHeader = FirstHeader;
282 while (LastHeader->getNext()) LastHeader = LastHeader->getNext();
283 }
284 // LastHeader is now valid
285 LastHeader->setNext(hi);
286 LastHeader = hi;
287 }
288
289 #if __OBJC2__
290 if ((hi->mhdr()->flags & MH_DYLIB_IN_CACHE) == 0) {
291 foreach_data_segment(hi->mhdr(), [](const segmentType *seg, intptr_t slide) {
292 uintptr_t start = (uintptr_t)seg->vmaddr + slide;
293 objc::dataSegmentsRanges.add(start, start + seg->vmsize);
294 });
295 }
296 #endif
297 }
298
299
300 /***********************************************************************
301 * removeHeader
302 * Remove the given header from the header list.
303 * FirstHeader is updated.
304 * LastHeader is set to NULL. Any code that uses LastHeader must
305 * detect this NULL and recompute LastHeader by traversing the list.
306 **********************************************************************/
307 void removeHeader(header_info *hi)
308 {
309 header_info *prev = NULL;
310 header_info *current = NULL;
311
312 for (current = FirstHeader; current != NULL; current = current->getNext()) {
313 if (current == hi) {
314 header_info *deadHead = current;
315
316 // Remove from the linked list.
317 if (prev)
318 prev->setNext(current->getNext());
319 else
320 FirstHeader = current->getNext(); // no prev so removing head
321
322 // Update LastHeader if necessary.
323 if (LastHeader == deadHead) {
324 LastHeader = NULL; // will be recomputed next time it's used
325 }
326 break;
327 }
328 prev = current;
329 }
330
331 #if __OBJC2__
332 if ((hi->mhdr()->flags & MH_DYLIB_IN_CACHE) == 0) {
333 foreach_data_segment(hi->mhdr(), [](const segmentType *seg, intptr_t slide) {
334 uintptr_t start = (uintptr_t)seg->vmaddr + slide;
335 objc::dataSegmentsRanges.remove(start, start + seg->vmsize);
336 });
337 }
338 #endif
339 }
340
341
342 /***********************************************************************
343 * environ_init
344 * Read environment variables that affect the runtime.
345 * Also print environment variable help, if requested.
346 **********************************************************************/
347 void environ_init(void)
348 {
349 if (issetugid()) {
350 // All environment variables are silently ignored when setuid or setgid
351 // This includes OBJC_HELP and OBJC_PRINT_OPTIONS themselves.
352 return;
353 }
354
355 bool PrintHelp = false;
356 bool PrintOptions = false;
357 bool maybeMallocDebugging = false;
358
359 // Scan environ[] directly instead of calling getenv() a lot.
360 // This optimizes the case where none are set.
361 for (char **p = *_NSGetEnviron(); *p != nil; p++) {
362 if (0 == strncmp(*p, "Malloc", 6) || 0 == strncmp(*p, "DYLD", 4) ||
363 0 == strncmp(*p, "NSZombiesEnabled", 16))
364 {
365 maybeMallocDebugging = true;
366 }
367
368 if (0 != strncmp(*p, "OBJC_", 5)) continue;
369
370 if (0 == strncmp(*p, "OBJC_HELP=", 10)) {
371 PrintHelp = true;
372 continue;
373 }
374 if (0 == strncmp(*p, "OBJC_PRINT_OPTIONS=", 19)) {
375 PrintOptions = true;
376 continue;
377 }
378
379 const char *value = strchr(*p, '=');
380 if (!*value) continue;
381 value++;
382
383 for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
384 const option_t *opt = &Settings[i];
385 if ((size_t)(value - *p) == 1+opt->envlen &&
386 0 == strncmp(*p, opt->env, opt->envlen))
387 {
388 *opt->var = (0 == strcmp(value, "YES"));
389 break;
390 }
391 }
392 }
393
394 // Special case: enable some autorelease pool debugging
395 // when some malloc debugging is enabled
396 // and OBJC_DEBUG_POOL_ALLOCATION is not set to something other than NO.
397 if (maybeMallocDebugging) {
398 const char *insert = getenv("DYLD_INSERT_LIBRARIES");
399 const char *zombie = getenv("NSZombiesEnabled");
400 const char *pooldebug = getenv("OBJC_DEBUG_POOL_ALLOCATION");
401 if ((getenv("MallocStackLogging")
402 || getenv("MallocStackLoggingNoCompact")
403 || (zombie && (*zombie == 'Y' || *zombie == 'y'))
404 || (insert && strstr(insert, "libgmalloc")))
405 &&
406 (!pooldebug || 0 == strcmp(pooldebug, "YES")))
407 {
408 DebugPoolAllocation = true;
409 }
410 }
411
412 // Print OBJC_HELP and OBJC_PRINT_OPTIONS output.
413 if (PrintHelp || PrintOptions) {
414 if (PrintHelp) {
415 _objc_inform("Objective-C runtime debugging. Set variable=YES to enable.");
416 _objc_inform("OBJC_HELP: describe available environment variables");
417 if (PrintOptions) {
418 _objc_inform("OBJC_HELP is set");
419 }
420 _objc_inform("OBJC_PRINT_OPTIONS: list which options are set");
421 }
422 if (PrintOptions) {
423 _objc_inform("OBJC_PRINT_OPTIONS is set");
424 }
425
426 for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
427 const option_t *opt = &Settings[i];
428 if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
429 if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
430 }
431 }
432 }
433
434
435 /***********************************************************************
436 * logReplacedMethod
437 * OBJC_PRINT_REPLACED_METHODS implementation
438 **********************************************************************/
439 void
440 logReplacedMethod(const char *className, SEL s,
441 bool isMeta, const char *catName,
442 IMP oldImp, IMP newImp)
443 {
444 const char *oldImage = "??";
445 const char *newImage = "??";
446
447 // Silently ignore +load replacement because category +load is special
448 if (s == @selector(load)) return;
449
450 #if TARGET_OS_WIN32
451 // don't know dladdr()/dli_fname equivalent
452 #else
453 Dl_info dl;
454
455 if (dladdr((void*)oldImp, &dl) && dl.dli_fname) oldImage = dl.dli_fname;
456 if (dladdr((void*)newImp, &dl) && dl.dli_fname) newImage = dl.dli_fname;
457 #endif
458
459 _objc_inform("REPLACED: %c[%s %s] %s%s (IMP was %p (%s), now %p (%s))",
460 isMeta ? '+' : '-', className, sel_getName(s),
461 catName ? "by category " : "", catName ? catName : "",
462 oldImp, oldImage, newImp, newImage);
463 }
464
465
466 /***********************************************************************
467 * _objc_fetch_pthread_data
468 * Fetch objc's pthread data for this thread.
469 * If the data doesn't exist yet and create is NO, return NULL.
470 * If the data doesn't exist yet and create is YES, allocate and return it.
471 **********************************************************************/
472 _objc_pthread_data *_objc_fetch_pthread_data(bool create)
473 {
474 _objc_pthread_data *data;
475
476 data = (_objc_pthread_data *)tls_get(_objc_pthread_key);
477 if (!data && create) {
478 data = (_objc_pthread_data *)
479 calloc(1, sizeof(_objc_pthread_data));
480 tls_set(_objc_pthread_key, data);
481 }
482
483 return data;
484 }
485
486
487 /***********************************************************************
488 * _objc_pthread_destroyspecific
489 * Destructor for objc's per-thread data.
490 * arg shouldn't be NULL, but we check anyway.
491 **********************************************************************/
492 extern void _destroyInitializingClassList(struct _objc_initializing_classes *list);
493 void _objc_pthread_destroyspecific(void *arg)
494 {
495 _objc_pthread_data *data = (_objc_pthread_data *)arg;
496 if (data != NULL) {
497 _destroyInitializingClassList(data->initializingClasses);
498 _destroySyncCache(data->syncCache);
499 _destroyAltHandlerList(data->handlerList);
500 for (int i = 0; i < (int)countof(data->printableNames); i++) {
501 if (data->printableNames[i]) {
502 free(data->printableNames[i]);
503 }
504 }
505 free(data->classNameLookups);
506
507 // add further cleanup here...
508
509 free(data);
510 }
511 }
512
513
514 void tls_init(void)
515 {
516 #if SUPPORT_DIRECT_THREAD_KEYS
517 pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
518 #else
519 _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
520 #endif
521 }
522
523
524 /***********************************************************************
525 * _objcInit
526 * Former library initializer. This function is now merely a placeholder
527 * for external callers. All runtime initialization has now been moved
528 * to map_images() and _objc_init.
529 **********************************************************************/
530 void _objcInit(void)
531 {
532 // do nothing
533 }
534
535
536 /***********************************************************************
537 * objc_setForwardHandler
538 **********************************************************************/
539
540 #if !__OBJC2__
541
542 // Default forward handler (nil) goes to forward:: dispatch.
543 void *_objc_forward_handler = nil;
544 void *_objc_forward_stret_handler = nil;
545
546 #else
547
548 // Default forward handler halts the process.
549 __attribute__((noreturn, cold)) void
550 objc_defaultForwardHandler(id self, SEL sel)
551 {
552 _objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
553 "(no message forward handler is installed)",
554 class_isMetaClass(object_getClass(self)) ? '+' : '-',
555 object_getClassName(self), sel_getName(sel), self);
556 }
557 void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
558
559 #if SUPPORT_STRET
560 struct stret { int i[100]; };
561 __attribute__((noreturn, cold)) struct stret
562 objc_defaultForwardStretHandler(id self, SEL sel)
563 {
564 objc_defaultForwardHandler(self, sel);
565 }
566 void *_objc_forward_stret_handler = (void*)objc_defaultForwardStretHandler;
567 #endif
568
569 #endif
570
571 void objc_setForwardHandler(void *fwd, void *fwd_stret)
572 {
573 _objc_forward_handler = fwd;
574 #if SUPPORT_STRET
575 _objc_forward_stret_handler = fwd_stret;
576 #endif
577 }
578
579
580 #if !__OBJC2__
581 // GrP fixme
582 extern "C" Class _objc_getOrigClass(const char *name);
583 #endif
584
585 static BOOL internal_class_getImageName(Class cls, const char **outName)
586 {
587 #if !__OBJC2__
588 cls = _objc_getOrigClass(cls->demangledName());
589 #endif
590 auto result = dyld_image_path_containing_address(cls);
591 *outName = result;
592 return (result != nil);
593 }
594
595
596 static ChainedHookFunction<objc_hook_getImageName>
597 GetImageNameHook{internal_class_getImageName};
598
599 void objc_setHook_getImageName(objc_hook_getImageName newValue,
600 objc_hook_getImageName *outOldValue)
601 {
602 GetImageNameHook.set(newValue, outOldValue);
603 }
604
605 const char *class_getImageName(Class cls)
606 {
607 if (!cls) return nil;
608
609 const char *name;
610 if (GetImageNameHook.get()(cls, &name)) return name;
611 else return nil;
612 }
613
614
615 /**********************************************************************
616 * Fast Enumeration Support
617 **********************************************************************/
618
619 static void (*enumerationMutationHandler)(id);
620
621 /**********************************************************************
622 * objc_enumerationMutation
623 * called by compiler when a mutation is detected during foreach iteration
624 **********************************************************************/
625 void objc_enumerationMutation(id object) {
626 if (enumerationMutationHandler == nil) {
627 _objc_fatal("mutation detected during 'for(... in ...)' enumeration of object %p.", (void*)object);
628 }
629 (*enumerationMutationHandler)(object);
630 }
631
632
633 /**********************************************************************
634 * objc_setEnumerationMutationHandler
635 * an entry point to customize mutation error handing
636 **********************************************************************/
637 void objc_setEnumerationMutationHandler(void (*handler)(id)) {
638 enumerationMutationHandler = handler;
639 }
640
641
642 /**********************************************************************
643 * Associative Reference Support
644 **********************************************************************/
645
646 id
647 objc_getAssociatedObject(id object, const void *key)
648 {
649 return _object_get_associative_reference(object, key);
650 }
651
652 static void
653 _base_objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
654 {
655 _object_set_associative_reference(object, key, value, policy);
656 }
657
658 static ChainedHookFunction<objc_hook_setAssociatedObject> SetAssocHook{_base_objc_setAssociatedObject};
659
660 void
661 objc_setHook_setAssociatedObject(objc_hook_setAssociatedObject _Nonnull newValue,
662 objc_hook_setAssociatedObject _Nullable * _Nonnull outOldValue) {
663 SetAssocHook.set(newValue, outOldValue);
664 }
665
666 void
667 objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
668 {
669 SetAssocHook.get()(object, key, value, policy);
670 }
671
672
673 void objc_removeAssociatedObjects(id object)
674 {
675 if (object && object->hasAssociatedObjects()) {
676 _object_remove_assocations(object);
677 }
678 }
679
680
681
682 #if SUPPORT_GC_COMPAT
683
684 #include <mach-o/fat.h>
685
686 // GC preflight for an app executable.
687
688 enum GCness {
689 WithGC = 1,
690 WithoutGC = 0,
691 Error = -1
692 };
693
694 // Overloaded template wrappers around clang's overflow-checked arithmetic.
695
696 template <typename T> bool uadd_overflow(T x, T y, T* sum);
697 template <typename T> bool usub_overflow(T x, T y, T* diff);
698 template <typename T> bool umul_overflow(T x, T y, T* prod);
699
700 template <typename T> bool sadd_overflow(T x, T y, T* sum);
701 template <typename T> bool ssub_overflow(T x, T y, T* diff);
702 template <typename T> bool smul_overflow(T x, T y, T* prod);
703
704 template <> bool uadd_overflow(unsigned x, unsigned y, unsigned* sum) { return __builtin_uadd_overflow(x, y, sum); }
705 template <> bool uadd_overflow(unsigned long x, unsigned long y, unsigned long* sum) { return __builtin_uaddl_overflow(x, y, sum); }
706 template <> bool uadd_overflow(unsigned long long x, unsigned long long y, unsigned long long* sum) { return __builtin_uaddll_overflow(x, y, sum); }
707
708 template <> bool usub_overflow(unsigned x, unsigned y, unsigned* diff) { return __builtin_usub_overflow(x, y, diff); }
709 template <> bool usub_overflow(unsigned long x, unsigned long y, unsigned long* diff) { return __builtin_usubl_overflow(x, y, diff); }
710 template <> bool usub_overflow(unsigned long long x, unsigned long long y, unsigned long long* diff) { return __builtin_usubll_overflow(x, y, diff); }
711
712 template <> bool umul_overflow(unsigned x, unsigned y, unsigned* prod) { return __builtin_umul_overflow(x, y, prod); }
713 template <> bool umul_overflow(unsigned long x, unsigned long y, unsigned long* prod) { return __builtin_umull_overflow(x, y, prod); }
714 template <> bool umul_overflow(unsigned long long x, unsigned long long y, unsigned long long* prod) { return __builtin_umulll_overflow(x, y, prod); }
715
716 template <> bool sadd_overflow(signed x, signed y, signed* sum) { return __builtin_sadd_overflow(x, y, sum); }
717 template <> bool sadd_overflow(signed long x, signed long y, signed long* sum) { return __builtin_saddl_overflow(x, y, sum); }
718 template <> bool sadd_overflow(signed long long x, signed long long y, signed long long* sum) { return __builtin_saddll_overflow(x, y, sum); }
719
720 template <> bool ssub_overflow(signed x, signed y, signed* diff) { return __builtin_ssub_overflow(x, y, diff); }
721 template <> bool ssub_overflow(signed long x, signed long y, signed long* diff) { return __builtin_ssubl_overflow(x, y, diff); }
722 template <> bool ssub_overflow(signed long long x, signed long long y, signed long long* diff) { return __builtin_ssubll_overflow(x, y, diff); }
723
724 template <> bool smul_overflow(signed x, signed y, signed* prod) { return __builtin_smul_overflow(x, y, prod); }
725 template <> bool smul_overflow(signed long x, signed long y, signed long* prod) { return __builtin_smull_overflow(x, y, prod); }
726 template <> bool smul_overflow(signed long long x, signed long long y, signed long long* prod) { return __builtin_smulll_overflow(x, y, prod); }
727
728
729 // Range-checking subview of a file.
730 class FileSlice {
731 int fd;
732 uint64_t sliceOffset;
733 uint64_t sliceSize;
734
735 public:
736 FileSlice() : fd(-1), sliceOffset(0), sliceSize(0) { }
737
738 FileSlice(int newfd, uint64_t newOffset, uint64_t newSize)
739 : fd(newfd) , sliceOffset(newOffset) , sliceSize(newSize) { }
740
741 // Read bytes from this slice.
742 // Returns YES if all bytes were read successfully.
743 bool pread(void *buf, uint64_t readSize, uint64_t readOffset = 0) {
744 uint64_t readEnd;
745 if (uadd_overflow(readOffset, readSize, &readEnd)) return NO;
746 if (readEnd > sliceSize) return NO;
747
748 uint64_t preadOffset;
749 if (uadd_overflow(sliceOffset, readOffset, &preadOffset)) return NO;
750
751 int64_t readed = ::pread(fd, buf, (size_t)readSize, preadOffset);
752 if (readed < 0 || (uint64_t)readed != readSize) return NO;
753 return YES;
754 }
755
756 // Create a new slice that is a subset of this slice.
757 // Returnes YES if successful.
758 bool slice(uint64_t newOffset, uint64_t newSize, FileSlice& result) {
759 // fixme arithmetic overflow
760 uint64_t newEnd;
761 if (uadd_overflow(newOffset, newSize, &newEnd)) return NO;
762 if (newEnd > sliceSize) return NO;
763
764 if (uadd_overflow(sliceOffset, newOffset, &result.sliceOffset)) {
765 return NO;
766 }
767 result.sliceSize = newSize;
768 result.fd = fd;
769 return YES;
770 }
771
772 // Shorten this slice in place by removing a range from the start.
773 bool advance(uint64_t distance) {
774 if (distance > sliceSize) return NO;
775 if (uadd_overflow(sliceOffset, distance, &sliceOffset)) return NO;
776 if (usub_overflow(sliceSize, distance, &sliceSize)) return NO;
777 return YES;
778 }
779 };
780
781
782 // Arch32 and Arch64 are used to specialize sliceRequiresGC()
783 // to interrogate old-ABI i386 and new-ABI x86_64 files.
784
785 struct Arch32 {
786 using mh_t = struct mach_header;
787 using segment_command_t = struct segment_command;
788 using section_t = struct section;
789
790 enum : cpu_type_t { cputype = CPU_TYPE_X86 };
791 enum : int { segment_cmd = LC_SEGMENT };
792
793 static bool isObjCSegment(const char *segname) {
794 return segnameEquals(segname, "__OBJC");
795 }
796
797 static bool isImageInfoSection(const char *sectname) {
798 return sectnameEquals(sectname, "__image_info");
799 }
800
801 static bool countClasses(FileSlice file, section_t& sect,
802 int& classCount, int& classrefCount)
803 {
804 if (sectnameEquals(sect.sectname, "__cls_refs")) {
805 classrefCount += sect.size / 4;
806 }
807 else if (sectnameEquals(sect.sectname, "__module_info")) {
808 struct module_t {
809 uint32_t version;
810 uint32_t size;
811 uint32_t name; // not bound
812 uint32_t symtab; // not bound
813 };
814 size_t mod_count = sect.size / sizeof(module_t);
815 if (mod_count == 0) {
816 // no classes defined
817 } else if (mod_count > 1) {
818 // AppleScriptObjC apps only have one module.
819 // Disqualify this app by setting classCount to non-zero.
820 // We don't actually need an accurate count.
821 classCount = 1;
822 } else if (mod_count == 1) {
823 FileSlice moduleSlice;
824 if (!file.slice(sect.offset, sect.size, moduleSlice)) return NO;
825 module_t module;
826 if (!moduleSlice.pread(&module, sizeof(module))) return NO;
827 if (module.symtab) {
828 // AppleScriptObjC apps only have a module with no symtab.
829 // Disqualify this app by setting classCount to non-zero.
830 // We don't actually need an accurate count.
831 classCount = 1;
832 }
833 }
834
835 }
836 return YES;
837 }
838
839 };
840
841 struct Arch64 {
842 using mh_t = struct mach_header_64;
843 using segment_command_t = struct segment_command_64;
844 using section_t = struct section_64;
845
846 enum : cpu_type_t { cputype = CPU_TYPE_X86_64 };
847 enum : int { segment_cmd = LC_SEGMENT_64 };
848
849 static bool isObjCSegment(const char *segname) {
850 return
851 segnameEquals(segname, "__DATA") ||
852 segnameEquals(segname, "__DATA_CONST") ||
853 segnameEquals(segname, "__DATA_DIRTY");
854 }
855
856 static bool isImageInfoSection(const char *sectname) {
857 return sectnameEquals(sectname, "__objc_imageinfo");
858 }
859
860 static bool countClasses(FileSlice, section_t& sect,
861 int& classCount, int& classrefCount)
862 {
863 if (sectnameEquals(sect.sectname, "__objc_classlist")) {
864 classCount += sect.size / 8;
865 }
866 else if (sectnameEquals(sect.sectname, "__objc_classrefs")) {
867 classrefCount += sect.size / 8;
868 }
869 return YES;
870 }
871 };
872
873
874 #define SANE_HEADER_SIZE (32*1024)
875
876 template <typename Arch>
877 static int sliceRequiresGC(typename Arch::mh_t mh, FileSlice file)
878 {
879 // We assume there is only one arch per pointer size that can support GC.
880 // (i386 and x86_64)
881 if (mh.cputype != Arch::cputype) return 0;
882
883 // We only check the main executable.
884 if (mh.filetype != MH_EXECUTE) return 0;
885
886 // Look for ObjC segment.
887 // Look for AppleScriptObjC linkage.
888 FileSlice cmds;
889 if (!file.slice(sizeof(mh), mh.sizeofcmds, cmds)) return Error;
890
891 // Exception: Some AppleScriptObjC apps built for GC can run without GC.
892 // 1. executable defines no classes
893 // 2. executable references NSBundle only
894 // 3. executable links to AppleScriptObjC.framework
895 // Note that shouldRejectGCApp() also knows about this.
896 bool wantsGC = NO;
897 bool linksToAppleScriptObjC = NO;
898 int classCount = 0;
899 int classrefCount = 0;
900
901 // Disallow abusively-large executables that could hang this checker.
902 // dyld performs similar checks (MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE)
903 if (mh.sizeofcmds > SANE_HEADER_SIZE) return Error;
904 if (mh.ncmds > mh.sizeofcmds / sizeof(struct load_command)) return Error;
905
906 for (uint32_t cmdindex = 0; cmdindex < mh.ncmds; cmdindex++) {
907 struct load_command lc;
908 if (!cmds.pread(&lc, sizeof(lc))) return Error;
909
910 // Disallow abusively-small load commands that could hang this checker.
911 // dyld performs a similar check.
912 if (lc.cmdsize < sizeof(lc)) return Error;
913
914 if (lc.cmd == LC_LOAD_DYLIB || lc.cmd == LC_LOAD_UPWARD_DYLIB ||
915 lc.cmd == LC_LOAD_WEAK_DYLIB || lc.cmd == LC_REEXPORT_DYLIB)
916 {
917 // Look for AppleScriptObjC linkage.
918 FileSlice dylibSlice;
919 if (!cmds.slice(0, lc.cmdsize, dylibSlice)) return Error;
920 struct dylib_command dylib;
921 if (!dylibSlice.pread(&dylib, sizeof(dylib))) return Error;
922
923 const char *asoFramework =
924 "/System/Library/Frameworks/AppleScriptObjC.framework"
925 "/Versions/A/AppleScriptObjC";
926 size_t asoLen = strlen(asoFramework);
927
928 FileSlice nameSlice;
929 if (dylibSlice.slice(dylib.dylib.name.offset, asoLen, nameSlice)) {
930 char name[asoLen];
931 if (!nameSlice.pread(name, asoLen)) return Error;
932 if (0 == memcmp(name, asoFramework, asoLen)) {
933 linksToAppleScriptObjC = YES;
934 }
935 }
936 }
937 else if (lc.cmd == Arch::segment_cmd) {
938 typename Arch::segment_command_t seg;
939 if (!cmds.pread(&seg, sizeof(seg))) return Error;
940
941 if (Arch::isObjCSegment(seg.segname)) {
942 // ObjC segment.
943 // Look for image info section.
944 // Look for class implementations and class references.
945 FileSlice sections;
946 if (!cmds.slice(0, seg.cmdsize, sections)) return Error;
947 if (!sections.advance(sizeof(seg))) return Error;
948
949 for (uint32_t segindex = 0; segindex < seg.nsects; segindex++) {
950 typename Arch::section_t sect;
951 if (!sections.pread(&sect, sizeof(sect))) return Error;
952 if (!Arch::isObjCSegment(sect.segname)) return Error;
953
954 if (!Arch::countClasses(file, sect,
955 classCount, classrefCount))
956 {
957 return Error;
958 }
959
960 if ((sect.flags & SECTION_TYPE) == S_REGULAR &&
961 Arch::isImageInfoSection(sect.sectname))
962 {
963 // ObjC image info section.
964 // Check its contents.
965 FileSlice section;
966 if (!file.slice(sect.offset, sect.size, section)) {
967 return Error;
968 }
969 // The subset of objc_image_info that was in use for GC.
970 struct {
971 uint32_t version;
972 uint32_t flags;
973 } ii;
974 if (!section.pread(&ii, sizeof(ii))) return Error;
975 if (ii.flags & (1<<1)) {
976 // App wants GC.
977 // Don't return yet because we need to
978 // check the AppleScriptObjC exception.
979 wantsGC = YES;
980 }
981 }
982
983 if (!sections.advance(sizeof(sect))) return Error;
984 }
985 }
986 }
987
988 if (!cmds.advance(lc.cmdsize)) return Error;
989 }
990
991 if (!wantsGC) {
992 // No GC bit set.
993 return WithoutGC;
994 }
995 else if (linksToAppleScriptObjC && classCount == 0 && classrefCount == 1) {
996 // Has GC bit but falls under the AppleScriptObjC exception.
997 return WithoutGC;
998 }
999 else {
1000 // Has GC bit and is not AppleScriptObjC.
1001 return WithGC;
1002 }
1003 }
1004
1005
1006 static int sliceRequiresGC(FileSlice file)
1007 {
1008 // Read mach-o header.
1009 struct mach_header_64 mh;
1010 if (!file.pread(&mh, sizeof(mh))) return Error;
1011
1012 // Check header magic. We assume only host-endian slices can support GC.
1013 switch (mh.magic) {
1014 case MH_MAGIC:
1015 return sliceRequiresGC<Arch32>(*(struct mach_header *)&mh, file);
1016 case MH_MAGIC_64:
1017 return sliceRequiresGC<Arch64>(mh, file);
1018 default:
1019 return WithoutGC;
1020 }
1021 }
1022
1023
1024 // Returns 1 if any slice requires GC.
1025 // Returns 0 if no slice requires GC.
1026 // Returns -1 on any I/O or file format error.
1027 int objc_appRequiresGC(int fd)
1028 {
1029 struct stat st;
1030 if (fstat(fd, &st) < 0) return Error;
1031
1032 FileSlice file(fd, 0, st.st_size);
1033
1034 // Read fat header, if any.
1035 struct fat_header fh;
1036
1037 if (! file.pread(&fh, sizeof(fh))) return Error;
1038
1039 int result;
1040
1041 if (OSSwapBigToHostInt32(fh.magic) == FAT_MAGIC) {
1042 // Fat header.
1043
1044 size_t nfat_arch = OSSwapBigToHostInt32(fh.nfat_arch);
1045 // Disallow abusively-large files that could hang this checker.
1046 if (nfat_arch > SANE_HEADER_SIZE/sizeof(struct fat_arch)) return Error;
1047
1048 size_t fat_size;
1049 if (umul_overflow(nfat_arch, sizeof(struct fat_arch), &fat_size)) {
1050 return Error;
1051 }
1052
1053 FileSlice archlist;
1054 if (!file.slice(sizeof(fh), fat_size, archlist)) return Error;
1055
1056 result = WithoutGC;
1057 for (size_t i = 0; i < nfat_arch; i++) {
1058 struct fat_arch fa;
1059 if (!archlist.pread(&fa, sizeof(fa))) return Error;
1060 if (!archlist.advance(sizeof(fa))) return Error;
1061
1062 FileSlice thin;
1063 if (!file.slice(OSSwapBigToHostInt32(fa.offset),
1064 OSSwapBigToHostInt32(fa.size), thin))
1065 {
1066 return Error;
1067 }
1068 switch (sliceRequiresGC(thin)) {
1069 case WithoutGC: break; // no change
1070 case WithGC: if (result != Error) result = WithGC; break;
1071 case Error: result = Error; break;
1072 }
1073 }
1074 }
1075 else {
1076 // Thin header or not a header.
1077 result = sliceRequiresGC(file);
1078 }
1079
1080 return result;
1081 }
1082
1083 // SUPPORT_GC_COMPAT
1084 #endif