]>
Commit | Line | Data |
---|---|---|
7af964d1 A |
1 | /* |
2 | * Copyright (c) 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 | /*********************************************************************** | |
25 | * objc-os.m | |
26 | * OS portability layer. | |
27 | **********************************************************************/ | |
28 | ||
7af964d1 A |
29 | #include "objc-private.h" |
30 | #include "objc-loadmethod.h" | |
31 | ||
32 | #if TARGET_OS_WIN32 | |
33 | ||
8972963c | 34 | #include "objc-runtime-old.h" |
7af964d1 A |
35 | #include "objcrt.h" |
36 | ||
37 | malloc_zone_t *_objc_internal_zone(void) | |
38 | { | |
39 | return NULL; | |
40 | } | |
41 | ||
42 | int monitor_init(monitor_t *c) | |
43 | { | |
44 | // fixme error checking | |
45 | HANDLE mutex = CreateMutex(NULL, TRUE, NULL); | |
46 | while (!c->mutex) { | |
47 | // fixme memory barrier here? | |
48 | if (0 == InterlockedCompareExchangePointer(&c->mutex, mutex, 0)) { | |
49 | // we win - finish construction | |
50 | c->waiters = CreateSemaphore(NULL, 0, 0x7fffffff, NULL); | |
51 | c->waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL); | |
52 | InitializeCriticalSection(&c->waitCountLock); | |
53 | c->waitCount = 0; | |
54 | c->didBroadcast = 0; | |
55 | ReleaseMutex(c->mutex); | |
56 | return 0; | |
57 | } | |
58 | } | |
59 | ||
60 | // someone else allocated the mutex and constructed the monitor | |
61 | ReleaseMutex(mutex); | |
62 | CloseHandle(mutex); | |
63 | return 0; | |
64 | } | |
65 | ||
66 | void mutex_init(mutex_t *m) | |
67 | { | |
68 | while (!m->lock) { | |
69 | CRITICAL_SECTION *newlock = malloc(sizeof(CRITICAL_SECTION)); | |
70 | InitializeCriticalSection(newlock); | |
71 | // fixme memory barrier here? | |
72 | if (0 == InterlockedCompareExchangePointer(&m->lock, newlock, 0)) { | |
73 | return; | |
74 | } | |
75 | // someone else installed their lock first | |
76 | DeleteCriticalSection(newlock); | |
77 | free(newlock); | |
78 | } | |
79 | } | |
80 | ||
81 | ||
82 | void recursive_mutex_init(recursive_mutex_t *m) | |
83 | { | |
84 | // fixme error checking | |
85 | HANDLE newmutex = CreateMutex(NULL, FALSE, NULL); | |
86 | while (!m->mutex) { | |
87 | // fixme memory barrier here? | |
88 | if (0 == InterlockedCompareExchangePointer(&m->mutex, newmutex, 0)) { | |
89 | // we win | |
90 | return; | |
91 | } | |
92 | } | |
93 | ||
94 | // someone else installed their lock first | |
95 | CloseHandle(newmutex); | |
96 | } | |
97 | ||
98 | ||
99 | WINBOOL APIENTRY DllMain( HMODULE hModule, | |
100 | DWORD ul_reason_for_call, | |
101 | LPVOID lpReserved | |
102 | ) | |
103 | { | |
104 | switch (ul_reason_for_call) { | |
105 | case DLL_PROCESS_ATTACH: | |
106 | environ_init(); | |
107 | tls_init(); | |
108 | lock_init(); | |
cd5f04f5 A |
109 | sel_init(NO, 3500); // old selector heuristic |
110 | exception_init(); | |
7af964d1 A |
111 | break; |
112 | ||
113 | case DLL_THREAD_ATTACH: | |
114 | break; | |
115 | ||
116 | case DLL_THREAD_DETACH: | |
117 | case DLL_PROCESS_DETACH: | |
118 | break; | |
119 | } | |
120 | return TRUE; | |
121 | } | |
122 | ||
123 | OBJC_EXPORT void *_objc_init_image(HMODULE image, const objc_sections *sects) | |
124 | { | |
125 | header_info *hi = _malloc_internal(sizeof(header_info)); | |
126 | size_t count, i; | |
127 | ||
128 | hi->mhdr = (const headerType *)image; | |
129 | hi->info = sects->iiStart; | |
130 | hi->allClassesRealized = NO; | |
cd5f04f5 A |
131 | hi->modules = sects->modStart ? (Module *)((void **)sects->modStart+1) : 0; |
132 | hi->moduleCount = (Module *)sects->modEnd - hi->modules; | |
133 | hi->protocols = sects->protoStart ? (struct old_protocol **)((void **)sects->protoStart+1) : 0; | |
134 | hi->protocolCount = (struct old_protocol **)sects->protoEnd - hi->protocols; | |
135 | hi->imageinfo = NULL; | |
136 | hi->imageinfoBytes = 0; | |
137 | // hi->imageinfo = sects->iiStart ? (uint8_t *)((void **)sects->iiStart+1) : 0;; | |
138 | // hi->imageinfoBytes = (uint8_t *)sects->iiEnd - hi->imageinfo; | |
139 | hi->selrefs = sects->selrefsStart ? (SEL *)((void **)sects->selrefsStart+1) : 0; | |
140 | hi->selrefCount = (SEL *)sects->selrefsEnd - hi->selrefs; | |
141 | hi->clsrefs = sects->clsrefsStart ? (Class *)((void **)sects->clsrefsStart+1) : 0; | |
142 | hi->clsrefCount = (Class *)sects->clsrefsEnd - hi->clsrefs; | |
7af964d1 A |
143 | |
144 | count = 0; | |
cd5f04f5 A |
145 | for (i = 0; i < hi->moduleCount; i++) { |
146 | if (hi->modules[i]) count++; | |
7af964d1 A |
147 | } |
148 | hi->mod_count = 0; | |
149 | hi->mod_ptr = 0; | |
150 | if (count > 0) { | |
151 | hi->mod_ptr = malloc(count * sizeof(struct objc_module)); | |
cd5f04f5 A |
152 | for (i = 0; i < hi->moduleCount; i++) { |
153 | if (hi->modules[i]) memcpy(&hi->mod_ptr[hi->mod_count++], hi->modules[i], sizeof(struct objc_module)); | |
7af964d1 A |
154 | } |
155 | } | |
8972963c | 156 | |
cd5f04f5 A |
157 | hi->moduleName = malloc(MAX_PATH * sizeof(TCHAR)); |
158 | GetModuleFileName((HMODULE)(hi->mhdr), hi->moduleName, MAX_PATH * sizeof(TCHAR)); | |
7af964d1 | 159 | |
cd5f04f5 | 160 | appendHeader(hi); |
7af964d1 A |
161 | |
162 | if (PrintImages) { | |
163 | _objc_inform("IMAGES: loading image for %s%s%s\n", | |
cd5f04f5 | 164 | hi->fname, |
7af964d1 A |
165 | headerIsBundle(hi) ? " (bundle)" : "", |
166 | _objcHeaderIsReplacement(hi) ? " (replacement)":""); | |
167 | } | |
168 | ||
169 | _read_images(&hi, 1); | |
170 | ||
171 | return hi; | |
172 | } | |
173 | ||
174 | OBJC_EXPORT void _objc_load_image(HMODULE image, header_info *hinfo) | |
175 | { | |
176 | prepare_load_methods(hinfo); | |
177 | call_load_methods(); | |
178 | } | |
179 | ||
180 | OBJC_EXPORT void _objc_unload_image(HMODULE image, header_info *hinfo) | |
181 | { | |
182 | _objc_fatal("image unload not supported"); | |
183 | } | |
184 | ||
185 | ||
cd5f04f5 | 186 | bool crashlog_header_name(header_info *hi) |
8972963c A |
187 | { |
188 | return true; | |
189 | } | |
190 | ||
191 | ||
7af964d1 A |
192 | // TARGET_OS_WIN32 |
193 | #elif TARGET_OS_MAC | |
194 | ||
8972963c | 195 | #include "objc-file-old.h" |
7257e56c | 196 | #include "objc-file.h" |
8972963c | 197 | |
cd5f04f5 | 198 | void mutex_init(mutex_t *m) |
7af964d1 A |
199 | { |
200 | pthread_mutex_init(m, NULL); | |
201 | } | |
202 | ||
203 | ||
cd5f04f5 | 204 | void recursive_mutex_init(recursive_mutex_t *m) |
7af964d1 A |
205 | { |
206 | // fixme error checking | |
207 | pthread_mutex_t *newmutex; | |
208 | ||
209 | // Build recursive mutex attributes, if needed | |
210 | static pthread_mutexattr_t *attr; | |
211 | if (!attr) { | |
cd5f04f5 | 212 | pthread_mutexattr_t *newattr = (pthread_mutexattr_t *) |
7af964d1 A |
213 | _malloc_internal(sizeof(pthread_mutexattr_t)); |
214 | pthread_mutexattr_init(newattr); | |
215 | pthread_mutexattr_settype(newattr, PTHREAD_MUTEX_RECURSIVE); | |
216 | while (!attr) { | |
217 | if (OSAtomicCompareAndSwapPtrBarrier(0, newattr, (void**)&attr)) { | |
218 | // we win | |
219 | goto attr_done; | |
220 | } | |
221 | } | |
222 | // someone else built the attr first | |
223 | _free_internal(newattr); | |
224 | } | |
225 | attr_done: | |
226 | ||
227 | // Build the mutex itself | |
cd5f04f5 | 228 | newmutex = (pthread_mutex_t *)_malloc_internal(sizeof(pthread_mutex_t)); |
7af964d1 A |
229 | pthread_mutex_init(newmutex, attr); |
230 | while (!m->mutex) { | |
231 | if (OSAtomicCompareAndSwapPtrBarrier(0, newmutex, (void**)&m->mutex)) { | |
232 | // we win | |
233 | return; | |
234 | } | |
235 | } | |
236 | ||
237 | // someone else installed their mutex first | |
238 | pthread_mutex_destroy(newmutex); | |
239 | } | |
240 | ||
241 | ||
242 | /*********************************************************************** | |
243 | * bad_magic. | |
244 | * Return YES if the header has invalid Mach-o magic. | |
245 | **********************************************************************/ | |
cd5f04f5 | 246 | BOOL bad_magic(const headerType *mhdr) |
7af964d1 A |
247 | { |
248 | return (mhdr->magic != MH_MAGIC && mhdr->magic != MH_MAGIC_64 && | |
249 | mhdr->magic != MH_CIGAM && mhdr->magic != MH_CIGAM_64); | |
250 | } | |
251 | ||
252 | ||
cd5f04f5 | 253 | static header_info * addHeader(const headerType *mhdr) |
7af964d1 | 254 | { |
cd5f04f5 | 255 | header_info *hi; |
7af964d1 A |
256 | |
257 | if (bad_magic(mhdr)) return NULL; | |
258 | ||
cd5f04f5 A |
259 | #if __OBJC2__ |
260 | // Look for hinfo from the dyld shared cache. | |
261 | hi = preoptimizedHinfoForHeader(mhdr); | |
262 | if (hi) { | |
263 | // Found an hinfo in the dyld shared cache. | |
264 | ||
265 | // Weed out duplicates. | |
266 | if (hi->loaded) { | |
267 | return NULL; | |
268 | } | |
269 | ||
270 | // Initialize fields not set by the shared cache | |
271 | // hi->next is set by appendHeader | |
272 | hi->fname = dyld_image_path_containing_address(hi->mhdr); | |
273 | hi->loaded = true; | |
274 | hi->inSharedCache = true; | |
275 | ||
276 | if (PrintPreopt) { | |
277 | _objc_inform("PREOPTIMIZATION: honoring preoptimized header info at %p for %s", hi, hi->fname); | |
278 | } | |
279 | ||
280 | # if !NDEBUG | |
281 | // Verify image_info | |
282 | size_t info_size = 0; | |
283 | const objc_image_info *image_info = _getObjcImageInfo(mhdr,&info_size); | |
284 | assert(image_info == hi->info); | |
285 | # endif | |
7af964d1 | 286 | } |
cd5f04f5 A |
287 | else |
288 | #endif | |
289 | { | |
290 | // Didn't find an hinfo in the dyld shared cache. | |
7af964d1 | 291 | |
cd5f04f5 A |
292 | // Weed out duplicates |
293 | for (hi = FirstHeader; hi; hi = hi->next) { | |
294 | if (mhdr == hi->mhdr) return NULL; | |
295 | } | |
7af964d1 | 296 | |
cd5f04f5 A |
297 | // Locate the __OBJC segment |
298 | size_t info_size = 0; | |
299 | unsigned long seg_size; | |
300 | const objc_image_info *image_info = _getObjcImageInfo(mhdr,&info_size); | |
301 | const uint8_t *objc_segment = getsegmentdata(mhdr,SEG_OBJC,&seg_size); | |
302 | if (!objc_segment && !image_info) return NULL; | |
7af964d1 | 303 | |
cd5f04f5 A |
304 | // Allocate a header_info entry. |
305 | hi = (header_info *)_calloc_internal(sizeof(header_info), 1); | |
306 | ||
307 | // Set up the new header_info entry. | |
308 | hi->mhdr = mhdr; | |
7af964d1 | 309 | #if !__OBJC2__ |
cd5f04f5 A |
310 | // mhdr must already be set |
311 | hi->mod_count = 0; | |
312 | hi->mod_ptr = _getObjcModules(hi, &hi->mod_count); | |
7af964d1 | 313 | #endif |
cd5f04f5 A |
314 | hi->info = image_info; |
315 | hi->fname = dyld_image_path_containing_address(hi->mhdr); | |
316 | hi->loaded = true; | |
317 | hi->inSharedCache = false; | |
318 | hi->allClassesRealized = NO; | |
319 | } | |
7af964d1 A |
320 | |
321 | // dylibs are not allowed to unload | |
322 | // ...except those with image_info and nothing else (5359412) | |
cd5f04f5 A |
323 | if (hi->mhdr->filetype == MH_DYLIB && _hasObjcContents(hi)) { |
324 | dlopen(hi->fname, RTLD_NOLOAD); | |
7af964d1 A |
325 | } |
326 | ||
cd5f04f5 | 327 | appendHeader(hi); |
7af964d1 | 328 | |
cd5f04f5 | 329 | return hi; |
7af964d1 A |
330 | } |
331 | ||
332 | ||
8972963c A |
333 | #if !SUPPORT_GC |
334 | ||
cd5f04f5 | 335 | const char *_gcForHInfo(const header_info *hinfo) |
8972963c A |
336 | { |
337 | return ""; | |
338 | } | |
cd5f04f5 | 339 | const char *_gcForHInfo2(const header_info *hinfo) |
8972963c A |
340 | { |
341 | return ""; | |
342 | } | |
343 | ||
344 | #else | |
7af964d1 A |
345 | |
346 | /*********************************************************************** | |
347 | * _gcForHInfo. | |
348 | **********************************************************************/ | |
cd5f04f5 | 349 | const char *_gcForHInfo(const header_info *hinfo) |
7af964d1 | 350 | { |
8972963c | 351 | if (_objcHeaderRequiresGC(hinfo)) { |
7257e56c | 352 | return "requires GC"; |
8972963c | 353 | } else if (_objcHeaderSupportsGC(hinfo)) { |
7257e56c | 354 | return "supports GC"; |
8972963c A |
355 | } else { |
356 | return "does not support GC"; | |
357 | } | |
7af964d1 | 358 | } |
cd5f04f5 | 359 | const char *_gcForHInfo2(const header_info *hinfo) |
7af964d1 | 360 | { |
8972963c | 361 | if (_objcHeaderRequiresGC(hinfo)) { |
7257e56c | 362 | return "(requires GC)"; |
8972963c | 363 | } else if (_objcHeaderSupportsGC(hinfo)) { |
7257e56c | 364 | return "(supports GC)"; |
8972963c A |
365 | } |
366 | return ""; | |
7af964d1 A |
367 | } |
368 | ||
369 | ||
7257e56c A |
370 | /*********************************************************************** |
371 | * linksToLibrary | |
372 | * Returns true if the image links directly to a dylib whose install name | |
373 | * is exactly the given name. | |
374 | **********************************************************************/ | |
375 | bool | |
376 | linksToLibrary(const header_info *hi, const char *name) | |
377 | { | |
378 | const struct dylib_command *cmd; | |
379 | unsigned long i; | |
380 | ||
381 | cmd = (const struct dylib_command *) (hi->mhdr + 1); | |
382 | for (i = 0; i < hi->mhdr->ncmds; i++) { | |
383 | if (cmd->cmd == LC_LOAD_DYLIB || cmd->cmd == LC_LOAD_UPWARD_DYLIB || | |
384 | cmd->cmd == LC_LOAD_WEAK_DYLIB || cmd->cmd == LC_REEXPORT_DYLIB) | |
385 | { | |
386 | const char *dylib = cmd->dylib.name.offset + (const char *)cmd; | |
387 | if (0 == strcmp(dylib, name)) return true; | |
388 | } | |
389 | cmd = (const struct dylib_command *)((char *)cmd + cmd->cmdsize); | |
390 | } | |
391 | ||
392 | return false; | |
393 | } | |
394 | ||
395 | ||
7af964d1 A |
396 | /*********************************************************************** |
397 | * check_gc | |
398 | * Check whether the executable supports or requires GC, and make sure | |
399 | * all already-loaded libraries support the executable's GC mode. | |
400 | * Returns TRUE if the executable wants GC on. | |
401 | **********************************************************************/ | |
7257e56c | 402 | static void check_wants_gc(BOOL *appWantsGC) |
7af964d1 A |
403 | { |
404 | const header_info *hi; | |
7af964d1 A |
405 | |
406 | // Environment variables can override the following. | |
407 | if (DisableGC) { | |
cd5f04f5 | 408 | _objc_inform_on_crash("GC: forcing GC OFF because OBJC_DISABLE_GC is set"); |
8972963c | 409 | *appWantsGC = NO; |
7af964d1 A |
410 | } |
411 | else { | |
412 | // Find the executable and check its GC bits. | |
413 | // If the executable cannot be found, default to NO. | |
414 | // (The executable will not be found if the executable contains | |
415 | // no Objective-C code.) | |
8972963c | 416 | *appWantsGC = NO; |
7af964d1 A |
417 | for (hi = FirstHeader; hi != NULL; hi = hi->next) { |
418 | if (hi->mhdr->filetype == MH_EXECUTE) { | |
8972963c | 419 | *appWantsGC = _objcHeaderSupportsGC(hi) ? YES : NO; |
7257e56c | 420 | |
7af964d1 A |
421 | if (PrintGC) { |
422 | _objc_inform("GC: executable '%s' %s", | |
cd5f04f5 | 423 | hi->fname, _gcForHInfo(hi)); |
7af964d1 | 424 | } |
7257e56c A |
425 | |
426 | if (*appWantsGC) { | |
427 | // Exception: AppleScriptObjC apps run without GC in 10.9+ | |
428 | // 1. executable defines no classes | |
429 | // 2. executable references NSBundle only | |
430 | // 3. executable links to AppleScriptObjC.framework | |
431 | size_t classcount = 0; | |
432 | size_t refcount = 0; | |
433 | #if __OBJC2__ | |
434 | _getObjc2ClassList(hi, &classcount); | |
435 | _getObjc2ClassRefs(hi, &refcount); | |
436 | #else | |
437 | if (hi->mod_count == 0 || (hi->mod_count == 1 && !hi->mod_ptr[0].symtab)) classcount = 0; | |
438 | else classcount = 1; | |
439 | _getObjcClassRefs(hi, &refcount); | |
440 | #endif | |
441 | if (classcount == 0 && refcount == 1 && | |
442 | linksToLibrary(hi, "/System/Library/Frameworks" | |
443 | "/AppleScriptObjC.framework/Versions/A" | |
444 | "/AppleScriptObjC")) | |
445 | { | |
446 | *appWantsGC = NO; | |
447 | if (PrintGC) { | |
448 | _objc_inform("GC: forcing GC OFF because this is " | |
449 | "a trivial AppleScriptObjC app"); | |
450 | } | |
451 | } | |
452 | } | |
7af964d1 A |
453 | } |
454 | } | |
455 | } | |
7af964d1 A |
456 | } |
457 | ||
458 | ||
459 | /*********************************************************************** | |
460 | * verify_gc_readiness | |
461 | * if we want gc, verify that every header describes files compiled | |
462 | * and presumably ready for gc. | |
463 | ************************************************************************/ | |
7257e56c | 464 | static void verify_gc_readiness(BOOL wantsGC, |
8972963c | 465 | header_info **hList, uint32_t hCount) |
7af964d1 A |
466 | { |
467 | BOOL busted = NO; | |
468 | uint32_t i; | |
469 | ||
470 | // Find the libraries and check their GC bits against the app's request | |
471 | for (i = 0; i < hCount; i++) { | |
472 | header_info *hi = hList[i]; | |
473 | if (hi->mhdr->filetype == MH_EXECUTE) { | |
474 | continue; | |
475 | } | |
476 | else if (hi->mhdr == &_mh_dylib_header) { | |
477 | // libobjc itself works with anything even though it is not | |
478 | // compiled with -fobjc-gc (fixme should it be?) | |
479 | } | |
480 | else if (wantsGC && ! _objcHeaderSupportsGC(hi)) { | |
481 | // App wants GC but library does not support it - bad | |
482 | _objc_inform_now_and_on_crash | |
483 | ("'%s' was not compiled with -fobjc-gc or -fobjc-gc-only, " | |
484 | "but the application requires GC", | |
cd5f04f5 | 485 | hi->fname); |
7af964d1 A |
486 | busted = YES; |
487 | } | |
488 | else if (!wantsGC && _objcHeaderRequiresGC(hi)) { | |
489 | // App doesn't want GC but library requires it - bad | |
490 | _objc_inform_now_and_on_crash | |
491 | ("'%s' was compiled with -fobjc-gc-only, " | |
492 | "but the application does not support GC", | |
cd5f04f5 | 493 | hi->fname); |
7af964d1 A |
494 | busted = YES; |
495 | } | |
8972963c | 496 | |
7af964d1 A |
497 | if (PrintGC) { |
498 | _objc_inform("GC: library '%s' %s", | |
cd5f04f5 | 499 | hi->fname, _gcForHInfo(hi)); |
7af964d1 A |
500 | } |
501 | } | |
502 | ||
503 | if (busted) { | |
504 | // GC state is not consistent. | |
505 | // Kill the process unless one of the forcing flags is set. | |
506 | if (!DisableGC) { | |
507 | _objc_fatal("*** GC capability of application and some libraries did not match"); | |
508 | } | |
509 | } | |
510 | } | |
511 | ||
512 | ||
513 | /*********************************************************************** | |
514 | * gc_enforcer | |
515 | * Make sure that images about to be loaded by dyld are GC-acceptable. | |
516 | * Images linked to the executable are always permitted; they are | |
517 | * enforced inside map_images() itself. | |
518 | **********************************************************************/ | |
519 | static BOOL InitialDyldRegistration = NO; | |
520 | static const char *gc_enforcer(enum dyld_image_states state, | |
521 | uint32_t infoCount, | |
522 | const struct dyld_image_info info[]) | |
523 | { | |
524 | uint32_t i; | |
525 | ||
526 | // Linked images get a free pass | |
527 | if (InitialDyldRegistration) return NULL; | |
528 | ||
529 | if (PrintImages) { | |
530 | _objc_inform("IMAGES: checking %d images for compatibility...", | |
531 | infoCount); | |
532 | } | |
533 | ||
534 | for (i = 0; i < infoCount; i++) { | |
8972963c A |
535 | crashlog_header_name_string(info[i].imageFilePath); |
536 | ||
7af964d1 A |
537 | const headerType *mhdr = (const headerType *)info[i].imageLoadAddress; |
538 | if (bad_magic(mhdr)) continue; | |
539 | ||
540 | objc_image_info *image_info; | |
541 | size_t size; | |
542 | ||
543 | if (mhdr == &_mh_dylib_header) { | |
544 | // libobjc itself - OK | |
545 | continue; | |
546 | } | |
547 | ||
548 | #if !__OBJC2__ | |
8972963c | 549 | unsigned long seg_size; |
7af964d1 | 550 | // 32-bit: __OBJC seg but no image_info means no GC support |
8972963c | 551 | if (!getsegmentdata(mhdr, "__OBJC", &seg_size)) { |
7af964d1 A |
552 | // not objc - assume OK |
553 | continue; | |
554 | } | |
8972963c | 555 | image_info = _getObjcImageInfo(mhdr, &size); |
7af964d1 A |
556 | if (!image_info) { |
557 | // No image_info - assume GC unsupported | |
558 | if (!UseGC) { | |
559 | // GC is OFF - ok | |
560 | continue; | |
561 | } else { | |
562 | // GC is ON - bad | |
563 | if (PrintImages || PrintGC) { | |
564 | _objc_inform("IMAGES: rejecting %d images because %s doesn't support GC (no image_info)", infoCount, info[i].imageFilePath); | |
565 | } | |
8972963c | 566 | goto reject; |
7af964d1 A |
567 | } |
568 | } | |
569 | #else | |
570 | // 64-bit: no image_info means no objc at all | |
8972963c | 571 | image_info = _getObjcImageInfo(mhdr, &size); |
7af964d1 A |
572 | if (!image_info) { |
573 | // not objc - assume OK | |
574 | continue; | |
575 | } | |
576 | #endif | |
577 | ||
578 | if (UseGC && !_objcInfoSupportsGC(image_info)) { | |
579 | // GC is ON, but image does not support GC | |
580 | if (PrintImages || PrintGC) { | |
581 | _objc_inform("IMAGES: rejecting %d images because %s doesn't support GC", infoCount, info[i].imageFilePath); | |
582 | } | |
8972963c | 583 | goto reject; |
7af964d1 A |
584 | } |
585 | if (!UseGC && _objcInfoRequiresGC(image_info)) { | |
586 | // GC is OFF, but image requires GC | |
587 | if (PrintImages || PrintGC) { | |
588 | _objc_inform("IMAGES: rejecting %d images because %s requires GC", infoCount, info[i].imageFilePath); | |
589 | } | |
8972963c | 590 | goto reject; |
7af964d1 A |
591 | } |
592 | } | |
593 | ||
8972963c | 594 | crashlog_header_name_string(NULL); |
7af964d1 | 595 | return NULL; |
8972963c A |
596 | |
597 | reject: | |
598 | crashlog_header_name_string(NULL); | |
599 | return "GC capability mismatch"; | |
7af964d1 A |
600 | } |
601 | ||
8972963c | 602 | // SUPPORT_GC |
7af964d1 A |
603 | #endif |
604 | ||
605 | ||
8070259c A |
606 | |
607 | /*********************************************************************** | |
608 | * getSDKVersion | |
609 | * Look up the build-time SDK version for an image. | |
610 | * Version X.Y.Z is encoded as 0xXXXXYYZZ. | |
611 | * Images without the load command are assumed to be old (version 0.0.0). | |
612 | **********************************************************************/ | |
613 | #if TARGET_OS_IPHONE | |
614 | // Simulator binaries encode an iOS version | |
615 | # define LC_VERSION_MIN LC_VERSION_MIN_IPHONEOS | |
616 | #elif TARGET_OS_MAC | |
617 | # define LC_VERSION_MIN LC_VERSION_MIN_MACOSX | |
618 | #else | |
619 | # error unknown OS | |
620 | #endif | |
621 | ||
622 | static uint32_t | |
623 | getSDKVersion(const header_info *hi) | |
624 | { | |
625 | const struct version_min_command *cmd; | |
626 | unsigned long i; | |
627 | ||
628 | cmd = (const struct version_min_command *) (hi->mhdr + 1); | |
629 | for (i = 0; i < hi->mhdr->ncmds; i++){ | |
630 | if (cmd->cmd == LC_VERSION_MIN && cmd->cmdsize >= 16) { | |
631 | return cmd->sdk; | |
632 | } | |
633 | cmd = (const struct version_min_command *)((char *)cmd + cmd->cmdsize); | |
634 | } | |
635 | ||
636 | // Lack of version load command is assumed to be old. | |
637 | return 0; | |
638 | } | |
639 | ||
640 | ||
7af964d1 A |
641 | /*********************************************************************** |
642 | * map_images_nolock | |
643 | * Process the given images which are being mapped in by dyld. | |
644 | * All class registration and fixups are performed (or deferred pending | |
645 | * discovery of missing superclasses etc), and +load methods are called. | |
646 | * | |
647 | * info[] is in bottom-up order i.e. libobjc will be earlier in the | |
648 | * array than any library that links to libobjc. | |
649 | * | |
650 | * Locking: loadMethodLock(old) or runtimeLock(new) acquired by map_images. | |
651 | **********************************************************************/ | |
cd5f04f5 A |
652 | #if __OBJC2__ |
653 | #include "objc-file.h" | |
654 | #else | |
655 | #include "objc-file-old.h" | |
656 | #endif | |
657 | ||
658 | const char * | |
7af964d1 A |
659 | map_images_nolock(enum dyld_image_states state, uint32_t infoCount, |
660 | const struct dyld_image_info infoList[]) | |
661 | { | |
662 | static BOOL firstTime = YES; | |
8972963c | 663 | static BOOL wantsGC = NO; |
7af964d1 A |
664 | uint32_t i; |
665 | header_info *hi; | |
666 | header_info *hList[infoCount]; | |
667 | uint32_t hCount; | |
cd5f04f5 | 668 | size_t selrefCount = 0; |
7af964d1 A |
669 | |
670 | // Perform first-time initialization if necessary. | |
671 | // This function is called before ordinary library initializers. | |
672 | // fixme defer initialization until an objc-using image is found? | |
673 | if (firstTime) { | |
cd5f04f5 | 674 | preopt_init(); |
8972963c | 675 | #if SUPPORT_GC |
7af964d1 A |
676 | InitialDyldRegistration = YES; |
677 | dyld_register_image_state_change_handler(dyld_image_state_mapped, 0 /* batch */, &gc_enforcer); | |
678 | InitialDyldRegistration = NO; | |
679 | #endif | |
680 | } | |
681 | ||
682 | if (PrintImages) { | |
683 | _objc_inform("IMAGES: processing %u newly-mapped images...\n", infoCount); | |
684 | } | |
685 | ||
686 | ||
687 | // Find all images with Objective-C metadata. | |
688 | hCount = 0; | |
689 | i = infoCount; | |
690 | while (i--) { | |
691 | const headerType *mhdr = (headerType *)infoList[i].imageLoadAddress; | |
692 | ||
cd5f04f5 | 693 | hi = addHeader(mhdr); |
7af964d1 A |
694 | if (!hi) { |
695 | // no objc data in this entry | |
696 | continue; | |
697 | } | |
cd5f04f5 | 698 | if (mhdr->filetype == MH_EXECUTE) { |
8070259c A |
699 | // Record main executable's build SDK version |
700 | AppSDKVersion = getSDKVersion(hi); | |
701 | ||
702 | // Size some data structures based on main executable's size | |
cd5f04f5 A |
703 | #if __OBJC2__ |
704 | size_t count; | |
705 | _getObjc2SelectorRefs(hi, &count); | |
706 | selrefCount += count; | |
707 | _getObjc2MessageRefs(hi, &count); | |
708 | selrefCount += count; | |
709 | #else | |
710 | _getObjcSelectorRefs(hi, &selrefCount); | |
711 | #endif | |
712 | } | |
7af964d1 A |
713 | |
714 | hList[hCount++] = hi; | |
715 | ||
716 | ||
717 | if (PrintImages) { | |
718 | _objc_inform("IMAGES: loading image for %s%s%s%s%s\n", | |
cd5f04f5 | 719 | hi->fname, |
7af964d1 A |
720 | mhdr->filetype == MH_BUNDLE ? " (bundle)" : "", |
721 | _objcHeaderIsReplacement(hi) ? " (replacement)" : "", | |
722 | _objcHeaderOptimizedByDyld(hi)?" (preoptimized)" : "", | |
723 | _gcForHInfo2(hi)); | |
724 | } | |
725 | } | |
726 | ||
727 | // Perform one-time runtime initialization that must be deferred until | |
728 | // the executable itself is found. This needs to be done before | |
729 | // further initialization. | |
730 | // (The executable may not be present in this infoList if the | |
731 | // executable does not contain Objective-C code but Objective-C | |
732 | // is dynamically loaded later. In that case, check_wants_gc() | |
733 | // will do the right thing.) | |
8972963c | 734 | #if SUPPORT_GC |
7af964d1 | 735 | if (firstTime) { |
7257e56c | 736 | check_wants_gc(&wantsGC); |
7af964d1 | 737 | |
7257e56c | 738 | verify_gc_readiness(wantsGC, hList, hCount); |
7af964d1 | 739 | |
7257e56c | 740 | gc_init(wantsGC); // needs executable for GC decision |
7af964d1 | 741 | } else { |
7257e56c | 742 | verify_gc_readiness(wantsGC, hList, hCount); |
7af964d1 A |
743 | } |
744 | ||
745 | if (wantsGC) { | |
746 | // tell the collector about the data segment ranges. | |
747 | for (i = 0; i < hCount; ++i) { | |
8972963c A |
748 | uint8_t *seg; |
749 | unsigned long seg_size; | |
7af964d1 | 750 | hi = hList[i]; |
8972963c A |
751 | |
752 | seg = getsegmentdata(hi->mhdr, "__DATA", &seg_size); | |
753 | if (seg) gc_register_datasegment((uintptr_t)seg, seg_size); | |
754 | ||
755 | seg = getsegmentdata(hi->mhdr, "__OBJC", &seg_size); | |
756 | if (seg) gc_register_datasegment((uintptr_t)seg, seg_size); | |
757 | // __OBJC contains no GC data, but pointers to it are | |
758 | // used as associated reference values (rdar://6953570) | |
7af964d1 A |
759 | } |
760 | } | |
761 | #endif | |
762 | ||
763 | if (firstTime) { | |
cd5f04f5 | 764 | sel_init(wantsGC, selrefCount); |
8972963c | 765 | arr_init(); |
7af964d1 A |
766 | } |
767 | ||
768 | _read_images(hList, hCount); | |
769 | ||
770 | firstTime = NO; | |
771 | ||
772 | return NULL; | |
773 | } | |
774 | ||
775 | ||
776 | /*********************************************************************** | |
777 | * load_images_nolock | |
778 | * Prepares +load in the given images which are being mapped in by dyld. | |
779 | * Returns YES if there are now +load methods to be called by call_load_methods. | |
780 | * | |
781 | * Locking: loadMethodLock(both) and runtimeLock(new) acquired by load_images | |
782 | **********************************************************************/ | |
cd5f04f5 | 783 | BOOL |
7af964d1 A |
784 | load_images_nolock(enum dyld_image_states state,uint32_t infoCount, |
785 | const struct dyld_image_info infoList[]) | |
786 | { | |
787 | BOOL found = NO; | |
788 | uint32_t i; | |
789 | ||
790 | i = infoCount; | |
791 | while (i--) { | |
792 | header_info *hi; | |
793 | for (hi = FirstHeader; hi != NULL; hi = hi->next) { | |
794 | const headerType *mhdr = (headerType*)infoList[i].imageLoadAddress; | |
795 | if (hi->mhdr == mhdr) { | |
796 | prepare_load_methods(hi); | |
797 | found = YES; | |
798 | } | |
799 | } | |
800 | } | |
801 | ||
802 | return found; | |
803 | } | |
804 | ||
805 | ||
806 | /*********************************************************************** | |
807 | * unmap_image_nolock | |
808 | * Process the given image which is about to be unmapped by dyld. | |
809 | * mh is mach_header instead of headerType because that's what | |
810 | * dyld_priv.h says even for 64-bit. | |
811 | * | |
812 | * Locking: loadMethodLock(both) and runtimeLock(new) acquired by unmap_image. | |
813 | **********************************************************************/ | |
cd5f04f5 | 814 | void |
8972963c | 815 | unmap_image_nolock(const struct mach_header *mh) |
7af964d1 A |
816 | { |
817 | if (PrintImages) { | |
818 | _objc_inform("IMAGES: processing 1 newly-unmapped image...\n"); | |
819 | } | |
820 | ||
821 | header_info *hi; | |
822 | ||
823 | // Find the runtime's header_info struct for the image | |
824 | for (hi = FirstHeader; hi != NULL; hi = hi->next) { | |
825 | if (hi->mhdr == (const headerType *)mh) { | |
826 | break; | |
827 | } | |
828 | } | |
829 | ||
830 | if (!hi) return; | |
831 | ||
832 | if (PrintImages) { | |
833 | _objc_inform("IMAGES: unloading image for %s%s%s%s\n", | |
cd5f04f5 | 834 | hi->fname, |
7af964d1 A |
835 | hi->mhdr->filetype == MH_BUNDLE ? " (bundle)" : "", |
836 | _objcHeaderIsReplacement(hi) ? " (replacement)" : "", | |
837 | _gcForHInfo2(hi)); | |
838 | } | |
839 | ||
8972963c | 840 | #if SUPPORT_GC |
7af964d1 | 841 | if (UseGC) { |
8972963c A |
842 | uint8_t *seg; |
843 | unsigned long seg_size; | |
844 | ||
845 | seg = getsegmentdata(hi->mhdr, "__DATA", &seg_size); | |
846 | if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size); | |
847 | ||
848 | seg = getsegmentdata(hi->mhdr, "__OBJC", &seg_size); | |
849 | if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size); | |
7af964d1 A |
850 | } |
851 | #endif | |
852 | ||
853 | _unload_image(hi); | |
854 | ||
855 | // Remove header_info from header list | |
cd5f04f5 | 856 | removeHeader(hi); |
7af964d1 A |
857 | _free_internal(hi); |
858 | } | |
859 | ||
860 | ||
861 | /*********************************************************************** | |
862 | * _objc_init | |
cd5f04f5 A |
863 | * Bootstrap initialization. Registers our image notifier with dyld. |
864 | * Old ABI: called by dyld as a library initializer | |
865 | * New ABI: called by libSystem BEFORE library initialization time | |
7af964d1 | 866 | **********************************************************************/ |
cd5f04f5 | 867 | #if !__OBJC2__ |
7af964d1 | 868 | static __attribute__((constructor)) |
cd5f04f5 | 869 | #endif |
7af964d1 A |
870 | void _objc_init(void) |
871 | { | |
cd5f04f5 A |
872 | static bool initialized = false; |
873 | if (initialized) return; | |
874 | initialized = true; | |
875 | ||
7af964d1 A |
876 | // fixme defer initialization until an objc-using image is found? |
877 | environ_init(); | |
878 | tls_init(); | |
879 | lock_init(); | |
880 | exception_init(); | |
8972963c | 881 | |
7af964d1 A |
882 | // Register for unmap first, in case some +load unmaps something |
883 | _dyld_register_func_for_remove_image(&unmap_image); | |
884 | dyld_register_image_state_change_handler(dyld_image_state_bound, | |
885 | 1/*batch*/, &map_images); | |
886 | dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, 0/*not batch*/, &load_images); | |
887 | } | |
888 | ||
889 | ||
890 | /*********************************************************************** | |
891 | * _headerForAddress. | |
892 | * addr can be a class or a category | |
893 | **********************************************************************/ | |
894 | static const header_info *_headerForAddress(void *addr) | |
895 | { | |
8972963c A |
896 | #if __OBJC2__ |
897 | const char *segname = "__DATA"; | |
898 | #else | |
899 | const char *segname = "__OBJC"; | |
900 | #endif | |
901 | header_info *hi; | |
7af964d1 A |
902 | |
903 | // Check all headers in the vector | |
904 | for (hi = FirstHeader; hi != NULL; hi = hi->next) | |
905 | { | |
8972963c A |
906 | uint8_t *seg; |
907 | unsigned long seg_size; | |
908 | ||
909 | seg = getsegmentdata(hi->mhdr, segname, &seg_size); | |
910 | if (!seg) continue; | |
7af964d1 A |
911 | |
912 | // Is the class in this header? | |
8972963c | 913 | if ((uint8_t *)addr >= seg && (uint8_t *)addr < seg + seg_size) |
7af964d1 A |
914 | return hi; |
915 | } | |
916 | ||
917 | // Not found | |
918 | return 0; | |
919 | } | |
920 | ||
921 | ||
922 | /*********************************************************************** | |
923 | * _headerForClass | |
924 | * Return the image header containing this class, or NULL. | |
925 | * Returns NULL on runtime-constructed classes, and the NSCF classes. | |
926 | **********************************************************************/ | |
cd5f04f5 | 927 | const header_info *_headerForClass(Class cls) |
7af964d1 A |
928 | { |
929 | return _headerForAddress(cls); | |
930 | } | |
931 | ||
932 | ||
933 | /********************************************************************** | |
934 | * secure_open | |
935 | * Securely open a file from a world-writable directory (like /tmp) | |
936 | * If the file does not exist, it will be atomically created with mode 0600 | |
937 | * If the file exists, it must be, and remain after opening: | |
938 | * 1. a regular file (in particular, not a symlink) | |
939 | * 2. owned by euid | |
940 | * 3. permissions 0600 | |
941 | * 4. link count == 1 | |
942 | * Returns a file descriptor or -1. Errno may or may not be set on error. | |
943 | **********************************************************************/ | |
cd5f04f5 | 944 | int secure_open(const char *filename, int flags, uid_t euid) |
7af964d1 A |
945 | { |
946 | struct stat fs, ls; | |
947 | int fd = -1; | |
948 | BOOL truncate = NO; | |
949 | BOOL create = NO; | |
950 | ||
951 | if (flags & O_TRUNC) { | |
952 | // Don't truncate the file until after it is open and verified. | |
953 | truncate = YES; | |
954 | flags &= ~O_TRUNC; | |
955 | } | |
956 | if (flags & O_CREAT) { | |
957 | // Don't create except when we're ready for it | |
958 | create = YES; | |
959 | flags &= ~O_CREAT; | |
960 | flags &= ~O_EXCL; | |
961 | } | |
962 | ||
963 | if (lstat(filename, &ls) < 0) { | |
964 | if (errno == ENOENT && create) { | |
965 | // No such file - create it | |
966 | fd = open(filename, flags | O_CREAT | O_EXCL, 0600); | |
967 | if (fd >= 0) { | |
968 | // File was created successfully. | |
969 | // New file does not need to be truncated. | |
970 | return fd; | |
971 | } else { | |
972 | // File creation failed. | |
973 | return -1; | |
974 | } | |
975 | } else { | |
976 | // lstat failed, or user doesn't want to create the file | |
977 | return -1; | |
978 | } | |
979 | } else { | |
980 | // lstat succeeded - verify attributes and open | |
981 | if (S_ISREG(ls.st_mode) && // regular file? | |
982 | ls.st_nlink == 1 && // link count == 1? | |
983 | ls.st_uid == euid && // owned by euid? | |
984 | (ls.st_mode & ALLPERMS) == (S_IRUSR | S_IWUSR)) // mode 0600? | |
985 | { | |
986 | // Attributes look ok - open it and check attributes again | |
987 | fd = open(filename, flags, 0000); | |
988 | if (fd >= 0) { | |
989 | // File is open - double-check attributes | |
990 | if (0 == fstat(fd, &fs) && | |
991 | fs.st_nlink == ls.st_nlink && // link count == 1? | |
992 | fs.st_uid == ls.st_uid && // owned by euid? | |
993 | fs.st_mode == ls.st_mode && // regular file, 0600? | |
994 | fs.st_ino == ls.st_ino && // same inode as before? | |
995 | fs.st_dev == ls.st_dev) // same device as before? | |
996 | { | |
997 | // File is open and OK | |
998 | if (truncate) ftruncate(fd, 0); | |
999 | return fd; | |
1000 | } else { | |
1001 | // Opened file looks funny - close it | |
1002 | close(fd); | |
1003 | return -1; | |
1004 | } | |
1005 | } else { | |
1006 | // File didn't open | |
1007 | return -1; | |
1008 | } | |
1009 | } else { | |
1010 | // Unopened file looks funny - don't open it | |
1011 | return -1; | |
1012 | } | |
1013 | } | |
1014 | } | |
1015 | ||
1016 | ||
1017 | /*********************************************************************** | |
1018 | * _objc_internal_zone. | |
1019 | * Malloc zone for internal runtime data. | |
1020 | * By default this is the default malloc zone, but a dedicated zone is | |
1021 | * used if environment variable OBJC_USE_INTERNAL_ZONE is set. | |
1022 | **********************************************************************/ | |
cd5f04f5 | 1023 | malloc_zone_t *_objc_internal_zone(void) |
7af964d1 A |
1024 | { |
1025 | static malloc_zone_t *z = (malloc_zone_t *)-1; | |
1026 | if (z == (malloc_zone_t *)-1) { | |
1027 | if (UseInternalZone) { | |
1028 | z = malloc_create_zone(vm_page_size, 0); | |
8070259c | 1029 | malloc_set_zone_name(z, "ObjC_Internal"); |
7af964d1 A |
1030 | } else { |
1031 | z = malloc_default_zone(); | |
1032 | } | |
1033 | } | |
1034 | return z; | |
1035 | } | |
1036 | ||
1037 | ||
cd5f04f5 | 1038 | bool crashlog_header_name(header_info *hi) |
8972963c | 1039 | { |
cd5f04f5 | 1040 | return crashlog_header_name_string(hi ? hi->fname : NULL); |
8972963c A |
1041 | } |
1042 | ||
cd5f04f5 | 1043 | bool crashlog_header_name_string(const char *name) |
8972963c A |
1044 | { |
1045 | CRSetCrashLogMessage2(name); | |
1046 | return true; | |
1047 | } | |
1048 | ||
1049 | ||
1050 | #if TARGET_OS_IPHONE | |
1051 | ||
cd5f04f5 | 1052 | const char *__crashreporter_info__ = NULL; |
8972963c | 1053 | |
cd5f04f5 | 1054 | const char *CRSetCrashLogMessage(const char *msg) |
8972963c A |
1055 | { |
1056 | __crashreporter_info__ = msg; | |
1057 | return msg; | |
1058 | } | |
cd5f04f5 | 1059 | const char *CRGetCrashLogMessage(void) |
8972963c A |
1060 | { |
1061 | return __crashreporter_info__; | |
1062 | } | |
1063 | ||
cd5f04f5 | 1064 | const char *CRSetCrashLogMessage2(const char *msg) |
8972963c A |
1065 | { |
1066 | // sorry | |
1067 | return msg; | |
1068 | } | |
1069 | ||
1070 | #endif | |
1071 | ||
7af964d1 A |
1072 | // TARGET_OS_MAC |
1073 | #else | |
1074 | ||
1075 | ||
1076 | #error unknown OS | |
1077 | ||
1078 | ||
1079 | #endif |