Skip to content

Commit

Permalink
Don't rely on load order for built-in classes.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
davidchisnall committed Oct 22, 2024
1 parent feaf007 commit a9d0313
Show file tree
Hide file tree
Showing 13 changed files with 212 additions and 176 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ set(libobjc_OBJCXX_SRCS
)
set(libobjc_OBJC_SRCS
NSBlocks.m
Protocol2.m
associate.m
blocks_runtime_np.m
properties.m)
set(libobjc_C_SRCS
alias_table.c
block_to_imp.c
builtin_classes.c
caps.c
category_loader.c
class_table.c
Expand Down
45 changes: 0 additions & 45 deletions Protocol2.m

This file was deleted.

2 changes: 1 addition & 1 deletion Test/PropertyIntrospectionTest2_arc.m
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ static BOOL testPropertyForProperty_alt(objc_property_t p,
attrsList = property_copyAttributeList(p, NULL);
OPT_ASSERT(0 != attrsList);
objc_property_attribute_t *ra;
for (attrsCount = 0, ra = attrsList; (ra->name != NULL) && (attrsCount < size); attrsCount++, ra++) {}
for (attrsCount = 0, ra = attrsList; (attrsCount < size) && (ra->name != NULL) ; attrsCount++, ra++) {}
OPT_ASSERT(attrsCount == size);
free(attrsList);
for (unsigned int index=0; index<size; index++) {
Expand Down
106 changes: 106 additions & 0 deletions builtin_classes.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#include "protocol.h"
#include "class.h"
#include "method.h"
#include "loader.h"

OBJC_PUBLIC struct objc_class _OBJC_CLASS_Object;

static struct objc_class _OBJC_METACLASS_Object = {
.isa = NULL,
.name = "Object",
.info = objc_class_flag_meta,
};
OBJC_PUBLIC struct objc_class _OBJC_CLASS_Object = {
.isa = &_OBJC_METACLASS_Object,
.super_class = NULL,
.name = "Object",
};

static struct objc_class _OBJC_METACLASS_Protocol = {
.isa = &_OBJC_METACLASS_Object,
.super_class = &_OBJC_METACLASS_Object,
.name = "Protocol",
.info = objc_class_flag_meta,
};
static struct objc_class _OBJC_METACLASS_ProtocolGCC = {
.isa = &_OBJC_METACLASS_Object,
.super_class = &_OBJC_METACLASS_Object,
.name = "ProtocolGCC",
.info = objc_class_flag_meta,
};
static struct objc_class _OBJC_METACLASS_ProtocolGSv1 = {
.isa = &_OBJC_METACLASS_Object,
.super_class = &_OBJC_METACLASS_Object,
.name = "ProtocolGSv2",
.info = objc_class_flag_meta,
};
static struct objc_class _OBJC_METACLASS___IncompleteProtocol = {
.isa = &_OBJC_METACLASS_Object,
.super_class = &_OBJC_METACLASS_Object,
.name = "ProtocolGCC",
.info = objc_class_flag_meta,
};

OBJC_PUBLIC struct objc_class _OBJC_CLASS_Protocol = {
.isa = &_OBJC_METACLASS_Protocol,
.super_class = &_OBJC_CLASS_Object,
.name = "Protocol",
.info = objc_class_flag_permanent_instances,
};

OBJC_PUBLIC struct objc_class _OBJC_CLASS_ProtocolGCC = {
.isa = &_OBJC_METACLASS_ProtocolGCC,
.super_class = &_OBJC_CLASS_Protocol,
.name = "ProtocolGCC",
.info = objc_class_flag_permanent_instances,
};

OBJC_PUBLIC struct objc_class _OBJC_CLASS___IncompleteProtocol = {
.isa = &_OBJC_METACLASS_Protocol,
.super_class = &_OBJC_CLASS_Protocol,
.name = "__IncompleteProtocol",
.info = objc_class_flag_permanent_instances,
};

OBJC_PUBLIC struct objc_class _OBJC_CLASS_ProtocolGSv1 = {
.isa = &_OBJC_METACLASS_Protocol,
.super_class = &_OBJC_CLASS_Protocol,
.name = "ProtocolGSv1",
.info = objc_class_flag_permanent_instances,
};

#ifdef OLDABI_COMPAT
static struct objc_class _OBJC_METACLASS___ObjC_Protocol_Holder_Ugly_Hack = {
.isa = NULL,
.name = "__ObjC_Protocol_Holder_Ugly_Hack",
.info = objc_class_flag_meta,
};
OBJC_PUBLIC struct objc_class _OBJC_CLASS__ObjC_Protocol_Holder_Ugly_Hack = {
.isa = &_OBJC_METACLASS___ObjC_Protocol_Holder_Ugly_Hack,
.super_class = NULL,
.name = "__ObjC_Protocol_Holder_Ugly_Hack",
};
#endif

PRIVATE void init_builtin_classes(void)
{
// Load the classes that are compiled into the runtime.
objc_load_class(&_OBJC_CLASS_Object);
objc_load_class(&_OBJC_CLASS_Protocol);
objc_load_class(&_OBJC_CLASS_ProtocolGCC);
objc_load_class(&_OBJC_CLASS_ProtocolGSv1);
objc_load_class(&_OBJC_CLASS___IncompleteProtocol);
objc_resolve_class(&_OBJC_CLASS_Object);
objc_resolve_class(&_OBJC_CLASS_Protocol);
objc_resolve_class(&_OBJC_CLASS_ProtocolGCC);
objc_resolve_class(&_OBJC_CLASS_ProtocolGSv1);
objc_resolve_class(&_OBJC_CLASS___IncompleteProtocol);
// Fix up the sizes of the various protocol classes that we will use.
_OBJC_CLASS_Object.instance_size = sizeof(void*);
_OBJC_CLASS_Protocol.instance_size = sizeof(struct objc_protocol);
_OBJC_CLASS___IncompleteProtocol.instance_size = sizeof(struct objc_protocol);
#ifdef OLDABI_COMPAT
objc_load_class(&_OBJC_CLASS__ObjC_Protocol_Holder_Ugly_Hack);
objc_resolve_class(&_OBJC_CLASS__ObjC_Protocol_Holder_Ugly_Hack);
#endif
}
2 changes: 2 additions & 0 deletions class.h
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,8 @@ void freeIvarLists(Class aClass);
*/
void freeMethodLists(Class aClass);

void objc_load_class(struct objc_class *cls);

#ifdef __cplusplus
} // extern "C"
#endif
Expand Down
1 change: 1 addition & 0 deletions class_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ PRIVATE Class class_table_next(void **e)
(struct class_table_internal_table_enumerator**)e);
}

PRIVATE BOOL objc_resolve_class(Class cls);
PRIVATE void init_class_tables(void)
{
class_table_internal_initialize(&class_table, 4096);
Expand Down
17 changes: 8 additions & 9 deletions legacy.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "objc/encoding.h"
#include "legacy.h"
#include "properties.h"
#include "protocol.h"
#include "class.h"
#include "loader.h"

Expand Down Expand Up @@ -411,14 +412,13 @@ upgrade_protocol_method_list_gcc(struct objc_protocol_method_description_list_gc
PRIVATE struct objc_protocol *objc_upgrade_protocol_gcc(struct objc_protocol_gcc *p)
{
// If the protocol has already been upgraded, the don't try to upgrade it twice.
if (p->isa == objc_getClass("ProtocolGCC"))
if (p->isa == (id)&_OBJC_CLASS_ProtocolGCC)
{
return objc_getProtocol(p->name);
}
p->isa = objc_getClass("ProtocolGCC");
p->isa = (id)&_OBJC_CLASS_ProtocolGCC;
Protocol *proto =
(Protocol*)class_createInstance((Class)objc_getClass("Protocol"),
sizeof(struct objc_protocol) - sizeof(id));
(Protocol*)class_createInstance(&_OBJC_CLASS_Protocol, 0);
proto->name = p->name;
// Aliasing of this between the new and old structures means that when this
// returns these will all be updated.
Expand All @@ -432,13 +432,12 @@ PRIVATE struct objc_protocol *objc_upgrade_protocol_gcc(struct objc_protocol_gcc
PRIVATE struct objc_protocol *objc_upgrade_protocol_gsv1(struct objc_protocol_gsv1 *p)
{
// If the protocol has already been upgraded, the don't try to upgrade it twice.
if (p->isa == objc_getClass("ProtocolGSv1"))
if (p->isa == (id)&_OBJC_CLASS_ProtocolGSv1)
{
return objc_getProtocol(p->name);
}
Protocol *n =
(Protocol*)class_createInstance((Class)objc_getClass("Protocol"),
sizeof(struct objc_protocol) - sizeof(id));
(Protocol*)class_createInstance(&_OBJC_CLASS_Protocol, 0);
n->instance_methods = upgrade_protocol_method_list_gcc(p->instance_methods);
// Aliasing of this between the new and old structures means that when this
// returns these will all be updated.
Expand All @@ -447,14 +446,14 @@ PRIVATE struct objc_protocol *objc_upgrade_protocol_gsv1(struct objc_protocol_gs
n->class_methods = upgrade_protocol_method_list_gcc(p->class_methods);
n->properties = upgradePropertyList(p->properties);
n->optional_properties = upgradePropertyList(p->optional_properties);
n->isa = objc_getClass("Protocol");
n->isa = (id)&_OBJC_CLASS_Protocol;
// We do in-place upgrading of these, because they might be referenced
// directly
p->instance_methods = (struct objc_protocol_method_description_list_gcc*)n->instance_methods;
p->class_methods = (struct objc_protocol_method_description_list_gcc*)n->class_methods;
p->properties = (struct objc_property_list_gsv1*)n->properties;
p->optional_properties = (struct objc_property_list_gsv1*)n->optional_properties;
p->isa = objc_getClass("ProtocolGSv1");
p->isa = (id)&_OBJC_CLASS_ProtocolGSv1;
assert(p->isa);
return n;
}
Expand Down
32 changes: 7 additions & 25 deletions loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,6 @@
PRIVATE mutex_t runtime_mutex;
LEGACY void *__objc_runtime_mutex = &runtime_mutex;

void init_alias_table(void);
void init_arc(void);
void init_class_tables(void);
void init_dispatch_tables(void);
void init_gc(void);
void init_protocol_table(void);
void init_selector_tables(void);
void init_trampolines(void);
void init_early_blocks(void);
void objc_send_load_message(Class class);

void log_selector_memory_usage(void);

static void log_memory_stats(void)
Expand All @@ -46,20 +35,11 @@ __attribute__((weak)) void (*dispatch_end_thread_4GC)(void);
__attribute__((weak)) void *(*_dispatch_begin_NSAutoReleasePool)(void);
__attribute__((weak)) void (*_dispatch_end_NSAutoReleasePool)(void *);

__attribute__((used))
static void link_protos(void)
{
link_protocol_classes();
}

static void init_runtime(void)
{
static BOOL first_run = YES;
if (first_run)
{
#if ENABLE_GC
init_gc();
#endif
// Create the main runtime lock. This is not safe in theory, but in
// practice the first time that this function is called will be in the
// loader, from the main thread. Future loaders may run concurrently,
Expand All @@ -80,6 +60,7 @@ static void init_runtime(void)
init_early_blocks();
init_arc();
init_trampolines();
init_builtin_classes();
first_run = NO;
if (getenv("LIBOBJC_MEMORY_PROFILE"))
{
Expand Down Expand Up @@ -253,22 +234,23 @@ OBJC_PUBLIC void __objc_load(struct objc_init *init)
assert(p);
*proto = p;
}
int classesLoaded = 0;
for (Class *cls = init->cls_begin ; cls < init->cls_end ; cls++)
{
if (*cls == NULL)
{
continue;
}
// As a special case, allow using legacy ABI code with a new runtime.
if (isFirstLoad && (strcmp((*cls)->name, "Protocol") == 0))
{
CurrentABI = UnknownABI;
}
#ifdef DEBUG_LOADING
fprintf(stderr, "Loading class %s\n", (*cls)->name);
#endif
objc_load_class(*cls);
}
if (isFirstLoad && (classesLoaded == 0))
{
// As a special case, allow using legacy ABI code with a new runtime.
CurrentABI = UnknownABI;
}
#if 0
// We currently don't do anything with these pointers. They exist to
// provide a level of indirection that will permit us to completely change
Expand Down
59 changes: 59 additions & 0 deletions loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,63 @@ void objc_init_statics(struct objc_static_instance_list *statics);
*/
void objc_init_buffered_statics(void);

/**
* Initialise built-in classes (Object and Protocol). This must be called
* after `init_class_tables`.
*/
void init_builtin_classes(void);

/**
* Initialise the aliases table.
*/
void init_alias_table(void);

/**
* Initialise the automatic reference counting system.
*/
void init_arc(void);

/**
* Initialise the class tables.
*/
void init_class_tables(void);

/**
* Initialise the dispatch table machinery.
*/
void init_dispatch_tables(void);

/**
* Initialise the protocol tables.
*/
void init_protocol_table(void);

/**
* Initialise the selector tables.
*/
void init_selector_tables(void);

/**
* Initialise the trampolines for using blocks as methods.
*/
void init_trampolines(void);

/**
* Send +load messages to a class if required.
*/
void objc_send_load_message(Class cls);

/**
* Resolve a class (populate its superclass and sibling class links). Returns
* YES if the class can be resolved, NO otherwise. Classes cannot be resolved
* unless their superclasses have all been resolved.
*/
BOOL objc_resolve_class(Class cls);

/**
* Initialise the block classes.
*/
void init_early_blocks(void);


#endif //__OBJC_LOADER_H_INCLUDED
1 change: 1 addition & 0 deletions method.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#pragma once
#include <assert.h>

/**
Expand Down
Loading

0 comments on commit a9d0313

Please sign in to comment.