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