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