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