1/* 2 * Copyright (c) 1999-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* objc-runtime.m 25* Copyright 1988-1996, NeXT Software, Inc. 26* Author: s. naroff 27* 28**********************************************************************/ 29 30 31 32/*********************************************************************** 33* Imports. 34**********************************************************************/ 35 36#include "objc-private.h" 37#include "objc-loadmethod.h" 38#include "message.h" 39 40OBJC_EXPORT Class getOriginalClassForPosingClass(Class); 41 42 43/*********************************************************************** 44* Exports. 45**********************************************************************/ 46 47// Settings from environment variables 48#define OPTION(var, env, help) bool var = false; 49#include "objc-env.h" 50#undef OPTION 51 52struct option_t { 53 bool* var; 54 const char *env; 55 const char *help; 56 size_t envlen; 57}; 58 59const option_t Settings[] = { 60#define OPTION(var, env, help) option_t{&var, #env, help, strlen(#env)}, 61#include "objc-env.h" 62#undef OPTION 63}; 64 65 66// objc's key for pthread_getspecific 67static tls_key_t _objc_pthread_key; 68 69// Selectors 70SEL SEL_load = NULL; 71SEL SEL_initialize = NULL; 72SEL SEL_resolveInstanceMethod = NULL; 73SEL SEL_resolveClassMethod = NULL; 74SEL SEL_cxx_construct = NULL; 75SEL SEL_cxx_destruct = NULL; 76SEL SEL_retain = NULL; 77SEL SEL_release = NULL; 78SEL SEL_autorelease = NULL; 79SEL SEL_retainCount = NULL; 80SEL SEL_alloc = NULL; 81SEL SEL_allocWithZone = NULL; 82SEL SEL_copy = NULL; 83SEL SEL_new = NULL; 84SEL SEL_finalize = NULL; 85SEL SEL_forwardInvocation = NULL; 86 87header_info *FirstHeader = 0; // NULL means empty list 88header_info *LastHeader = 0; // NULL means invalid; recompute it 89int HeaderCount = 0; 90 91 92 93/*********************************************************************** 94* objc_getClass. Return the id of the named class. If the class does 95* not exist, call _objc_classLoader and then objc_classHandler, either of 96* which may create a new class. 97* Warning: doesn't work if aClassName is the name of a posed-for class's isa! 98**********************************************************************/ 99Class objc_getClass(const char *aClassName) 100{ 101 if (!aClassName) return Nil; 102 103 // NO unconnected, YES class handler 104 return look_up_class(aClassName, NO, YES); 105} 106 107 108/*********************************************************************** 109* objc_getRequiredClass. 110* Same as objc_getClass, but kills the process if the class is not found. 111* This is used by ZeroLink, where failing to find a class would be a 112* compile-time link error without ZeroLink. 113**********************************************************************/ 114Class objc_getRequiredClass(const char *aClassName) 115{ 116 Class cls = objc_getClass(aClassName); 117 if (!cls) _objc_fatal("link error: class '%s' not found.", aClassName); 118 return cls; 119} 120 121 122/*********************************************************************** 123* objc_lookUpClass. Return the id of the named class. 124* If the class does not exist, call _objc_classLoader, which may create 125* a new class. 126* 127* Formerly objc_getClassWithoutWarning () 128**********************************************************************/ 129Class objc_lookUpClass(const char *aClassName) 130{ 131 if (!aClassName) return Nil; 132 133 // NO unconnected, NO class handler 134 return look_up_class(aClassName, NO, NO); 135} 136 137/*********************************************************************** 138* objc_getFutureClass. Return the id of the named class. 139* If the class does not exist, return an uninitialized class 140* structure that will be used for the class when and if it 141* does get loaded. 142* Not thread safe. 143**********************************************************************/ 144Class objc_getFutureClass(const char *name) 145{ 146 Class cls; 147 148 // YES unconnected, NO class handler 149 // (unconnected is OK because it will someday be the real class) 150 cls = look_up_class(name, YES, NO); 151 if (cls) { 152 if (PrintFuture) { 153 _objc_inform("FUTURE: found %p already in use for %s", 154 (void*)cls, name); 155 } 156 return cls; 157 } 158 159 // No class or future class with that name yet. Make one. 160 // fixme not thread-safe with respect to 161 // simultaneous library load or getFutureClass. 162 return _objc_allocateFutureClass(name); 163} 164 165 166/*********************************************************************** 167* objc_getMetaClass. Return the id of the meta class the named class. 168* Warning: doesn't work if aClassName is the name of a posed-for class's isa! 169**********************************************************************/ 170Class objc_getMetaClass(const char *aClassName) 171{ 172 Class cls; 173 174 if (!aClassName) return Nil; 175 176 cls = objc_getClass (aClassName); 177 if (!cls) 178 { 179 _objc_inform ("class `%s' not linked into application", aClassName); 180 return Nil; 181 } 182 183 return cls->ISA(); 184} 185 186 187/*********************************************************************** 188* appendHeader. Add a newly-constructed header_info to the list. 189**********************************************************************/ 190void appendHeader(header_info *hi) 191{ 192 // Add the header to the header list. 193 // The header is appended to the list, to preserve the bottom-up order. 194 HeaderCount++; 195 hi->next = NULL; 196 if (!FirstHeader) { 197 // list is empty 198 FirstHeader = LastHeader = hi; 199 } else { 200 if (!LastHeader) { 201 // list is not empty, but LastHeader is invalid - recompute it 202 LastHeader = FirstHeader; 203 while (LastHeader->next) LastHeader = LastHeader->next; 204 } 205 // LastHeader is now valid 206 LastHeader->next = hi; 207 LastHeader = hi; 208 } 209} 210 211 212/*********************************************************************** 213* removeHeader 214* Remove the given header from the header list. 215* FirstHeader is updated. 216* LastHeader is set to NULL. Any code that uses LastHeader must 217* detect this NULL and recompute LastHeader by traversing the list. 218**********************************************************************/ 219void removeHeader(header_info *hi) 220{ 221 header_info **hiP; 222 223 for (hiP = &FirstHeader; *hiP != NULL; hiP = &(**hiP).next) { 224 if (*hiP == hi) { 225 header_info *deadHead = *hiP; 226 227 // Remove from the linked list (updating FirstHeader if necessary). 228 *hiP = (**hiP).next; 229 230 // Update LastHeader if necessary. 231 if (LastHeader == deadHead) { 232 LastHeader = NULL; // will be recomputed next time it's used 233 } 234 235 HeaderCount--; 236 break; 237 } 238 } 239} 240 241 242/*********************************************************************** 243* environ_init 244* Read environment variables that affect the runtime. 245* Also print environment variable help, if requested. 246**********************************************************************/ 247void environ_init(void) 248{ 249 if (issetugid()) { 250 // All environment variables are silently ignored when setuid or setgid 251 // This includes OBJC_HELP and OBJC_PRINT_OPTIONS themselves. 252 return; 253 } 254 255 bool PrintHelp = false; 256 bool PrintOptions = false; 257 258 // Scan environ[] directly instead of calling getenv() a lot. 259 // This optimizes the case where none are set. 260 for (char **p = *_NSGetEnviron(); *p != nil; p++) { 261 if (0 != strncmp(*p, "OBJC_", 5)) continue; 262 263 if (0 == strncmp(*p, "OBJC_HELP=", 10)) { 264 PrintHelp = true; 265 continue; 266 } 267 if (0 == strncmp(*p, "OBJC_PRINT_OPTIONS=", 19)) { 268 PrintOptions = true; 269 continue; 270 } 271 272 const char *value = strchr(*p, '='); 273 if (!*value) continue; 274 value++; 275 276 for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) { 277 const option_t *opt = &Settings[i]; 278 if ((size_t)(value - *p) == 1+opt->envlen && 279 0 == strncmp(*p, opt->env, opt->envlen)) 280 { 281 *opt->var = (0 == strcmp(value, "YES")); 282 break; 283 } 284 } 285 } 286 287 // Print OBJC_HELP and OBJC_PRINT_OPTIONS output. 288 if (PrintHelp || PrintOptions) { 289 if (PrintHelp) { 290 _objc_inform("Objective-C runtime debugging. Set variable=YES to enable."); 291 _objc_inform("OBJC_HELP: describe available environment variables"); 292 if (PrintOptions) { 293 _objc_inform("OBJC_HELP is set"); 294 } 295 _objc_inform("OBJC_PRINT_OPTIONS: list which options are set"); 296 } 297 if (PrintOptions) { 298 _objc_inform("OBJC_PRINT_OPTIONS is set"); 299 } 300 301 for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) { 302 const option_t *opt = &Settings[i]; 303 if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help); 304 if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env); 305 } 306 } 307} 308 309 310/*********************************************************************** 311* logReplacedMethod 312* OBJC_PRINT_REPLACED_METHODS implementation 313**********************************************************************/ 314void 315logReplacedMethod(const char *className, SEL s, 316 BOOL isMeta, const char *catName, 317 IMP oldImp, IMP newImp) 318{ 319 const char *oldImage = "??"; 320 const char *newImage = "??"; 321 322 // Silently ignore +load replacement because category +load is special 323 if (s == SEL_load) return; 324 325#if TARGET_OS_WIN32 326 // don't know dladdr()/dli_fname equivalent 327#else 328 Dl_info dl; 329 330 if (dladdr((void*)oldImp, &dl) && dl.dli_fname) oldImage = dl.dli_fname; 331 if (dladdr((void*)newImp, &dl) && dl.dli_fname) newImage = dl.dli_fname; 332#endif 333 334 _objc_inform("REPLACED: %c[%s %s] %s%s (IMP was %p (%s), now %p (%s))", 335 isMeta ? '+' : '-', className, sel_getName(s), 336 catName ? "by category " : "", catName ? catName : "", 337 oldImp, oldImage, newImp, newImage); 338} 339 340 341 342/*********************************************************************** 343* objc_setMultithreaded. 344**********************************************************************/ 345void objc_setMultithreaded (BOOL flag) 346{ 347 OBJC_WARN_DEPRECATED; 348 349 // Nothing here. Thread synchronization in the runtime is always active. 350} 351 352 353/*********************************************************************** 354* _objc_fetch_pthread_data 355* Fetch objc's pthread data for this thread. 356* If the data doesn't exist yet and create is NO, return NULL. 357* If the data doesn't exist yet and create is YES, allocate and return it. 358**********************************************************************/ 359_objc_pthread_data *_objc_fetch_pthread_data(BOOL create) 360{ 361 _objc_pthread_data *data; 362 363 data = (_objc_pthread_data *)tls_get(_objc_pthread_key); 364 if (!data && create) { 365 data = (_objc_pthread_data *) 366 _calloc_internal(1, sizeof(_objc_pthread_data)); 367 tls_set(_objc_pthread_key, data); 368 } 369 370 return data; 371} 372 373 374/*********************************************************************** 375* _objc_pthread_destroyspecific 376* Destructor for objc's per-thread data. 377* arg shouldn't be NULL, but we check anyway. 378**********************************************************************/ 379extern void _destroyInitializingClassList(struct _objc_initializing_classes *list); 380void _objc_pthread_destroyspecific(void *arg) 381{ 382 _objc_pthread_data *data = (_objc_pthread_data *)arg; 383 if (data != NULL) { 384 _destroyInitializingClassList(data->initializingClasses); 385 _destroySyncCache(data->syncCache); 386 _destroyAltHandlerList(data->handlerList); 387 388 // add further cleanup here... 389 390 _free_internal(data); 391 } 392} 393 394 395void tls_init(void) 396{ 397#if SUPPORT_DIRECT_THREAD_KEYS 398 _objc_pthread_key = TLS_DIRECT_KEY; 399 pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific); 400#else 401 _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific); 402#endif 403} 404 405 406/*********************************************************************** 407* _objcInit 408* Former library initializer. This function is now merely a placeholder 409* for external callers. All runtime initialization has now been moved 410* to map_images() and _objc_init. 411**********************************************************************/ 412void _objcInit(void) 413{ 414 // do nothing 415} 416 417 418#if !(TARGET_OS_WIN32 || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE) 419/*********************************************************************** 420* _objc_setNilReceiver 421**********************************************************************/ 422id _objc_setNilReceiver(id newNilReceiver) 423{ 424 id oldNilReceiver; 425 426 oldNilReceiver = _objc_nilReceiver; 427 _objc_nilReceiver = newNilReceiver; 428 429 return oldNilReceiver; 430} 431 432/*********************************************************************** 433* _objc_getNilReceiver 434**********************************************************************/ 435id _objc_getNilReceiver(void) 436{ 437 return _objc_nilReceiver; 438} 439#endif 440 441 442/*********************************************************************** 443* objc_setForwardHandler 444**********************************************************************/ 445void objc_setForwardHandler(void *fwd, void *fwd_stret) 446{ 447 _objc_forward_handler = fwd; 448 _objc_forward_stret_handler = fwd_stret; 449} 450 451 452#if !__OBJC2__ 453// GrP fixme 454extern "C" Class _objc_getOrigClass(const char *name); 455#endif 456const char *class_getImageName(Class cls) 457{ 458#if TARGET_OS_WIN32 459 TCHAR *szFileName; 460 DWORD charactersCopied; 461 Class origCls; 462 HMODULE classModule; 463 BOOL res; 464#endif 465 if (!cls) return NULL; 466 467#if !__OBJC2__ 468 cls = _objc_getOrigClass(cls->getName()); 469#endif 470#if TARGET_OS_WIN32 471 charactersCopied = 0; 472 szFileName = malloc(MAX_PATH * sizeof(TCHAR)); 473 474 origCls = objc_getOrigClass(cls->getName()); 475 classModule = NULL; 476 res = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)origCls, &classModule); 477 if (res && classModule) { 478 charactersCopied = GetModuleFileName(classModule, szFileName, MAX_PATH * sizeof(TCHAR)); 479 } 480 if (classModule) FreeLibrary(classModule); 481 if (charactersCopied) { 482 return (const char *)szFileName; 483 } else { 484 free(szFileName); 485 } 486 return NULL; 487#else 488 return dyld_image_path_containing_address(cls); 489#endif 490} 491 492 493const char **objc_copyImageNames(unsigned int *outCount) 494{ 495 header_info *hi; 496 int count = 0; 497 int max = HeaderCount; 498#if TARGET_OS_WIN32 499 const TCHAR **names = (const TCHAR **)calloc(max+1, sizeof(TCHAR *)); 500#else 501 const char **names = (const char **)calloc(max+1, sizeof(char *)); 502#endif 503 504 for (hi = FirstHeader; hi != NULL && count < max; hi = hi->next) { 505#if TARGET_OS_WIN32 506 if (hi->moduleName) { 507 names[count++] = hi->moduleName; 508 } 509#else 510 if (hi->fname) { 511 names[count++] = hi->fname; 512 } 513#endif 514 } 515 names[count] = NULL; 516 517 if (count == 0) { 518 // Return NULL instead of empty list if there are no images 519 free((void *)names); 520 names = NULL; 521 } 522 523 if (outCount) *outCount = count; 524 return names; 525} 526 527 528/********************************************************************** 529* 530**********************************************************************/ 531const char ** 532objc_copyClassNamesForImage(const char *image, unsigned int *outCount) 533{ 534 header_info *hi; 535 536 if (!image) { 537 if (outCount) *outCount = 0; 538 return NULL; 539 } 540 541 // Find the image. 542 for (hi = FirstHeader; hi != NULL; hi = hi->next) { 543#if TARGET_OS_WIN32 544 if (0 == wcscmp((TCHAR *)image, hi->moduleName)) break; 545#else 546 if (0 == strcmp(image, hi->fname)) break; 547#endif 548 } 549 550 if (!hi) { 551 if (outCount) *outCount = 0; 552 return NULL; 553 } 554 555 return _objc_copyClassNamesForImage(hi, outCount); 556} 557 558 559/********************************************************************** 560* Fast Enumeration Support 561**********************************************************************/ 562 563static void (*enumerationMutationHandler)(id); 564 565/********************************************************************** 566* objc_enumerationMutation 567* called by compiler when a mutation is detected during foreach iteration 568**********************************************************************/ 569void objc_enumerationMutation(id object) { 570 if (enumerationMutationHandler == nil) { 571 _objc_fatal("mutation detected during 'for(... in ...)' enumeration of object %p.", (void*)object); 572 } 573 (*enumerationMutationHandler)(object); 574} 575 576 577/********************************************************************** 578* objc_setEnumerationMutationHandler 579* an entry point to customize mutation error handing 580**********************************************************************/ 581void objc_setEnumerationMutationHandler(void (*handler)(id)) { 582 enumerationMutationHandler = handler; 583} 584 585 586/********************************************************************** 587* Associative Reference Support 588**********************************************************************/ 589 590id objc_getAssociatedObject_non_gc(id object, const void *key) { 591 return _object_get_associative_reference(object, (void *)key); 592} 593 594 595void objc_setAssociatedObject_non_gc(id object, const void *key, id value, objc_AssociationPolicy policy) { 596 _object_set_associative_reference(object, (void *)key, value, policy); 597} 598 599 600#if SUPPORT_GC 601 602id objc_getAssociatedObject_gc(id object, const void *key) { 603 return (id)auto_zone_get_associative_ref(gc_zone, object, (void *)key); 604} 605 606void objc_setAssociatedObject_gc(id object, const void *key, id value, objc_AssociationPolicy policy) { 607 if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) { 608 value = ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy); 609 } 610 auto_zone_set_associative_ref(gc_zone, object, (void *)key, value); 611} 612 613// objc_setAssociatedObject and objc_getAssociatedObject are 614// resolver functions in objc-auto.mm. 615 616#else 617 618id 619objc_getAssociatedObject(id object, const void *key) 620{ 621 return objc_getAssociatedObject_non_gc(object, key); 622} 623 624void 625objc_setAssociatedObject(id object, const void *key, id value, 626 objc_AssociationPolicy policy) 627{ 628 objc_setAssociatedObject_non_gc(object, key, value, policy); 629} 630 631#endif 632 633 634void objc_removeAssociatedObjects(id object) 635{ 636#if SUPPORT_GC 637 if (UseGC) { 638 auto_zone_erase_associative_refs(gc_zone, object); 639 } else 640#endif 641 { 642 if (object && object->getIsa()->instancesHaveAssociatedObjects()) { 643 _object_remove_assocations(object); 644 } 645 } 646} 647 648