]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-runtime-old.m
946f7f7c940d7aa859f25c5980319db05dec6670
[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 // Update GC layouts
876 // For paranoia, this is a conservative update: only non-strong -> strong
877 // is corrected. Any bugs will be leaks instead of crashes.
878 // rdar://5791689 covers any less-paranoid more-complete fix.
879 if (UseGC && supercls &&
880 (cls->info & CLS_EXT) && (supercls->info & CLS_EXT))
881 {
882 BOOL layoutsChanged = NO;
883 layout_bitmap ivarBitmap =
884 layout_bitmap_create(cls->ivar_layout,
885 cls->instance_size,
886 cls->instance_size, NO);
887
888 layout_bitmap superBitmap =
889 layout_bitmap_create(supercls->ivar_layout,
890 supercls->instance_size,
891 supercls->instance_size, NO);
892 layoutsChanged |= layout_bitmap_or(ivarBitmap, superBitmap, cls->name);
893 layout_bitmap_free(superBitmap);
894
895 if (layoutsChanged) {
896 // Rebuild layout strings.
897 if (PrintIvars) {
898 _objc_inform("IVARS: gc layout changed for class %s (super %s)",
899 cls->name, supercls->name);
900 }
901 cls->ivar_layout = layout_string_create(ivarBitmap);
902 }
903
904 layout_bitmap_free(ivarBitmap);
905 }
906
907 // Done!
908 cls->info |= CLS_CONNECTED;
909
910 OBJC_LOCK(&classLock);
911
912 // Update hash tables.
913 NXHashRemove(unconnected_class_hash, cls);
914 oldCls = NXHashInsert(class_hash, cls);
915
916 // Delete unconnected_class_hash if it is now empty.
917 if (NXCountHashTable(unconnected_class_hash) == 0) {
918 NXFreeHashTable(unconnected_class_hash);
919 unconnected_class_hash = NULL;
920 }
921
922 OBJC_UNLOCK(&classLock);
923
924 // Warn if the new class has the same name as a previously-installed class.
925 // The new class is kept and the old class is discarded.
926 if (oldCls) {
927 inform_duplicate(oldCls, cls);
928 }
929
930 // Connect newly-connectable subclasses
931 resolve_subclasses_of_class(cls);
932
933 // GC debugging: make sure all classes with -dealloc also have -finalize
934 if (DebugFinalizers) {
935 extern IMP findIMPInClass(struct old_class *cls, SEL sel);
936 if (findIMPInClass(cls, sel_getUid("dealloc")) &&
937 ! findIMPInClass(cls, sel_getUid("finalize")))
938 {
939 _objc_inform("GC: class '%s' implements -dealloc but not -finalize", cls->name);
940 }
941 }
942
943 // Debugging: if this class has ivars, make sure this class's ivars don't
944 // overlap with its super's. This catches some broken fragile base classes.
945 // Do not use super->instance_size vs. self->ivar[0] to check this.
946 // Ivars may be packed across instance_size boundaries.
947 if (DebugFragileSuperclasses && cls->ivars && cls->ivars->ivar_count) {
948 struct old_class *ivar_cls = supercls;
949
950 // Find closest superclass that has some ivars, if one exists.
951 while (ivar_cls &&
952 (!ivar_cls->ivars || ivar_cls->ivars->ivar_count == 0))
953 {
954 ivar_cls = ivar_cls->super_class;
955 }
956
957 if (ivar_cls) {
958 // Compare superclass's last ivar to this class's first ivar
959 struct old_ivar *super_ivar =
960 &ivar_cls->ivars->ivar_list[ivar_cls->ivars->ivar_count - 1];
961 struct old_ivar *self_ivar =
962 &cls->ivars->ivar_list[0];
963
964 // fixme could be smarter about super's ivar size
965 if (self_ivar->ivar_offset <= super_ivar->ivar_offset) {
966 _objc_inform("WARNING: ivars of superclass '%s' and "
967 "subclass '%s' overlap; superclass may have "
968 "changed since subclass was compiled",
969 ivar_cls->name, cls->name);
970 }
971 }
972 }
973 }
974
975
976 /***********************************************************************
977 * connect_class
978 * Connect class cls to its superclasses, if possible.
979 * If cls becomes connected, move it from unconnected_class_hash
980 * to connected_class_hash.
981 * Returns TRUE if cls is connected.
982 * Returns FALSE if cls could not be connected for some reason
983 * (missing superclass or still-unconnected superclass)
984 **********************************************************************/
985 static BOOL connect_class(struct old_class *cls)
986 {
987 if (class_is_connected(cls)) {
988 // This class is already connected to its superclass.
989 // Do nothing.
990 return TRUE;
991 }
992 else if (cls->super_class == NULL) {
993 // This class is a root class.
994 // Connect it to itself.
995
996 if (PrintConnecting) {
997 _objc_inform("CONNECT: class '%s' now connected (root class)",
998 cls->name);
999 }
1000
1001 really_connect_class(cls, NULL);
1002 return TRUE;
1003 }
1004 else {
1005 // This class is not a root class and is not yet connected.
1006 // Connect it if its superclass and root class are already connected.
1007 // Otherwise, add this class to the to-be-connected list,
1008 // pending the completion of its superclass and root class.
1009
1010 // At this point, cls->super_class and cls->isa->isa are still STRINGS
1011 char *supercls_name = (char *)cls->super_class;
1012 struct old_class *supercls;
1013
1014 // YES unconnected, YES class handler
1015 if (NULL == (supercls = _class_asOld(look_up_class(supercls_name, YES, YES)))) {
1016 // Superclass does not exist yet.
1017 // pendClassInstallation will handle duplicate pends of this class
1018 pendClassInstallation(cls, supercls_name);
1019
1020 if (PrintConnecting) {
1021 _objc_inform("CONNECT: class '%s' NOT connected (missing super)", cls->name);
1022 }
1023 return FALSE;
1024 }
1025
1026 if (! connect_class(supercls)) {
1027 // Superclass exists but is not yet connected.
1028 // pendClassInstallation will handle duplicate pends of this class
1029 pendClassInstallation(cls, supercls_name);
1030
1031 if (PrintConnecting) {
1032 _objc_inform("CONNECT: class '%s' NOT connected (unconnected super)", cls->name);
1033 }
1034 return FALSE;
1035 }
1036
1037 // Superclass exists and is connected.
1038 // Connect this class to the superclass.
1039
1040 if (PrintConnecting) {
1041 _objc_inform("CONNECT: class '%s' now connected", cls->name);
1042 }
1043
1044 really_connect_class(cls, supercls);
1045 return TRUE;
1046 }
1047 }
1048
1049
1050 /***********************************************************************
1051 * _objc_read_categories_from_image.
1052 * Read all categories from the given image.
1053 * Install them on their parent classes, or register them for later
1054 * installation.
1055 * Returns YES if some method caches now need to be flushed.
1056 **********************************************************************/
1057 static BOOL _objc_read_categories_from_image (header_info * hi)
1058 {
1059 Module mods;
1060 size_t midx;
1061 BOOL needFlush = NO;
1062
1063 if (_objcHeaderIsReplacement(hi)) {
1064 // Ignore any categories in this image
1065 return NO;
1066 }
1067
1068
1069 // Major loop - process all modules in the header
1070 mods = hi->mod_ptr;
1071
1072 // NOTE: The module and category lists are traversed backwards
1073 // to preserve the pre-10.4 processing order. Changing the order
1074 // would have a small chance of introducing binary compatibility bugs.
1075 midx = hi->mod_count;
1076 while (midx-- > 0) {
1077 unsigned int index;
1078 unsigned int total;
1079
1080 // Nothing to do for a module without a symbol table
1081 if (mods[midx].symtab == NULL)
1082 continue;
1083
1084 // Total entries in symbol table (class entries followed
1085 // by category entries)
1086 total = mods[midx].symtab->cls_def_cnt +
1087 mods[midx].symtab->cat_def_cnt;
1088
1089 // Minor loop - register all categories from given module
1090 index = total;
1091 while (index-- > mods[midx].symtab->cls_def_cnt) {
1092 struct old_category *cat = mods[midx].symtab->defs[index];
1093 needFlush |= _objc_register_category(cat, (int)mods[midx].version);
1094 }
1095 }
1096
1097 return needFlush;
1098 }
1099
1100
1101 /***********************************************************************
1102 * _objc_read_classes_from_image.
1103 * Read classes from the given image, perform assorted minor fixups,
1104 * scan for +load implementation.
1105 * Does not connect classes to superclasses.
1106 * Does attach pended categories to the classes.
1107 * Adds all classes to unconnected_class_hash. class_hash is unchanged.
1108 **********************************************************************/
1109 static void _objc_read_classes_from_image(header_info *hi)
1110 {
1111 unsigned int index;
1112 unsigned int midx;
1113 Module mods;
1114 int isBundle = (hi->mhdr->filetype == MH_BUNDLE);
1115
1116 if (_objcHeaderIsReplacement(hi)) {
1117 // Ignore any classes in this image
1118 return;
1119 }
1120
1121 // class_hash starts small, enough only for libobjc itself.
1122 // If other Objective-C libraries are found, immediately resize
1123 // class_hash, assuming that Foundation and AppKit are about
1124 // to add lots of classes.
1125 OBJC_LOCK(&classLock);
1126 if (hi->mhdr != (headerType *)&_mh_dylib_header && _NXHashCapacity(class_hash) < 1024) {
1127 _NXHashRehashToCapacity(class_hash, 1024);
1128 }
1129 OBJC_UNLOCK(&classLock);
1130
1131 // Major loop - process all modules in the image
1132 mods = hi->mod_ptr;
1133 for (midx = 0; midx < hi->mod_count; midx += 1)
1134 {
1135 // Skip module containing no classes
1136 if (mods[midx].symtab == NULL)
1137 continue;
1138
1139 // Minor loop - process all the classes in given module
1140 for (index = 0; index < mods[midx].symtab->cls_def_cnt; index += 1)
1141 {
1142 struct old_class *newCls, *oldCls;
1143
1144 // Locate the class description pointer
1145 newCls = mods[midx].symtab->defs[index];
1146
1147 // Classes loaded from Mach-O bundles can be unloaded later.
1148 // Nothing uses this class yet, so _class_setInfo is not needed.
1149 if (isBundle) newCls->info |= CLS_FROM_BUNDLE;
1150 if (isBundle) newCls->isa->info |= CLS_FROM_BUNDLE;
1151
1152 // Use common static empty cache instead of NULL
1153 if (newCls->cache == NULL)
1154 newCls->cache = (Cache) &_objc_empty_cache;
1155 if (newCls->isa->cache == NULL)
1156 newCls->isa->cache = (Cache) &_objc_empty_cache;
1157
1158 // Set metaclass version
1159 newCls->isa->version = mods[midx].version;
1160
1161 // methodLists is NULL or a single list, not an array
1162 newCls->info |= CLS_NO_METHOD_ARRAY|CLS_NO_PROPERTY_ARRAY;
1163 newCls->isa->info |= CLS_NO_METHOD_ARRAY|CLS_NO_PROPERTY_ARRAY;
1164
1165 // class has no subclasses for cache flushing
1166 newCls->info |= CLS_LEAF;
1167 newCls->isa->info |= CLS_LEAF;
1168
1169 if (mods[midx].version >= 6) {
1170 // class structure has ivar_layout and ext fields
1171 newCls->info |= CLS_EXT;
1172 newCls->isa->info |= CLS_EXT;
1173 }
1174
1175 // Check for +load implementation before categories are attached
1176 if (_class_hasLoadMethod((Class)newCls)) {
1177 newCls->isa->info |= CLS_HAS_LOAD_METHOD;
1178 }
1179
1180 // Install into unconnected_class_hash.
1181 OBJC_LOCK(&classLock);
1182
1183 if (future_class_hash) {
1184 struct old_class *futureCls =
1185 NXHashRemove(future_class_hash, newCls);
1186 if (futureCls) {
1187 // Another class structure for this class was already
1188 // prepared by objc_getFutureClass(). Use it instead.
1189 _free_internal((char *)futureCls->name);
1190 memcpy(futureCls, newCls, sizeof(*newCls));
1191 setOriginalClassForFutureClass(futureCls, newCls);
1192 newCls = futureCls;
1193
1194 if (NXCountHashTable(future_class_hash) == 0) {
1195 NXFreeHashTable(future_class_hash);
1196 future_class_hash = NULL;
1197 }
1198 }
1199 }
1200
1201 if (!unconnected_class_hash) {
1202 unconnected_class_hash =
1203 NXCreateHashTableFromZone(classHashPrototype, 128,
1204 NULL, _objc_internal_zone());
1205 }
1206
1207 oldCls = NXHashInsert(unconnected_class_hash, newCls);
1208 if (oldCls) {
1209 // Duplicate classes loaded.
1210 // newCls has been inserted over oldCls,
1211 // same as really_connect_class
1212 inform_duplicate(oldCls, newCls);
1213 }
1214
1215 OBJC_UNLOCK(&classLock);
1216
1217 // Fix up pended class refs to this class, if any
1218 resolve_references_to_class(newCls);
1219
1220 // Attach pended categories for this class, if any
1221 resolve_categories_for_class(newCls);
1222 }
1223 }
1224 }
1225
1226
1227 /***********************************************************************
1228 * _objc_connect_classes_from_image.
1229 * Connect the classes in the given image to their superclasses,
1230 * or register them for later connection if any superclasses are missing.
1231 **********************************************************************/
1232 static void _objc_connect_classes_from_image(header_info *hi)
1233 {
1234 unsigned int index;
1235 unsigned int midx;
1236 Module mods;
1237 BOOL replacement = _objcHeaderIsReplacement(hi);
1238
1239 // Major loop - process all modules in the image
1240 mods = hi->mod_ptr;
1241 for (midx = 0; midx < hi->mod_count; midx += 1)
1242 {
1243 // Skip module containing no classes
1244 if (mods[midx].symtab == NULL)
1245 continue;
1246
1247 // Minor loop - process all the classes in given module
1248 for (index = 0; index < mods[midx].symtab->cls_def_cnt; index += 1)
1249 {
1250 struct old_class *cls = mods[midx].symtab->defs[index];
1251 if (! replacement) {
1252 BOOL connected;
1253 struct old_class *futureCls = getFutureClassForOriginalClass(cls);
1254 if (futureCls) {
1255 // objc_getFutureClass() requested a different class
1256 // struct. Fix up the original struct's super_class
1257 // field for [super ...] use, but otherwise perform
1258 // fixups on the new class struct only.
1259 const char *super_name = (const char *) cls->super_class;
1260 if (super_name) cls->super_class = _class_asOld(objc_getClass(super_name));
1261 cls = futureCls;
1262 }
1263 connected = connect_class(cls);
1264 if (connected && callbackFunction) {
1265 (*callbackFunction)((Class)cls, 0);
1266 }
1267 } else {
1268 // Replacement image - fix up super_class only (#3704817)
1269 // And metaclass's super_class (#5351107)
1270 const char *super_name = (const char *) cls->super_class;
1271 if (super_name) {
1272 cls->super_class = _class_asOld(objc_getClass(super_name));
1273 // metaclass's superclass is superclass's metaclass
1274 cls->isa->super_class = cls->super_class->isa;
1275 } else {
1276 // Replacement for a root class
1277 // cls->super_class already NULL
1278 // root metaclass's superclass is root class
1279 cls->isa->super_class = cls;
1280 }
1281 }
1282 }
1283 }
1284 }
1285
1286
1287 /***********************************************************************
1288 * _objc_map_class_refs_for_image. Convert the class ref entries from
1289 * a class name string pointer to a class pointer. If the class does
1290 * not yet exist, the reference is added to a list of pending references
1291 * to be fixed up at a later date.
1292 **********************************************************************/
1293 static void fix_class_ref(struct old_class **ref, const char *name, BOOL isMeta)
1294 {
1295 struct old_class *cls;
1296
1297 // Get pointer to class of this name
1298 // YES unconnected, YES class loader
1299 cls = _class_asOld(look_up_class(name, YES, YES));
1300 if (cls) {
1301 // Referenced class exists. Fix up the reference.
1302 *ref = isMeta ? cls->isa : cls;
1303 } else {
1304 // Referenced class does not exist yet. Insert a placeholder
1305 // class and fix up the reference later.
1306 pendClassReference (ref, name, isMeta);
1307 *ref = (struct old_class *)_class_getNonexistentObjectClass();
1308 }
1309 }
1310
1311 static void _objc_map_class_refs_for_image (header_info * hi)
1312 {
1313 struct old_class **cls_refs;
1314 size_t count;
1315 unsigned int index;
1316
1317 // Locate class refs in image
1318 cls_refs = _getObjcClassRefs (hi, &count);
1319 if (cls_refs) {
1320 // Process each class ref
1321 for (index = 0; index < count; index += 1) {
1322 // Ref is initially class name char*
1323 const char *name = (const char *) cls_refs[index];
1324 if (name == NULL) {
1325 // rdar://5453039 is the entire page zero, or just this pointer
1326 uintptr_t *p = (uintptr_t *)(((uintptr_t)&cls_refs[index]) & ~0xfff);
1327 uintptr_t *end = (uintptr_t *)(((uintptr_t)p)+0x1000);
1328 int clear = 1;
1329 for ( ; p < end; p++) {
1330 if (*p != 0) {
1331 clear = 0;
1332 break;
1333 }
1334 }
1335 _objc_inform_on_crash("rdar://5453039 page around %p IS%s clear",
1336 &cls_refs[index], clear ? "" : " NOT");
1337 // crash in the usual spot so CrashTracer coalesces it
1338 }
1339 fix_class_ref(&cls_refs[index], name, NO /*never meta*/);
1340 }
1341 }
1342 }
1343
1344
1345 /***********************************************************************
1346 * _objc_remove_pending_class_refs_in_image
1347 * Delete any pending class ref fixups for class refs in the given image,
1348 * because the image is about to be unloaded.
1349 **********************************************************************/
1350 static void removePendingReferences(struct old_class **refs, size_t count)
1351 {
1352 struct old_class **end = refs + count;
1353
1354 if (!refs) return;
1355 if (!pendingClassRefsMap) return;
1356
1357 // Search the pending class ref table for class refs in this range.
1358 // The class refs may have already been stomped with nonexistentClass,
1359 // so there's no way to recover the original class name.
1360
1361 const char *key;
1362 PendingClassRef *pending;
1363 NXMapState state = NXInitMapState(pendingClassRefsMap);
1364 while(NXNextMapState(pendingClassRefsMap, &state,
1365 (const void **)&key, (const void **)&pending))
1366 {
1367 for ( ; pending != NULL; pending = pending->next) {
1368 if (pending->ref >= refs && pending->ref < end) {
1369 pending->ref = NULL;
1370 }
1371 }
1372 }
1373 }
1374
1375 static void _objc_remove_pending_class_refs_in_image(header_info *hi)
1376 {
1377 struct old_class **cls_refs;
1378 size_t count;
1379
1380 // Locate class refs in this image
1381 cls_refs = _getObjcClassRefs(hi, &count);
1382 removePendingReferences(cls_refs, count);
1383 }
1384
1385
1386 /***********************************************************************
1387 * map_selrefs. For each selector in the specified array,
1388 * replace the name pointer with a uniqued selector.
1389 * If copy is TRUE, all selector data is always copied. This is used
1390 * for registering selectors from unloadable bundles, so the selector
1391 * can still be used after the bundle's data segment is unmapped.
1392 * Returns YES if dst was written to, NO if it was unchanged.
1393 **********************************************************************/
1394 static inline BOOL map_selrefs(SEL *src, SEL *dst, size_t size, BOOL copy)
1395 {
1396 BOOL result = NO;
1397 size_t cnt = size / sizeof(SEL);
1398 size_t index;
1399
1400 sel_lock();
1401
1402 // Process each selector
1403 for (index = 0; index < cnt; index += 1)
1404 {
1405 SEL sel;
1406
1407 // Lookup pointer to uniqued string
1408 sel = sel_registerNameNoLock((const char *) src[index], copy);
1409
1410 // Replace this selector with uniqued one (avoid
1411 // modifying the VM page if this would be a NOP)
1412 if (dst[index] != sel) {
1413 dst[index] = sel;
1414 result = YES;
1415 }
1416 }
1417
1418 sel_unlock();
1419
1420 return result;
1421 }
1422
1423
1424 /***********************************************************************
1425 * map_message_refs. For each message ref in the specified array,
1426 * replace the name pointer with a uniqued selector.
1427 * If copy is TRUE, all selector data is always copied. This is used
1428 * for registering selectors from unloadable bundles, so the selector
1429 * can still be used after the bundle's data segment is unmapped.
1430 * Returns YES if dst was written to, NO if it was unchanged.
1431 **********************************************************************/
1432 static inline BOOL map_message_refs(message_ref *src, message_ref *dst, size_t size, BOOL copy)
1433 {
1434 BOOL result = NO;
1435 size_t cnt = size / sizeof(message_ref);
1436 size_t index;
1437
1438 sel_lock();
1439
1440 // Process each selector
1441 for (index = 0; index < cnt; index += 1)
1442 {
1443 SEL sel;
1444
1445 // Lookup pointer to uniqued string
1446 sel = sel_registerNameNoLock((const char *) src[index].sel, copy);
1447
1448 // Replace this selector with uniqued one (avoid
1449 // modifying the VM page if this would be a NOP)
1450 if (dst[index].sel != sel) {
1451 dst[index].sel = sel;
1452 result = YES;
1453 }
1454 }
1455
1456 sel_unlock();
1457
1458 return result;
1459 }
1460
1461
1462 /***********************************************************************
1463 * map_method_descs. For each method in the specified method list,
1464 * replace the name pointer with a uniqued selector.
1465 * If copy is TRUE, all selector data is always copied. This is used
1466 * for registering selectors from unloadable bundles, so the selector
1467 * can still be used after the bundle's data segment is unmapped.
1468 **********************************************************************/
1469 static void map_method_descs (struct objc_method_description_list * methods, BOOL copy)
1470 {
1471 unsigned int index;
1472
1473 if (!methods) return;
1474
1475 sel_lock();
1476
1477 // Process each method
1478 for (index = 0; index < methods->count; index += 1)
1479 {
1480 struct objc_method_description * method;
1481 SEL sel;
1482
1483 // Get method entry to fix up
1484 method = &methods->list[index];
1485
1486 // Lookup pointer to uniqued string
1487 sel = sel_registerNameNoLock((const char *) method->name, copy);
1488
1489 // Replace this selector with uniqued one (avoid
1490 // modifying the VM page if this would be a NOP)
1491 if (method->name != sel)
1492 method->name = sel;
1493 }
1494
1495 sel_unlock();
1496 }
1497
1498
1499 /***********************************************************************
1500 * ext_for_protocol
1501 * Returns the protocol extension for the given protocol.
1502 * Returns NULL if the protocol has no extension.
1503 **********************************************************************/
1504 static struct old_protocol_ext *ext_for_protocol(struct old_protocol *proto)
1505 {
1506 if (!proto) return NULL;
1507 if (!protocol_ext_map) return NULL;
1508 else return (struct old_protocol_ext *)NXMapGet(protocol_ext_map, proto);
1509 }
1510
1511
1512 /***********************************************************************
1513 * lookup_method
1514 * Search a protocol method list for a selector.
1515 **********************************************************************/
1516 static struct objc_method_description *
1517 lookup_method(struct objc_method_description_list *mlist, SEL aSel)
1518 {
1519 if (mlist) {
1520 int i;
1521 for (i = 0; i < mlist->count; i++) {
1522 if (mlist->list[i].name == aSel) {
1523 return mlist->list+i;
1524 }
1525 }
1526 }
1527 return NULL;
1528 }
1529
1530
1531 /***********************************************************************
1532 * lookup_protocol_method
1533 * Recursively search for a selector in a protocol
1534 * (and all incorporated protocols)
1535 **********************************************************************/
1536 __private_extern__ struct objc_method_description *
1537 lookup_protocol_method(struct old_protocol *proto, SEL aSel,
1538 BOOL isRequiredMethod, BOOL isInstanceMethod)
1539 {
1540 struct objc_method_description *m = NULL;
1541 struct old_protocol_ext *ext;
1542
1543 if (isRequiredMethod) {
1544 if (isInstanceMethod) {
1545 m = lookup_method(proto->instance_methods, aSel);
1546 } else {
1547 m = lookup_method(proto->class_methods, aSel);
1548 }
1549 } else if ((ext = ext_for_protocol(proto))) {
1550 if (isInstanceMethod) {
1551 m = lookup_method(ext->optional_instance_methods, aSel);
1552 } else {
1553 m = lookup_method(ext->optional_class_methods, aSel);
1554 }
1555 }
1556
1557 if (!m && proto->protocol_list) {
1558 int i;
1559 for (i = 0; !m && i < proto->protocol_list->count; i++) {
1560 m = lookup_protocol_method(proto->protocol_list->list[i], aSel,
1561 isRequiredMethod, isInstanceMethod);
1562 }
1563 }
1564
1565 return m;
1566 }
1567
1568
1569 /***********************************************************************
1570 * protocol_getName
1571 * Returns the name of the given protocol.
1572 **********************************************************************/
1573 const char *protocol_getName(Protocol *p)
1574 {
1575 struct old_protocol *proto = oldprotocol(p);
1576 if (!proto) return "nil";
1577 return proto->protocol_name;
1578 }
1579
1580
1581 /***********************************************************************
1582 * protocol_getMethodDescription
1583 * Returns the description of a named method.
1584 * Searches either required or optional methods.
1585 * Searches either instance or class methods.
1586 **********************************************************************/
1587 struct objc_method_description
1588 protocol_getMethodDescription(Protocol *p, SEL aSel,
1589 BOOL isRequiredMethod, BOOL isInstanceMethod)
1590 {
1591 struct old_protocol *proto = oldprotocol(p);
1592 if (!proto) return (struct objc_method_description){NULL, NULL};
1593
1594 struct objc_method_description *desc =
1595 lookup_protocol_method(proto, aSel,
1596 isRequiredMethod, isInstanceMethod);
1597 if (desc) return *desc;
1598 else return (struct objc_method_description){NULL, NULL};
1599 }
1600
1601
1602 /***********************************************************************
1603 * protocol_copyMethodDescriptionList
1604 * Returns an array of method descriptions from a protocol.
1605 * Copies either required or optional methods.
1606 * Copies either instance or class methods.
1607 **********************************************************************/
1608 struct objc_method_description *
1609 protocol_copyMethodDescriptionList(Protocol *p,
1610 BOOL isRequiredMethod,
1611 BOOL isInstanceMethod,
1612 unsigned int *outCount)
1613 {
1614 struct objc_method_description_list *mlist = NULL;
1615 struct old_protocol *proto = oldprotocol(p);
1616 struct old_protocol_ext *ext;
1617
1618 if (!proto) {
1619 if (outCount) *outCount = 0;
1620 return NULL;
1621 }
1622
1623 if (isRequiredMethod) {
1624 if (isInstanceMethod) {
1625 mlist = proto->instance_methods;
1626 } else {
1627 mlist = proto->class_methods;
1628 }
1629 } else if ((ext = ext_for_protocol(proto))) {
1630 if (isInstanceMethod) {
1631 mlist = ext->optional_instance_methods;
1632 } else {
1633 mlist = ext->optional_class_methods;
1634 }
1635 }
1636
1637 if (!mlist) {
1638 if (outCount) *outCount = 0;
1639 return NULL;
1640 }
1641
1642 unsigned int i;
1643 unsigned int count = mlist->count;
1644 struct objc_method_description *result =
1645 calloc(count + 1, sizeof(struct objc_method_description));
1646 for (i = 0; i < count; i++) {
1647 result[i] = mlist->list[i];
1648 }
1649
1650 if (outCount) *outCount = count;
1651 return result;
1652 }
1653
1654
1655 Property protocol_getProperty(Protocol *p, const char *name,
1656 BOOL isRequiredProperty, BOOL isInstanceProperty)
1657 {
1658 struct old_protocol *proto = oldprotocol(p);
1659
1660 if (!proto || !name) return NULL;
1661
1662 if (!isRequiredProperty || !isInstanceProperty) {
1663 // Only required instance properties are currently supported
1664 return NULL;
1665 }
1666
1667 struct old_protocol_ext *ext;
1668 if ((ext = ext_for_protocol(proto))) {
1669 struct objc_property_list *plist;
1670 if ((plist = ext->instance_properties)) {
1671 uint32_t i;
1672 for (i = 0; i < plist->count; i++) {
1673 Property prop = property_list_nth(plist, i);
1674 if (0 == strcmp(name, prop->name)) {
1675 return prop;
1676 }
1677 }
1678 }
1679 }
1680
1681 struct old_protocol_list *plist;
1682 if ((plist = proto->protocol_list)) {
1683 int i;
1684 for (i = 0; i < plist->count; i++) {
1685 Property prop =
1686 protocol_getProperty((Protocol *)plist->list[i], name,
1687 isRequiredProperty, isInstanceProperty);
1688 if (prop) return prop;
1689 }
1690 }
1691
1692 return NULL;
1693 }
1694
1695
1696 Property *protocol_copyPropertyList(Protocol *p, unsigned int *outCount)
1697 {
1698 Property *result = NULL;
1699 struct old_protocol_ext *ext;
1700
1701 struct old_protocol *proto = oldprotocol(p);
1702 if (! (ext = ext_for_protocol(proto))) {
1703 if (outCount) *outCount = 0;
1704 return NULL;
1705 }
1706
1707 struct objc_property_list *plist = ext->instance_properties;
1708 result = copyPropertyList(plist, outCount);
1709
1710 return result;
1711 }
1712
1713
1714 /***********************************************************************
1715 * protocol_copyProtocolList
1716 * Copies this protocol's incorporated protocols.
1717 * Does not copy those protocol's incorporated protocols in turn.
1718 **********************************************************************/
1719 Protocol **protocol_copyProtocolList(Protocol *p, unsigned int *outCount)
1720 {
1721 unsigned int count = 0;
1722 Protocol **result = NULL;
1723 struct old_protocol *proto = oldprotocol(p);
1724
1725 if (!proto) {
1726 if (outCount) *outCount = 0;
1727 return NULL;
1728 }
1729
1730 if (proto->protocol_list) {
1731 count = (unsigned int)proto->protocol_list->count;
1732 }
1733 if (count > 0) {
1734 result = malloc((count+1) * sizeof(Protocol *));
1735
1736 unsigned int i;
1737 for (i = 0; i < count; i++) {
1738 result[i] = (Protocol *)proto->protocol_list->list[i];
1739 }
1740 result[i] = NULL;
1741 }
1742
1743 if (outCount) *outCount = count;
1744 return result;
1745 }
1746
1747
1748 BOOL protocol_conformsToProtocol(Protocol *self_gen, Protocol *other_gen)
1749 {
1750 struct old_protocol *self = oldprotocol(self_gen);
1751 struct old_protocol *other = oldprotocol(other_gen);
1752
1753 if (!self || !other) {
1754 return NO;
1755 }
1756
1757 if (0 == strcmp(self->protocol_name, other->protocol_name)) {
1758 return YES;
1759 }
1760
1761 if (self->protocol_list) {
1762 int i;
1763 for (i = 0; i < self->protocol_list->count; i++) {
1764 struct old_protocol *proto = self->protocol_list->list[i];
1765 if (0 == strcmp(other->protocol_name, proto->protocol_name)) {
1766 return YES;
1767 }
1768 if (protocol_conformsToProtocol((Protocol *)proto, other_gen)) {
1769 return YES;
1770 }
1771 }
1772 }
1773
1774 return NO;
1775 }
1776
1777
1778 BOOL protocol_isEqual(Protocol *self, Protocol *other)
1779 {
1780 if (self == other) return YES;
1781 if (!self || !other) return NO;
1782
1783 if (!protocol_conformsToProtocol(self, other)) return NO;
1784 if (!protocol_conformsToProtocol(other, self)) return NO;
1785
1786 return YES;
1787 }
1788
1789
1790 /***********************************************************************
1791 * _objc_fixup_protocol_objects_for_image. For each protocol in the
1792 * specified image, selectorize the method names and add to the protocol hash.
1793 **********************************************************************/
1794
1795 static BOOL versionIsExt(uintptr_t version, const char *names, size_t size)
1796 {
1797 // CodeWarrior used isa field for string "Protocol"
1798 // from section __OBJC,__class_names. rdar://4951638
1799 // gcc (10.4 and earlier) used isa field for version number;
1800 // the only version number used on Mac OS X was 2.
1801 // gcc (10.5 and later) uses isa field for ext pointer
1802
1803 if (version < 4096) {
1804 return NO;
1805 }
1806
1807 if (version >= (uintptr_t)names && version < (uintptr_t)(names + size)) {
1808 return NO;
1809 }
1810
1811 return YES;
1812 }
1813
1814 static void fix_protocol(struct old_protocol *proto, Class protocolClass,
1815 BOOL isBundle, const char *names, size_t names_size)
1816 {
1817 #warning GrP fixme hack
1818 if (!proto) return;
1819
1820 uintptr_t version = (uintptr_t)proto->isa;
1821
1822 // Set the protocol's isa
1823 proto->isa = protocolClass;
1824
1825 // Fix up method lists
1826 // fixme share across duplicates
1827 map_method_descs (proto->instance_methods, isBundle);
1828 map_method_descs (proto->class_methods, isBundle);
1829
1830 // Fix up ext, if any
1831 if (versionIsExt(version, names, names_size)) {
1832 struct old_protocol_ext *ext = (struct old_protocol_ext *)version;
1833 NXMapInsert(protocol_ext_map, proto, ext);
1834 map_method_descs (ext->optional_instance_methods, isBundle);
1835 map_method_descs (ext->optional_class_methods, isBundle);
1836 }
1837
1838 // Record the protocol it if we don't have one with this name yet
1839 // fixme bundles - copy protocol
1840 // fixme unloading
1841 if (!NXMapGet(protocol_map, proto->protocol_name)) {
1842 NXMapKeyCopyingInsert(protocol_map, proto->protocol_name, proto);
1843 if (PrintProtocols) {
1844 _objc_inform("PROTOCOLS: protocol at %p is %s",
1845 proto, proto->protocol_name);
1846 }
1847 } else {
1848 // duplicate - do nothing
1849 if (PrintProtocols) {
1850 _objc_inform("PROTOCOLS: protocol at %p is %s (duplicate)",
1851 proto, proto->protocol_name);
1852 }
1853 }
1854 }
1855
1856 static void _objc_fixup_protocol_objects_for_image (header_info * hi)
1857 {
1858 Class protocolClass = objc_getClass("Protocol");
1859 size_t count, i;
1860 struct old_protocol *protos;
1861 int isBundle = hi->mhdr->filetype == MH_BUNDLE;
1862 const char *names;
1863 size_t names_size;
1864
1865 OBJC_LOCK(&classLock);
1866
1867 // Allocate the protocol registry if necessary.
1868 if (!protocol_map) {
1869 protocol_map =
1870 NXCreateMapTableFromZone(NXStrValueMapPrototype, 32,
1871 _objc_internal_zone());
1872 }
1873 if (!protocol_ext_map) {
1874 protocol_ext_map =
1875 NXCreateMapTableFromZone(NXPtrValueMapPrototype, 32,
1876 _objc_internal_zone());
1877 }
1878
1879 protos = _getObjcProtocols(hi, &count);
1880 names = _getObjcClassNames(hi, &names_size);
1881 for (i = 0; i < count; i++) {
1882 fix_protocol(&protos[i], protocolClass, isBundle, names, names_size);
1883 }
1884
1885 OBJC_UNLOCK(&classLock);
1886 }
1887
1888
1889 /***********************************************************************
1890 * _objc_fixup_selector_refs. Register all of the selectors in each
1891 * image, and fix them all up.
1892 **********************************************************************/
1893 static void _objc_fixup_selector_refs (const header_info *hi)
1894 {
1895 size_t count;
1896 SEL *sels;
1897
1898 // Fix up selector refs
1899 sels = _getObjcSelectorRefs (hi, &count);
1900 if (sels) {
1901 map_selrefs(sels, sels, count * sizeof(SEL),
1902 hi->mhdr->filetype == MH_BUNDLE);
1903 }
1904 }
1905
1906
1907 /***********************************************************************
1908 * _read_images
1909 * Perform metadata processing for hCount images starting with firstNewHeader
1910 **********************************************************************/
1911 __private_extern__ void _read_images(header_info **hList, uint32_t hCount)
1912 {
1913 uint32_t i;
1914
1915 if (!class_hash) _objc_init_class_hash();
1916
1917 // Parts of this order are important for correctness or performance.
1918
1919 // Read classes from all images.
1920 for (i = 0; i < hCount; i++) {
1921 _objc_read_classes_from_image(hList[i]);
1922 }
1923
1924 // Read categories from all images.
1925 BOOL needFlush = NO;
1926 for (i = 0; i < hCount; i++) {
1927 needFlush |= _objc_read_categories_from_image(hList[i]);
1928 }
1929 if (needFlush) flush_marked_caches();
1930
1931 // Connect classes from all images.
1932 for (i = 0; i < hCount; i++) {
1933 _objc_connect_classes_from_image(hList[i]);
1934 }
1935
1936 // Fix up class refs, selector refs, and protocol objects from all images.
1937 for (i = 0; i < hCount; i++) {
1938 _objc_map_class_refs_for_image(hList[i]);
1939 _objc_fixup_selector_refs(hList[i]);
1940 _objc_fixup_protocol_objects_for_image(hList[i]);
1941 }
1942 }
1943
1944
1945 /***********************************************************************
1946 * prepare_load_methods
1947 * Schedule +load for classes in this image, any un-+load-ed
1948 * superclasses in other images, and any categories in this image.
1949 **********************************************************************/
1950 // Recursively schedule +load for cls and any un-+load-ed superclasses.
1951 // cls must already be connected.
1952 static void schedule_class_load(struct old_class *cls)
1953 {
1954 if (cls->info & CLS_LOADED) return;
1955 if (cls->super_class) schedule_class_load(cls->super_class);
1956 add_class_to_loadable_list((Class)cls);
1957 cls->info |= CLS_LOADED;
1958 }
1959
1960 __private_extern__ void prepare_load_methods(header_info *hi)
1961 {
1962 Module mods;
1963 unsigned int midx;
1964
1965
1966 if (_objcHeaderIsReplacement(hi)) {
1967 // Ignore any classes in this image
1968 return;
1969 }
1970
1971 // Major loop - process all modules in the image
1972 mods = hi->mod_ptr;
1973 for (midx = 0; midx < hi->mod_count; midx += 1)
1974 {
1975 unsigned int index;
1976
1977 // Skip module containing no classes
1978 if (mods[midx].symtab == NULL)
1979 continue;
1980
1981 // Minor loop - process all the classes in given module
1982 for (index = 0; index < mods[midx].symtab->cls_def_cnt; index += 1)
1983 {
1984 // Locate the class description pointer
1985 struct old_class *cls = mods[midx].symtab->defs[index];
1986 if (cls->info & CLS_CONNECTED) {
1987 schedule_class_load(cls);
1988 }
1989 }
1990 }
1991
1992
1993 // Major loop - process all modules in the header
1994 mods = hi->mod_ptr;
1995
1996 // NOTE: The module and category lists are traversed backwards
1997 // to preserve the pre-10.4 processing order. Changing the order
1998 // would have a small chance of introducing binary compatibility bugs.
1999 midx = hi->mod_count;
2000 while (midx-- > 0) {
2001 unsigned int index;
2002 unsigned int total;
2003 Symtab symtab = mods[midx].symtab;
2004
2005 // Nothing to do for a module without a symbol table
2006 if (mods[midx].symtab == NULL)
2007 continue;
2008 // Total entries in symbol table (class entries followed
2009 // by category entries)
2010 total = mods[midx].symtab->cls_def_cnt +
2011 mods[midx].symtab->cat_def_cnt;
2012
2013 // Minor loop - register all categories from given module
2014 index = total;
2015 while (index-- > mods[midx].symtab->cls_def_cnt) {
2016 struct old_category *cat = symtab->defs[index];
2017 add_category_to_loadable_list((Category)cat);
2018 }
2019 }
2020 }
2021
2022
2023 /***********************************************************************
2024 * _objc_remove_classes_in_image
2025 * Remove all classes in the given image from the runtime, because
2026 * the image is about to be unloaded.
2027 * Things to clean up:
2028 * class_hash
2029 * unconnected_class_hash
2030 * pending subclasses list (only if class is still unconnected)
2031 * loadable class list
2032 * class's method caches
2033 * class refs in all other images
2034 **********************************************************************/
2035 // Re-pend any class references in refs that point into [start..end)
2036 static void rependClassReferences(struct old_class **refs, size_t count,
2037 uintptr_t start, uintptr_t end)
2038 {
2039 size_t i;
2040
2041 if (!refs) return;
2042
2043 // Process each class ref
2044 for (i = 0; i < count; i++) {
2045 if ((uintptr_t)(refs[i]) >= start && (uintptr_t)(refs[i]) < end) {
2046 pendClassReference(&refs[i], refs[i]->name,
2047 (refs[i]->info & CLS_META) ? YES : NO);
2048 refs[i] = (struct old_class *)_class_getNonexistentObjectClass();
2049 }
2050 }
2051 }
2052
2053
2054 static void try_free(const void *p)
2055 {
2056 if (p && malloc_size(p)) free((void *)p);
2057 }
2058
2059 // Deallocate all memory in a method list
2060 static void unload_mlist(struct old_method_list *mlist)
2061 {
2062 int i;
2063 if (mlist->obsolete == _OBJC_FIXED_UP) {
2064 for (i = 0; i < mlist->method_count; i++) {
2065 try_free(mlist->method_list[i].method_types);
2066 }
2067
2068 try_free(mlist);
2069 }
2070 }
2071
2072 // Deallocate all memory in a class.
2073 __private_extern__ void unload_class(struct old_class *cls)
2074 {
2075 // Free ivar lists
2076 if (cls->ivars) {
2077 int i;
2078 for (i = 0; i < cls->ivars->ivar_count; i++) {
2079 try_free(cls->ivars->ivar_list[i].ivar_name);
2080 try_free(cls->ivars->ivar_list[i].ivar_type);
2081 }
2082 try_free(cls->ivars);
2083 }
2084
2085 // Free fixed-up method lists and method list array
2086 if (cls->methodLists) {
2087 // more than zero method lists
2088 if (cls->info & CLS_NO_METHOD_ARRAY) {
2089 // one method list
2090 unload_mlist((struct old_method_list *)cls->methodLists);
2091 }
2092 else {
2093 // more than one method list
2094 struct old_method_list **mlistp;
2095 for (mlistp = cls->methodLists;
2096 *mlistp != NULL && *mlistp != END_OF_METHODS_LIST;
2097 mlistp++)
2098 {
2099 unload_mlist(*mlistp);
2100 }
2101 free(cls->methodLists);
2102 }
2103 }
2104
2105 // Free protocol list
2106 struct old_protocol_list *protos = cls->protocols;
2107 while (protos) {
2108 struct old_protocol_list *dead = protos;
2109 protos = protos->next;
2110 try_free(dead);
2111 }
2112
2113 // Free method cache
2114 if (cls->cache && cls->cache != &_objc_empty_cache) {
2115 _cache_free(cls->cache);
2116 }
2117
2118 if ((cls->info & CLS_EXT)) {
2119 if (cls->ext) {
2120 // Free property lists and property list array
2121 if (cls->ext->propertyLists) {
2122 // more than zero property lists
2123 if (cls->info & CLS_NO_PROPERTY_ARRAY) {
2124 // one property list
2125 try_free(cls->ext->propertyLists);
2126 } else {
2127 // more than one property list
2128 struct objc_property_list **plistp;
2129 for (plistp = cls->ext->propertyLists;
2130 *plistp != NULL;
2131 plistp++)
2132 {
2133 try_free(*plistp);
2134 }
2135 try_free(cls->ext->propertyLists);
2136 }
2137 }
2138
2139 // Free weak ivar layout
2140 try_free(cls->ext->weak_ivar_layout);
2141
2142 // Free ext
2143 try_free(cls->ext);
2144 }
2145
2146 // Free non-weak ivar layout
2147 try_free(cls->ivar_layout);
2148 }
2149
2150 // Free class name
2151 try_free(cls->name);
2152
2153 // Free cls
2154 try_free(cls);
2155 }
2156
2157
2158 static void _objc_remove_classes_in_image(header_info *hi)
2159 {
2160 unsigned int index;
2161 unsigned int midx;
2162 Module mods;
2163
2164 OBJC_LOCK(&classLock);
2165
2166 // Major loop - process all modules in the image
2167 mods = hi->mod_ptr;
2168 for (midx = 0; midx < hi->mod_count; midx += 1)
2169 {
2170 // Skip module containing no classes
2171 if (mods[midx].symtab == NULL)
2172 continue;
2173
2174 // Minor loop - process all the classes in given module
2175 for (index = 0; index < mods[midx].symtab->cls_def_cnt; index += 1)
2176 {
2177 struct old_class *cls;
2178
2179 // Locate the class description pointer
2180 cls = mods[midx].symtab->defs[index];
2181
2182 // Remove from loadable class list, if present
2183 remove_class_from_loadable_list((Class)cls);
2184
2185 // Remove from unconnected_class_hash and pending subclasses
2186 if (unconnected_class_hash && NXHashMember(unconnected_class_hash, cls)) {
2187 NXHashRemove(unconnected_class_hash, cls);
2188 if (pendingSubclassesMap) {
2189 // Find this class in its superclass's pending list
2190 char *supercls_name = (char *)cls->super_class;
2191 PendingSubclass *pending =
2192 NXMapGet(pendingSubclassesMap, supercls_name);
2193 for ( ; pending != NULL; pending = pending->next) {
2194 if (pending->subclass == cls) {
2195 pending->subclass = Nil;
2196 break;
2197 }
2198 }
2199 }
2200 }
2201
2202 // Remove from class_hash
2203 NXHashRemove(class_hash, cls);
2204
2205 // Free heap memory pointed to by the class
2206 unload_class(cls->isa);
2207 unload_class(cls);
2208 }
2209 }
2210
2211
2212 // Search all other images for class refs that point back to this range.
2213 // Un-fix and re-pend any such class refs.
2214
2215 // Get the location of the dying image's __OBJC segment
2216 uintptr_t seg = hi->objcSegmentHeader->vmaddr + hi->image_slide;
2217 size_t seg_size = hi->objcSegmentHeader->filesize;
2218
2219 header_info *other_hi;
2220 for (other_hi = _objc_headerStart();
2221 other_hi != NULL;
2222 other_hi = other_hi->next)
2223 {
2224 struct old_class **other_refs;
2225 size_t count;
2226 if (other_hi == hi) continue; // skip the image being unloaded
2227
2228 // Fix class refs in the other image
2229 other_refs = _getObjcClassRefs(other_hi, &count);
2230 rependClassReferences(other_refs, count, seg, seg+seg_size);
2231 }
2232
2233 OBJC_UNLOCK(&classLock);
2234 }
2235
2236
2237 /***********************************************************************
2238 * _objc_remove_categories_in_image
2239 * Remove all categories in the given image from the runtime, because
2240 * the image is about to be unloaded.
2241 * Things to clean up:
2242 * unresolved category list
2243 * loadable category list
2244 **********************************************************************/
2245 static void _objc_remove_categories_in_image(header_info *hi)
2246 {
2247 Module mods;
2248 unsigned int midx;
2249
2250 // Major loop - process all modules in the header
2251 mods = hi->mod_ptr;
2252
2253 for (midx = 0; midx < hi->mod_count; midx++) {
2254 unsigned int index;
2255 unsigned int total;
2256 Symtab symtab = mods[midx].symtab;
2257
2258 // Nothing to do for a module without a symbol table
2259 if (symtab == NULL) continue;
2260
2261 // Total entries in symbol table (class entries followed
2262 // by category entries)
2263 total = symtab->cls_def_cnt + symtab->cat_def_cnt;
2264
2265 // Minor loop - check all categories from given module
2266 for (index = symtab->cls_def_cnt; index < total; index++) {
2267 struct old_category *cat = symtab->defs[index];
2268
2269 // Clean up loadable category list
2270 remove_category_from_loadable_list((Category)cat);
2271
2272 // Clean up category_hash
2273 if (category_hash) {
2274 _objc_unresolved_category *cat_entry =
2275 NXMapGet(category_hash, cat->class_name);
2276 for ( ; cat_entry != NULL; cat_entry = cat_entry->next) {
2277 if (cat_entry->cat == cat) {
2278 cat_entry->cat = NULL;
2279 break;
2280 }
2281 }
2282 }
2283 }
2284 }
2285 }
2286
2287
2288 /***********************************************************************
2289 * unload_paranoia
2290 * Various paranoid debugging checks that look for poorly-behaving
2291 * unloadable bundles.
2292 * Called by _objc_unmap_image when OBJC_UNLOAD_DEBUG is set.
2293 **********************************************************************/
2294 static void unload_paranoia(header_info *hi)
2295 {
2296 // Get the location of the dying image's __OBJC segment
2297 uintptr_t seg = hi->objcSegmentHeader->vmaddr + hi->image_slide;
2298 size_t seg_size = hi->objcSegmentHeader->filesize;
2299
2300 _objc_inform("UNLOAD DEBUG: unloading image '%s' [%p..%p]",
2301 _nameForHeader(hi->mhdr), (void *)seg, (void*)(seg+seg_size));
2302
2303 OBJC_LOCK(&classLock);
2304
2305 // Make sure the image contains no categories on surviving classes.
2306 {
2307 Module mods;
2308 unsigned int midx;
2309
2310 // Major loop - process all modules in the header
2311 mods = hi->mod_ptr;
2312
2313 for (midx = 0; midx < hi->mod_count; midx++) {
2314 unsigned int index;
2315 unsigned int total;
2316 Symtab symtab = mods[midx].symtab;
2317
2318 // Nothing to do for a module without a symbol table
2319 if (symtab == NULL) continue;
2320
2321 // Total entries in symbol table (class entries followed
2322 // by category entries)
2323 total = symtab->cls_def_cnt + symtab->cat_def_cnt;
2324
2325 // Minor loop - check all categories from given module
2326 for (index = symtab->cls_def_cnt; index < total; index++) {
2327 struct old_category *cat = symtab->defs[index];
2328 struct old_class query;
2329
2330 query.name = cat->class_name;
2331 if (NXHashMember(class_hash, &query)) {
2332 _objc_inform("UNLOAD DEBUG: dying image contains category '%s(%s)' on surviving class '%s'!", cat->class_name, cat->category_name, cat->class_name);
2333 }
2334 }
2335 }
2336 }
2337
2338 // Make sure no surviving class is in the dying image.
2339 // Make sure no surviving class has a superclass in the dying image.
2340 // fixme check method implementations too
2341 {
2342 struct old_class *cls;
2343 NXHashState state;
2344
2345 state = NXInitHashState(class_hash);
2346 while (NXNextHashState(class_hash, &state, (void **)&cls)) {
2347 if ((vm_address_t)cls >= seg &&
2348 (vm_address_t)cls < seg+seg_size)
2349 {
2350 _objc_inform("UNLOAD DEBUG: dying image contains surviving class '%s'!", cls->name);
2351 }
2352
2353 if ((vm_address_t)cls->super_class >= seg &&
2354 (vm_address_t)cls->super_class < seg+seg_size)
2355 {
2356 _objc_inform("UNLOAD DEBUG: dying image contains superclass '%s' of surviving class '%s'!", cls->super_class->name, cls->name);
2357 }
2358 }
2359 }
2360
2361 OBJC_UNLOCK(&classLock);
2362 }
2363
2364
2365 /***********************************************************************
2366 * _unload_image
2367 * Only handles MH_BUNDLE for now.
2368 **********************************************************************/
2369 __private_extern__ void _unload_image(header_info *hi)
2370 {
2371 // Cleanup:
2372 // Remove image's classes from the class list and free auxiliary data.
2373 // Remove image's unresolved or loadable categories and free auxiliary data
2374 // Remove image's unresolved class refs.
2375 _objc_remove_classes_in_image(hi);
2376 _objc_remove_categories_in_image(hi);
2377 _objc_remove_pending_class_refs_in_image(hi);
2378
2379 // Perform various debugging checks if requested.
2380 if (DebugUnload) unload_paranoia(hi);
2381 }
2382
2383
2384 /***********************************************************************
2385 * objc_addClass. Add the specified class to the table of known classes,
2386 * after doing a little verification and fixup.
2387 **********************************************************************/
2388 void objc_addClass (Class cls_gen)
2389 {
2390 struct old_class *cls = _class_asOld(cls_gen);
2391
2392 OBJC_WARN_DEPRECATED;
2393
2394 // Synchronize access to hash table
2395 OBJC_LOCK (&classLock);
2396
2397 // Make sure both the class and the metaclass have caches!
2398 // Clear all bits of the info fields except CLS_CLASS and CLS_META.
2399 // Normally these bits are already clear but if someone tries to cons
2400 // up their own class on the fly they might need to be cleared.
2401 if (cls->cache == NULL) {
2402 cls->cache = (Cache) &_objc_empty_cache;
2403 cls->info = CLS_CLASS;
2404 }
2405
2406 if (cls->isa->cache == NULL) {
2407 cls->isa->cache = (Cache) &_objc_empty_cache;
2408 cls->isa->info = CLS_META;
2409 }
2410
2411 // methodLists should be:
2412 // 1. NULL (Tiger and later only)
2413 // 2. A -1 terminated method list array
2414 // In either case, CLS_NO_METHOD_ARRAY remains clear.
2415 // If the user manipulates the method list directly,
2416 // they must use the magic private format.
2417
2418 // Add the class to the table
2419 (void) NXHashInsert (class_hash, cls);
2420
2421 // Superclass is no longer a leaf for cache flushing
2422 if (cls->super_class && (cls->super_class->info & CLS_LEAF)) {
2423 _class_clearInfo((Class)cls->super_class, CLS_LEAF);
2424 _class_clearInfo((Class)cls->super_class->isa, CLS_LEAF);
2425 }
2426
2427 // Desynchronize
2428 OBJC_UNLOCK (&classLock);
2429 }
2430
2431 /***********************************************************************
2432 * _objcTweakMethodListPointerForClass.
2433 * Change the class's method list pointer to a method list array.
2434 * Does nothing if the method list pointer is already a method list array.
2435 * If the class is currently in use, methodListLock must be held by the caller.
2436 **********************************************************************/
2437 static void _objcTweakMethodListPointerForClass(struct old_class *cls)
2438 {
2439 struct old_method_list * originalList;
2440 const int initialEntries = 4;
2441 size_t mallocSize;
2442 struct old_method_list ** ptr;
2443
2444 // Do nothing if methodLists is already an array.
2445 if (cls->methodLists && !(cls->info & CLS_NO_METHOD_ARRAY)) return;
2446
2447 // Remember existing list
2448 originalList = (struct old_method_list *) cls->methodLists;
2449
2450 // Allocate and zero a method list array
2451 mallocSize = sizeof(struct old_method_list *) * initialEntries;
2452 ptr = (struct old_method_list **) _calloc_internal(1, mallocSize);
2453
2454 // Insert the existing list into the array
2455 ptr[initialEntries - 1] = END_OF_METHODS_LIST;
2456 ptr[0] = originalList;
2457
2458 // Replace existing list with array
2459 cls->methodLists = ptr;
2460 _class_clearInfo((Class)cls, CLS_NO_METHOD_ARRAY);
2461 }
2462
2463
2464 /***********************************************************************
2465 * _objc_insertMethods.
2466 * Adds methods to a class.
2467 * Does not flush any method caches.
2468 * Does not take any locks.
2469 * If the class is already in use, use class_addMethods() instead.
2470 **********************************************************************/
2471 __private_extern__ void _objc_insertMethods(struct old_class *cls,
2472 struct old_method_list *mlist,
2473 struct old_category *cat)
2474 {
2475 struct old_method_list ***list;
2476 struct old_method_list **ptr;
2477 ptrdiff_t endIndex;
2478 size_t oldSize;
2479 size_t newSize;
2480
2481 if (!cls->methodLists) {
2482 // cls has no methods - simply use this method list
2483 cls->methodLists = (struct old_method_list **)mlist;
2484 _class_setInfo((Class)cls, CLS_NO_METHOD_ARRAY);
2485 return;
2486 }
2487
2488 // Log any existing methods being replaced
2489 if (PrintReplacedMethods) {
2490 int i;
2491 for (i = 0; i < mlist->method_count; i++) {
2492 extern IMP findIMPInClass(struct old_class *cls, SEL sel);
2493 SEL sel = sel_registerName((char *)mlist->method_list[i].method_name);
2494 IMP newImp = mlist->method_list[i].method_imp;
2495 IMP oldImp;
2496
2497 if ((oldImp = findIMPInClass(cls, sel))) {
2498 _objc_inform("REPLACED: %c[%s %s] %s%s (IMP was %p, now %p)",
2499 ISMETA(cls) ? '+' : '-',
2500 cls->name, sel_getName(sel),
2501 cat ? "by category " : "",
2502 cat ? cat->category_name : "",
2503 oldImp, newImp);
2504 }
2505 }
2506 }
2507
2508 // Create method list array if necessary
2509 _objcTweakMethodListPointerForClass(cls);
2510
2511 list = &cls->methodLists;
2512
2513 // Locate unused entry for insertion point
2514 ptr = *list;
2515 while ((*ptr != 0) && (*ptr != END_OF_METHODS_LIST))
2516 ptr += 1;
2517
2518 // If array is full, add to it
2519 if (*ptr == END_OF_METHODS_LIST)
2520 {
2521 // Calculate old and new dimensions
2522 endIndex = ptr - *list;
2523 oldSize = (endIndex + 1) * sizeof(void *);
2524 newSize = oldSize + sizeof(struct old_method_list *); // only increase by 1
2525
2526 // Grow the method list array by one.
2527 // This block may be from user code; don't use _realloc_internal
2528 *list = (struct old_method_list **)realloc(*list, newSize);
2529
2530 // Zero out addition part of new array
2531 bzero (&((*list)[endIndex]), newSize - oldSize);
2532
2533 // Place new end marker
2534 (*list)[(newSize/sizeof(void *)) - 1] = END_OF_METHODS_LIST;
2535
2536 // Insertion point corresponds to old array end
2537 ptr = &((*list)[endIndex]);
2538 }
2539
2540 // Right shift existing entries by one
2541 bcopy (*list, (*list) + 1, ((void *) ptr) - ((void *) *list));
2542
2543 // Insert at method list at beginning of array
2544 **list = mlist;
2545 }
2546
2547 /***********************************************************************
2548 * _objc_removeMethods.
2549 * Remove methods from a class.
2550 * Does not take any locks.
2551 * Does not flush any method caches.
2552 * If the class is currently in use, use class_removeMethods() instead.
2553 **********************************************************************/
2554 __private_extern__ void _objc_removeMethods(struct old_class *cls,
2555 struct old_method_list *mlist)
2556 {
2557 struct old_method_list ***list;
2558 struct old_method_list **ptr;
2559
2560 if (cls->methodLists == NULL) {
2561 // cls has no methods
2562 return;
2563 }
2564 if (cls->methodLists == (struct old_method_list **)mlist) {
2565 // mlist is the class's only method list - erase it
2566 cls->methodLists = NULL;
2567 return;
2568 }
2569 if (cls->info & CLS_NO_METHOD_ARRAY) {
2570 // cls has only one method list, and this isn't it - do nothing
2571 return;
2572 }
2573
2574 // cls has a method list array - search it
2575
2576 list = &cls->methodLists;
2577
2578 // Locate list in the array
2579 ptr = *list;
2580 while (*ptr != mlist) {
2581 // fix for radar # 2538790
2582 if ( *ptr == END_OF_METHODS_LIST ) return;
2583 ptr += 1;
2584 }
2585
2586 // Remove this entry
2587 *ptr = 0;
2588
2589 // Left shift the following entries
2590 while (*(++ptr) != END_OF_METHODS_LIST)
2591 *(ptr-1) = *ptr;
2592 *(ptr-1) = 0;
2593 }
2594
2595 /***********************************************************************
2596 * _objc_add_category. Install the specified category's methods and
2597 * protocols into the class it augments.
2598 * The class is assumed not to be in use yet: no locks are taken and
2599 * no method caches are flushed.
2600 **********************************************************************/
2601 static inline void _objc_add_category(struct old_class *cls, struct old_category *category, int version)
2602 {
2603 if (PrintConnecting) {
2604 _objc_inform("CONNECT: attaching category '%s (%s)'", cls->name, category->category_name);
2605 }
2606
2607 // Augment instance methods
2608 if (category->instance_methods)
2609 _objc_insertMethods (cls, category->instance_methods, category);
2610
2611 // Augment class methods
2612 if (category->class_methods)
2613 _objc_insertMethods (cls->isa, category->class_methods, category);
2614
2615 // Augment protocols
2616 if ((version >= 5) && category->protocols)
2617 {
2618 if (cls->isa->version >= 5)
2619 {
2620 category->protocols->next = cls->protocols;
2621 cls->protocols = category->protocols;
2622 cls->isa->protocols = category->protocols;
2623 }
2624 else
2625 {
2626 _objc_inform ("unable to add protocols from category %s...\n", category->category_name);
2627 _objc_inform ("class `%s' must be recompiled\n", category->class_name);
2628 }
2629 }
2630
2631 // Augment properties
2632 if (version >= 7 && category->instance_properties) {
2633 if (cls->isa->version >= 6) {
2634 _class_addProperties(cls, category->instance_properties);
2635 } else {
2636 _objc_inform ("unable to add properties from category %s...\n", category->category_name);
2637 _objc_inform ("class `%s' must be recompiled\n", category->class_name);
2638 }
2639 }
2640 }
2641
2642 /***********************************************************************
2643 * _objc_add_category_flush_caches. Install the specified category's
2644 * methods into the class it augments, and flush the class' method cache.
2645 * Return YES if some method caches now need to be flushed.
2646 **********************************************************************/
2647 static BOOL _objc_add_category_flush_caches(struct old_class *cls, struct old_category *category, int version)
2648 {
2649 BOOL needFlush = NO;
2650
2651 // Install the category's methods into its intended class
2652 OBJC_LOCK(&methodListLock);
2653 _objc_add_category (cls, category, version);
2654 OBJC_UNLOCK(&methodListLock);
2655
2656 // Queue for cache flushing so category's methods can get called
2657 if (category->instance_methods) {
2658 _class_setInfo((Class)cls, CLS_FLUSH_CACHE);
2659 needFlush = YES;
2660 }
2661 if (category->class_methods) {
2662 _class_setInfo((Class)cls->isa, CLS_FLUSH_CACHE);
2663 needFlush = YES;
2664 }
2665
2666 return needFlush;
2667 }
2668
2669
2670 /***********************************************************************
2671 * reverse_cat
2672 * Reverse the given linked list of pending categories.
2673 * The pending category list is built backwards, and needs to be
2674 * reversed before actually attaching the categories to a class.
2675 * Returns the head of the new linked list.
2676 **********************************************************************/
2677 static _objc_unresolved_category *reverse_cat(_objc_unresolved_category *cat)
2678 {
2679 if (!cat) return NULL;
2680
2681 _objc_unresolved_category *prev = NULL;
2682 _objc_unresolved_category *cur = cat;
2683 _objc_unresolved_category *ahead = cat->next;
2684
2685 while (cur) {
2686 ahead = cur->next;
2687 cur->next = prev;
2688 prev = cur;
2689 cur = ahead;
2690 }
2691
2692 return prev;
2693 }
2694
2695
2696 /***********************************************************************
2697 * resolve_categories_for_class.
2698 * Install all existing categories intended for the specified class.
2699 * cls must be a true class and not a metaclass.
2700 **********************************************************************/
2701 static void resolve_categories_for_class(struct old_class *cls)
2702 {
2703 _objc_unresolved_category * pending;
2704 _objc_unresolved_category * next;
2705
2706 // Nothing to do if there are no categories at all
2707 if (!category_hash) return;
2708
2709 // Locate and remove first element in category list
2710 // associated with this class
2711 pending = NXMapKeyFreeingRemove (category_hash, cls->name);
2712
2713 // Traverse the list of categories, if any, registered for this class
2714
2715 // The pending list is built backwards. Reverse it and walk forwards.
2716 pending = reverse_cat(pending);
2717
2718 while (pending) {
2719 if (pending->cat) {
2720 // Install the category
2721 // use the non-flush-cache version since we are only
2722 // called from the class intialization code
2723 _objc_add_category(cls, pending->cat, (int)pending->version);
2724 }
2725
2726 // Delink and reclaim this registration
2727 next = pending->next;
2728 _free_internal(pending);
2729 pending = next;
2730 }
2731 }
2732
2733
2734 /***********************************************************************
2735 * _objc_resolve_categories_for_class.
2736 * Public version of resolve_categories_for_class. This was
2737 * exported pre-10.4 for Omni et al. to workaround a problem
2738 * with too-lazy category attachment.
2739 * cls should be a class, but this function can also cope with metaclasses.
2740 **********************************************************************/
2741 void _objc_resolve_categories_for_class(Class cls_gen)
2742 {
2743 struct old_class *cls = _class_asOld(cls_gen);
2744
2745 // If cls is a metaclass, get the class.
2746 // resolve_categories_for_class() requires a real class to work correctly.
2747 if (ISMETA(cls)) {
2748 if (strncmp(cls->name, "_%", 2) == 0) {
2749 // Posee's meta's name is smashed and isn't in the class_hash,
2750 // so objc_getClass doesn't work.
2751 char *baseName = strchr(cls->name, '%'); // get posee's real name
2752 cls = _class_asOld(objc_getClass(baseName));
2753 } else {
2754 cls = _class_asOld(objc_getClass(cls->name));
2755 }
2756 }
2757
2758 resolve_categories_for_class(cls);
2759 }
2760
2761
2762 /***********************************************************************
2763 * _objc_register_category.
2764 * Process a category read from an image.
2765 * If the category's class exists, attach the category immediately.
2766 * Classes that need cache flushing are marked but not flushed.
2767 * If the category's class does not exist yet, pend the category for
2768 * later attachment. Pending categories are attached in the order
2769 * they were discovered.
2770 * Returns YES if some method caches now need to be flushed.
2771 **********************************************************************/
2772 static BOOL _objc_register_category(struct old_category *cat, int version)
2773 {
2774 _objc_unresolved_category * new_cat;
2775 _objc_unresolved_category * old;
2776 struct old_class *theClass;
2777
2778 // If the category's class exists, attach the category.
2779 if ((theClass = _class_asOld(objc_lookUpClass(cat->class_name)))) {
2780 return _objc_add_category_flush_caches(theClass, cat, version);
2781 }
2782
2783 // If the category's class exists but is unconnected,
2784 // then attach the category to the class but don't bother
2785 // flushing any method caches (because they must be empty).
2786 // YES unconnected, NO class_handler
2787 if ((theClass = _class_asOld(look_up_class(cat->class_name, YES, NO)))) {
2788 _objc_add_category(theClass, cat, version);
2789 return NO;
2790 }
2791
2792
2793 // Category's class does not exist yet.
2794 // Save the category for later attachment.
2795
2796 if (PrintConnecting) {
2797 _objc_inform("CONNECT: pending category '%s (%s)'", cat->class_name, cat->category_name);
2798 }
2799
2800 // Create category lookup table if needed
2801 if (!category_hash)
2802 category_hash = NXCreateMapTableFromZone (NXStrValueMapPrototype,
2803 128,
2804 _objc_internal_zone ());
2805
2806 // Locate an existing list of categories, if any, for the class.
2807 old = NXMapGet (category_hash, cat->class_name);
2808
2809 // Register the category to be fixed up later.
2810 // The category list is built backwards, and is reversed again
2811 // by resolve_categories_for_class().
2812 new_cat = _malloc_internal(sizeof(_objc_unresolved_category));
2813 new_cat->next = old;
2814 new_cat->cat = cat;
2815 new_cat->version = version;
2816 (void) NXMapKeyCopyingInsert (category_hash, cat->class_name, new_cat);
2817
2818 return NO;
2819 }
2820
2821
2822 __private_extern__ const char **
2823 _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount)
2824 {
2825 Module mods;
2826 int m;
2827 const char **list;
2828 int count;
2829 int allocated;
2830
2831 list = NULL;
2832 count = 0;
2833 allocated = 0;
2834
2835 mods = hi->mod_ptr;
2836 for (m = 0; m < hi->mod_count; m++) {
2837 int d;
2838
2839 if (!mods[m].symtab) continue;
2840
2841 for (d = 0; d < mods[m].symtab->cls_def_cnt; d++) {
2842 struct old_class *cls = mods[m].symtab->defs[d];
2843 // fixme what about future-ified classes?
2844 if (class_is_connected(cls)) {
2845 if (count == allocated) {
2846 allocated = allocated*2 + 16;
2847 list = realloc(list, allocated * sizeof(char *));
2848 }
2849 list[count++] = cls->name;
2850 }
2851 }
2852 }
2853
2854 if (count > 0) {
2855 // NULL-terminate non-empty list
2856 if (count == allocated) {
2857 allocated = allocated+1;
2858 list = realloc(list, allocated * sizeof(char *));
2859 }
2860 list[count] = NULL;
2861 }
2862
2863 if (outCount) *outCount = count;
2864 return list;
2865 }
2866
2867 #endif