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