]> git.saurik.com Git - apple/objc4.git/blobdiff - runtime/objc-os.mm
objc4-532.tar.gz
[apple/objc4.git] / runtime / objc-os.mm
diff --git a/runtime/objc-os.mm b/runtime/objc-os.mm
new file mode 100644 (file)
index 0000000..06bb95e
--- /dev/null
@@ -0,0 +1,1024 @@
+/*
+ * 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.
+**********************************************************************/
+
+#include "objc-os.h"
+#include "objc-private.h"
+#include "objc-loadmethod.h"
+
+#if TARGET_OS_WIN32
+
+#include "objc-runtime-old.h"
+#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, 3500);  // old selector heuristic
+        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->modules = sects->modStart ? (Module *)((void **)sects->modStart+1) : 0;
+    hi->moduleCount = (Module *)sects->modEnd - hi->modules;
+    hi->protocols = sects->protoStart ? (struct old_protocol **)((void **)sects->protoStart+1) : 0;
+    hi->protocolCount = (struct old_protocol **)sects->protoEnd - hi->protocols;
+    hi->imageinfo = NULL;
+    hi->imageinfoBytes = 0;
+    // hi->imageinfo = sects->iiStart ? (uint8_t *)((void **)sects->iiStart+1) : 0;;
+//     hi->imageinfoBytes = (uint8_t *)sects->iiEnd - hi->imageinfo;
+    hi->selrefs = sects->selrefsStart ? (SEL *)((void **)sects->selrefsStart+1) : 0;
+    hi->selrefCount = (SEL *)sects->selrefsEnd - hi->selrefs;
+    hi->clsrefs = sects->clsrefsStart ? (Class *)((void **)sects->clsrefsStart+1) : 0;
+    hi->clsrefCount = (Class *)sects->clsrefsEnd - hi->clsrefs;
+
+    count = 0;
+    for (i = 0; i < hi->moduleCount; i++) {
+        if (hi->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->moduleCount; i++) {
+            if (hi->modules[i]) memcpy(&hi->mod_ptr[hi->mod_count++], hi->modules[i], sizeof(struct objc_module));
+        }
+    }
+    
+    hi->moduleName = malloc(MAX_PATH * sizeof(TCHAR));
+    GetModuleFileName((HMODULE)(hi->mhdr), hi->moduleName, MAX_PATH * sizeof(TCHAR));
+
+    appendHeader(hi);
+
+    if (PrintImages) {
+        _objc_inform("IMAGES: loading image for %s%s%s\n", 
+            hi->fname, 
+            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");
+}
+
+
+bool crashlog_header_name(header_info *hi)
+{
+    return true;
+}
+
+
+// TARGET_OS_WIN32
+#elif TARGET_OS_MAC
+
+#if !__OBJC2__
+#include "objc-file-old.h"
+#endif
+
+void mutex_init(mutex_t *m)
+{
+    pthread_mutex_init(m, NULL);
+}
+
+
+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 = (pthread_mutexattr_t *)
+            _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 = (pthread_mutex_t *)_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.
+**********************************************************************/
+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 header_info * addHeader(const headerType *mhdr)
+{
+    header_info *hi;
+
+    if (bad_magic(mhdr)) return NULL;
+
+#if __OBJC2__
+    // Look for hinfo from the dyld shared cache.
+    hi = preoptimizedHinfoForHeader(mhdr);
+    if (hi) {
+        // Found an hinfo in the dyld shared cache.
+
+        // Weed out duplicates.
+        if (hi->loaded) {
+            return NULL;
+        }
+
+        // Initialize fields not set by the shared cache
+        // hi->next is set by appendHeader
+        hi->fname = dyld_image_path_containing_address(hi->mhdr);
+        hi->loaded = true;
+        hi->inSharedCache = true;
+
+        if (PrintPreopt) {
+            _objc_inform("PREOPTIMIZATION: honoring preoptimized header info at %p for %s", hi, hi->fname);
+        }
+
+# if !NDEBUG
+        // Verify image_info
+        size_t info_size = 0;
+        const objc_image_info *image_info = _getObjcImageInfo(mhdr,&info_size);
+        assert(image_info == hi->info);
+# endif
+    }
+    else 
+#endif
+    {
+        // Didn't find an hinfo in the dyld shared cache.
+
+        // Weed out duplicates
+        for (hi = FirstHeader; hi; hi = hi->next) {
+            if (mhdr == hi->mhdr) return NULL;
+        }
+
+        // Locate the __OBJC segment
+        size_t info_size = 0;
+        unsigned long seg_size;
+        const objc_image_info *image_info = _getObjcImageInfo(mhdr,&info_size);
+        const uint8_t *objc_segment = getsegmentdata(mhdr,SEG_OBJC,&seg_size);
+        if (!objc_segment  &&  !image_info) return NULL;
+
+        // Allocate a header_info entry.
+        hi = (header_info *)_calloc_internal(sizeof(header_info), 1);
+
+        // Set up the new header_info entry.
+        hi->mhdr = mhdr;
+#if !__OBJC2__
+        // mhdr must already be set
+        hi->mod_count = 0;
+        hi->mod_ptr = _getObjcModules(hi, &hi->mod_count);
+#endif
+        hi->info = image_info;
+        hi->fname = dyld_image_path_containing_address(hi->mhdr);
+        hi->loaded = true;
+        hi->inSharedCache = false;
+        hi->allClassesRealized = NO;
+    }
+
+    // dylibs are not allowed to unload
+    // ...except those with image_info and nothing else (5359412)
+    if (hi->mhdr->filetype == MH_DYLIB  &&  _hasObjcContents(hi)) {
+        dlopen(hi->fname, RTLD_NOLOAD);
+    }
+
+    appendHeader(hi);
+    
+    return hi;
+}
+
+
+#if !SUPPORT_GC
+
+const char *_gcForHInfo(const header_info *hinfo)
+{
+    return "";
+}
+const char *_gcForHInfo2(const header_info *hinfo)
+{
+    return "";
+}
+
+#else
+
+/***********************************************************************
+* _gcForHInfo.
+**********************************************************************/
+const char *_gcForHInfo(const header_info *hinfo)
+{
+    if (_objcHeaderRequiresGC(hinfo)) {
+        if (_objcHeaderSupportsCompaction(hinfo))
+            return "requires GC, supports compaction";
+        else
+            return "requires GC";
+    } else if (_objcHeaderSupportsGC(hinfo)) {
+        if (_objcHeaderSupportsCompaction(hinfo))
+            return "supports GC, supports compaction";
+        else
+            return "supports GC";
+    } else {
+        return "does not support GC";
+    }
+}
+const char *_gcForHInfo2(const header_info *hinfo)
+{
+    if (_objcHeaderRequiresGC(hinfo)) {
+        if (_objcHeaderSupportsCompaction(hinfo))
+            return "(requires GC) (supports compaction)";
+        else
+            return "(requires GC)";
+    } else if (_objcHeaderSupportsGC(hinfo)) {
+        if (_objcHeaderSupportsCompaction(hinfo))
+            return "(supports GC) (supports compaction)";
+        else
+            return "(supports GC)";
+    }
+    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 void check_wants_gc(BOOL *appWantsGC, BOOL *appSupportsCompaction)
+{
+    const header_info *hi;
+
+    // Environment variables can override the following.
+    if (DisableGC) {
+        _objc_inform_on_crash("GC: forcing GC OFF because OBJC_DISABLE_GC is set");
+        *appWantsGC = NO;
+        *appSupportsCompaction = 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;
+        *appSupportsCompaction = NO;
+        for (hi = FirstHeader; hi != NULL; hi = hi->next) {
+            if (hi->mhdr->filetype == MH_EXECUTE) {
+                *appWantsGC = _objcHeaderSupportsGC(hi) ? YES : NO;
+                *appSupportsCompaction = (*appWantsGC && _objcHeaderSupportsCompaction(hi)) ? YES : NO;
+                if (PrintGC) {
+                    _objc_inform("GC: executable '%s' %s",
+                                 hi->fname, _gcForHInfo(hi));
+                }
+            }
+        }
+    }
+}
+
+
+/***********************************************************************
+* 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, BOOL *wantsCompaction,
+                                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",
+                 hi->fname);
+            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",
+                 hi->fname);
+            busted = YES;            
+        }
+        
+        if (*wantsCompaction && !_objcHeaderSupportsCompaction(hi)) {
+            // App supports compaction, but library doesn't.
+            _objc_inform_now_and_on_crash
+                ("'%s' was not linked with -Xlinker -objc_gc_compaction, "
+                 "but the application wants compaction.",
+                 hi->fname);
+            // Simply warn for now until radars are filed. Eventually,
+            // objc_disableCompaction() will block until any current compaction completes.
+            objc_disableCompaction();
+            *wantsCompaction = NO;
+        }
+
+        if (PrintGC) {
+            _objc_inform("GC: library '%s' %s", 
+                         hi->fname, _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++) {
+        crashlog_header_name_string(info[i].imageFilePath);
+
+        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__
+        unsigned long seg_size;
+        // 32-bit: __OBJC seg but no image_info means no GC support
+        if (!getsegmentdata(mhdr, "__OBJC", &seg_size)) {
+            // not objc - assume OK
+            continue;
+        }
+        image_info = _getObjcImageInfo(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);
+                }
+                goto reject;
+            }
+        }
+#else
+        // 64-bit: no image_info means no objc at all
+        image_info = _getObjcImageInfo(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);
+            }
+            goto reject;
+        }
+        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);
+            }
+            goto reject;
+        }
+    }
+
+    crashlog_header_name_string(NULL);
+    return NULL;
+
+ reject:
+    crashlog_header_name_string(NULL);
+    return "GC capability mismatch";
+}
+
+// SUPPORT_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.
+**********************************************************************/
+#if __OBJC2__
+#include "objc-file.h"
+#else
+#include "objc-file-old.h"
+#endif
+
+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 = NO;
+    static BOOL wantsCompaction = NO;
+    uint32_t i;
+    header_info *hi;
+    header_info *hList[infoCount];
+    uint32_t hCount;
+    size_t selrefCount = 0;
+
+    // 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) {
+        preopt_init();
+#if SUPPORT_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 = addHeader(mhdr);
+        if (!hi) {
+            // no objc data in this entry
+            continue;
+        }
+        if (mhdr->filetype == MH_EXECUTE) {
+#if __OBJC2__
+            size_t count;
+            _getObjc2SelectorRefs(hi, &count);
+            selrefCount += count;
+            _getObjc2MessageRefs(hi, &count);
+            selrefCount += count;
+#else
+            _getObjcSelectorRefs(hi, &selrefCount);
+#endif
+        }
+
+        hList[hCount++] = hi;
+        
+
+        if (PrintImages) {
+            _objc_inform("IMAGES: loading image for %s%s%s%s%s\n", 
+                         hi->fname, 
+                         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.)
+#if SUPPORT_GC
+    if (firstTime) {
+        check_wants_gc(&wantsGC, &wantsCompaction);
+
+        verify_gc_readiness(wantsGC, &wantsCompaction, hList, hCount);
+        
+        gc_init(wantsGC, wantsCompaction);  // needs executable for GC decision
+        rtp_init();                         // needs GC decision first
+    } else {
+        verify_gc_readiness(wantsGC, &wantsCompaction, hList, hCount);
+    }
+
+    if (wantsGC) {
+        // tell the collector about the data segment ranges.
+        for (i = 0; i < hCount; ++i) {
+            uint8_t *seg;
+            unsigned long seg_size;
+            hi = hList[i];
+
+            seg = getsegmentdata(hi->mhdr, "__DATA", &seg_size);
+            if (seg) gc_register_datasegment((uintptr_t)seg, seg_size);
+
+            seg = getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
+            if (seg) gc_register_datasegment((uintptr_t)seg, seg_size);
+            // __OBJC contains no GC data, but pointers to it are 
+            // used as associated reference values (rdar://6953570)
+        }
+    }
+
+    // Need to fixup barriers in all libraries that call into libobjc, whether GC is on or not.
+    for (i = 0; i < infoCount; ++i) {
+        gc_fixup_barrier_stubs(&infoList[i]);
+    }
+#endif
+
+    if (firstTime) {
+        extern SEL FwdSel;  // in objc-msg-*.s
+        sel_init(wantsGC, selrefCount);
+        FwdSel = sel_registerName("forward::");
+
+        arr_init();
+    }
+
+    _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
+**********************************************************************/
+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.
+**********************************************************************/
+void 
+unmap_image_nolock(const struct mach_header *mh)
+{
+    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", 
+                     hi->fname, 
+                     hi->mhdr->filetype == MH_BUNDLE ? " (bundle)" : "", 
+                     _objcHeaderIsReplacement(hi) ? " (replacement)" : "", 
+                     _gcForHInfo2(hi));
+    }
+
+#if SUPPORT_GC
+    if (UseGC) {
+        uint8_t *seg;
+        unsigned long seg_size;
+
+        seg = getsegmentdata(hi->mhdr, "__DATA", &seg_size);
+        if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size);
+
+        seg = getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
+        if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size);
+    }
+#endif
+
+    _unload_image(hi);
+
+    // Remove header_info from header list
+    removeHeader(hi);
+    _free_internal(hi);
+}
+
+
+/***********************************************************************
+* _objc_init
+* Bootstrap initialization. Registers our image notifier with dyld.
+* Old ABI: called by dyld as a library initializer
+* New ABI: called by libSystem BEFORE library initialization time
+**********************************************************************/
+#if !__OBJC2__
+static __attribute__((constructor))
+#endif
+void _objc_init(void)
+{
+    static bool initialized = false;
+    if (initialized) return;
+    initialized = true;
+    
+    // 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)
+{
+#if __OBJC2__
+    const char *segname = "__DATA";
+#else
+    const char *segname = "__OBJC";
+#endif
+    header_info *hi;
+
+    // Check all headers in the vector
+    for (hi = FirstHeader; hi != NULL; hi = hi->next)
+    {
+        uint8_t *seg;
+        unsigned long seg_size;
+
+        seg = getsegmentdata(hi->mhdr, segname, &seg_size);
+        if (!seg) continue;
+
+        // Is the class in this header?
+        if ((uint8_t *)addr >= seg  &&  (uint8_t *)addr < seg + 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.
+**********************************************************************/
+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.
+**********************************************************************/
+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.
+**********************************************************************/
+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;
+}
+
+
+bool crashlog_header_name(header_info *hi)
+{
+    return crashlog_header_name_string(hi ? hi->fname : NULL);
+}
+
+bool crashlog_header_name_string(const char *name)
+{
+    CRSetCrashLogMessage2(name);
+    return true;
+}
+
+
+#if TARGET_OS_IPHONE
+
+const char *__crashreporter_info__ = NULL;
+
+const char *CRSetCrashLogMessage(const char *msg)
+{
+    __crashreporter_info__ = msg;
+    return msg;
+}
+const char *CRGetCrashLogMessage(void)
+{
+    return __crashreporter_info__;
+}
+
+const char *CRSetCrashLogMessage2(const char *msg)
+{
+    // sorry
+    return msg;
+}
+
+#endif
+
+// TARGET_OS_MAC
+#else
+
+
+#error unknown OS
+
+
+#endif