X-Git-Url: https://git.saurik.com/apple/objc4.git/blobdiff_plain/7c0e6487d7b67b6bf6c632300ee4b74e8950b051..7af964d1562d70f51a8e9aca24215ac3d83d0624:/runtime/objc-os.m diff --git a/runtime/objc-os.m b/runtime/objc-os.m new file mode 100644 index 0000000..5993e9d --- /dev/null +++ b/runtime/objc-os.m @@ -0,0 +1,913 @@ +/* + * Copyright (c) 2007 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/*********************************************************************** +* objc-os.m +* OS portability layer. +**********************************************************************/ + +#define OLD 1 +#include "objc-os.h" +#include "objc-private.h" +#include "objc-loadmethod.h" + +#if TARGET_OS_WIN32 + +#include "objcrt.h" + +malloc_zone_t *_objc_internal_zone(void) +{ + return NULL; +} + +int monitor_init(monitor_t *c) +{ + // fixme error checking + HANDLE mutex = CreateMutex(NULL, TRUE, NULL); + while (!c->mutex) { + // fixme memory barrier here? + if (0 == InterlockedCompareExchangePointer(&c->mutex, mutex, 0)) { + // we win - finish construction + c->waiters = CreateSemaphore(NULL, 0, 0x7fffffff, NULL); + c->waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL); + InitializeCriticalSection(&c->waitCountLock); + c->waitCount = 0; + c->didBroadcast = 0; + ReleaseMutex(c->mutex); + return 0; + } + } + + // someone else allocated the mutex and constructed the monitor + ReleaseMutex(mutex); + CloseHandle(mutex); + return 0; +} + +void mutex_init(mutex_t *m) +{ + while (!m->lock) { + CRITICAL_SECTION *newlock = malloc(sizeof(CRITICAL_SECTION)); + InitializeCriticalSection(newlock); + // fixme memory barrier here? + if (0 == InterlockedCompareExchangePointer(&m->lock, newlock, 0)) { + return; + } + // someone else installed their lock first + DeleteCriticalSection(newlock); + free(newlock); + } +} + + +void recursive_mutex_init(recursive_mutex_t *m) +{ + // fixme error checking + HANDLE newmutex = CreateMutex(NULL, FALSE, NULL); + while (!m->mutex) { + // fixme memory barrier here? + if (0 == InterlockedCompareExchangePointer(&m->mutex, newmutex, 0)) { + // we win + return; + } + } + + // someone else installed their lock first + CloseHandle(newmutex); +} + + +WINBOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) { + case DLL_PROCESS_ATTACH: + environ_init(); + tls_init(); + lock_init(); + sel_init(NO); + exception_init(); + break; + + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +OBJC_EXPORT void *_objc_init_image(HMODULE image, const objc_sections *sects) +{ + header_info *hi = _malloc_internal(sizeof(header_info)); + size_t count, i; + + hi->mhdr = (const headerType *)image; + hi->info = sects->iiStart; + hi->allClassesRealized = NO; + hi->os.modules = sects->modStart ? (Module *)((void **)sects->modStart+1) : 0; + hi->os.moduleCount = (Module *)sects->modEnd - hi->os.modules; + hi->os.protocols = sects->protoStart ? (struct old_protocol **)((void **)sects->protoStart+1) : 0; + hi->os.protocolCount = (struct old_protocol **)sects->protoEnd - hi->os.protocols; + hi->os.imageinfo = NULL; + hi->os.imageinfoBytes = 0; + // hi->os.imageinfo = sects->iiStart ? (uint8_t *)((void **)sects->iiStart+1) : 0;; +// hi->os.imageinfoBytes = (uint8_t *)sects->iiEnd - hi->os.imageinfo; + hi->os.selrefs = sects->selrefsStart ? (SEL *)((void **)sects->selrefsStart+1) : 0; + hi->os.selrefCount = (SEL *)sects->selrefsEnd - hi->os.selrefs; + hi->os.clsrefs = sects->clsrefsStart ? (Class *)((void **)sects->clsrefsStart+1) : 0; + hi->os.clsrefCount = (Class *)sects->clsrefsEnd - hi->os.clsrefs; + + count = 0; + for (i = 0; i < hi->os.moduleCount; i++) { + if (hi->os.modules[i]) count++; + } + hi->mod_count = 0; + hi->mod_ptr = 0; + if (count > 0) { + hi->mod_ptr = malloc(count * sizeof(struct objc_module)); + for (i = 0; i < hi->os.moduleCount; i++) { + if (hi->os.modules[i]) memcpy(&hi->mod_ptr[hi->mod_count++], hi->os.modules[i], sizeof(struct objc_module)); + } + } + + _objc_appendHeader(hi); + + if (PrintImages) { + _objc_inform("IMAGES: loading image for %s%s%s\n", + _nameForHeader(hi->mhdr), + headerIsBundle(hi) ? " (bundle)" : "", + _objcHeaderIsReplacement(hi) ? " (replacement)":""); + } + + _read_images(&hi, 1); + + return hi; +} + +OBJC_EXPORT void _objc_load_image(HMODULE image, header_info *hinfo) +{ + prepare_load_methods(hinfo); + call_load_methods(); +} + +OBJC_EXPORT void _objc_unload_image(HMODULE image, header_info *hinfo) +{ + _objc_fatal("image unload not supported"); +} + + +// TARGET_OS_WIN32 +#elif TARGET_OS_MAC + +__private_extern__ void mutex_init(mutex_t *m) +{ + pthread_mutex_init(m, NULL); +} + + +__private_extern__ void recursive_mutex_init(recursive_mutex_t *m) +{ + // fixme error checking + pthread_mutex_t *newmutex; + + // Build recursive mutex attributes, if needed + static pthread_mutexattr_t *attr; + if (!attr) { + pthread_mutexattr_t *newattr = + _malloc_internal(sizeof(pthread_mutexattr_t)); + pthread_mutexattr_init(newattr); + pthread_mutexattr_settype(newattr, PTHREAD_MUTEX_RECURSIVE); + while (!attr) { + if (OSAtomicCompareAndSwapPtrBarrier(0, newattr, (void**)&attr)) { + // we win + goto attr_done; + } + } + // someone else built the attr first + _free_internal(newattr); + } + attr_done: + + // Build the mutex itself + newmutex = _malloc_internal(sizeof(pthread_mutex_t)); + pthread_mutex_init(newmutex, attr); + while (!m->mutex) { + if (OSAtomicCompareAndSwapPtrBarrier(0, newmutex, (void**)&m->mutex)) { + // we win + return; + } + } + + // someone else installed their mutex first + pthread_mutex_destroy(newmutex); +} + + +/*********************************************************************** +* bad_magic. +* Return YES if the header has invalid Mach-o magic. +**********************************************************************/ +__private_extern__ BOOL bad_magic(const headerType *mhdr) +{ + return (mhdr->magic != MH_MAGIC && mhdr->magic != MH_MAGIC_64 && + mhdr->magic != MH_CIGAM && mhdr->magic != MH_CIGAM_64); +} + + +static const segmentType * +getsegbynamefromheader(const headerType *head, const char *segname) +{ +#ifndef __LP64__ +#define SEGMENT_CMD LC_SEGMENT +#else +#define SEGMENT_CMD LC_SEGMENT_64 +#endif + const segmentType *sgp; + unsigned long i; + + sgp = (const segmentType *) (head + 1); + for (i = 0; i < head->ncmds; i++){ + if (sgp->cmd == SEGMENT_CMD) { + if (strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0) { + return sgp; + } + } + sgp = (const segmentType *)((char *)sgp + sgp->cmdsize); + } + return NULL; +#undef SEGMENT_CMD +} + + +static header_info * _objc_addHeader(const headerType *mhdr) +{ + size_t info_size = 0; + const segmentType *objc_segment; + const objc_image_info *image_info; + const segmentType *data_segment; + header_info *result; + ptrdiff_t image_slide; + + if (bad_magic(mhdr)) return NULL; + + // Weed out duplicates + for (result = FirstHeader; result; result = result->next) { + if (mhdr == result->mhdr) return NULL; + } + + // Locate the __OBJC segment + image_slide = _getImageSlide(mhdr); + image_info = _getObjcImageInfo(mhdr, image_slide, &info_size); + objc_segment = getsegbynamefromheader(mhdr, SEG_OBJC); + data_segment = getsegbynamefromheader(mhdr, SEG_DATA); + if (!objc_segment && !image_info) return NULL; + + // Allocate a header_info entry. + result = _calloc_internal(sizeof(header_info), 1); + + // Set up the new header_info entry. + result->mhdr = mhdr; + result->os.image_slide = image_slide; + result->os.objcSegmentHeader = objc_segment; + result->os.dataSegmentHeader = data_segment; +#if !__OBJC2__ + // mhdr and image_slide must already be set + result->mod_count = 0; + result->mod_ptr = _getObjcModules(result, &result->mod_count); +#endif + result->info = image_info; + dladdr(result->mhdr, &result->os.dl_info); + result->allClassesRealized = NO; + + // dylibs are not allowed to unload + // ...except those with image_info and nothing else (5359412) + if (result->mhdr->filetype == MH_DYLIB && _hasObjcContents(result)) { + dlopen(result->os.dl_info.dli_fname, RTLD_NOLOAD); + } + + // Make sure every copy of objc_image_info in this image is the same. + // This means same version and same bitwise contents. + if (result->info) { + const objc_image_info *start = result->info; + const objc_image_info *end = + (objc_image_info *)(info_size + (uint8_t *)start); + const objc_image_info *info = start; + while (info < end) { + // version is byte size, except for version 0 + size_t struct_size = info->version; + if (struct_size == 0) struct_size = 2 * sizeof(uint32_t); + if (info->version != start->version || + 0 != memcmp(info, start, struct_size)) + { + _objc_inform("'%s' has inconsistently-compiled Objective-C " + "code. Please recompile all code in it.", + _nameForHeader(mhdr)); + } + info = (objc_image_info *)(struct_size + (uint8_t *)info); + } + } + + _objc_appendHeader(result); + + return result; +} + + +#ifndef NO_GC + +/*********************************************************************** +* _gcForHInfo. +**********************************************************************/ +__private_extern__ const char *_gcForHInfo(const header_info *hinfo) +{ + if (_objcHeaderRequiresGC(hinfo)) return "requires GC"; + else if (_objcHeaderSupportsGC(hinfo)) return "supports GC"; + else return "does not support GC"; +} +__private_extern__ const char *_gcForHInfo2(const header_info *hinfo) +{ + if (_objcHeaderRequiresGC(hinfo)) return " (requires GC)"; + else if (_objcHeaderSupportsGC(hinfo)) return " (supports GC)"; + else return ""; +} + + +/*********************************************************************** +* check_gc +* Check whether the executable supports or requires GC, and make sure +* all already-loaded libraries support the executable's GC mode. +* Returns TRUE if the executable wants GC on. +**********************************************************************/ +static BOOL check_wants_gc(void) +{ + const header_info *hi; + BOOL appWantsGC; + + // Environment variables can override the following. + if (DisableGC) { + _objc_inform("GC: forcing GC OFF because OBJC_DISABLE_GC is set"); + appWantsGC = NO; + } + else { + // Find the executable and check its GC bits. + // If the executable cannot be found, default to NO. + // (The executable will not be found if the executable contains + // no Objective-C code.) + appWantsGC = NO; + for (hi = FirstHeader; hi != NULL; hi = hi->next) { + if (hi->mhdr->filetype == MH_EXECUTE) { + appWantsGC = _objcHeaderSupportsGC(hi) ? YES : NO; + if (PrintGC) { + _objc_inform("GC: executable '%s' %s", + _nameForHeader(hi->mhdr), _gcForHInfo(hi)); + } + } + } + } + return appWantsGC; +} + + +/*********************************************************************** +* verify_gc_readiness +* if we want gc, verify that every header describes files compiled +* and presumably ready for gc. +************************************************************************/ +static void verify_gc_readiness(BOOL wantsGC, header_info **hList, + uint32_t hCount) +{ + BOOL busted = NO; + uint32_t i; + + // Find the libraries and check their GC bits against the app's request + for (i = 0; i < hCount; i++) { + header_info *hi = hList[i]; + if (hi->mhdr->filetype == MH_EXECUTE) { + continue; + } + else if (hi->mhdr == &_mh_dylib_header) { + // libobjc itself works with anything even though it is not + // compiled with -fobjc-gc (fixme should it be?) + } + else if (wantsGC && ! _objcHeaderSupportsGC(hi)) { + // App wants GC but library does not support it - bad + _objc_inform_now_and_on_crash + ("'%s' was not compiled with -fobjc-gc or -fobjc-gc-only, " + "but the application requires GC", + _nameForHeader(hi->mhdr)); + busted = YES; + } + else if (!wantsGC && _objcHeaderRequiresGC(hi)) { + // App doesn't want GC but library requires it - bad + _objc_inform_now_and_on_crash + ("'%s' was compiled with -fobjc-gc-only, " + "but the application does not support GC", + _nameForHeader(hi->mhdr)); + busted = YES; + } + + if (PrintGC) { + _objc_inform("GC: library '%s' %s", + _nameForHeader(hi->mhdr), _gcForHInfo(hi)); + } + } + + if (busted) { + // GC state is not consistent. + // Kill the process unless one of the forcing flags is set. + if (!DisableGC) { + _objc_fatal("*** GC capability of application and some libraries did not match"); + } + } +} + + +/*********************************************************************** +* gc_enforcer +* Make sure that images about to be loaded by dyld are GC-acceptable. +* Images linked to the executable are always permitted; they are +* enforced inside map_images() itself. +**********************************************************************/ +static BOOL InitialDyldRegistration = NO; +static const char *gc_enforcer(enum dyld_image_states state, + uint32_t infoCount, + const struct dyld_image_info info[]) +{ + uint32_t i; + + // Linked images get a free pass + if (InitialDyldRegistration) return NULL; + + if (PrintImages) { + _objc_inform("IMAGES: checking %d images for compatibility...", + infoCount); + } + + for (i = 0; i < infoCount; i++) { + const headerType *mhdr = (const headerType *)info[i].imageLoadAddress; + if (bad_magic(mhdr)) continue; + + objc_image_info *image_info; + size_t size; + + if (mhdr == &_mh_dylib_header) { + // libobjc itself - OK + continue; + } + +#if !__OBJC2__ + // 32-bit: __OBJC seg but no image_info means no GC support + if (!getsegbynamefromheader(mhdr, SEG_OBJC)) { + // not objc - assume OK + continue; + } + image_info = _getObjcImageInfo(mhdr, _getImageSlide(mhdr), &size); + if (!image_info) { + // No image_info - assume GC unsupported + if (!UseGC) { + // GC is OFF - ok + continue; + } else { + // GC is ON - bad + if (PrintImages || PrintGC) { + _objc_inform("IMAGES: rejecting %d images because %s doesn't support GC (no image_info)", infoCount, info[i].imageFilePath); + } + return "GC capability mismatch"; + } + } +#else + // 64-bit: no image_info means no objc at all + image_info = _getObjcImageInfo(mhdr, _getImageSlide(mhdr), &size); + if (!image_info) { + // not objc - assume OK + continue; + } +#endif + + if (UseGC && !_objcInfoSupportsGC(image_info)) { + // GC is ON, but image does not support GC + if (PrintImages || PrintGC) { + _objc_inform("IMAGES: rejecting %d images because %s doesn't support GC", infoCount, info[i].imageFilePath); + } + return "GC capability mismatch"; + } + if (!UseGC && _objcInfoRequiresGC(image_info)) { + // GC is OFF, but image requires GC + if (PrintImages || PrintGC) { + _objc_inform("IMAGES: rejecting %d images because %s requires GC", infoCount, info[i].imageFilePath); + } + return "GC capability mismatch"; + } + } + + return NULL; +} + +// !defined(NO_GC) +#endif + + +/*********************************************************************** +* map_images_nolock +* Process the given images which are being mapped in by dyld. +* All class registration and fixups are performed (or deferred pending +* discovery of missing superclasses etc), and +load methods are called. +* +* info[] is in bottom-up order i.e. libobjc will be earlier in the +* array than any library that links to libobjc. +* +* Locking: loadMethodLock(old) or runtimeLock(new) acquired by map_images. +**********************************************************************/ +__private_extern__ const char * +map_images_nolock(enum dyld_image_states state, uint32_t infoCount, + const struct dyld_image_info infoList[]) +{ + static BOOL firstTime = YES; + static BOOL wantsGC NOBSS = NO; + uint32_t i; + header_info *hi; + header_info *hList[infoCount]; + uint32_t hCount; + + // Perform first-time initialization if necessary. + // This function is called before ordinary library initializers. + // fixme defer initialization until an objc-using image is found? + if (firstTime) { +#ifndef NO_GC + InitialDyldRegistration = YES; + dyld_register_image_state_change_handler(dyld_image_state_mapped, 0 /* batch */, &gc_enforcer); + InitialDyldRegistration = NO; +#endif + } + + if (PrintImages) { + _objc_inform("IMAGES: processing %u newly-mapped images...\n", infoCount); + } + + + // Find all images with Objective-C metadata. + hCount = 0; + i = infoCount; + while (i--) { + const headerType *mhdr = (headerType *)infoList[i].imageLoadAddress; + + hi = _objc_addHeader(mhdr); + if (!hi) { + // no objc data in this entry + continue; + } + + hList[hCount++] = hi; + + + if (PrintImages) { + _objc_inform("IMAGES: loading image for %s%s%s%s%s\n", + _nameForHeader(mhdr), + mhdr->filetype == MH_BUNDLE ? " (bundle)" : "", + _objcHeaderIsReplacement(hi) ? " (replacement)" : "", + _objcHeaderOptimizedByDyld(hi)?" (preoptimized)" : "", + _gcForHInfo2(hi)); + } + } + + // Perform one-time runtime initialization that must be deferred until + // the executable itself is found. This needs to be done before + // further initialization. + // (The executable may not be present in this infoList if the + // executable does not contain Objective-C code but Objective-C + // is dynamically loaded later. In that case, check_wants_gc() + // will do the right thing.) +#ifndef NO_GC + if (firstTime) { + wantsGC = check_wants_gc(); + + verify_gc_readiness(wantsGC, hList, hCount); + + gc_init(wantsGC); // needs executable for GC decision + rtp_init(); // needs GC decision first + } else { + verify_gc_readiness(wantsGC, hList, hCount); + } + + if (wantsGC) { + // tell the collector about the data segment ranges. + for (i = 0; i < hCount; ++i) { + hi = hList[i]; + const segmentType *dataSegment = hi->os.dataSegmentHeader; + const segmentType *objcSegment = hi->os.objcSegmentHeader; + if (dataSegment) { + gc_register_datasegment(dataSegment->vmaddr + hi->os.image_slide, dataSegment->vmsize); + } + if (objcSegment) { + // __OBJC contains no GC data, but pointers to it are + // used as associated reference values (rdar://6953570) + gc_register_datasegment(objcSegment->vmaddr + hi->os.image_slide, objcSegment->vmsize); + } + } + } +#endif + + if (firstTime) { + extern SEL FwdSel; // in objc-msg-*.s + sel_init(wantsGC); + FwdSel = sel_registerName("forward::"); + } + + _read_images(hList, hCount); + + firstTime = NO; + + return NULL; +} + + +/*********************************************************************** +* load_images_nolock +* Prepares +load in the given images which are being mapped in by dyld. +* Returns YES if there are now +load methods to be called by call_load_methods. +* +* Locking: loadMethodLock(both) and runtimeLock(new) acquired by load_images +**********************************************************************/ +__private_extern__ BOOL +load_images_nolock(enum dyld_image_states state,uint32_t infoCount, + const struct dyld_image_info infoList[]) +{ + BOOL found = NO; + uint32_t i; + + i = infoCount; + while (i--) { + header_info *hi; + for (hi = FirstHeader; hi != NULL; hi = hi->next) { + const headerType *mhdr = (headerType*)infoList[i].imageLoadAddress; + if (hi->mhdr == mhdr) { + prepare_load_methods(hi); + found = YES; + } + } + } + + return found; +} + + +/*********************************************************************** +* unmap_image_nolock +* Process the given image which is about to be unmapped by dyld. +* mh is mach_header instead of headerType because that's what +* dyld_priv.h says even for 64-bit. +* +* Locking: loadMethodLock(both) and runtimeLock(new) acquired by unmap_image. +**********************************************************************/ +__private_extern__ void +unmap_image_nolock(const struct mach_header *mh, intptr_t vmaddr_slide) +{ + if (PrintImages) { + _objc_inform("IMAGES: processing 1 newly-unmapped image...\n"); + } + + header_info *hi; + + // Find the runtime's header_info struct for the image + for (hi = FirstHeader; hi != NULL; hi = hi->next) { + if (hi->mhdr == (const headerType *)mh) { + break; + } + } + + if (!hi) return; + + if (PrintImages) { + _objc_inform("IMAGES: unloading image for %s%s%s%s\n", + _nameForHeader(hi->mhdr), + hi->mhdr->filetype == MH_BUNDLE ? " (bundle)" : "", + _objcHeaderIsReplacement(hi) ? " (replacement)" : "", + _gcForHInfo2(hi)); + } + +#ifndef NO_GC + if (UseGC) { + const segmentType *dataSegment = hi->os.dataSegmentHeader; + const segmentType *objcSegment = hi->os.objcSegmentHeader; + if (dataSegment) { + gc_unregister_datasegment(dataSegment->vmaddr + hi->os.image_slide, dataSegment->vmsize); + } + if (objcSegment) { + gc_unregister_datasegment(objcSegment->vmaddr + hi->os.image_slide, objcSegment->vmsize); + } + } +#endif + + _unload_image(hi); + + // Remove header_info from header list + _objc_removeHeader(hi); + _free_internal(hi); +} + + +/*********************************************************************** +* _objc_init +* Static initializer. Registers our image notifier with dyld. +**********************************************************************/ +static __attribute__((constructor)) +void _objc_init(void) +{ + // fixme defer initialization until an objc-using image is found? + environ_init(); + tls_init(); + lock_init(); + exception_init(); + + // Register for unmap first, in case some +load unmaps something + _dyld_register_func_for_remove_image(&unmap_image); + dyld_register_image_state_change_handler(dyld_image_state_bound, + 1/*batch*/, &map_images); + dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, 0/*not batch*/, &load_images); +} + + +/*********************************************************************** +* _headerForAddress. +* addr can be a class or a category +**********************************************************************/ +static const header_info *_headerForAddress(void *addr) +{ + unsigned long size; + unsigned long seg; + header_info * hi; + + // Check all headers in the vector + for (hi = FirstHeader; hi != NULL; hi = hi->next) + { + // Locate header data, if any + const segmentType *segHeader; +#if __OBJC2__ + segHeader = hi->os.dataSegmentHeader; +#else + segHeader = hi->os.objcSegmentHeader; +#endif + if (!segHeader) continue; + seg = segHeader->vmaddr + hi->os.image_slide; + size = segHeader->filesize; + + // Is the class in this header? + if ((seg <= (unsigned long) addr) && + ((unsigned long) addr < (seg + size))) + return hi; + } + + // Not found + return 0; +} + + +/*********************************************************************** +* _headerForClass +* Return the image header containing this class, or NULL. +* Returns NULL on runtime-constructed classes, and the NSCF classes. +**********************************************************************/ +__private_extern__ const header_info *_headerForClass(Class cls) +{ + return _headerForAddress(cls); +} + + +/********************************************************************** +* secure_open +* Securely open a file from a world-writable directory (like /tmp) +* If the file does not exist, it will be atomically created with mode 0600 +* If the file exists, it must be, and remain after opening: +* 1. a regular file (in particular, not a symlink) +* 2. owned by euid +* 3. permissions 0600 +* 4. link count == 1 +* Returns a file descriptor or -1. Errno may or may not be set on error. +**********************************************************************/ +__private_extern__ int secure_open(const char *filename, int flags, uid_t euid) +{ + struct stat fs, ls; + int fd = -1; + BOOL truncate = NO; + BOOL create = NO; + + if (flags & O_TRUNC) { + // Don't truncate the file until after it is open and verified. + truncate = YES; + flags &= ~O_TRUNC; + } + if (flags & O_CREAT) { + // Don't create except when we're ready for it + create = YES; + flags &= ~O_CREAT; + flags &= ~O_EXCL; + } + + if (lstat(filename, &ls) < 0) { + if (errno == ENOENT && create) { + // No such file - create it + fd = open(filename, flags | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + // File was created successfully. + // New file does not need to be truncated. + return fd; + } else { + // File creation failed. + return -1; + } + } else { + // lstat failed, or user doesn't want to create the file + return -1; + } + } else { + // lstat succeeded - verify attributes and open + if (S_ISREG(ls.st_mode) && // regular file? + ls.st_nlink == 1 && // link count == 1? + ls.st_uid == euid && // owned by euid? + (ls.st_mode & ALLPERMS) == (S_IRUSR | S_IWUSR)) // mode 0600? + { + // Attributes look ok - open it and check attributes again + fd = open(filename, flags, 0000); + if (fd >= 0) { + // File is open - double-check attributes + if (0 == fstat(fd, &fs) && + fs.st_nlink == ls.st_nlink && // link count == 1? + fs.st_uid == ls.st_uid && // owned by euid? + fs.st_mode == ls.st_mode && // regular file, 0600? + fs.st_ino == ls.st_ino && // same inode as before? + fs.st_dev == ls.st_dev) // same device as before? + { + // File is open and OK + if (truncate) ftruncate(fd, 0); + return fd; + } else { + // Opened file looks funny - close it + close(fd); + return -1; + } + } else { + // File didn't open + return -1; + } + } else { + // Unopened file looks funny - don't open it + return -1; + } + } +} + + +/*********************************************************************** +* _objc_internal_zone. +* Malloc zone for internal runtime data. +* By default this is the default malloc zone, but a dedicated zone is +* used if environment variable OBJC_USE_INTERNAL_ZONE is set. +**********************************************************************/ +__private_extern__ malloc_zone_t *_objc_internal_zone(void) +{ + static malloc_zone_t *z = (malloc_zone_t *)-1; + if (z == (malloc_zone_t *)-1) { + if (UseInternalZone) { + z = malloc_create_zone(vm_page_size, 0); + malloc_set_zone_name(z, "ObjC"); + } else { + z = malloc_default_zone(); + } + } + return z; +} + + +// TARGET_OS_MAC +#else + + +#error unknown OS + + +#endif