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