#include #include #include "objc/runtime.h" #include "objc/objc-auto.h" #include "objc/objc-arc.h" #include "lock.h" #include "loader.h" #include "visibility.h" #include "legacy.h" #ifdef ENABLE_GC #include #endif #include #include /** * Runtime lock. This is exposed in */ PRIVATE mutex_t runtime_mutex; LEGACY void *__objc_runtime_mutex = &runtime_mutex; void init_alias_table(void); void init_arc(void); void init_class_tables(void); void init_dispatch_tables(void); void init_gc(void); void init_protocol_table(void); void init_selector_tables(void); void init_trampolines(void); void objc_send_load_message(Class class); void log_selector_memory_usage(void); static void log_memory_stats(void) { log_selector_memory_usage(); } /* Number of threads that are alive. */ int __objc_runtime_threads_alive = 1; /* !T:MUTEX */ // libdispatch hooks for registering threads __attribute__((weak)) void (*dispatch_begin_thread_4GC)(void); __attribute__((weak)) void (*dispatch_end_thread_4GC)(void); __attribute__((weak)) void *(*_dispatch_begin_NSAutoReleasePool)(void); __attribute__((weak)) void (*_dispatch_end_NSAutoReleasePool)(void *); __attribute__((used)) static void link_protos(void) { link_protocol_classes(); } static void init_runtime(void) { static BOOL first_run = YES; if (first_run) { #if ENABLE_GC init_gc(); #endif // Create the main runtime lock. This is not safe in theory, but in // practice the first time that this function is called will be in the // loader, from the main thread. Future loaders may run concurrently, // but that is likely to break the semantics of a lot of languages, so // we don't have to worry about it for a long time. // // The only case when this can potentially go badly wrong is when a // pure-C main() function spawns two threads which then, concurrently, // call dlopen() or equivalent, and the platform's implementation of // this does not perform any synchronization. INIT_LOCK(runtime_mutex); // Create the various tables that the runtime needs. init_selector_tables(); init_protocol_table(); init_class_tables(); init_dispatch_tables(); init_alias_table(); init_arc(); init_trampolines(); first_run = NO; if (getenv("LIBOBJC_MEMORY_PROFILE")) { atexit(log_memory_stats); } if (dispatch_begin_thread_4GC != 0) { dispatch_begin_thread_4GC = objc_registerThreadWithCollector; } if (dispatch_end_thread_4GC != 0) { dispatch_end_thread_4GC = objc_unregisterThreadWithCollector; } if (_dispatch_begin_NSAutoReleasePool != 0) { _dispatch_begin_NSAutoReleasePool = objc_autoreleasePoolPush; } if (_dispatch_end_NSAutoReleasePool != 0) { _dispatch_end_NSAutoReleasePool = objc_autoreleasePoolPop; } } } /** * Structure for a class alias. */ struct objc_alias { /** * The name by which this class is referenced. */ const char *alias_name; /** * A pointer to the indirection variable for this class. */ Class *alias; }; /** * Type of the NSConstantString structure. */ struct nsstr { /** Class pointer. */ id isa; /** * Flags. Low 2 bits store the encoding: * 0: ASCII * 1: UTF-8 * 2: UTF-16 * 3: UTF-32 * * Low 16 bits are reserved for the compiler, high 32 bits are reserved for * the Foundation framework. */ uint32_t flags; /** * Number of UTF-16 code units in the string. */ uint32_t length; /** * Number of bytes in the string. */ uint32_t size; /** * Hash (Foundation framework defines the hash algorithm). */ uint32_t hash; /** * Character data. */ const char *data; }; // begin: objc_init struct objc_init { uint64_t version; SEL sel_begin; SEL sel_end; Class *cls_begin; Class *cls_end; Class *cls_ref_begin; Class *cls_ref_end; struct objc_category *cat_begin; struct objc_category *cat_end; struct objc_protocol *proto_begin; struct objc_protocol *proto_end; struct objc_protocol **proto_ref_begin; struct objc_protocol **proto_ref_end; struct objc_alias *alias_begin; struct objc_alias *alias_end; struct nsstr *strings_begin; struct nsstr *strings_end; }; // end: objc_init #ifdef DEBUG_LOADING #include #endif static enum { LegacyABI, NewABI, UnknownABI } CurrentABI = UnknownABI; void registerProtocol(Protocol *proto); OBJC_PUBLIC void __objc_load(struct objc_init *init) { init_runtime(); #ifdef DEBUG_LOADING Dl_info info; if (dladdr(init, &info)) { fprintf(stderr, "Loading %p from object: %s (%p)\n", init, info.dli_fname, __builtin_return_address(0)); } else { fprintf(stderr, "Loading %p from unknown object\n", init); } #endif LOCK_RUNTIME_FOR_SCOPE(); BOOL isFirstLoad = NO; switch (CurrentABI) { case LegacyABI: fprintf(stderr, "Version 2 Objective-C ABI may not be mixed with earlier versions.\n"); abort(); case UnknownABI: isFirstLoad = YES; CurrentABI = NewABI; break; case NewABI: break; } // If we've already loaded this module, don't load it again. if (init->version == ULONG_MAX) { return; } assert(init->version == 0); assert((((uintptr_t)init->sel_end-(uintptr_t)init->sel_begin) % sizeof(*init->sel_begin)) == 0); assert((((uintptr_t)init->cls_end-(uintptr_t)init->cls_begin) % sizeof(*init->cls_begin)) == 0); assert((((uintptr_t)init->cat_end-(uintptr_t)init->cat_begin) % sizeof(*init->cat_begin)) == 0); for (SEL sel = init->sel_begin ; sel < init->sel_end ; sel++) { if (sel->name == 0) { continue; } objc_register_selector(sel); } for (struct objc_protocol *proto = init->proto_begin ; proto < init->proto_end ; proto++) { if (proto->name == NULL) { continue; } registerProtocol((struct objc_protocol*)proto); } for (struct objc_protocol **proto = init->proto_ref_begin ; proto < init->proto_ref_end ; proto++) { if (*proto == NULL) { continue; } struct objc_protocol *p = objc_getProtocol((*proto)->name); assert(p); *proto = p; } for (Class *cls = init->cls_begin ; cls < init->cls_end ; cls++) { if (*cls == NULL) { continue; } // As a special case, allow using legacy ABI code with a new runtime. if (isFirstLoad && (strcmp((*cls)->name, "Protocol") == 0)) { CurrentABI = UnknownABI; } #ifdef DEBUG_LOADING fprintf(stderr, "Loading class %s\n", (*cls)->name); #endif objc_load_class(*cls); } #if 0 // We currently don't do anything with these pointers. They exist to // provide a level of indirection that will permit us to completely change // the `objc_class` struct without breaking the ABI (again) for (Class *cls = init->cls_ref_begin ; cls < init->cls_ref_end ; cls++) { } #endif for (struct objc_category *cat = init->cat_begin ; cat < init->cat_end ; cat++) { if ((cat == NULL) || (cat->class_name == NULL)) { continue; } objc_try_load_category(cat); #ifdef DEBUG_LOADING fprintf(stderr, "Loading category %s (%s)\n", cat->class_name, cat->name); #endif } // Load categories and statics that were deferred. objc_load_buffered_categories(); // Fix up the class links for loaded classes. objc_resolve_class_links(); for (struct objc_category *cat = init->cat_begin ; cat < init->cat_end ; cat++) { Class class = (Class)objc_getClass(cat->class_name); if ((Nil != class) && objc_test_class_flag(class, objc_class_flag_resolved)) { objc_send_load_message(class); } } // Register aliases for (struct objc_alias *alias = init->alias_begin ; alias < init->alias_end ; alias++) { if (alias->alias_name) { class_registerAlias_np(*alias->alias, alias->alias_name); } } #if 0 // If future versions of the ABI need to do anything with constant strings, // they may do so here. for (struct nsstr *string = init->strings_begin ; string < init->strings_end ; string++) { if (string->isa) { } } #endif init->version = ULONG_MAX; } #ifdef OLDABI_COMPAT OBJC_PUBLIC void __objc_exec_class(struct objc_module_abi_8 *module) { init_runtime(); switch (CurrentABI) { case UnknownABI: CurrentABI = LegacyABI; break; case LegacyABI: break; case NewABI: fprintf(stderr, "Version 2 Objective-C ABI may not be mixed with earlier versions.\n"); abort(); } // Check that this module uses an ABI version that we recognise. // In future, we should pass the ABI version to the class / category load // functions so that we can change various structures more easily. assert(objc_check_abi_version(module)); // The runtime mutex is held for the entire duration of a load. It does // not need to be acquired or released in any of the called load functions. LOCK_RUNTIME_FOR_SCOPE(); struct objc_symbol_table_abi_8 *symbols = module->symbol_table; // Register all of the selectors used in this module. if (symbols->selectors) { objc_register_selector_array(symbols->selectors, symbols->selector_count); } unsigned short defs = 0; // Load the classes from this module for (unsigned short i=0 ; iclass_count ; i++) { objc_load_class(objc_upgrade_class(symbols->definitions[defs++])); } unsigned int category_start = defs; // Load the categories from this module for (unsigned short i=0 ; icategory_count; i++) { objc_try_load_category(objc_upgrade_category(symbols->definitions[defs++])); } // Load the static instances struct objc_static_instance_list **statics = (void*)symbols->definitions[defs]; while (NULL != statics && NULL != *statics) { objc_init_statics(*(statics++)); } // Load categories and statics that were deferred. objc_load_buffered_categories(); objc_init_buffered_statics(); // Fix up the class links for loaded classes. objc_resolve_class_links(); for (unsigned short i=0 ; icategory_count; i++) { struct objc_category *cat = (struct objc_category*) symbols->definitions[category_start++]; Class class = (Class)objc_getClass(cat->class_name); if ((Nil != class) && objc_test_class_flag(class, objc_class_flag_resolved)) { objc_send_load_message(class); } } } #endif