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