2 * Copyright (c) 2007 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 /***********************************************************************
26 * OS portability layer.
27 **********************************************************************/
29 #include "objc-private.h"
30 #include "objc-loadmethod.h"
34 #include "objc-runtime-old.h"
37 int monitor_init(monitor_t *c)
39 // fixme error checking
40 HANDLE mutex = CreateMutex(NULL, TRUE, NULL);
42 // fixme memory barrier here?
43 if (0 == InterlockedCompareExchangePointer(&c->mutex, mutex, 0)) {
44 // we win - finish construction
45 c->waiters = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
46 c->waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL);
47 InitializeCriticalSection(&c->waitCountLock);
50 ReleaseMutex(c->mutex);
55 // someone else allocated the mutex and constructed the monitor
61 void mutex_init(mutex_t *m)
64 CRITICAL_SECTION *newlock = malloc(sizeof(CRITICAL_SECTION));
65 InitializeCriticalSection(newlock);
66 // fixme memory barrier here?
67 if (0 == InterlockedCompareExchangePointer(&m->lock, newlock, 0)) {
70 // someone else installed their lock first
71 DeleteCriticalSection(newlock);
77 void recursive_mutex_init(recursive_mutex_t *m)
79 // fixme error checking
80 HANDLE newmutex = CreateMutex(NULL, FALSE, NULL);
82 // fixme memory barrier here?
83 if (0 == InterlockedCompareExchangePointer(&m->mutex, newmutex, 0)) {
89 // someone else installed their lock first
90 CloseHandle(newmutex);
94 WINBOOL APIENTRY DllMain( HMODULE hModule,
95 DWORD ul_reason_for_call,
99 switch (ul_reason_for_call) {
100 case DLL_PROCESS_ATTACH:
104 sel_init(3500); // old selector heuristic
108 case DLL_THREAD_ATTACH:
111 case DLL_THREAD_DETACH:
112 case DLL_PROCESS_DETACH:
118 OBJC_EXPORT void *_objc_init_image(HMODULE image, const objc_sections *sects)
120 header_info *hi = malloc(sizeof(header_info));
123 hi->mhdr = (const headerType *)image;
124 hi->info = sects->iiStart;
125 hi->allClassesRealized = NO;
126 hi->modules = sects->modStart ? (Module *)((void **)sects->modStart+1) : 0;
127 hi->moduleCount = (Module *)sects->modEnd - hi->modules;
128 hi->protocols = sects->protoStart ? (struct old_protocol **)((void **)sects->protoStart+1) : 0;
129 hi->protocolCount = (struct old_protocol **)sects->protoEnd - hi->protocols;
130 hi->imageinfo = NULL;
131 hi->imageinfoBytes = 0;
132 // hi->imageinfo = sects->iiStart ? (uint8_t *)((void **)sects->iiStart+1) : 0;;
133 // hi->imageinfoBytes = (uint8_t *)sects->iiEnd - hi->imageinfo;
134 hi->selrefs = sects->selrefsStart ? (SEL *)((void **)sects->selrefsStart+1) : 0;
135 hi->selrefCount = (SEL *)sects->selrefsEnd - hi->selrefs;
136 hi->clsrefs = sects->clsrefsStart ? (Class *)((void **)sects->clsrefsStart+1) : 0;
137 hi->clsrefCount = (Class *)sects->clsrefsEnd - hi->clsrefs;
140 for (i = 0; i < hi->moduleCount; i++) {
141 if (hi->modules[i]) count++;
146 hi->mod_ptr = malloc(count * sizeof(struct objc_module));
147 for (i = 0; i < hi->moduleCount; i++) {
148 if (hi->modules[i]) memcpy(&hi->mod_ptr[hi->mod_count++], hi->modules[i], sizeof(struct objc_module));
152 hi->moduleName = malloc(MAX_PATH * sizeof(TCHAR));
153 GetModuleFileName((HMODULE)(hi->mhdr), hi->moduleName, MAX_PATH * sizeof(TCHAR));
158 _objc_inform("IMAGES: loading image for %s%s%s%s\n",
160 headerIsBundle(hi) ? " (bundle)" : "",
161 hi->info->isReplacement() ? " (replacement)":"",
162 hi->info->hasCategoryClassProperties() ? " (has class properties)":"");
165 // Count classes. Size various table based on the total.
167 int unoptimizedTotal = 0;
169 if (_getObjc2ClassList(hi, &count)) {
171 if (!hi->getInSharedCache()) unoptimizedTotal += count;
175 _read_images(&hi, 1, total, unoptimizedTotal);
180 OBJC_EXPORT void _objc_load_image(HMODULE image, header_info *hinfo)
182 prepare_load_methods(hinfo);
186 OBJC_EXPORT void _objc_unload_image(HMODULE image, header_info *hinfo)
188 _objc_fatal("image unload not supported");
195 #include "objc-file-old.h"
196 #include "objc-file.h"
199 /***********************************************************************
200 * libobjc must never run static destructors.
201 * Cover libc's __cxa_atexit with our own definition that runs nothing.
202 * rdar://21734598 ER: Compiler option to suppress C++ static destructors
203 **********************************************************************/
204 extern "C" int __cxa_atexit();
205 extern "C" int __cxa_atexit() { return 0; }
208 /***********************************************************************
210 * Return YES if the header has invalid Mach-o magic.
211 **********************************************************************/
212 bool bad_magic(const headerType *mhdr)
214 return (mhdr->magic != MH_MAGIC && mhdr->magic != MH_MAGIC_64 &&
215 mhdr->magic != MH_CIGAM && mhdr->magic != MH_CIGAM_64);
219 static header_info * addHeader(const headerType *mhdr, const char *path, int &totalClasses, int &unoptimizedTotalClasses)
223 if (bad_magic(mhdr)) return NULL;
225 bool inSharedCache = false;
227 // Look for hinfo from the dyld shared cache.
228 hi = preoptimizedHinfoForHeader(mhdr);
230 // Found an hinfo in the dyld shared cache.
232 // Weed out duplicates.
233 if (hi->isLoaded()) {
237 inSharedCache = true;
239 // Initialize fields not set by the shared cache
240 // hi->next is set by appendHeader
244 _objc_inform("PREOPTIMIZATION: honoring preoptimized header info at %p for %s", hi, hi->fname());
248 _objc_fatal("shouldn't be here");
252 size_t info_size = 0;
253 const objc_image_info *image_info = _getObjcImageInfo(mhdr,&info_size);
254 assert(image_info == hi->info());
259 // Didn't find an hinfo in the dyld shared cache.
261 // Weed out duplicates
262 for (hi = FirstHeader; hi; hi = hi->getNext()) {
263 if (mhdr == hi->mhdr()) return NULL;
266 // Locate the __OBJC segment
267 size_t info_size = 0;
268 unsigned long seg_size;
269 const objc_image_info *image_info = _getObjcImageInfo(mhdr,&info_size);
270 const uint8_t *objc_segment = getsegmentdata(mhdr,SEG_OBJC,&seg_size);
271 if (!objc_segment && !image_info) return NULL;
273 // Allocate a header_info entry.
274 // Note we also allocate space for a single header_info_rw in the
275 // rw_data[] inside header_info.
276 hi = (header_info *)calloc(sizeof(header_info) + sizeof(header_info_rw), 1);
278 // Set up the new header_info entry.
281 // mhdr must already be set
283 hi->mod_ptr = _getObjcModules(hi, &hi->mod_count);
285 // Install a placeholder image_info if absent to simplify code elsewhere
286 static const objc_image_info emptyInfo = {0, 0};
287 hi->setinfo(image_info ?: &emptyInfo);
290 hi->setAllClassesRealized(NO);
296 if (_getObjc2ClassList(hi, &count)) {
297 totalClasses += (int)count;
298 if (!inSharedCache) unoptimizedTotalClasses += count;
309 /***********************************************************************
311 * Returns true if the image links directly to a dylib whose install name
312 * is exactly the given name.
313 **********************************************************************/
315 linksToLibrary(const header_info *hi, const char *name)
317 const struct dylib_command *cmd;
320 cmd = (const struct dylib_command *) (hi->mhdr() + 1);
321 for (i = 0; i < hi->mhdr()->ncmds; i++) {
322 if (cmd->cmd == LC_LOAD_DYLIB || cmd->cmd == LC_LOAD_UPWARD_DYLIB ||
323 cmd->cmd == LC_LOAD_WEAK_DYLIB || cmd->cmd == LC_REEXPORT_DYLIB)
325 const char *dylib = cmd->dylib.name.offset + (const char *)cmd;
326 if (0 == strcmp(dylib, name)) return true;
328 cmd = (const struct dylib_command *)((char *)cmd + cmd->cmdsize);
335 #if SUPPORT_GC_COMPAT
337 /***********************************************************************
339 * Return YES if the executable requires GC.
340 **********************************************************************/
341 static bool shouldRejectGCApp(const header_info *hi)
343 assert(hi->mhdr()->filetype == MH_EXECUTE);
345 if (!hi->info()->supportsGC()) {
346 // App does not use GC. Don't reject it.
350 // Exception: Trivial AppleScriptObjC apps can run without GC.
351 // 1. executable defines no classes
352 // 2. executable references NSBundle only
353 // 3. executable links to AppleScriptObjC.framework
354 // Note that objc_appRequiresGC() also knows about this.
355 size_t classcount = 0;
358 _getObjc2ClassList(hi, &classcount);
359 _getObjc2ClassRefs(hi, &refcount);
361 if (hi->mod_count == 0 || (hi->mod_count == 1 && !hi->mod_ptr[0].symtab)) classcount = 0;
363 _getObjcClassRefs(hi, &refcount);
365 if (classcount == 0 && refcount == 1 &&
366 linksToLibrary(hi, "/System/Library/Frameworks"
367 "/AppleScriptObjC.framework/Versions/A"
370 // It's AppleScriptObjC. Don't reject it.
374 // GC and not trivial AppleScriptObjC. Reject it.
380 /***********************************************************************
382 * Halt if an image requires GC.
383 * Testing of the main executable should use rejectGCApp() instead.
384 **********************************************************************/
385 static bool shouldRejectGCImage(const headerType *mhdr)
387 assert(mhdr->filetype != MH_EXECUTE);
389 objc_image_info *image_info;
393 unsigned long seg_size;
394 // 32-bit: __OBJC seg but no image_info means no GC support
395 if (!getsegmentdata(mhdr, "__OBJC", &seg_size)) {
396 // Not objc, therefore not GC. Don't reject it.
399 image_info = _getObjcImageInfo(mhdr, &size);
401 // No image_info, therefore not GC. Don't reject it.
405 // 64-bit: no image_info means no objc at all
406 image_info = _getObjcImageInfo(mhdr, &size);
408 // Not objc, therefore not GC. Don't reject it.
413 return image_info->requiresGC();
420 /***********************************************************************
422 * Process the given images which are being mapped in by dyld.
423 * All class registration and fixups are performed (or deferred pending
424 * discovery of missing superclasses etc), and +load methods are called.
426 * info[] is in bottom-up order i.e. libobjc will be earlier in the
427 * array than any library that links to libobjc.
429 * Locking: loadMethodLock(old) or runtimeLock(new) acquired by map_images.
430 **********************************************************************/
432 #include "objc-file.h"
434 #include "objc-file-old.h"
438 map_images_nolock(unsigned mhCount, const char * const mhPaths[],
439 const struct mach_header * const mhdrs[])
441 static bool firstTime = YES;
442 header_info *hList[mhCount];
444 size_t selrefCount = 0;
446 // Perform first-time initialization if necessary.
447 // This function is called before ordinary library initializers.
448 // fixme defer initialization until an objc-using image is found?
454 _objc_inform("IMAGES: processing %u newly-mapped images...\n", mhCount);
458 // Find all images with Objective-C metadata.
461 // Count classes. Size various table based on the total.
462 int totalClasses = 0;
463 int unoptimizedTotalClasses = 0;
465 uint32_t i = mhCount;
467 const headerType *mhdr = (const headerType *)mhdrs[i];
469 auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
471 // no objc data in this entry
475 if (mhdr->filetype == MH_EXECUTE) {
476 // Size some data structures based on main executable's size
479 _getObjc2SelectorRefs(hi, &count);
480 selrefCount += count;
481 _getObjc2MessageRefs(hi, &count);
482 selrefCount += count;
484 _getObjcSelectorRefs(hi, &selrefCount);
487 #if SUPPORT_GC_COMPAT
488 // Halt if this is a GC app.
489 if (shouldRejectGCApp(hi)) {
490 _objc_fatal_with_reason
491 (OBJC_EXIT_REASON_GC_NOT_SUPPORTED,
492 OS_REASON_FLAG_CONSISTENT_FAILURE,
493 "Objective-C garbage collection "
494 "is no longer supported.");
499 hList[hCount++] = hi;
502 _objc_inform("IMAGES: loading image for %s%s%s%s%s\n",
504 mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",
505 hi->info()->isReplacement() ? " (replacement)" : "",
506 hi->info()->hasCategoryClassProperties() ? " (has class properties)" : "",
507 hi->info()->optimizedByDyld()?" (preoptimized)":"");
512 // Perform one-time runtime initialization that must be deferred until
513 // the executable itself is found. This needs to be done before
514 // further initialization.
515 // (The executable may not be present in this infoList if the
516 // executable does not contain Objective-C code but Objective-C
517 // is dynamically loaded later.
519 sel_init(selrefCount);
522 #if SUPPORT_GC_COMPAT
523 // Reject any GC images linked to the main executable.
524 // We already rejected the app itself above.
525 // Images loaded after launch will be rejected by dyld.
527 for (uint32_t i = 0; i < hCount; i++) {
529 auto mh = hi->mhdr();
530 if (mh->filetype != MH_EXECUTE && shouldRejectGCImage(mh)) {
531 _objc_fatal_with_reason
532 (OBJC_EXIT_REASON_GC_NOT_SUPPORTED,
533 OS_REASON_FLAG_CONSISTENT_FAILURE,
534 "%s requires Objective-C garbage collection "
535 "which is no longer supported.", hi->fname());
542 _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
549 /***********************************************************************
551 * Process the given image which is about to be unmapped by dyld.
552 * mh is mach_header instead of headerType because that's what
553 * dyld_priv.h says even for 64-bit.
555 * Locking: loadMethodLock(both) and runtimeLock(new) acquired by unmap_image.
556 **********************************************************************/
558 unmap_image_nolock(const struct mach_header *mh)
561 _objc_inform("IMAGES: processing 1 newly-unmapped image...\n");
566 // Find the runtime's header_info struct for the image
567 for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
568 if (hi->mhdr() == (const headerType *)mh) {
576 _objc_inform("IMAGES: unloading image for %s%s%s\n",
578 hi->mhdr()->filetype == MH_BUNDLE ? " (bundle)" : "",
579 hi->info()->isReplacement() ? " (replacement)" : "");
584 // Remove header_info from header list
590 /***********************************************************************
592 * Run C++ static constructor functions.
593 * libc calls _objc_init() before dyld would call our static constructors,
594 * so we have to do it ourselves.
595 **********************************************************************/
596 static void static_init()
599 Initializer *inits = getLibobjcInitializers(&_mh_dylib_header, &count);
600 for (size_t i = 0; i < count; i++) {
606 /***********************************************************************
608 * Bootstrap initialization. Registers our image notifier with dyld.
609 * Called by libSystem BEFORE library initialization time
610 **********************************************************************/
612 void _objc_init(void)
614 static bool initialized = false;
615 if (initialized) return;
618 // fixme defer initialization until an objc-using image is found?
625 _dyld_objc_notify_register(&map_2_images, load_images, unmap_image);
629 /***********************************************************************
631 * addr can be a class or a category
632 **********************************************************************/
633 static const header_info *_headerForAddress(void *addr)
636 const char *segnames[] = { "__DATA", "__DATA_CONST", "__DATA_DIRTY" };
638 const char *segnames[] = { "__OBJC" };
642 for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
643 for (size_t i = 0; i < sizeof(segnames)/sizeof(segnames[0]); i++) {
644 unsigned long seg_size;
645 uint8_t *seg = getsegmentdata(hi->mhdr(), segnames[i], &seg_size);
648 // Is the class in this header?
649 if ((uint8_t *)addr >= seg && (uint8_t *)addr < seg + seg_size) {
660 /***********************************************************************
662 * Return the image header containing this class, or NULL.
663 * Returns NULL on runtime-constructed classes, and the NSCF classes.
664 **********************************************************************/
665 const header_info *_headerForClass(Class cls)
667 return _headerForAddress(cls);
671 /**********************************************************************
673 * Securely open a file from a world-writable directory (like /tmp)
674 * If the file does not exist, it will be atomically created with mode 0600
675 * If the file exists, it must be, and remain after opening:
676 * 1. a regular file (in particular, not a symlink)
678 * 3. permissions 0600
680 * Returns a file descriptor or -1. Errno may or may not be set on error.
681 **********************************************************************/
682 int secure_open(const char *filename, int flags, uid_t euid)
689 if (flags & O_TRUNC) {
690 // Don't truncate the file until after it is open and verified.
694 if (flags & O_CREAT) {
695 // Don't create except when we're ready for it
701 if (lstat(filename, &ls) < 0) {
702 if (errno == ENOENT && create) {
703 // No such file - create it
704 fd = open(filename, flags | O_CREAT | O_EXCL, 0600);
706 // File was created successfully.
707 // New file does not need to be truncated.
710 // File creation failed.
714 // lstat failed, or user doesn't want to create the file
718 // lstat succeeded - verify attributes and open
719 if (S_ISREG(ls.st_mode) && // regular file?
720 ls.st_nlink == 1 && // link count == 1?
721 ls.st_uid == euid && // owned by euid?
722 (ls.st_mode & ALLPERMS) == (S_IRUSR | S_IWUSR)) // mode 0600?
724 // Attributes look ok - open it and check attributes again
725 fd = open(filename, flags, 0000);
727 // File is open - double-check attributes
728 if (0 == fstat(fd, &fs) &&
729 fs.st_nlink == ls.st_nlink && // link count == 1?
730 fs.st_uid == ls.st_uid && // owned by euid?
731 fs.st_mode == ls.st_mode && // regular file, 0600?
732 fs.st_ino == ls.st_ino && // same inode as before?
733 fs.st_dev == ls.st_dev) // same device as before?
735 // File is open and OK
736 if (truncate) ftruncate(fd, 0);
739 // Opened file looks funny - close it
748 // Unopened file looks funny - don't open it
757 const char *__crashreporter_info__ = NULL;
759 const char *CRSetCrashLogMessage(const char *msg)
761 __crashreporter_info__ = msg;
764 const char *CRGetCrashLogMessage(void)
766 return __crashreporter_info__;