]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-runtime-old.m
objc4-371.tar.gz
[apple/objc4.git] / runtime / objc-runtime-old.m
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 /***********************************************************************
25 * objc-runtime-old.m
26 * Support for old-ABI classes and images.
27 **********************************************************************/
28
29 /***********************************************************************
30 * Class loading and connecting (GrP 2004-2-11)
31 *
32 * When images are loaded (during program startup or otherwise), the
33 * runtime needs to load classes and categories from the images, connect
34 * classes to superclasses and categories to parent classes, and call
35 * +load methods.
36 *
37 * The Objective-C runtime can cope with classes arriving in any order.
38 * That is, a class may be discovered by the runtime before some
39 * superclass is known. To handle out-of-order class loads, the
40 * runtime uses a "pending class" system.
41 *
42 * (Historical note)
43 * Panther and earlier: many classes arrived out-of-order because of
44 * the poorly-ordered callback from dyld. However, the runtime's
45 * pending mechanism only handled "missing superclass" and not
46 * "present superclass but missing higher class". See Radar #3225652.
47 * Tiger: The runtime's pending mechanism was augmented to handle
48 * arbitrary missing classes. In addition, dyld was rewritten and
49 * now sends the callbacks in strictly bottom-up link order.
50 * The pending mechanism may now be needed only for rare and
51 * hard to construct programs.
52 * (End historical note)
53 *
54 * A class when first seen in an image is considered "unconnected".
55 * It is stored in `unconnected_class_hash`. If all of the class's
56 * superclasses exist and are already "connected", then the new class
57 * can be connected to its superclasses and moved to `class_hash` for
58 * normal use. Otherwise, the class waits in `unconnected_class_hash`
59 * until the superclasses finish connecting.
60 *
61 * A "connected" class is
62 * (1) in `class_hash`,
63 * (2) connected to its superclasses,
64 * (3) has no unconnected superclasses,
65 * (4) is otherwise initialized and ready for use, and
66 * (5) is eligible for +load if +load has not already been called.
67 *
68 * An "unconnected" class is
69 * (1) in `unconnected_class_hash`,
70 * (2) not connected to its superclasses,
71 * (3) has an immediate superclass which is either missing or unconnected,
72 * (4) is not ready for use, and
73 * (5) is not yet eligible for +load.
74 *
75 * Image mapping is NOT CURRENTLY THREAD-SAFE with respect to just about
76 * * * anything. Image mapping IS RE-ENTRANT in several places: superclass
77 * lookup may cause ZeroLink to load another image, and +load calls may
78 * cause dyld to load another image.
79 *
80 * Image mapping sequence:
81 *
82 * Read all classes in all new images.
83 * Add them all to unconnected_class_hash.
84 * Note any +load implementations before categories are attached.
85 * Fix up any pended classrefs referring to them.
86 * Attach any pending categories.
87 * Read all categories in all new images.
88 * Attach categories whose parent class exists (connected or not),
89 * and pend the rest.
90 * Mark them all eligible for +load (if implemented), even if the
91 * parent class is missing.
92 * Try to connect all classes in all new images.
93 * If the superclass is missing, pend the class
94 * If the superclass is unconnected, try to recursively connect it
95 * If the superclass is connected:
96 * connect the class
97 * mark the class eligible for +load, if implemented
98 * connect any pended subclasses of the class
99 * Resolve selector refs and class refs in all new images.
100 * Class refs whose classes still do not exist are pended.
101 * Fix up protocol objects in all new images.
102 * Call +load for classes and categories.
103 * May include classes or categories that are not in these images,
104 * but are newly eligible because of these image.
105 * Class +loads will be called superclass-first because of the
106 * superclass-first nature of the connecting process.
107 * Category +load needs to be deferred until the parent class is
108 * connected and has had its +load called.
109 *
110 * Performance: all classes are read before any categories are read.
111 * Fewer categories need be pended for lack of a parent class.
112 *
113 * Performance: all categories are attempted to be attached before
114 * any classes are connected. Fewer class caches need be flushed.
115 * (Unconnected classes and their respective subclasses are guaranteed
116 * to be un-messageable, so their caches will be empty.)
117 *
118 * Performance: all classes are read before any classes are connected.
119 * Fewer classes need be pended for lack of a superclass.
120 *
121 * Correctness: all selector and class refs are fixed before any
122 * protocol fixups or +load methods. libobjc itself contains selector
123 * and class refs which are used in protocol fixup and +load.
124 *
125 * Correctness: +load methods are scheduled in bottom-up link order.
126 * This constraint is in addition to superclass order. Some +load
127 * implementations expect to use another class in a linked-to library,
128 * even if the two classes don't share a direct superclass relationship.
129 *
130 * Correctness: all classes are scanned for +load before any categories
131 * are attached. Otherwise, if a category implements +load and its class
132 * has no class methods, the class's +load scan would find the category's
133 * +load method, which would then be called twice.
134 *
135 **********************************************************************/
136
137 #if !__OBJC2__
138
139 #include <mach/mach.h>
140 #include <mach-o/ldsyms.h>
141 #include <mach-o/dyld.h>
142 #include <assert.h>
143
144 #define OLD 1
145 #import "objc-private.h"
146 #import "objc-loadmethod.h"
147 #import "hashtable2.h"
148 #import "maptable.h"
149
150 /* NXHashTable SPI */
151 extern unsigned _NXHashCapacity(NXHashTable *table);
152 extern void _NXHashRehashToCapacity(NXHashTable *table, unsigned newCapacity);
153
154
155 typedef struct _objc_unresolved_category
156 {
157 struct _objc_unresolved_category *next;
158 struct old_category *cat; // may be NULL
159 long version;
160 } _objc_unresolved_category;
161
162 typedef struct _PendingSubclass
163 {
164 struct old_class *subclass; // subclass to finish connecting; may be NULL
165 struct _PendingSubclass *next;
166 } PendingSubclass;
167
168 typedef struct _PendingClassRef
169 {
170 struct old_class **ref; // class reference to fix up; may be NULL
171 // (ref & 1) is a metaclass reference
172 struct _PendingClassRef *next;
173 } PendingClassRef;
174
175
176 static uintptr_t classHash(void *info, Class data);
177 static int classIsEqual(void *info, Class name, Class cls);
178 static int _objc_defaultClassHandler(const char *clsName);
179 static BOOL class_is_connected(struct old_class *cls);
180 static inline NXMapTable *pendingClassRefsMapTable(void);
181 static inline NXMapTable *pendingSubclassesMapTable(void);
182 static void pendClassInstallation(struct old_class *cls, const char *superName);
183 static void pendClassReference(struct old_class **ref, const char *className, BOOL isMeta);
184 static void resolve_references_to_class(struct old_class *cls);
185 static void resolve_subclasses_of_class(struct old_class *cls);
186 static void really_connect_class(struct old_class *cls, struct old_class *supercls);
187 static BOOL connect_class(struct old_class *cls);
188 static inline BOOL map_selrefs(SEL *src, SEL *dst, size_t size, BOOL copy);
189 static void map_method_descs (struct objc_method_description_list * methods, BOOL copy);
190 static void _objcTweakMethodListPointerForClass(struct old_class *cls);
191 static inline void _objc_add_category(struct old_class *cls, struct old_category *category, int version);
192 static BOOL _objc_add_category_flush_caches(struct old_class *cls, struct old_category *category, int version);
193 static _objc_unresolved_category *reverse_cat(_objc_unresolved_category *cat);
194 static void resolve_categories_for_class(struct old_class *cls);
195 static BOOL _objc_register_category(struct old_category *cat, int version);
196
197
198 // Function called when a class is loaded from an image
199 __private_extern__ void (*callbackFunction)(Class, const char *) = 0;
200
201 // Lock for class and protocol hashtables
202 // classLock > cacheUpdateLock
203 __private_extern__ OBJC_DECLARE_LOCK (classLock);
204
205 // Hash table of classes
206 __private_extern__ NXHashTable * class_hash NOBSS = 0;
207 static NXHashTablePrototype classHashPrototype =
208 {
209 (uintptr_t (*) (const void *, const void *)) classHash,
210 (int (*)(const void *, const void *, const void *)) classIsEqual,
211 NXNoEffectFree, 0
212 };
213
214 // Hash table of unconnected classes
215 static NXHashTable *unconnected_class_hash NOBSS = NULL;
216
217 // Exported copy of class_hash variable (hook for debugging tools)
218 NXHashTable *_objc_debug_class_hash = NULL;
219
220 // Category and class registries
221 // Keys are COPIES of strings, to prevent stale pointers with unloaded bundles
222 // Use NXMapKeyCopyingInsert and NXMapKeyFreeingRemove
223 static NXMapTable * category_hash = NULL;
224
225 // Keys are COPIES of strings, to prevent stale pointers with unloaded bundles
226 // Use NXMapKeyCopyingInsert and NXMapKeyFreeingRemove
227 static NXMapTable * pendingClassRefsMap = NULL;
228 static NXMapTable * pendingSubclassesMap = NULL;
229
230 // Protocols
231 static NXMapTable *protocol_map = NULL; // name -> protocol
232 static NXMapTable *protocol_ext_map = NULL; // protocol -> protocol ext
233
234 // Function pointer objc_getClass calls through when class is not found
235 static int (*objc_classHandler) (const char *) = _objc_defaultClassHandler;
236
237 // Function pointer called by objc_getClass and objc_lookupClass when
238 // class is not found. _objc_classLoader is called before objc_classHandler.
239 static BOOL (*_objc_classLoader)(const char *) = NULL;
240
241
242 /***********************************************************************
243 * inform_duplicate. Complain about duplicate class implementations.
244 **********************************************************************/
245 static void inform_duplicate(struct old_class *oldCls, struct old_class *cls)
246 {
247 const header_info *oldHeader = _headerForClass((Class)oldCls);
248 const header_info *newHeader = _headerForClass((Class)cls);
249 const char *oldName = _nameForHeader(oldHeader->mhdr);
250 const char *newName = _nameForHeader(newHeader->mhdr);
251
252 _objc_inform ("Class %s is implemented in both %s and %s. "
253 "Using implementation from %s.",
254 oldCls->name, oldName, newName, newName);
255 }
256
257
258 /***********************************************************************
259 * objc_dump_class_hash. Log names of all known classes.
260 **********************************************************************/
261 __private_extern__ void objc_dump_class_hash(void)
262 {
263 NXHashTable *table;
264 unsigned count;
265 Class data;
266 NXHashState state;
267
268 table = class_hash;
269 count = 0;
270 state = NXInitHashState (table);
271 while (NXNextHashState (table, &state, (void **) &data))
272 printf ("class %d: %s\n", ++count, _class_getName(data));
273 }
274
275
276 /***********************************************************************
277 * _objc_init_class_hash. Return the class lookup table, create it if
278 * necessary.
279 **********************************************************************/
280 __private_extern__ void _objc_init_class_hash(void)
281 {
282 // Do nothing if class hash table already exists
283 if (class_hash)
284 return;
285
286 // class_hash starts small, with only enough capacity for libobjc itself.
287 // If a second library is found by map_images(), class_hash is immediately
288 // resized to capacity 1024 to cut down on rehashes.
289 // Old numbers: A smallish Foundation+AppKit program will have
290 // about 520 classes. Larger apps (like IB or WOB) have more like
291 // 800 classes. Some customers have massive quantities of classes.
292 // Foundation-only programs aren't likely to notice the ~6K loss.
293 class_hash = NXCreateHashTableFromZone (classHashPrototype,
294 16,
295 nil,
296 _objc_internal_zone ());
297 _objc_debug_class_hash = class_hash;
298 }
299
300
301 /***********************************************************************
302 * objc_getClassList. Return the known classes.
303 **********************************************************************/
304 int objc_getClassList(Class *buffer, int bufferLen)
305 {
306 NXHashState state;
307 Class class;
308 int cnt, num;
309
310 OBJC_LOCK(&classLock);
311 num = NXCountHashTable(class_hash);
312 if (NULL == buffer) {
313 OBJC_UNLOCK(&classLock);
314 return num;
315 }
316 cnt = 0;
317 state = NXInitHashState(class_hash);
318 while (cnt < bufferLen &&
319 NXNextHashState(class_hash, &state, (void **)&class))
320 {
321 buffer[cnt++] = class;
322 }
323 OBJC_UNLOCK(&classLock);
324 return num;
325 }
326
327
328 /***********************************************************************
329 * objc_copyProtocolList
330 * Returns pointers to all protocols.
331 * Locking: acquires classLock
332 **********************************************************************/
333 Protocol **
334 objc_copyProtocolList(unsigned int *outCount)
335 {
336 OBJC_LOCK(&classLock);
337
338 int count, i;
339 Protocol *proto;
340 const char *name;
341 NXMapState state;
342 Protocol **result;
343
344 count = NXCountMapTable(protocol_map);
345 if (count == 0) {
346 OBJC_UNLOCK(&classLock);
347 if (outCount) *outCount = 0;
348 return NULL;
349 }
350
351 result = calloc(1 + count, sizeof(Protocol *));
352
353 i = 0;
354 state = NXInitMapState(protocol_map);
355 while (NXNextMapState(protocol_map, &state,
356 (const void **)&name, (const void **)&proto))
357 {
358 result[i++] = proto;
359 }
360
361 result[i++] = NULL;
362 assert(i == count+1);
363
364 OBJC_UNLOCK(&classLock);
365
366 if (outCount) *outCount = count;
367 return result;
368 }
369
370
371 /***********************************************************************
372 * objc_getClasses. Return class lookup table.
373 *
374 * NOTE: This function is very dangerous, since you cannot safely use
375 * the hashtable without locking it, and the lock is private!
376 **********************************************************************/
377 void *objc_getClasses(void)
378 {
379 OBJC_WARN_DEPRECATED;
380
381 // Return the class lookup hash table
382 return class_hash;
383 }
384
385
386 /***********************************************************************
387 * classHash.
388 **********************************************************************/
389 static uintptr_t classHash(void *info, Class data)
390 {
391 // Nil classes hash to zero
392 if (!data)
393 return 0;
394
395 // Call through to real hash function
396 return _objc_strhash (_class_getName(data));
397 }
398
399 /***********************************************************************
400 * classIsEqual. Returns whether the class names match. If we ever
401 * check more than the name, routines like objc_lookUpClass have to
402 * change as well.
403 **********************************************************************/
404 static int classIsEqual(void *info, Class name, Class cls)
405 {
406 // Standard string comparison
407 // Our local inlined version is significantly shorter on PPC and avoids the
408 // mflr/mtlr and dyld_stub overhead when calling strcmp.
409 return _objc_strcmp(_class_getName(name), _class_getName(cls)) == 0;
410 }
411
412
413 // Unresolved future classes
414 static NXHashTable *future_class_hash = NULL;
415
416 // Resolved future<->original classes
417 static NXMapTable *future_class_to_original_class_map = NULL;
418 static NXMapTable *original_class_to_future_class_map = NULL;
419
420 // CF requests about 20 future classes; HIToolbox requests one.
421 #define FUTURE_COUNT 32
422
423
424 /***********************************************************************
425 * setOriginalClassForFutureClass
426 * Record resolution of a future class.
427 **********************************************************************/
428 static void setOriginalClassForFutureClass(struct old_class *futureClass,
429 struct old_class *originalClass)
430 {
431 if (!future_class_to_original_class_map) {
432 future_class_to_original_class_map =
433 NXCreateMapTableFromZone (NXPtrValueMapPrototype, FUTURE_COUNT,
434 _objc_internal_zone ());
435 original_class_to_future_class_map =
436 NXCreateMapTableFromZone (NXPtrValueMapPrototype, FUTURE_COUNT,
437 _objc_internal_zone ());
438 }
439
440 NXMapInsert (future_class_to_original_class_map,
441 futureClass, originalClass);
442 NXMapInsert (original_class_to_future_class_map,
443 originalClass, futureClass);
444
445 if (PrintFuture) {
446 _objc_inform("FUTURE: using %p instead of %p for %s", futureClass, originalClass, originalClass->name);
447 }
448 }
449
450 /***********************************************************************
451 * getOriginalClassForFutureClass
452 * getFutureClassForOriginalClass
453 * Switch between a future class and its corresponding original class.
454 * The future class is the one actually in use.
455 * The original class is the one from disk.
456 **********************************************************************/
457 /*
458 static struct old_class *
459 getOriginalClassForFutureClass(struct old_class *futureClass)
460 {
461 if (!future_class_to_original_class_map) return Nil;
462 return NXMapGet (future_class_to_original_class_map, futureClass);
463 }
464 */
465 static struct old_class *
466 getFutureClassForOriginalClass(struct old_class *originalClass)
467 {
468 if (!original_class_to_future_class_map) return Nil;
469 return NXMapGet (original_class_to_future_class_map, originalClass);
470 }
471
472
473 /***********************************************************************
474 * makeFutureClass
475 * Initialize the memory in *cls with an unresolved future class with the
476 * given name. The memory is recorded in future_class_hash.
477 **********************************************************************/
478 static void makeFutureClass(struct old_class *cls, const char *name)
479 {
480 // CF requests about 20 future classes, plus HIToolbox has one.
481 if (!future_class_hash) {
482 future_class_hash =
483 NXCreateHashTableFromZone(classHashPrototype, FUTURE_COUNT,
484 NULL, _objc_internal_zone());
485 }
486
487 cls->name = _strdup_internal(name);
488 NXHashInsert(future_class_hash, cls);
489
490 if (PrintFuture) {
491 _objc_inform("FUTURE: reserving %p for %s", cls, name);
492 }
493 }
494
495
496 /***********************************************************************
497 * _objc_allocateFutureClass
498 * Allocate an unresolved future class for the given class name.
499 * Returns any existing allocation if one was already made.
500 * Assumes the named class doesn't exist yet.
501 * Not thread safe.
502 **********************************************************************/
503 __private_extern__ Class _objc_allocateFutureClass(const char *name)
504 {
505 struct old_class *cls;
506
507 if (future_class_hash) {
508 struct old_class query;
509 query.name = name;
510 if ((cls = NXHashGet(future_class_hash, &query))) {
511 // Already have a future class for this name.
512 return (Class)cls;
513 }
514 }
515
516 cls = _calloc_internal(sizeof(*cls), 1);
517 makeFutureClass(cls, name);
518 return (Class)cls;
519 }
520
521
522 /***********************************************************************
523 * objc_setFutureClass.
524 * Like objc_getFutureClass, but uses the provided memory block.
525 * If the class already exists, a posing-like substitution is performed.
526 * Not thread safe.
527 **********************************************************************/
528 void objc_setFutureClass(Class cls, const char *name)
529 {
530 struct old_class *oldcls;
531 struct old_class *newcls = (struct old_class *)cls; // Not a real class!
532
533 if ((oldcls = _class_asOld(look_up_class(name, NO/*unconnected*/, NO/*classhandler*/)))) {
534 setOriginalClassForFutureClass(newcls, oldcls);
535 // fixme hack
536 memcpy(newcls, oldcls, sizeof(struct objc_class));
537 newcls->info &= ~CLS_EXT;
538
539 OBJC_LOCK(&classLock);
540 NXHashRemove(class_hash, oldcls);
541 change_class_references(newcls, oldcls, nil, YES);
542 NXHashInsert(class_hash, newcls);
543 OBJC_UNLOCK(&classLock);
544 } else {
545 makeFutureClass(newcls, name);
546 }
547 }
548
549
550 /***********************************************************************
551 * _objc_defaultClassHandler. Default objc_classHandler. Does nothing.
552 **********************************************************************/
553 static int _objc_defaultClassHandler(const char *clsName)
554 {
555 // Return zero so objc_getClass doesn't bother re-searching
556 return 0;
557 }
558
559 /***********************************************************************
560 * objc_setClassHandler. Set objc_classHandler to the specified value.
561 *
562 * NOTE: This should probably deal with userSuppliedHandler being NULL,
563 * because the objc_classHandler caller does not check... it would bus
564 * error. It would make sense to handle NULL by restoring the default
565 * handler. Is anyone hacking with this, though?
566 **********************************************************************/
567 void objc_setClassHandler(int (*userSuppliedHandler)(const char *))
568 {
569 OBJC_WARN_DEPRECATED;
570
571 objc_classHandler = userSuppliedHandler;
572 }
573
574
575 /***********************************************************************
576 * _objc_setClassLoader
577 * Similar to objc_setClassHandler, but objc_classLoader is used for
578 * both objc_getClass() and objc_lookupClass(), and objc_classLoader
579 * pre-empts objc_classHandler.
580 **********************************************************************/
581 void _objc_setClassLoader(BOOL (*newClassLoader)(const char *))
582 {
583 _objc_classLoader = newClassLoader;
584 }
585
586
587 /***********************************************************************
588 * objc_getProtocol
589 * Get a protocol by name, or NULL.
590 **********************************************************************/
591 Protocol *objc_getProtocol(const char *name)
592 {
593 Protocol *result;
594 if (!protocol_map) return NULL;
595 OBJC_LOCK(&classLock);
596 result = (Protocol *)NXMapGet(protocol_map, name);
597 OBJC_UNLOCK(&classLock);
598 return result;
599 }
600
601
602 /***********************************************************************
603 * look_up_class
604 * Map a class name to a class using various methods.
605 * This is the common implementation of objc_lookUpClass and objc_getClass,
606 * and is also used internally to get additional search options.
607 * Sequence:
608 * 1. class_hash
609 * 2. unconnected_class_hash (optional)
610 * 3. classLoader callback
611 * 4. classHandler callback (optional)
612 **********************************************************************/
613 __private_extern__ id look_up_class(const char *aClassName, BOOL includeUnconnected, BOOL includeClassHandler)
614 {
615 BOOL includeClassLoader = YES; // class loader cannot be skipped
616 id result = nil;
617 struct old_class query;
618
619 query.name = aClassName;
620
621 retry:
622
623 if (!result && class_hash) {
624 // Check ordinary classes
625 OBJC_LOCK (&classLock);
626 result = (id)NXHashGet(class_hash, &query);
627 OBJC_UNLOCK (&classLock);
628 }
629
630 if (!result && includeUnconnected && unconnected_class_hash) {
631 // Check not-yet-connected classes
632 OBJC_LOCK(&classLock);
633 result = (id)NXHashGet(unconnected_class_hash, &query);
634 OBJC_UNLOCK(&classLock);
635 }
636
637 if (!result && includeClassLoader && _objc_classLoader) {
638 // Try class loader callback
639 if ((*_objc_classLoader)(aClassName)) {
640 // Re-try lookup without class loader
641 includeClassLoader = NO;
642 goto retry;
643 }
644 }
645
646 if (!result && includeClassHandler && objc_classHandler) {
647 // Try class handler callback
648 if ((*objc_classHandler)(aClassName)) {
649 // Re-try lookup without class handler or class loader
650 includeClassLoader = NO;
651 includeClassHandler = NO;
652 goto retry;
653 }
654 }
655
656 return result;
657 }
658
659
660 /***********************************************************************
661 * class_is_connected.
662 * Returns TRUE if class cls is connected.
663 * A connected class has either a connected superclass or a NULL superclass,
664 * and is present in class_hash.
665 **********************************************************************/
666 static BOOL class_is_connected(struct old_class *cls)
667 {
668 BOOL result;
669 OBJC_LOCK(&classLock);
670 result = NXHashMember(class_hash, cls);
671 OBJC_UNLOCK(&classLock);
672 return result;
673 }
674
675
676 /***********************************************************************
677 * _class_isLoadable.
678 * Returns TRUE if class cls is ready for its +load method to be called.
679 * A class is ready for +load if it is connected.
680 **********************************************************************/
681 __private_extern__ BOOL _class_isLoadable(Class cls)
682 {
683 return class_is_connected(_class_asOld(cls));
684 }
685
686
687 /***********************************************************************
688 * pendingClassRefsMapTable. Return a pointer to the lookup table for
689 * pending class refs.
690 **********************************************************************/
691 static inline NXMapTable *pendingClassRefsMapTable(void)
692 {
693 // Allocate table if needed
694 if (!pendingClassRefsMap) {
695 pendingClassRefsMap =
696 NXCreateMapTableFromZone(NXStrValueMapPrototype,
697 10, _objc_internal_zone ());
698 }
699
700 // Return table pointer
701 return pendingClassRefsMap;
702 }
703
704
705 /***********************************************************************
706 * pendingSubclassesMapTable. Return a pointer to the lookup table for
707 * pending subclasses.
708 **********************************************************************/
709 static inline NXMapTable *pendingSubclassesMapTable(void)
710 {
711 // Allocate table if needed
712 if (!pendingSubclassesMap) {
713 pendingSubclassesMap =
714 NXCreateMapTableFromZone(NXStrValueMapPrototype,
715 10, _objc_internal_zone ());
716 }
717
718 // Return table pointer
719 return pendingSubclassesMap;
720 }
721
722
723 /***********************************************************************
724 * pendClassInstallation
725 * Finish connecting class cls when its superclass becomes connected.
726 * Check for multiple pends of the same class because connect_class does not.
727 **********************************************************************/
728 static void pendClassInstallation(struct old_class *cls, const char *superName)
729 {
730 NXMapTable *table;
731 PendingSubclass *pending;
732 PendingSubclass *oldList;
733 PendingSubclass *l;
734
735 // Create and/or locate pending class lookup table
736 table = pendingSubclassesMapTable ();
737
738 // Make sure this class isn't already in the pending list.
739 oldList = NXMapGet (table, superName);
740 for (l = oldList; l != NULL; l = l->next) {
741 if (l->subclass == cls) return; // already here, nothing to do
742 }
743
744 // Create entry referring to this class
745 pending = _malloc_internal(sizeof(PendingSubclass));
746 pending->subclass = cls;
747
748 // Link new entry into head of list of entries for this class
749 pending->next = oldList;
750
751 // (Re)place entry list in the table
752 (void) NXMapKeyCopyingInsert (table, superName, pending);
753 }
754
755
756 /***********************************************************************
757 * pendClassReference
758 * Fix up a class ref when the class with the given name becomes connected.
759 **********************************************************************/
760 static void pendClassReference(struct old_class **ref, const char *className, BOOL isMeta)
761 {
762 NXMapTable *table;
763 PendingClassRef *pending;
764
765 // Create and/or locate pending class lookup table
766 table = pendingClassRefsMapTable ();
767
768 // Create entry containing the class reference
769 pending = _malloc_internal(sizeof(PendingClassRef));
770 pending->ref = ref;
771 if (isMeta) {
772 pending->ref = (struct old_class **)((uintptr_t)pending->ref | 1);
773 }
774
775 // Link new entry into head of list of entries for this class
776 pending->next = NXMapGet (table, className);
777
778 // (Re)place entry list in the table
779 (void) NXMapKeyCopyingInsert (table, className, pending);
780
781 if (PrintConnecting) {
782 _objc_inform("CONNECT: pended reference to class '%s%s' at %p",
783 className, isMeta ? " (meta)" : "", (void *)ref);
784 }
785 }
786
787
788 /***********************************************************************
789 * resolve_references_to_class
790 * Fix up any pending class refs to this class.
791 **********************************************************************/
792 static void resolve_references_to_class(struct old_class *cls)
793 {
794 PendingClassRef *pending;
795
796 if (!pendingClassRefsMap) return; // no unresolved refs for any class
797
798 pending = NXMapGet(pendingClassRefsMap, cls->name);
799 if (!pending) return; // no unresolved refs for this class
800
801 NXMapKeyFreeingRemove(pendingClassRefsMap, cls->name);
802
803 if (PrintConnecting) {
804 _objc_inform("CONNECT: resolving references to class '%s'", cls->name);
805 }
806
807 while (pending) {
808 PendingClassRef *next = pending->next;
809 if (pending->ref) {
810 BOOL isMeta = ((uintptr_t)pending->ref & 1) ? YES : NO;
811 struct old_class **ref =
812 (struct old_class **)((uintptr_t)pending->ref & ~(uintptr_t)1);
813 *ref = isMeta ? cls->isa : cls;
814 }
815 _free_internal(pending);
816 pending = next;
817 }
818
819 if (NXCountMapTable(pendingClassRefsMap) == 0) {
820 NXFreeMapTable(pendingClassRefsMap);
821 pendingClassRefsMap = NULL;
822 }
823 }
824
825
826 /***********************************************************************
827 * resolve_subclasses_of_class
828 * Fix up any pending subclasses of this class.
829 **********************************************************************/
830 static void resolve_subclasses_of_class(struct old_class *cls)
831 {
832 PendingSubclass *pending;
833
834 if (!pendingSubclassesMap) return; // no unresolved subclasses
835
836 pending = NXMapGet(pendingSubclassesMap, cls->name);
837 if (!pending) return; // no unresolved subclasses for this class
838
839 NXMapKeyFreeingRemove(pendingSubclassesMap, cls->name);
840
841 // Destroy the pending table if it's now empty, to save memory.
842 if (NXCountMapTable(pendingSubclassesMap) == 0) {
843 NXFreeMapTable(pendingSubclassesMap);
844 pendingSubclassesMap = NULL;
845 }
846
847 if (PrintConnecting) {
848 _objc_inform("CONNECT: resolving subclasses of class '%s'", cls->name);
849 }
850
851 while (pending) {
852 PendingSubclass *next = pending->next;
853 if (pending->subclass) connect_class(pending->subclass);
854 _free_internal(pending);
855 pending = next;
856 }
857 }
858
859
860 /***********************************************************************
861 * really_connect_class
862 * Connect cls to superclass supercls unconditionally.
863 * Also adjust the class hash tables and handle pended subclasses.
864 *
865 * This should be called from connect_class() ONLY.
866 **********************************************************************/
867 static void really_connect_class(struct old_class *cls,
868 struct old_class *supercls)
869 {
870 struct old_class *oldCls;
871
872 // Connect superclass pointers.
873 set_superclass(cls, supercls);
874
875 cls->info |= CLS_CONNECTED;
876
877 OBJC_LOCK(&classLock);
878
879 // Update hash tables.
880 NXHashRemove(unconnected_class_hash, cls);
881 oldCls = NXHashInsert(class_hash, cls);
882
883 // Delete unconnected_class_hash if it is now empty.
884 if (NXCountHashTable(unconnected_class_hash) == 0) {
885 NXFreeHashTable(unconnected_class_hash);
886 unconnected_class_hash = NULL;
887 }
888
889 OBJC_UNLOCK(&classLock);
890
891 // Warn if the new class has the same name as a previously-installed class.
892 // The new class is kept and the old class is discarded.
893 if (oldCls) {
894 inform_duplicate(oldCls, cls);
895 }
896
897 // Connect newly-connectable subclasses
898 resolve_subclasses_of_class(cls);
899
900 // GC debugging: make sure all classes with -dealloc also have -finalize
901 if (DebugFinalizers) {
902 extern IMP findIMPInClass(struct old_class *cls, SEL sel);
903 if (findIMPInClass(cls, sel_getUid("dealloc")) &&
904 ! findIMPInClass(cls, sel_getUid("finalize")))
905 {
906 _objc_inform("GC: class '%s' implements -dealloc but not -finalize", cls->name);
907 }
908 }
909
910 // Debugging: if this class has ivars, make sure this class's ivars don't
911 // overlap with its super's. This catches some broken fragile base classes.
912 // Do not use super->instance_size vs. self->ivar[0] to check this.
913 // Ivars may be packed across instance_size boundaries.
914 if (DebugFragileSuperclasses && cls->ivars && cls->ivars->ivar_count) {
915 struct old_class *ivar_cls = supercls;
916
917 // Find closest superclass that has some ivars, if one exists.
918 while (ivar_cls &&
919 (!ivar_cls->ivars || ivar_cls->ivars->ivar_count == 0))
920 {
921 ivar_cls = ivar_cls->super_class;
922 }
923
924 if (ivar_cls) {
925 // Compare superclass's last ivar to this class's first ivar
926 struct old_ivar *super_ivar =
927 &ivar_cls->ivars->ivar_list[ivar_cls->ivars->ivar_count - 1];
928 struct old_ivar *self_ivar =
929 &cls->ivars->ivar_list[0];
930
931 // fixme could be smarter about super's ivar size
932 if (self_ivar->ivar_offset <= super_ivar->ivar_offset) {
933 _objc_inform("WARNING: ivars of superclass '%s' and "
934 "subclass '%s' overlap; superclass may have "
935 "changed since subclass was compiled",
936 ivar_cls->name, cls->name);
937 }
938 }
939 }
940 }
941
942
943 /***********************************************************************
944 * connect_class
945 * Connect class cls to its superclasses, if possible.
946 * If cls becomes connected, move it from unconnected_class_hash
947 * to connected_class_hash.
948 * Returns TRUE if cls is connected.
949 * Returns FALSE if cls could not be connected for some reason
950 * (missing superclass or still-unconnected superclass)
951 **********************************************************************/
952 static BOOL connect_class(struct old_class *cls)
953 {
954 if (class_is_connected(cls)) {
955 // This class is already connected to its superclass.
956 // Do nothing.
957 return TRUE;
958 }
959 else if (cls->super_class == NULL) {
960 // This class is a root class.
961 // Connect it to itself.
962
963 if (PrintConnecting) {
964 _objc_inform("CONNECT: class '%s' now connected (root class)",
965 cls->name);
966 }
967
968 really_connect_class(cls, NULL);
969 return TRUE;
970 }
971 else {
972 // This class is not a root class and is not yet connected.
973 // Connect it if its superclass and root class are already connected.
974 // Otherwise, add this class to the to-be-connected list,
975 // pending the completion of its superclass and root class.
976
977 // At this point, cls->super_class and cls->isa->isa are still STRINGS
978 char *supercls_name = (char *)cls->super_class;
979 struct old_class *supercls;
980
981 // YES unconnected, YES class handler
982 if (NULL == (supercls = _class_asOld(look_up_class(supercls_name, YES, YES)))) {
983 // Superclass does not exist yet.
984 // pendClassInstallation will handle duplicate pends of this class
985 pendClassInstallation(cls, supercls_name);
986
987 if (PrintConnecting) {
988 _objc_inform("CONNECT: class '%s' NOT connected (missing super)", cls->name);
989 }
990 return FALSE;
991 }
992
993 if (! connect_class(supercls)) {
994 // Superclass exists but is not yet connected.
995 // pendClassInstallation will handle duplicate pends of this class
996 pendClassInstallation(cls, supercls_name);
997
998 if (PrintConnecting) {
999 _objc_inform("CONNECT: class '%s' NOT connected (unconnected super)", cls->name);
1000 }
1001 return FALSE;
1002 }
1003
1004 // Superclass exists and is connected.
1005 // Connect this class to the superclass.
1006
1007 if (PrintConnecting) {
1008 _objc_inform("CONNECT: class '%s' now connected", cls->name);
1009 }
1010
1011 really_connect_class(cls, supercls);
1012 return TRUE;
1013 }
1014 }
1015
1016
1017 /***********************************************************************
1018 * _objc_read_categories_from_image.
1019 * Read all categories from the given image.
1020 * Install them on their parent classes, or register them for later
1021 * installation.
1022 * Returns YES if some method caches now need to be flushed.
1023 **********************************************************************/
1024 static BOOL _objc_read_categories_from_image (header_info * hi)
1025 {
1026 Module mods;
1027 size_t midx;
1028 BOOL needFlush = NO;
1029
1030 if (_objcHeaderIsReplacement(hi)) {
1031 // Ignore any categories in this image
1032 return NO;
1033 }
1034
1035
1036 // Major loop - process all modules in the header
1037 mods = hi->mod_ptr;
1038
1039 // NOTE: The module and category lists are traversed backwards
1040 // to preserve the pre-10.4 processing order. Changing the order
1041 // would have a small chance of introducing binary compatibility bugs.
1042 midx = hi->mod_count;
1043 while (midx-- > 0) {
1044 unsigned int index;
1045 unsigned int total;
1046
1047 // Nothing to do for a module without a symbol table
1048 if (mods[midx].symtab == NULL)
1049 continue;
1050
1051 // Total entries in symbol table (class entries followed
1052 // by category entries)
1053 total = mods[midx].symtab->cls_def_cnt +
1054 mods[midx].symtab->cat_def_cnt;
1055
1056 // Minor loop - register all categories from given module
1057 index = total;
1058 while (index-- > mods[midx].symtab->cls_def_cnt) {
1059 struct old_category *cat = mods[midx].symtab->defs[index];
1060 needFlush |= _objc_register_category(cat, (int)mods[midx].version);
1061 }
1062 }
1063
1064 return needFlush;
1065 }
1066
1067
1068 /***********************************************************************
1069 * _objc_read_classes_from_image.
1070 * Read classes from the given image, perform assorted minor fixups,
1071 * scan for +load implementation.
1072 * Does not connect classes to superclasses.
1073 * Does attach pended categories to the classes.
1074 * Adds all classes to unconnected_class_hash. class_hash is unchanged.
1075 **********************************************************************/
1076 static void _objc_read_classes_from_image(header_info *hi)
1077 {
1078 unsigned int index;
1079 unsigned int midx;
1080 Module mods;
1081 int isBundle = (hi->mhdr->filetype == MH_BUNDLE);
1082
1083 if (_objcHeaderIsReplacement(hi)) {
1084 // Ignore any classes in this image
1085 return;
1086 }
1087
1088 // class_hash starts small, enough only for libobjc itself.
1089 // If other Objective-C libraries are found, immediately resize
1090 // class_hash, assuming that Foundation and AppKit are about
1091 // to add lots of classes.
1092 OBJC_LOCK(&classLock);
1093 if (hi->mhdr != (headerType *)&_mh_dylib_header && _NXHashCapacity(class_hash) < 1024) {
1094 _NXHashRehashToCapacity(class_hash, 1024);
1095 }
1096 OBJC_UNLOCK(&classLock);
1097
1098 // Major loop - process all modules in the image
1099 mods = hi->mod_ptr;
1100 for (midx = 0; midx < hi->mod_count; midx += 1)
1101 {
1102 // Skip module containing no classes
1103 if (mods[midx].symtab == NULL)
1104 continue;
1105
1106 // Minor loop - process all the classes in given module
1107 for (index = 0; index < mods[midx].symtab->cls_def_cnt; index += 1)
1108 {
1109 struct old_class *newCls, *oldCls;
1110
1111 // Locate the class description pointer
1112 newCls = mods[midx].symtab->defs[index];
1113
1114 // Classes loaded from Mach-O bundles can be unloaded later.
1115 // Nothing uses this class yet, so _class_setInfo is not needed.
1116 if (isBundle) newCls->info |= CLS_FROM_BUNDLE;
1117 if (isBundle) newCls->isa->info |= CLS_FROM_BUNDLE;
1118
1119 // Use common static empty cache instead of NULL
1120 if (newCls->cache == NULL)
1121 newCls->cache = (Cache) &_objc_empty_cache;
1122 if (newCls->isa->cache == NULL)
1123 newCls->isa->cache = (Cache) &_objc_empty_cache;
1124
1125 // Set metaclass version
1126 newCls->isa->version = mods[midx].version;
1127
1128 // methodLists is NULL or a single list, not an array
1129 newCls->info |= CLS_NO_METHOD_ARRAY|CLS_NO_PROPERTY_ARRAY;
1130 newCls->isa->info |= CLS_NO_METHOD_ARRAY|CLS_NO_PROPERTY_ARRAY;
1131
1132 // class has no subclasses for cache flushing
1133 newCls->info |= CLS_LEAF;
1134 newCls->isa->info |= CLS_LEAF;
1135
1136 if (mods[midx].version >= 6) {
1137 // class structure has ivar_layout and ext fields
1138 newCls->info |= CLS_EXT;
1139 newCls->isa->info |= CLS_EXT;
1140 }
1141
1142 // Check for +load implementation before categories are attached
1143 if (_class_hasLoadMethod((Class)newCls)) {
1144 newCls->isa->info |= CLS_HAS_LOAD_METHOD;
1145 }
1146
1147 // Install into unconnected_class_hash.
1148 OBJC_LOCK(&classLock);
1149
1150 if (future_class_hash) {
1151 struct old_class *futureCls =
1152 NXHashRemove(future_class_hash, newCls);
1153 if (futureCls) {
1154 // Another class structure for this class was already
1155 // prepared by objc_getFutureClass(). Use it instead.
1156 _free_internal((char *)futureCls->name);
1157 memcpy(futureCls, newCls, sizeof(*newCls));
1158 setOriginalClassForFutureClass(futureCls, newCls);
1159 newCls = futureCls;
1160
1161 if (NXCountHashTable(future_class_hash) == 0) {
1162 NXFreeHashTable(future_class_hash);
1163 future_class_hash = NULL;
1164 }
1165 }
1166 }
1167
1168 if (!unconnected_class_hash) {
1169 unconnected_class_hash =
1170 NXCreateHashTableFromZone(classHashPrototype, 128,
1171 NULL, _objc_internal_zone());
1172 }
1173
1174 oldCls = NXHashInsert(unconnected_class_hash, newCls);
1175 if (oldCls) {
1176 // Duplicate classes loaded.
1177 // newCls has been inserted over oldCls,
1178 // same as really_connect_class
1179 inform_duplicate(oldCls, newCls);
1180 }
1181
1182 OBJC_UNLOCK(&classLock);
1183
1184 // Fix up pended class refs to this class, if any
1185 resolve_references_to_class(newCls);
1186
1187 // Attach pended categories for this class, if any
1188 resolve_categories_for_class(newCls);
1189 }
1190 }
1191 }
1192
1193
1194 /***********************************************************************
1195 * _objc_connect_classes_from_image.
1196 * Connect the classes in the given image to their superclasses,
1197 * or register them for later connection if any superclasses are missing.
1198 **********************************************************************/
1199 static void _objc_connect_classes_from_image(header_info *hi)
1200 {
1201 unsigned int index;
1202 unsigned int midx;
1203 Module mods;
1204 BOOL replacement = _objcHeaderIsReplacement(hi);
1205
1206 // Major loop - process all modules in the image
1207 mods = hi->mod_ptr;
1208 for (midx = 0; midx < hi->mod_count; midx += 1)
1209 {
1210 // Skip module containing no classes
1211 if (mods[midx].symtab == NULL)
1212 continue;
1213
1214 // Minor loop - process all the classes in given module
1215 for (index = 0; index < mods[midx].symtab->cls_def_cnt; index += 1)
1216 {
1217 struct old_class *cls = mods[midx].symtab->defs[index];
1218 if (! replacement) {
1219 BOOL connected;
1220 struct old_class *futureCls = getFutureClassForOriginalClass(cls);
1221 if (futureCls) {
1222 // objc_getFutureClass() requested a different class
1223 // struct. Fix up the original struct's super_class
1224 // field for [super ...] use, but otherwise perform
1225 // fixups on the new class struct only.
1226 const char *super_name = (const char *) cls->super_class;
1227 if (super_name) cls->super_class = _class_asOld(objc_getClass(super_name));
1228 cls = futureCls;
1229 }
1230 connected = connect_class(cls);
1231 if (connected && callbackFunction) {
1232 (*callbackFunction)((Class)cls, 0);
1233 }
1234 } else {
1235 // Replacement image - fix up super_class only (#3704817)
1236 // And metaclass's super_class (#5351107)
1237 const char *super_name = (const char *) cls->super_class;
1238 if (super_name) {
1239 cls->super_class = _class_asOld(objc_getClass(super_name));
1240 // metaclass's superclass is superclass's metaclass
1241 cls->isa->super_class = cls->super_class->isa;
1242 } else {
1243 // Replacement for a root class
1244 // cls->super_class already NULL
1245 // root metaclass's superclass is root class
1246 cls->isa->super_class = cls;
1247 }
1248 }
1249 }
1250 }
1251 }
1252
1253
1254 /***********************************************************************
1255 * _objc_map_class_refs_for_image. Convert the class ref entries from
1256 * a class name string pointer to a class pointer. If the class does
1257 * not yet exist, the reference is added to a list of pending references
1258 * to be fixed up at a later date.
1259 **********************************************************************/
1260 static void fix_class_ref(struct old_class **ref, const char *name, BOOL isMeta)
1261 {
1262 struct old_class *cls;
1263
1264 // Get pointer to class of this name
1265 // YES unconnected, YES class loader
1266 cls = _class_asOld(look_up_class(name, YES, YES));
1267 if (cls) {
1268 // Referenced class exists. Fix up the reference.
1269 *ref = isMeta ? cls->isa : cls;
1270 } else {
1271 // Referenced class does not exist yet. Insert a placeholder
1272 // class and fix up the reference later.
1273 pendClassReference (ref, name, isMeta);
1274 *ref = (struct old_class *)_class_getNonexistentObjectClass();
1275 }
1276 }
1277
1278 static void _objc_map_class_refs_for_image (header_info * hi)
1279 {
1280 struct old_class **cls_refs;
1281 size_t count;
1282 unsigned int index;
1283
1284 // Locate class refs in image
1285 cls_refs = _getObjcClassRefs (hi, &count);
1286 if (cls_refs) {
1287 // Process each class ref
1288 for (index = 0; index < count; index += 1) {
1289 // Ref is initially class name char*
1290 const char *name = (const char *) cls_refs[index];
1291 if (name == NULL) {
1292 // rdar://5453039 is the entire page zero, or just this pointer
1293 uintptr_t *p = (uintptr_t *)(((uintptr_t)&cls_refs[index]) & ~0xfff);
1294 uintptr_t *end = (uintptr_t *)(((uintptr_t)p)+0x1000);
1295 int clear = 1;
1296 for ( ; p < end; p++) {
1297 if (*p != 0) {
1298 clear = 0;
1299 break;
1300 }
1301 }
1302 _objc_inform_on_crash("rdar://5453039 page around %p IS%s clear",
1303 &cls_refs[index], clear ? "" : " NOT");
1304 // crash in the usual spot so CrashTracer coalesces it
1305 }
1306 fix_class_ref(&cls_refs[index], name, NO /*never meta*/);
1307 }
1308 }
1309 }
1310
1311
1312 /***********************************************************************
1313 * _objc_remove_pending_class_refs_in_image
1314 * Delete any pending class ref fixups for class refs in the given image,
1315 * because the image is about to be unloaded.
1316 **********************************************************************/
1317 static void removePendingReferences(struct old_class **refs, size_t count)
1318 {
1319 struct old_class **end = refs + count;
1320
1321 if (!refs) return;
1322 if (!pendingClassRefsMap) return;
1323
1324 // Search the pending class ref table for class refs in this range.
1325 // The class refs may have already been stomped with nonexistentClass,
1326 // so there's no way to recover the original class name.
1327
1328 const char *key;
1329 PendingClassRef *pending;
1330 NXMapState state = NXInitMapState(pendingClassRefsMap);
1331 while(NXNextMapState(pendingClassRefsMap, &state,
1332 (const void **)&key, (const void **)&pending))
1333 {
1334 for ( ; pending != NULL; pending = pending->next) {
1335 if (pending->ref >= refs && pending->ref < end) {
1336 pending->ref = NULL;
1337 }
1338 }
1339 }
1340 }
1341
1342 static void _objc_remove_pending_class_refs_in_image(header_info *hi)
1343 {
1344 struct old_class **cls_refs;
1345 size_t count;
1346
1347 // Locate class refs in this image
1348 cls_refs = _getObjcClassRefs(hi, &count);
1349 removePendingReferences(cls_refs, count);
1350 }
1351
1352
1353 /***********************************************************************
1354 * map_selrefs. For each selector in the specified array,
1355 * replace the name pointer with a uniqued selector.
1356 * If copy is TRUE, all selector data is always copied. This is used
1357 * for registering selectors from unloadable bundles, so the selector
1358 * can still be used after the bundle's data segment is unmapped.
1359 * Returns YES if dst was written to, NO if it was unchanged.
1360 **********************************************************************/
1361 static inline BOOL map_selrefs(SEL *src, SEL *dst, size_t size, BOOL copy)
1362 {
1363 BOOL result = NO;
1364 size_t cnt = size / sizeof(SEL);
1365 size_t index;
1366
1367 sel_lock();
1368
1369 // Process each selector
1370 for (index = 0; index < cnt; index += 1)
1371 {
1372 SEL sel;
1373
1374 // Lookup pointer to uniqued string
1375 sel = sel_registerNameNoLock((const char *) src[index], copy);
1376
1377 // Replace this selector with uniqued one (avoid
1378 // modifying the VM page if this would be a NOP)
1379 if (dst[index] != sel) {
1380 dst[index] = sel;
1381 result = YES;
1382 }
1383 }
1384
1385 sel_unlock();
1386
1387 return result;
1388 }
1389
1390
1391 /***********************************************************************
1392 * map_message_refs. For each message ref in the specified array,
1393 * replace the name pointer with a uniqued selector.
1394 * If copy is TRUE, all selector data is always copied. This is used
1395 * for registering selectors from unloadable bundles, so the selector
1396 * can still be used after the bundle's data segment is unmapped.
1397 * Returns YES if dst was written to, NO if it was unchanged.
1398 **********************************************************************/
1399 static inline BOOL map_message_refs(message_ref *src, message_ref *dst, size_t size, BOOL copy)
1400 {
1401 BOOL result = NO;
1402 size_t cnt = size / sizeof(message_ref);
1403 size_t index;
1404
1405 sel_lock();
1406
1407 // Process each selector
1408 for (index = 0; index < cnt; index += 1)
1409 {
1410 SEL sel;
1411
1412 // Lookup pointer to uniqued string
1413 sel = sel_registerNameNoLock((const char *) src[index].sel, copy);
1414
1415 // Replace this selector with uniqued one (avoid
1416 // modifying the VM page if this would be a NOP)
1417 if (dst[index].sel != sel) {
1418 dst[index].sel = sel;
1419 result = YES;
1420 }
1421 }
1422
1423 sel_unlock();
1424
1425 return result;
1426 }
1427
1428
1429 /***********************************************************************
1430 * map_method_descs. For each method in the specified method list,
1431 * replace the name pointer with a uniqued selector.
1432 * If copy is TRUE, all selector data is always copied. This is used
1433 * for registering selectors from unloadable bundles, so the selector
1434 * can still be used after the bundle's data segment is unmapped.
1435 **********************************************************************/
1436 static void map_method_descs (struct objc_method_description_list * methods, BOOL copy)
1437 {
1438 unsigned int index;
1439
1440 if (!methods) return;
1441
1442 sel_lock();
1443
1444 // Process each method
1445 for (index = 0; index < methods->count; index += 1)
1446 {
1447 struct objc_method_description * method;
1448 SEL sel;
1449
1450 // Get method entry to fix up
1451 method = &methods->list[index];
1452
1453 // Lookup pointer to uniqued string
1454 sel = sel_registerNameNoLock((const char *) method->name, copy);
1455
1456 // Replace this selector with uniqued one (avoid
1457 // modifying the VM page if this would be a NOP)
1458 if (method->name != sel)
1459 method->name = sel;
1460 }
1461
1462 sel_unlock();
1463 }
1464
1465
1466 /***********************************************************************
1467 * ext_for_protocol
1468 * Returns the protocol extension for the given protocol.
1469 * Returns NULL if the protocol has no extension.
1470 **********************************************************************/
1471 static struct old_protocol_ext *ext_for_protocol(struct old_protocol *proto)
1472 {
1473 if (!proto) return NULL;
1474 if (!protocol_ext_map) return NULL;
1475 else return (struct old_protocol_ext *)NXMapGet(protocol_ext_map, proto);
1476 }
1477
1478
1479 /***********************************************************************
1480 * lookup_method
1481 * Search a protocol method list for a selector.
1482 **********************************************************************/
1483 static struct objc_method_description *
1484 lookup_method(struct objc_method_description_list *mlist, SEL aSel)
1485 {
1486 if (mlist) {
1487 int i;
1488 for (i = 0; i < mlist->count; i++) {
1489 if (mlist->list[i].name == aSel) {
1490 return mlist->list+i;
1491 }
1492 }
1493 }
1494 return NULL;
1495 }
1496
1497
1498 /***********************************************************************
1499 * lookup_protocol_method
1500 * Recursively search for a selector in a protocol
1501 * (and all incorporated protocols)
1502 **********************************************************************/
1503 __private_extern__ struct objc_method_description *
1504 lookup_protocol_method(struct old_protocol *proto, SEL aSel,
1505 BOOL isRequiredMethod, BOOL isInstanceMethod)
1506 {
1507 struct objc_method_description *m = NULL;
1508 struct old_protocol_ext *ext;
1509
1510 if (isRequiredMethod) {
1511 if (isInstanceMethod) {
1512 m = lookup_method(proto->instance_methods, aSel);
1513 } else {
1514 m = lookup_method(proto->class_methods, aSel);
1515 }
1516 } else if ((ext = ext_for_protocol(proto))) {
1517 if (isInstanceMethod) {
1518 m = lookup_method(ext->optional_instance_methods, aSel);
1519 } else {
1520 m = lookup_method(ext->optional_class_methods, aSel);
1521 }
1522 }
1523
1524 if (!m && proto->protocol_list) {
1525 int i;
1526 for (i = 0; !m && i < proto->protocol_list->count; i++) {
1527 m = lookup_protocol_method(proto->protocol_list->list[i], aSel,
1528 isRequiredMethod, isInstanceMethod);
1529 }
1530 }
1531
1532 return m;
1533 }
1534
1535
1536 /***********************************************************************
1537 * protocol_getName
1538 * Returns the name of the given protocol.
1539 **********************************************************************/
1540 const char *protocol_getName(Protocol *p)
1541 {
1542 struct old_protocol *proto = oldprotocol(p);
1543 if (!proto) return "nil";
1544 return proto->protocol_name;
1545 }
1546
1547
1548 /***********************************************************************
1549 * protocol_getMethodDescription
1550 * Returns the description of a named method.
1551 * Searches either required or optional methods.
1552 * Searches either instance or class methods.
1553 **********************************************************************/
1554 struct objc_method_description
1555 protocol_getMethodDescription(Protocol *p, SEL aSel,
1556 BOOL isRequiredMethod, BOOL isInstanceMethod)
1557 {
1558 struct old_protocol *proto = oldprotocol(p);
1559 if (!proto) return (struct objc_method_description){NULL, NULL};
1560
1561 struct objc_method_description *desc =
1562 lookup_protocol_method(proto, aSel,
1563 isRequiredMethod, isInstanceMethod);
1564 if (desc) return *desc;
1565 else return (struct objc_method_description){NULL, NULL};
1566 }
1567
1568
1569 /***********************************************************************
1570 * protocol_copyMethodDescriptionList
1571 * Returns an array of method descriptions from a protocol.
1572 * Copies either required or optional methods.
1573 * Copies either instance or class methods.
1574 **********************************************************************/
1575 struct objc_method_description *
1576 protocol_copyMethodDescriptionList(Protocol *p,
1577 BOOL isRequiredMethod,
1578 BOOL isInstanceMethod,
1579 unsigned int *outCount)
1580 {
1581 struct objc_method_description_list *mlist = NULL;
1582 struct old_protocol *proto = oldprotocol(p);
1583 struct old_protocol_ext *ext;
1584
1585 if (!proto) {
1586 if (outCount) *outCount = 0;
1587 return NULL;
1588 }
1589
1590 if (isRequiredMethod) {
1591 if (isInstanceMethod) {
1592 mlist = proto->instance_methods;
1593 } else {
1594 mlist = proto->class_methods;
1595 }
1596 } else if ((ext = ext_for_protocol(proto))) {
1597 if (isInstanceMethod) {
1598 mlist = ext->optional_instance_methods;
1599 } else {
1600 mlist = ext->optional_class_methods;
1601 }
1602 }
1603
1604 if (!mlist) {
1605 if (outCount) *outCount = 0;
1606 return NULL;
1607 }
1608
1609 unsigned int i;
1610 unsigned int count = mlist->count;
1611 struct objc_method_description *result =
1612 calloc(count + 1, sizeof(struct objc_method_description));
1613 for (i = 0; i < count; i++) {
1614 result[i] = mlist->list[i];
1615 }
1616
1617 if (outCount) *outCount = count;
1618 return result;
1619 }
1620
1621
1622 Property protocol_getProperty(Protocol *p, const char *name,
1623 BOOL isRequiredProperty, BOOL isInstanceProperty)
1624 {
1625 struct old_protocol *proto = oldprotocol(p);
1626
1627 if (!proto || !name) return NULL;
1628
1629 if (!isRequiredProperty || !isInstanceProperty) {
1630 // Only required instance properties are currently supported
1631 return NULL;
1632 }
1633
1634 struct old_protocol_ext *ext;
1635 if ((ext = ext_for_protocol(proto))) {
1636 struct objc_property_list *plist;
1637 if ((plist = ext->instance_properties)) {
1638 uint32_t i;
1639 for (i = 0; i < plist->count; i++) {
1640 Property prop = property_list_nth(plist, i);
1641 if (0 == strcmp(name, prop->name)) {
1642 return prop;
1643 }
1644 }
1645 }
1646 }
1647
1648 struct old_protocol_list *plist;
1649 if ((plist = proto->protocol_list)) {
1650 int i;
1651 for (i = 0; i < plist->count; i++) {
1652 Property prop =
1653 protocol_getProperty((Protocol *)plist->list[i], name,
1654 isRequiredProperty, isInstanceProperty);
1655 if (prop) return prop;
1656 }
1657 }
1658
1659 return NULL;
1660 }
1661
1662
1663 Property *protocol_copyPropertyList(Protocol *p, unsigned int *outCount)
1664 {
1665 Property *result = NULL;
1666 struct old_protocol_ext *ext;
1667
1668 struct old_protocol *proto = oldprotocol(p);
1669 if (! (ext = ext_for_protocol(proto))) {
1670 if (outCount) *outCount = 0;
1671 return NULL;
1672 }
1673
1674 struct objc_property_list *plist = ext->instance_properties;
1675 result = copyPropertyList(plist, outCount);
1676
1677 return result;
1678 }
1679
1680
1681 /***********************************************************************
1682 * protocol_copyProtocolList
1683 * Copies this protocol's incorporated protocols.
1684 * Does not copy those protocol's incorporated protocols in turn.
1685 **********************************************************************/
1686 Protocol **protocol_copyProtocolList(Protocol *p, unsigned int *outCount)
1687 {
1688 unsigned int count = 0;
1689 Protocol **result = NULL;
1690 struct old_protocol *proto = oldprotocol(p);
1691
1692 if (!proto) {
1693 if (outCount) *outCount = 0;
1694 return NULL;
1695 }
1696
1697 if (proto->protocol_list) {
1698 count = (unsigned int)proto->protocol_list->count;
1699 }
1700 if (count > 0) {
1701 result = malloc((count+1) * sizeof(Protocol *));
1702
1703 unsigned int i;
1704 for (i = 0; i < count; i++) {
1705 result[i] = (Protocol *)proto->protocol_list->list[i];
1706 }
1707 result[i] = NULL;
1708 }
1709
1710 if (outCount) *outCount = count;
1711 return result;
1712 }
1713
1714
1715 BOOL protocol_conformsToProtocol(Protocol *self_gen, Protocol *other_gen)
1716 {
1717 struct old_protocol *self = oldprotocol(self_gen);
1718 struct old_protocol *other = oldprotocol(other_gen);
1719
1720 if (!self || !other) {
1721 return NO;
1722 }
1723
1724 if (0 == strcmp(self->protocol_name, other->protocol_name)) {
1725 return YES;
1726 }
1727
1728 if (self->protocol_list) {
1729 int i;
1730 for (i = 0; i < self->protocol_list->count; i++) {
1731 struct old_protocol *proto = self->protocol_list->list[i];
1732 if (0 == strcmp(other->protocol_name, proto->protocol_name)) {
1733 return YES;
1734 }
1735 if (protocol_conformsToProtocol((Protocol *)proto, other_gen)) {
1736 return YES;
1737 }
1738 }
1739 }
1740
1741 return NO;
1742 }
1743
1744
1745 BOOL protocol_isEqual(Protocol *self, Protocol *other)
1746 {
1747 if (self == other) return YES;
1748 if (!self || !other) return NO;
1749
1750 if (!protocol_conformsToProtocol(self, other)) return NO;
1751 if (!protocol_conformsToProtocol(other, self)) return NO;
1752
1753 return YES;
1754 }
1755
1756
1757 /***********************************************************************
1758 * _objc_fixup_protocol_objects_for_image. For each protocol in the
1759 * specified image, selectorize the method names and add to the protocol hash.
1760 **********************************************************************/
1761
1762 static BOOL versionIsExt(uintptr_t version, const char *names, size_t size)
1763 {
1764 // CodeWarrior used isa field for string "Protocol"
1765 // from section __OBJC,__class_names. rdar://4951638
1766 // gcc (10.4 and earlier) used isa field for version number;
1767 // the only version number used on Mac OS X was 2.
1768 // gcc (10.5 and later) uses isa field for ext pointer
1769
1770 if (version < 4096) {
1771 return NO;
1772 }
1773
1774 if (version >= (uintptr_t)names && version < (uintptr_t)(names + size)) {
1775 return NO;
1776 }
1777
1778 return YES;
1779 }
1780
1781 static void fix_protocol(struct old_protocol *proto, Class protocolClass,
1782 BOOL isBundle, const char *names, size_t names_size)
1783 {
1784 #warning GrP fixme hack
1785 if (!proto) return;
1786
1787 uintptr_t version = (uintptr_t)proto->isa;
1788
1789 // Set the protocol's isa
1790 proto->isa = protocolClass;
1791
1792 // Fix up method lists
1793 // fixme share across duplicates
1794 map_method_descs (proto->instance_methods, isBundle);
1795 map_method_descs (proto->class_methods, isBundle);
1796
1797 // Fix up ext, if any
1798 if (versionIsExt(version, names, names_size)) {
1799 struct old_protocol_ext *ext = (struct old_protocol_ext *)version;
1800 NXMapInsert(protocol_ext_map, proto, ext);
1801 map_method_descs (ext->optional_instance_methods, isBundle);
1802 map_method_descs (ext->optional_class_methods, isBundle);
1803 }
1804
1805 // Record the protocol it if we don't have one with this name yet
1806 // fixme bundles - copy protocol
1807 // fixme unloading
1808 if (!NXMapGet(protocol_map, proto->protocol_name)) {
1809 NXMapKeyCopyingInsert(protocol_map, proto->protocol_name, proto);
1810 if (PrintProtocols) {
1811 _objc_inform("PROTOCOLS: protocol at %p is %s",
1812 proto, proto->protocol_name);
1813 }
1814 } else {
1815 // duplicate - do nothing
1816 if (PrintProtocols) {
1817 _objc_inform("PROTOCOLS: protocol at %p is %s (duplicate)",
1818 proto, proto->protocol_name);
1819 }
1820 }
1821 }
1822
1823 static void _objc_fixup_protocol_objects_for_image (header_info * hi)
1824 {
1825 Class protocolClass = objc_getClass("Protocol");
1826 size_t count, i;
1827 struct old_protocol *protos;
1828 int isBundle = hi->mhdr->filetype == MH_BUNDLE;
1829 const char *names;
1830 size_t names_size;
1831
1832 OBJC_LOCK(&classLock);
1833
1834 // Allocate the protocol registry if necessary.
1835 if (!protocol_map) {
1836 protocol_map =
1837 NXCreateMapTableFromZone(NXStrValueMapPrototype, 32,
1838 _objc_internal_zone());
1839 }
1840 if (!protocol_ext_map) {
1841 protocol_ext_map =
1842 NXCreateMapTableFromZone(NXPtrValueMapPrototype, 32,
1843 _objc_internal_zone());
1844 }
1845
1846 protos = _getObjcProtocols(hi, &count);
1847 names = _getObjcClassNames(hi, &names_size);
1848 for (i = 0; i < count; i++) {
1849 fix_protocol(&protos[i], protocolClass, isBundle, names, names_size);
1850 }
1851
1852 OBJC_UNLOCK(&classLock);
1853 }
1854
1855
1856 /***********************************************************************
1857 * _objc_fixup_selector_refs. Register all of the selectors in each
1858 * image, and fix them all up.
1859 **********************************************************************/
1860 static void _objc_fixup_selector_refs (const header_info *hi)
1861 {
1862 size_t count;
1863 SEL *sels;
1864
1865 // Fix up selector refs
1866 sels = _getObjcSelectorRefs (hi, &count);
1867 if (sels) {
1868 map_selrefs(sels, sels, count * sizeof(SEL),
1869 hi->mhdr->filetype == MH_BUNDLE);
1870 }
1871 }
1872
1873
1874 /***********************************************************************
1875 * _read_images
1876 * Perform metadata processing for hCount images starting with firstNewHeader
1877 **********************************************************************/
1878 __private_extern__ void _read_images(header_info **hList, uint32_t hCount)
1879 {
1880 uint32_t i;
1881
1882 if (!class_hash) _objc_init_class_hash();
1883
1884 // Parts of this order are important for correctness or performance.
1885
1886 // Read classes from all images.
1887 for (i = 0; i < hCount; i++) {
1888 _objc_read_classes_from_image(hList[i]);
1889 }
1890
1891 // Read categories from all images.
1892 BOOL needFlush = NO;
1893 for (i = 0; i < hCount; i++) {
1894 needFlush |= _objc_read_categories_from_image(hList[i]);
1895 }
1896 if (needFlush) flush_marked_caches();
1897
1898 // Connect classes from all images.
1899 for (i = 0; i < hCount; i++) {
1900 _objc_connect_classes_from_image(hList[i]);
1901 }
1902
1903 // Fix up class refs, selector refs, and protocol objects from all images.
1904 for (i = 0; i < hCount; i++) {
1905 _objc_map_class_refs_for_image(hList[i]);
1906 _objc_fixup_selector_refs(hList[i]);
1907 _objc_fixup_protocol_objects_for_image(hList[i]);
1908 }
1909 }
1910
1911
1912 /***********************************************************************
1913 * prepare_load_methods
1914 * Schedule +load for classes in this image, any un-+load-ed
1915 * superclasses in other images, and any categories in this image.
1916 **********************************************************************/
1917 // Recursively schedule +load for cls and any un-+load-ed superclasses.
1918 // cls must already be connected.
1919 static void schedule_class_load(struct old_class *cls)
1920 {
1921 if (cls->info & CLS_LOADED) return;
1922 if (cls->super_class) schedule_class_load(cls->super_class);
1923 add_class_to_loadable_list((Class)cls);
1924 cls->info |= CLS_LOADED;
1925 }
1926
1927 __private_extern__ void prepare_load_methods(header_info *hi)
1928 {
1929 Module mods;
1930 unsigned int midx;
1931
1932
1933 if (_objcHeaderIsReplacement(hi)) {
1934 // Ignore any classes in this image
1935 return;
1936 }
1937
1938 // Major loop - process all modules in the image
1939 mods = hi->mod_ptr;
1940 for (midx = 0; midx < hi->mod_count; midx += 1)
1941 {
1942 unsigned int index;
1943
1944 // Skip module containing no classes
1945 if (mods[midx].symtab == NULL)
1946 continue;
1947
1948 // Minor loop - process all the classes in given module
1949 for (index = 0; index < mods[midx].symtab->cls_def_cnt; index += 1)
1950 {
1951 // Locate the class description pointer
1952 struct old_class *cls = mods[midx].symtab->defs[index];
1953 if (cls->info & CLS_CONNECTED) {
1954 schedule_class_load(cls);
1955 }
1956 }
1957 }
1958
1959
1960 // Major loop - process all modules in the header
1961 mods = hi->mod_ptr;
1962
1963 // NOTE: The module and category lists are traversed backwards
1964 // to preserve the pre-10.4 processing order. Changing the order
1965 // would have a small chance of introducing binary compatibility bugs.
1966 midx = hi->mod_count;
1967 while (midx-- > 0) {
1968 unsigned int index;
1969 unsigned int total;
1970 Symtab symtab = mods[midx].symtab;
1971
1972 // Nothing to do for a module without a symbol table
1973 if (mods[midx].symtab == NULL)
1974 continue;
1975 // Total entries in symbol table (class entries followed
1976 // by category entries)
1977 total = mods[midx].symtab->cls_def_cnt +
1978 mods[midx].symtab->cat_def_cnt;
1979
1980 // Minor loop - register all categories from given module
1981 index = total;
1982 while (index-- > mods[midx].symtab->cls_def_cnt) {
1983 struct old_category *cat = symtab->defs[index];
1984 add_category_to_loadable_list((Category)cat);
1985 }
1986 }
1987 }
1988
1989
1990 /***********************************************************************
1991 * _objc_remove_classes_in_image
1992 * Remove all classes in the given image from the runtime, because
1993 * the image is about to be unloaded.
1994 * Things to clean up:
1995 * class_hash
1996 * unconnected_class_hash
1997 * pending subclasses list (only if class is still unconnected)
1998 * loadable class list
1999 * class's method caches
2000 * class refs in all other images
2001 **********************************************************************/
2002 // Re-pend any class references in refs that point into [start..end)
2003 static void rependClassReferences(struct old_class **refs, size_t count,
2004 uintptr_t start, uintptr_t end)
2005 {
2006 size_t i;
2007
2008 if (!refs) return;
2009
2010 // Process each class ref
2011 for (i = 0; i < count; i++) {
2012 if ((uintptr_t)(refs[i]) >= start && (uintptr_t)(refs[i]) < end) {
2013 pendClassReference(&refs[i], refs[i]->name,
2014 (refs[i]->info & CLS_META) ? YES : NO);
2015 refs[i] = (struct old_class *)_class_getNonexistentObjectClass();
2016 }
2017 }
2018 }
2019
2020
2021 static void try_free(const void *p)
2022 {
2023 if (p && malloc_size(p)) free((void *)p);
2024 }
2025
2026 // Deallocate all memory in a method list
2027 static void unload_mlist(struct old_method_list *mlist)
2028 {
2029 int i;
2030 if (mlist->obsolete == _OBJC_FIXED_UP) {
2031 for (i = 0; i < mlist->method_count; i++) {
2032 try_free(mlist->method_list[i].method_types);
2033 }
2034
2035 try_free(mlist);
2036 }
2037 }
2038
2039 // Deallocate all memory in a class.
2040 __private_extern__ void unload_class(struct old_class *cls)
2041 {
2042 // Free ivar lists
2043 if (cls->ivars) {
2044 int i;
2045 for (i = 0; i < cls->ivars->ivar_count; i++) {
2046 try_free(cls->ivars->ivar_list[i].ivar_name);
2047 try_free(cls->ivars->ivar_list[i].ivar_type);
2048 }
2049 try_free(cls->ivars);
2050 }
2051
2052 // Free fixed-up method lists and method list array
2053 if (cls->methodLists) {
2054 // more than zero method lists
2055 if (cls->info & CLS_NO_METHOD_ARRAY) {
2056 // one method list
2057 unload_mlist((struct old_method_list *)cls->methodLists);
2058 }
2059 else {
2060 // more than one method list
2061 struct old_method_list **mlistp;
2062 for (mlistp = cls->methodLists;
2063 *mlistp != NULL && *mlistp != END_OF_METHODS_LIST;
2064 mlistp++)
2065 {
2066 unload_mlist(*mlistp);
2067 }
2068 free(cls->methodLists);
2069 }
2070 }
2071
2072 // Free protocol list
2073 struct old_protocol_list *protos = cls->protocols;
2074 while (protos) {
2075 struct old_protocol_list *dead = protos;
2076 protos = protos->next;
2077 try_free(dead);
2078 }
2079
2080 // Free method cache
2081 if (cls->cache && cls->cache != &_objc_empty_cache) {
2082 _cache_free(cls->cache);
2083 }
2084
2085 if ((cls->info & CLS_EXT)) {
2086 if (cls->ext) {
2087 // Free property lists and property list array
2088 if (cls->ext->propertyLists) {
2089 // more than zero property lists
2090 if (cls->info & CLS_NO_PROPERTY_ARRAY) {
2091 // one property list
2092 try_free(cls->ext->propertyLists);
2093 } else {
2094 // more than one property list
2095 struct objc_property_list **plistp;
2096 for (plistp = cls->ext->propertyLists;
2097 *plistp != NULL;
2098 plistp++)
2099 {
2100 try_free(*plistp);
2101 }
2102 try_free(cls->ext->propertyLists);
2103 }
2104 }
2105
2106 // Free weak ivar layout
2107 try_free(cls->ext->weak_ivar_layout);
2108
2109 // Free ext
2110 try_free(cls->ext);
2111 }
2112
2113 // Free non-weak ivar layout
2114 try_free(cls->ivar_layout);
2115 }
2116
2117 // Free class name
2118 try_free(cls->name);
2119
2120 // Free cls
2121 try_free(cls);
2122 }
2123
2124
2125 static void _objc_remove_classes_in_image(header_info *hi)
2126 {
2127 unsigned int index;
2128 unsigned int midx;
2129 Module mods;
2130
2131 OBJC_LOCK(&classLock);
2132
2133 // Major loop - process all modules in the image
2134 mods = hi->mod_ptr;
2135 for (midx = 0; midx < hi->mod_count; midx += 1)
2136 {
2137 // Skip module containing no classes
2138 if (mods[midx].symtab == NULL)
2139 continue;
2140
2141 // Minor loop - process all the classes in given module
2142 for (index = 0; index < mods[midx].symtab->cls_def_cnt; index += 1)
2143 {
2144 struct old_class *cls;
2145
2146 // Locate the class description pointer
2147 cls = mods[midx].symtab->defs[index];
2148
2149 // Remove from loadable class list, if present
2150 remove_class_from_loadable_list((Class)cls);
2151
2152 // Remove from unconnected_class_hash and pending subclasses
2153 if (unconnected_class_hash && NXHashMember(unconnected_class_hash, cls)) {
2154 NXHashRemove(unconnected_class_hash, cls);
2155 if (pendingSubclassesMap) {
2156 // Find this class in its superclass's pending list
2157 char *supercls_name = (char *)cls->super_class;
2158 PendingSubclass *pending =
2159 NXMapGet(pendingSubclassesMap, supercls_name);
2160 for ( ; pending != NULL; pending = pending->next) {
2161 if (pending->subclass == cls) {
2162 pending->subclass = Nil;
2163 break;
2164 }
2165 }
2166 }
2167 }
2168
2169 // Remove from class_hash
2170 NXHashRemove(class_hash, cls);
2171
2172 // Free heap memory pointed to by the class
2173 unload_class(cls->isa);
2174 unload_class(cls);
2175 }
2176 }
2177
2178
2179 // Search all other images for class refs that point back to this range.
2180 // Un-fix and re-pend any such class refs.
2181
2182 // Get the location of the dying image's __OBJC segment
2183 uintptr_t seg = hi->objcSegmentHeader->vmaddr + hi->image_slide;
2184 size_t seg_size = hi->objcSegmentHeader->filesize;
2185
2186 header_info *other_hi;
2187 for (other_hi = _objc_headerStart();
2188 other_hi != NULL;
2189 other_hi = other_hi->next)
2190 {
2191 struct old_class **other_refs;
2192 size_t count;
2193 if (other_hi == hi) continue; // skip the image being unloaded
2194
2195 // Fix class refs in the other image
2196 other_refs = _getObjcClassRefs(other_hi, &count);
2197 rependClassReferences(other_refs, count, seg, seg+seg_size);
2198 }
2199
2200 OBJC_UNLOCK(&classLock);
2201 }
2202
2203
2204 /***********************************************************************
2205 * _objc_remove_categories_in_image
2206 * Remove all categories in the given image from the runtime, because
2207 * the image is about to be unloaded.
2208 * Things to clean up:
2209 * unresolved category list
2210 * loadable category list
2211 **********************************************************************/
2212 static void _objc_remove_categories_in_image(header_info *hi)
2213 {
2214 Module mods;
2215 unsigned int midx;
2216
2217 // Major loop - process all modules in the header
2218 mods = hi->mod_ptr;
2219
2220 for (midx = 0; midx < hi->mod_count; midx++) {
2221 unsigned int index;
2222 unsigned int total;
2223 Symtab symtab = mods[midx].symtab;
2224
2225 // Nothing to do for a module without a symbol table
2226 if (symtab == NULL) continue;
2227
2228 // Total entries in symbol table (class entries followed
2229 // by category entries)
2230 total = symtab->cls_def_cnt + symtab->cat_def_cnt;
2231
2232 // Minor loop - check all categories from given module
2233 for (index = symtab->cls_def_cnt; index < total; index++) {
2234 struct old_category *cat = symtab->defs[index];
2235
2236 // Clean up loadable category list
2237 remove_category_from_loadable_list((Category)cat);
2238
2239 // Clean up category_hash
2240 if (category_hash) {
2241 _objc_unresolved_category *cat_entry =
2242 NXMapGet(category_hash, cat->class_name);
2243 for ( ; cat_entry != NULL; cat_entry = cat_entry->next) {
2244 if (cat_entry->cat == cat) {
2245 cat_entry->cat = NULL;
2246 break;
2247 }
2248 }
2249 }
2250 }
2251 }
2252 }
2253
2254
2255 /***********************************************************************
2256 * unload_paranoia
2257 * Various paranoid debugging checks that look for poorly-behaving
2258 * unloadable bundles.
2259 * Called by _objc_unmap_image when OBJC_UNLOAD_DEBUG is set.
2260 **********************************************************************/
2261 static void unload_paranoia(header_info *hi)
2262 {
2263 // Get the location of the dying image's __OBJC segment
2264 uintptr_t seg = hi->objcSegmentHeader->vmaddr + hi->image_slide;
2265 size_t seg_size = hi->objcSegmentHeader->filesize;
2266
2267 _objc_inform("UNLOAD DEBUG: unloading image '%s' [%p..%p]",
2268 _nameForHeader(hi->mhdr), (void *)seg, (void*)(seg+seg_size));
2269
2270 OBJC_LOCK(&classLock);
2271
2272 // Make sure the image contains no categories on surviving classes.
2273 {
2274 Module mods;
2275 unsigned int midx;
2276
2277 // Major loop - process all modules in the header
2278 mods = hi->mod_ptr;
2279
2280 for (midx = 0; midx < hi->mod_count; midx++) {
2281 unsigned int index;
2282 unsigned int total;
2283 Symtab symtab = mods[midx].symtab;
2284
2285 // Nothing to do for a module without a symbol table
2286 if (symtab == NULL) continue;
2287
2288 // Total entries in symbol table (class entries followed
2289 // by category entries)
2290 total = symtab->cls_def_cnt + symtab->cat_def_cnt;
2291
2292 // Minor loop - check all categories from given module
2293 for (index = symtab->cls_def_cnt; index < total; index++) {
2294 struct old_category *cat = symtab->defs[index];
2295 struct old_class query;
2296
2297 query.name = cat->class_name;
2298 if (NXHashMember(class_hash, &query)) {
2299 _objc_inform("UNLOAD DEBUG: dying image contains category '%s(%s)' on surviving class '%s'!", cat->class_name, cat->category_name, cat->class_name);
2300 }
2301 }
2302 }
2303 }
2304
2305 // Make sure no surviving class is in the dying image.
2306 // Make sure no surviving class has a superclass in the dying image.
2307 // fixme check method implementations too
2308 {
2309 struct old_class *cls;
2310 NXHashState state;
2311
2312 state = NXInitHashState(class_hash);
2313 while (NXNextHashState(class_hash, &state, (void **)&cls)) {
2314 if ((vm_address_t)cls >= seg &&
2315 (vm_address_t)cls < seg+seg_size)
2316 {
2317 _objc_inform("UNLOAD DEBUG: dying image contains surviving class '%s'!", cls->name);
2318 }
2319
2320 if ((vm_address_t)cls->super_class >= seg &&
2321 (vm_address_t)cls->super_class < seg+seg_size)
2322 {
2323 _objc_inform("UNLOAD DEBUG: dying image contains superclass '%s' of surviving class '%s'!", cls->super_class->name, cls->name);
2324 }
2325 }
2326 }
2327
2328 OBJC_UNLOCK(&classLock);
2329 }
2330
2331
2332 /***********************************************************************
2333 * _unload_image
2334 * Only handles MH_BUNDLE for now.
2335 **********************************************************************/
2336 __private_extern__ void _unload_image(header_info *hi)
2337 {
2338 // Cleanup:
2339 // Remove image's classes from the class list and free auxiliary data.
2340 // Remove image's unresolved or loadable categories and free auxiliary data
2341 // Remove image's unresolved class refs.
2342 _objc_remove_classes_in_image(hi);
2343 _objc_remove_categories_in_image(hi);
2344 _objc_remove_pending_class_refs_in_image(hi);
2345
2346 // Perform various debugging checks if requested.
2347 if (DebugUnload) unload_paranoia(hi);
2348 }
2349
2350
2351 /***********************************************************************
2352 * objc_addClass. Add the specified class to the table of known classes,
2353 * after doing a little verification and fixup.
2354 **********************************************************************/
2355 void objc_addClass (Class cls_gen)
2356 {
2357 struct old_class *cls = _class_asOld(cls_gen);
2358
2359 OBJC_WARN_DEPRECATED;
2360
2361 // Synchronize access to hash table
2362 OBJC_LOCK (&classLock);
2363
2364 // Make sure both the class and the metaclass have caches!
2365 // Clear all bits of the info fields except CLS_CLASS and CLS_META.
2366 // Normally these bits are already clear but if someone tries to cons
2367 // up their own class on the fly they might need to be cleared.
2368 if (cls->cache == NULL) {
2369 cls->cache = (Cache) &_objc_empty_cache;
2370 cls->info = CLS_CLASS;
2371 }
2372
2373 if (cls->isa->cache == NULL) {
2374 cls->isa->cache = (Cache) &_objc_empty_cache;
2375 cls->isa->info = CLS_META;
2376 }
2377
2378 // methodLists should be:
2379 // 1. NULL (Tiger and later only)
2380 // 2. A -1 terminated method list array
2381 // In either case, CLS_NO_METHOD_ARRAY remains clear.
2382 // If the user manipulates the method list directly,
2383 // they must use the magic private format.
2384
2385 // Add the class to the table
2386 (void) NXHashInsert (class_hash, cls);
2387
2388 // Superclass is no longer a leaf for cache flushing
2389 if (cls->super_class && (cls->super_class->info & CLS_LEAF)) {
2390 _class_clearInfo((Class)cls->super_class, CLS_LEAF);
2391 _class_clearInfo((Class)cls->super_class->isa, CLS_LEAF);
2392 }
2393
2394 // Desynchronize
2395 OBJC_UNLOCK (&classLock);
2396 }
2397
2398 /***********************************************************************
2399 * _objcTweakMethodListPointerForClass.
2400 * Change the class's method list pointer to a method list array.
2401 * Does nothing if the method list pointer is already a method list array.
2402 * If the class is currently in use, methodListLock must be held by the caller.
2403 **********************************************************************/
2404 static void _objcTweakMethodListPointerForClass(struct old_class *cls)
2405 {
2406 struct old_method_list * originalList;
2407 const int initialEntries = 4;
2408 size_t mallocSize;
2409 struct old_method_list ** ptr;
2410
2411 // Do nothing if methodLists is already an array.
2412 if (cls->methodLists && !(cls->info & CLS_NO_METHOD_ARRAY)) return;
2413
2414 // Remember existing list
2415 originalList = (struct old_method_list *) cls->methodLists;
2416
2417 // Allocate and zero a method list array
2418 mallocSize = sizeof(struct old_method_list *) * initialEntries;
2419 ptr = (struct old_method_list **) _calloc_internal(1, mallocSize);
2420
2421 // Insert the existing list into the array
2422 ptr[initialEntries - 1] = END_OF_METHODS_LIST;
2423 ptr[0] = originalList;
2424
2425 // Replace existing list with array
2426 cls->methodLists = ptr;
2427 _class_clearInfo((Class)cls, CLS_NO_METHOD_ARRAY);
2428 }
2429
2430
2431 /***********************************************************************
2432 * _objc_insertMethods.
2433 * Adds methods to a class.
2434 * Does not flush any method caches.
2435 * Does not take any locks.
2436 * If the class is already in use, use class_addMethods() instead.
2437 **********************************************************************/
2438 __private_extern__ void _objc_insertMethods(struct old_class *cls,
2439 struct old_method_list *mlist,
2440 struct old_category *cat)
2441 {
2442 struct old_method_list ***list;
2443 struct old_method_list **ptr;
2444 ptrdiff_t endIndex;
2445 size_t oldSize;
2446 size_t newSize;
2447
2448 if (!cls->methodLists) {
2449 // cls has no methods - simply use this method list
2450 cls->methodLists = (struct old_method_list **)mlist;
2451 _class_setInfo((Class)cls, CLS_NO_METHOD_ARRAY);
2452 return;
2453 }
2454
2455 // Log any existing methods being replaced
2456 if (PrintReplacedMethods) {
2457 int i;
2458 for (i = 0; i < mlist->method_count; i++) {
2459 extern IMP findIMPInClass(struct old_class *cls, SEL sel);
2460 SEL sel = sel_registerName((char *)mlist->method_list[i].method_name);
2461 IMP newImp = mlist->method_list[i].method_imp;
2462 IMP oldImp;
2463
2464 if ((oldImp = findIMPInClass(cls, sel))) {
2465 _objc_inform("REPLACED: %c[%s %s] %s%s (IMP was %p, now %p)",
2466 ISMETA(cls) ? '+' : '-',
2467 cls->name, sel_getName(sel),
2468 cat ? "by category " : "",
2469 cat ? cat->category_name : "",
2470 oldImp, newImp);
2471 }
2472 }
2473 }
2474
2475 // Create method list array if necessary
2476 _objcTweakMethodListPointerForClass(cls);
2477
2478 list = &cls->methodLists;
2479
2480 // Locate unused entry for insertion point
2481 ptr = *list;
2482 while ((*ptr != 0) && (*ptr != END_OF_METHODS_LIST))
2483 ptr += 1;
2484
2485 // If array is full, add to it
2486 if (*ptr == END_OF_METHODS_LIST)
2487 {
2488 // Calculate old and new dimensions
2489 endIndex = ptr - *list;
2490 oldSize = (endIndex + 1) * sizeof(void *);
2491 newSize = oldSize + sizeof(struct old_method_list *); // only increase by 1
2492
2493 // Grow the method list array by one.
2494 // This block may be from user code; don't use _realloc_internal
2495 *list = (struct old_method_list **)realloc(*list, newSize);
2496
2497 // Zero out addition part of new array
2498 bzero (&((*list)[endIndex]), newSize - oldSize);
2499
2500 // Place new end marker
2501 (*list)[(newSize/sizeof(void *)) - 1] = END_OF_METHODS_LIST;
2502
2503 // Insertion point corresponds to old array end
2504 ptr = &((*list)[endIndex]);
2505 }
2506
2507 // Right shift existing entries by one
2508 bcopy (*list, (*list) + 1, ((void *) ptr) - ((void *) *list));
2509
2510 // Insert at method list at beginning of array
2511 **list = mlist;
2512 }
2513
2514 /***********************************************************************
2515 * _objc_removeMethods.
2516 * Remove methods from a class.
2517 * Does not take any locks.
2518 * Does not flush any method caches.
2519 * If the class is currently in use, use class_removeMethods() instead.
2520 **********************************************************************/
2521 __private_extern__ void _objc_removeMethods(struct old_class *cls,
2522 struct old_method_list *mlist)
2523 {
2524 struct old_method_list ***list;
2525 struct old_method_list **ptr;
2526
2527 if (cls->methodLists == NULL) {
2528 // cls has no methods
2529 return;
2530 }
2531 if (cls->methodLists == (struct old_method_list **)mlist) {
2532 // mlist is the class's only method list - erase it
2533 cls->methodLists = NULL;
2534 return;
2535 }
2536 if (cls->info & CLS_NO_METHOD_ARRAY) {
2537 // cls has only one method list, and this isn't it - do nothing
2538 return;
2539 }
2540
2541 // cls has a method list array - search it
2542
2543 list = &cls->methodLists;
2544
2545 // Locate list in the array
2546 ptr = *list;
2547 while (*ptr != mlist) {
2548 // fix for radar # 2538790
2549 if ( *ptr == END_OF_METHODS_LIST ) return;
2550 ptr += 1;
2551 }
2552
2553 // Remove this entry
2554 *ptr = 0;
2555
2556 // Left shift the following entries
2557 while (*(++ptr) != END_OF_METHODS_LIST)
2558 *(ptr-1) = *ptr;
2559 *(ptr-1) = 0;
2560 }
2561
2562 /***********************************************************************
2563 * _objc_add_category. Install the specified category's methods and
2564 * protocols into the class it augments.
2565 * The class is assumed not to be in use yet: no locks are taken and
2566 * no method caches are flushed.
2567 **********************************************************************/
2568 static inline void _objc_add_category(struct old_class *cls, struct old_category *category, int version)
2569 {
2570 if (PrintConnecting) {
2571 _objc_inform("CONNECT: attaching category '%s (%s)'", cls->name, category->category_name);
2572 }
2573
2574 // Augment instance methods
2575 if (category->instance_methods)
2576 _objc_insertMethods (cls, category->instance_methods, category);
2577
2578 // Augment class methods
2579 if (category->class_methods)
2580 _objc_insertMethods (cls->isa, category->class_methods, category);
2581
2582 // Augment protocols
2583 if ((version >= 5) && category->protocols)
2584 {
2585 if (cls->isa->version >= 5)
2586 {
2587 category->protocols->next = cls->protocols;
2588 cls->protocols = category->protocols;
2589 cls->isa->protocols = category->protocols;
2590 }
2591 else
2592 {
2593 _objc_inform ("unable to add protocols from category %s...\n", category->category_name);
2594 _objc_inform ("class `%s' must be recompiled\n", category->class_name);
2595 }
2596 }
2597
2598 // Augment properties
2599 if (version >= 7 && category->instance_properties) {
2600 if (cls->isa->version >= 6) {
2601 _class_addProperties(cls, category->instance_properties);
2602 } else {
2603 _objc_inform ("unable to add properties from category %s...\n", category->category_name);
2604 _objc_inform ("class `%s' must be recompiled\n", category->class_name);
2605 }
2606 }
2607 }
2608
2609 /***********************************************************************
2610 * _objc_add_category_flush_caches. Install the specified category's
2611 * methods into the class it augments, and flush the class' method cache.
2612 * Return YES if some method caches now need to be flushed.
2613 **********************************************************************/
2614 static BOOL _objc_add_category_flush_caches(struct old_class *cls, struct old_category *category, int version)
2615 {
2616 BOOL needFlush = NO;
2617
2618 // Install the category's methods into its intended class
2619 OBJC_LOCK(&methodListLock);
2620 _objc_add_category (cls, category, version);
2621 OBJC_UNLOCK(&methodListLock);
2622
2623 // Queue for cache flushing so category's methods can get called
2624 if (category->instance_methods) {
2625 _class_setInfo((Class)cls, CLS_FLUSH_CACHE);
2626 needFlush = YES;
2627 }
2628 if (category->class_methods) {
2629 _class_setInfo((Class)cls->isa, CLS_FLUSH_CACHE);
2630 needFlush = YES;
2631 }
2632
2633 return needFlush;
2634 }
2635
2636
2637 /***********************************************************************
2638 * reverse_cat
2639 * Reverse the given linked list of pending categories.
2640 * The pending category list is built backwards, and needs to be
2641 * reversed before actually attaching the categories to a class.
2642 * Returns the head of the new linked list.
2643 **********************************************************************/
2644 static _objc_unresolved_category *reverse_cat(_objc_unresolved_category *cat)
2645 {
2646 if (!cat) return NULL;
2647
2648 _objc_unresolved_category *prev = NULL;
2649 _objc_unresolved_category *cur = cat;
2650 _objc_unresolved_category *ahead = cat->next;
2651
2652 while (cur) {
2653 ahead = cur->next;
2654 cur->next = prev;
2655 prev = cur;
2656 cur = ahead;
2657 }
2658
2659 return prev;
2660 }
2661
2662
2663 /***********************************************************************
2664 * resolve_categories_for_class.
2665 * Install all existing categories intended for the specified class.
2666 * cls must be a true class and not a metaclass.
2667 **********************************************************************/
2668 static void resolve_categories_for_class(struct old_class *cls)
2669 {
2670 _objc_unresolved_category * pending;
2671 _objc_unresolved_category * next;
2672
2673 // Nothing to do if there are no categories at all
2674 if (!category_hash) return;
2675
2676 // Locate and remove first element in category list
2677 // associated with this class
2678 pending = NXMapKeyFreeingRemove (category_hash, cls->name);
2679
2680 // Traverse the list of categories, if any, registered for this class
2681
2682 // The pending list is built backwards. Reverse it and walk forwards.
2683 pending = reverse_cat(pending);
2684
2685 while (pending) {
2686 if (pending->cat) {
2687 // Install the category
2688 // use the non-flush-cache version since we are only
2689 // called from the class intialization code
2690 _objc_add_category(cls, pending->cat, (int)pending->version);
2691 }
2692
2693 // Delink and reclaim this registration
2694 next = pending->next;
2695 _free_internal(pending);
2696 pending = next;
2697 }
2698 }
2699
2700
2701 /***********************************************************************
2702 * _objc_resolve_categories_for_class.
2703 * Public version of resolve_categories_for_class. This was
2704 * exported pre-10.4 for Omni et al. to workaround a problem
2705 * with too-lazy category attachment.
2706 * cls should be a class, but this function can also cope with metaclasses.
2707 **********************************************************************/
2708 void _objc_resolve_categories_for_class(Class cls_gen)
2709 {
2710 struct old_class *cls = _class_asOld(cls_gen);
2711
2712 // If cls is a metaclass, get the class.
2713 // resolve_categories_for_class() requires a real class to work correctly.
2714 if (ISMETA(cls)) {
2715 if (strncmp(cls->name, "_%", 2) == 0) {
2716 // Posee's meta's name is smashed and isn't in the class_hash,
2717 // so objc_getClass doesn't work.
2718 char *baseName = strchr(cls->name, '%'); // get posee's real name
2719 cls = _class_asOld(objc_getClass(baseName));
2720 } else {
2721 cls = _class_asOld(objc_getClass(cls->name));
2722 }
2723 }
2724
2725 resolve_categories_for_class(cls);
2726 }
2727
2728
2729 /***********************************************************************
2730 * _objc_register_category.
2731 * Process a category read from an image.
2732 * If the category's class exists, attach the category immediately.
2733 * Classes that need cache flushing are marked but not flushed.
2734 * If the category's class does not exist yet, pend the category for
2735 * later attachment. Pending categories are attached in the order
2736 * they were discovered.
2737 * Returns YES if some method caches now need to be flushed.
2738 **********************************************************************/
2739 static BOOL _objc_register_category(struct old_category *cat, int version)
2740 {
2741 _objc_unresolved_category * new_cat;
2742 _objc_unresolved_category * old;
2743 struct old_class *theClass;
2744
2745 // If the category's class exists, attach the category.
2746 if ((theClass = _class_asOld(objc_lookUpClass(cat->class_name)))) {
2747 return _objc_add_category_flush_caches(theClass, cat, version);
2748 }
2749
2750 // If the category's class exists but is unconnected,
2751 // then attach the category to the class but don't bother
2752 // flushing any method caches (because they must be empty).
2753 // YES unconnected, NO class_handler
2754 if ((theClass = _class_asOld(look_up_class(cat->class_name, YES, NO)))) {
2755 _objc_add_category(theClass, cat, version);
2756 return NO;
2757 }
2758
2759
2760 // Category's class does not exist yet.
2761 // Save the category for later attachment.
2762
2763 if (PrintConnecting) {
2764 _objc_inform("CONNECT: pending category '%s (%s)'", cat->class_name, cat->category_name);
2765 }
2766
2767 // Create category lookup table if needed
2768 if (!category_hash)
2769 category_hash = NXCreateMapTableFromZone (NXStrValueMapPrototype,
2770 128,
2771 _objc_internal_zone ());
2772
2773 // Locate an existing list of categories, if any, for the class.
2774 old = NXMapGet (category_hash, cat->class_name);
2775
2776 // Register the category to be fixed up later.
2777 // The category list is built backwards, and is reversed again
2778 // by resolve_categories_for_class().
2779 new_cat = _malloc_internal(sizeof(_objc_unresolved_category));
2780 new_cat->next = old;
2781 new_cat->cat = cat;
2782 new_cat->version = version;
2783 (void) NXMapKeyCopyingInsert (category_hash, cat->class_name, new_cat);
2784
2785 return NO;
2786 }
2787
2788
2789 __private_extern__ const char **
2790 _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount)
2791 {
2792 Module mods;
2793 int m;
2794 const char **list;
2795 int count;
2796 int allocated;
2797
2798 list = NULL;
2799 count = 0;
2800 allocated = 0;
2801
2802 mods = hi->mod_ptr;
2803 for (m = 0; m < hi->mod_count; m++) {
2804 int d;
2805
2806 if (!mods[m].symtab) continue;
2807
2808 for (d = 0; d < mods[m].symtab->cls_def_cnt; d++) {
2809 struct old_class *cls = mods[m].symtab->defs[d];
2810 // fixme what about future-ified classes?
2811 if (class_is_connected(cls)) {
2812 if (count == allocated) {
2813 allocated = allocated*2 + 16;
2814 list = realloc(list, allocated * sizeof(char *));
2815 }
2816 list[count++] = cls->name;
2817 }
2818 }
2819 }
2820
2821 if (count > 0) {
2822 // NULL-terminate non-empty list
2823 if (count == allocated) {
2824 allocated = allocated+1;
2825 list = realloc(list, allocated * sizeof(char *));
2826 }
2827 list[count] = NULL;
2828 }
2829
2830 if (outCount) *outCount = count;
2831 return list;
2832 }
2833
2834 #endif