Skip to content

Commit a9d0313

Browse files
committed
Don't rely on load order for built-in classes.
Generate these classes using the structures that the runtime expects internally, rather than relying on the Objective-C compiler. This change means that they can always be the latest version, even if the runtime is compiled with an older compiler, and ensures that the `Protocol` class is always available, independent of global constructor ordering between libraries. Fixes #283
1 parent feaf007 commit a9d0313

13 files changed

+212
-176
lines changed

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,13 @@ set(libobjc_OBJCXX_SRCS
5757
)
5858
set(libobjc_OBJC_SRCS
5959
NSBlocks.m
60-
Protocol2.m
6160
associate.m
6261
blocks_runtime_np.m
6362
properties.m)
6463
set(libobjc_C_SRCS
6564
alias_table.c
6665
block_to_imp.c
66+
builtin_classes.c
6767
caps.c
6868
category_loader.c
6969
class_table.c

Protocol2.m

-45
This file was deleted.

Test/PropertyIntrospectionTest2_arc.m

+1-1
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ static BOOL testPropertyForProperty_alt(objc_property_t p,
302302
attrsList = property_copyAttributeList(p, NULL);
303303
OPT_ASSERT(0 != attrsList);
304304
objc_property_attribute_t *ra;
305-
for (attrsCount = 0, ra = attrsList; (ra->name != NULL) && (attrsCount < size); attrsCount++, ra++) {}
305+
for (attrsCount = 0, ra = attrsList; (attrsCount < size) && (ra->name != NULL) ; attrsCount++, ra++) {}
306306
OPT_ASSERT(attrsCount == size);
307307
free(attrsList);
308308
for (unsigned int index=0; index<size; index++) {

builtin_classes.c

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#include "protocol.h"
2+
#include "class.h"
3+
#include "method.h"
4+
#include "loader.h"
5+
6+
OBJC_PUBLIC struct objc_class _OBJC_CLASS_Object;
7+
8+
static struct objc_class _OBJC_METACLASS_Object = {
9+
.isa = NULL,
10+
.name = "Object",
11+
.info = objc_class_flag_meta,
12+
};
13+
OBJC_PUBLIC struct objc_class _OBJC_CLASS_Object = {
14+
.isa = &_OBJC_METACLASS_Object,
15+
.super_class = NULL,
16+
.name = "Object",
17+
};
18+
19+
static struct objc_class _OBJC_METACLASS_Protocol = {
20+
.isa = &_OBJC_METACLASS_Object,
21+
.super_class = &_OBJC_METACLASS_Object,
22+
.name = "Protocol",
23+
.info = objc_class_flag_meta,
24+
};
25+
static struct objc_class _OBJC_METACLASS_ProtocolGCC = {
26+
.isa = &_OBJC_METACLASS_Object,
27+
.super_class = &_OBJC_METACLASS_Object,
28+
.name = "ProtocolGCC",
29+
.info = objc_class_flag_meta,
30+
};
31+
static struct objc_class _OBJC_METACLASS_ProtocolGSv1 = {
32+
.isa = &_OBJC_METACLASS_Object,
33+
.super_class = &_OBJC_METACLASS_Object,
34+
.name = "ProtocolGSv2",
35+
.info = objc_class_flag_meta,
36+
};
37+
static struct objc_class _OBJC_METACLASS___IncompleteProtocol = {
38+
.isa = &_OBJC_METACLASS_Object,
39+
.super_class = &_OBJC_METACLASS_Object,
40+
.name = "ProtocolGCC",
41+
.info = objc_class_flag_meta,
42+
};
43+
44+
OBJC_PUBLIC struct objc_class _OBJC_CLASS_Protocol = {
45+
.isa = &_OBJC_METACLASS_Protocol,
46+
.super_class = &_OBJC_CLASS_Object,
47+
.name = "Protocol",
48+
.info = objc_class_flag_permanent_instances,
49+
};
50+
51+
OBJC_PUBLIC struct objc_class _OBJC_CLASS_ProtocolGCC = {
52+
.isa = &_OBJC_METACLASS_ProtocolGCC,
53+
.super_class = &_OBJC_CLASS_Protocol,
54+
.name = "ProtocolGCC",
55+
.info = objc_class_flag_permanent_instances,
56+
};
57+
58+
OBJC_PUBLIC struct objc_class _OBJC_CLASS___IncompleteProtocol = {
59+
.isa = &_OBJC_METACLASS_Protocol,
60+
.super_class = &_OBJC_CLASS_Protocol,
61+
.name = "__IncompleteProtocol",
62+
.info = objc_class_flag_permanent_instances,
63+
};
64+
65+
OBJC_PUBLIC struct objc_class _OBJC_CLASS_ProtocolGSv1 = {
66+
.isa = &_OBJC_METACLASS_Protocol,
67+
.super_class = &_OBJC_CLASS_Protocol,
68+
.name = "ProtocolGSv1",
69+
.info = objc_class_flag_permanent_instances,
70+
};
71+
72+
#ifdef OLDABI_COMPAT
73+
static struct objc_class _OBJC_METACLASS___ObjC_Protocol_Holder_Ugly_Hack = {
74+
.isa = NULL,
75+
.name = "__ObjC_Protocol_Holder_Ugly_Hack",
76+
.info = objc_class_flag_meta,
77+
};
78+
OBJC_PUBLIC struct objc_class _OBJC_CLASS__ObjC_Protocol_Holder_Ugly_Hack = {
79+
.isa = &_OBJC_METACLASS___ObjC_Protocol_Holder_Ugly_Hack,
80+
.super_class = NULL,
81+
.name = "__ObjC_Protocol_Holder_Ugly_Hack",
82+
};
83+
#endif
84+
85+
PRIVATE void init_builtin_classes(void)
86+
{
87+
// Load the classes that are compiled into the runtime.
88+
objc_load_class(&_OBJC_CLASS_Object);
89+
objc_load_class(&_OBJC_CLASS_Protocol);
90+
objc_load_class(&_OBJC_CLASS_ProtocolGCC);
91+
objc_load_class(&_OBJC_CLASS_ProtocolGSv1);
92+
objc_load_class(&_OBJC_CLASS___IncompleteProtocol);
93+
objc_resolve_class(&_OBJC_CLASS_Object);
94+
objc_resolve_class(&_OBJC_CLASS_Protocol);
95+
objc_resolve_class(&_OBJC_CLASS_ProtocolGCC);
96+
objc_resolve_class(&_OBJC_CLASS_ProtocolGSv1);
97+
objc_resolve_class(&_OBJC_CLASS___IncompleteProtocol);
98+
// Fix up the sizes of the various protocol classes that we will use.
99+
_OBJC_CLASS_Object.instance_size = sizeof(void*);
100+
_OBJC_CLASS_Protocol.instance_size = sizeof(struct objc_protocol);
101+
_OBJC_CLASS___IncompleteProtocol.instance_size = sizeof(struct objc_protocol);
102+
#ifdef OLDABI_COMPAT
103+
objc_load_class(&_OBJC_CLASS__ObjC_Protocol_Holder_Ugly_Hack);
104+
objc_resolve_class(&_OBJC_CLASS__ObjC_Protocol_Holder_Ugly_Hack);
105+
#endif
106+
}

class.h

+2
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,8 @@ void freeIvarLists(Class aClass);
462462
*/
463463
void freeMethodLists(Class aClass);
464464

465+
void objc_load_class(struct objc_class *cls);
466+
465467
#ifdef __cplusplus
466468
} // extern "C"
467469
#endif

class_table.c

+1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ PRIVATE Class class_table_next(void **e)
137137
(struct class_table_internal_table_enumerator**)e);
138138
}
139139

140+
PRIVATE BOOL objc_resolve_class(Class cls);
140141
PRIVATE void init_class_tables(void)
141142
{
142143
class_table_internal_initialize(&class_table, 4096);

legacy.c

+8-9
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "objc/encoding.h"
88
#include "legacy.h"
99
#include "properties.h"
10+
#include "protocol.h"
1011
#include "class.h"
1112
#include "loader.h"
1213

@@ -411,14 +412,13 @@ upgrade_protocol_method_list_gcc(struct objc_protocol_method_description_list_gc
411412
PRIVATE struct objc_protocol *objc_upgrade_protocol_gcc(struct objc_protocol_gcc *p)
412413
{
413414
// If the protocol has already been upgraded, the don't try to upgrade it twice.
414-
if (p->isa == objc_getClass("ProtocolGCC"))
415+
if (p->isa == (id)&_OBJC_CLASS_ProtocolGCC)
415416
{
416417
return objc_getProtocol(p->name);
417418
}
418-
p->isa = objc_getClass("ProtocolGCC");
419+
p->isa = (id)&_OBJC_CLASS_ProtocolGCC;
419420
Protocol *proto =
420-
(Protocol*)class_createInstance((Class)objc_getClass("Protocol"),
421-
sizeof(struct objc_protocol) - sizeof(id));
421+
(Protocol*)class_createInstance(&_OBJC_CLASS_Protocol, 0);
422422
proto->name = p->name;
423423
// Aliasing of this between the new and old structures means that when this
424424
// returns these will all be updated.
@@ -432,13 +432,12 @@ PRIVATE struct objc_protocol *objc_upgrade_protocol_gcc(struct objc_protocol_gcc
432432
PRIVATE struct objc_protocol *objc_upgrade_protocol_gsv1(struct objc_protocol_gsv1 *p)
433433
{
434434
// If the protocol has already been upgraded, the don't try to upgrade it twice.
435-
if (p->isa == objc_getClass("ProtocolGSv1"))
435+
if (p->isa == (id)&_OBJC_CLASS_ProtocolGSv1)
436436
{
437437
return objc_getProtocol(p->name);
438438
}
439439
Protocol *n =
440-
(Protocol*)class_createInstance((Class)objc_getClass("Protocol"),
441-
sizeof(struct objc_protocol) - sizeof(id));
440+
(Protocol*)class_createInstance(&_OBJC_CLASS_Protocol, 0);
442441
n->instance_methods = upgrade_protocol_method_list_gcc(p->instance_methods);
443442
// Aliasing of this between the new and old structures means that when this
444443
// returns these will all be updated.
@@ -447,14 +446,14 @@ PRIVATE struct objc_protocol *objc_upgrade_protocol_gsv1(struct objc_protocol_gs
447446
n->class_methods = upgrade_protocol_method_list_gcc(p->class_methods);
448447
n->properties = upgradePropertyList(p->properties);
449448
n->optional_properties = upgradePropertyList(p->optional_properties);
450-
n->isa = objc_getClass("Protocol");
449+
n->isa = (id)&_OBJC_CLASS_Protocol;
451450
// We do in-place upgrading of these, because they might be referenced
452451
// directly
453452
p->instance_methods = (struct objc_protocol_method_description_list_gcc*)n->instance_methods;
454453
p->class_methods = (struct objc_protocol_method_description_list_gcc*)n->class_methods;
455454
p->properties = (struct objc_property_list_gsv1*)n->properties;
456455
p->optional_properties = (struct objc_property_list_gsv1*)n->optional_properties;
457-
p->isa = objc_getClass("ProtocolGSv1");
456+
p->isa = (id)&_OBJC_CLASS_ProtocolGSv1;
458457
assert(p->isa);
459458
return n;
460459
}

loader.c

+7-25
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,6 @@
1919
PRIVATE mutex_t runtime_mutex;
2020
LEGACY void *__objc_runtime_mutex = &runtime_mutex;
2121

22-
void init_alias_table(void);
23-
void init_arc(void);
24-
void init_class_tables(void);
25-
void init_dispatch_tables(void);
26-
void init_gc(void);
27-
void init_protocol_table(void);
28-
void init_selector_tables(void);
29-
void init_trampolines(void);
30-
void init_early_blocks(void);
31-
void objc_send_load_message(Class class);
32-
3322
void log_selector_memory_usage(void);
3423

3524
static void log_memory_stats(void)
@@ -46,20 +35,11 @@ __attribute__((weak)) void (*dispatch_end_thread_4GC)(void);
4635
__attribute__((weak)) void *(*_dispatch_begin_NSAutoReleasePool)(void);
4736
__attribute__((weak)) void (*_dispatch_end_NSAutoReleasePool)(void *);
4837

49-
__attribute__((used))
50-
static void link_protos(void)
51-
{
52-
link_protocol_classes();
53-
}
54-
5538
static void init_runtime(void)
5639
{
5740
static BOOL first_run = YES;
5841
if (first_run)
5942
{
60-
#if ENABLE_GC
61-
init_gc();
62-
#endif
6343
// Create the main runtime lock. This is not safe in theory, but in
6444
// practice the first time that this function is called will be in the
6545
// loader, from the main thread. Future loaders may run concurrently,
@@ -80,6 +60,7 @@ static void init_runtime(void)
8060
init_early_blocks();
8161
init_arc();
8262
init_trampolines();
63+
init_builtin_classes();
8364
first_run = NO;
8465
if (getenv("LIBOBJC_MEMORY_PROFILE"))
8566
{
@@ -253,22 +234,23 @@ OBJC_PUBLIC void __objc_load(struct objc_init *init)
253234
assert(p);
254235
*proto = p;
255236
}
237+
int classesLoaded = 0;
256238
for (Class *cls = init->cls_begin ; cls < init->cls_end ; cls++)
257239
{
258240
if (*cls == NULL)
259241
{
260242
continue;
261243
}
262-
// As a special case, allow using legacy ABI code with a new runtime.
263-
if (isFirstLoad && (strcmp((*cls)->name, "Protocol") == 0))
264-
{
265-
CurrentABI = UnknownABI;
266-
}
267244
#ifdef DEBUG_LOADING
268245
fprintf(stderr, "Loading class %s\n", (*cls)->name);
269246
#endif
270247
objc_load_class(*cls);
271248
}
249+
if (isFirstLoad && (classesLoaded == 0))
250+
{
251+
// As a special case, allow using legacy ABI code with a new runtime.
252+
CurrentABI = UnknownABI;
253+
}
272254
#if 0
273255
// We currently don't do anything with these pointers. They exist to
274256
// provide a level of indirection that will permit us to completely change

loader.h

+59
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,63 @@ void objc_init_statics(struct objc_static_instance_list *statics);
6464
*/
6565
void objc_init_buffered_statics(void);
6666

67+
/**
68+
* Initialise built-in classes (Object and Protocol). This must be called
69+
* after `init_class_tables`.
70+
*/
71+
void init_builtin_classes(void);
72+
73+
/**
74+
* Initialise the aliases table.
75+
*/
76+
void init_alias_table(void);
77+
78+
/**
79+
* Initialise the automatic reference counting system.
80+
*/
81+
void init_arc(void);
82+
83+
/**
84+
* Initialise the class tables.
85+
*/
86+
void init_class_tables(void);
87+
88+
/**
89+
* Initialise the dispatch table machinery.
90+
*/
91+
void init_dispatch_tables(void);
92+
93+
/**
94+
* Initialise the protocol tables.
95+
*/
96+
void init_protocol_table(void);
97+
98+
/**
99+
* Initialise the selector tables.
100+
*/
101+
void init_selector_tables(void);
102+
103+
/**
104+
* Initialise the trampolines for using blocks as methods.
105+
*/
106+
void init_trampolines(void);
107+
108+
/**
109+
* Send +load messages to a class if required.
110+
*/
111+
void objc_send_load_message(Class cls);
112+
113+
/**
114+
* Resolve a class (populate its superclass and sibling class links). Returns
115+
* YES if the class can be resolved, NO otherwise. Classes cannot be resolved
116+
* unless their superclasses have all been resolved.
117+
*/
118+
BOOL objc_resolve_class(Class cls);
119+
120+
/**
121+
* Initialise the block classes.
122+
*/
123+
void init_early_blocks(void);
124+
125+
67126
#endif //__OBJC_LOADER_H_INCLUDED

method.h

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#pragma once
12
#include <assert.h>
23

34
/**

0 commit comments

Comments
 (0)