From c8ecc3548cfc73c530537e15580a1dddbafc9792 Mon Sep 17 00:00:00 2001 From: Ben Viglietta Date: Thu, 19 Jan 2017 11:30:47 -0800 Subject: [PATCH] RECONSIDER: recognize selectors that have been pre-hashed by the compiler --- abi_version.c | 9 ++++++--- loader.c | 9 ++++++++- module.h | 13 +++++++++++++ selector.h | 35 +++++++++++++++++++++++++++++++-- selector_table.c | 50 ++++++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 106 insertions(+), 10 deletions(-) diff --git a/abi_version.c b/abi_version.c index a75a3f5c..6243b4ab 100644 --- a/abi_version.c +++ b/abi_version.c @@ -34,7 +34,8 @@ enum { gcc_abi = 8, gnustep_abi = 9, - gc_abi = 10 + gc_abi = 10, + selhash_abi = 11, }; /** @@ -45,9 +46,11 @@ static struct objc_abi_version known_abis[] = /* GCC ABI. */ {gcc_abi, gcc_abi, gnustep_abi, sizeof(struct objc_module_abi_8)}, /* Non-fragile ABI. */ - {gnustep_abi, gcc_abi, gc_abi, sizeof(struct objc_module_abi_8)}, + {gnustep_abi, gcc_abi, selhash_abi, sizeof(struct objc_module_abi_8)}, /* GC ABI. Adds a field describing the GC mode. */ - {gc_abi, gcc_abi, gc_abi, sizeof(struct objc_module_abi_10)} + {gc_abi, gcc_abi, selhash_abi, sizeof(struct objc_module_abi_10)}, + /* ABI with hash in selector */ + {selhash_abi, gcc_abi, selhash_abi, sizeof(struct objc_module_abi_11)} }; static int known_abi_count = diff --git a/loader.c b/loader.c index 634b758a..db46dec7 100644 --- a/loader.c +++ b/loader.c @@ -99,8 +99,15 @@ void __objc_load_module(struct objc_module_abi_8 *module) // Register all of the selectors used in this module. if (symbols->selectors) { - objc_register_selector_array(symbols->selectors, + if (module->version < 11) { + // the module has the old selector format + objc_register_selector_array8(symbols->selectors, symbols->selector_count); + } + else { + objc_register_selector_array(symbols->selectors, + symbols->selector_count); + } } unsigned short defs = 0; diff --git a/module.h b/module.h index 96e0a773..880c8c13 100644 --- a/module.h +++ b/module.h @@ -85,6 +85,19 @@ struct objc_module_abi_10 int gc_mode; }; +struct objc_module_abi_11 +{ + /** + * Inherited fields from version 10 of the ABI. + */ + struct objc_module_abi_10 old; + + /** + * Flags for configuration of this module. Currently unused. + */ + uint32_t flags; +}; + /** * List of static instances of a named class provided in this module. */ diff --git a/selector.h b/selector.h index 116d0b9b..6de14e22 100644 --- a/selector.h +++ b/selector.h @@ -5,7 +5,7 @@ * test to see whether a selector is polymorphic and allows enumeration of all * type encodings for a given selector. * - * This is the same size as an objc_selector, so we can allocate them from the + * This is <= the size of an objc_selector, so we can allocate them from the * objc_selector pool. * * Note: For ABI v10, we can probably do something a bit more sensible here and @@ -19,8 +19,9 @@ struct sel_type_list /** * Structure used to store selectors in the list. + This is the selctor from abi version 8. */ -struct objc_selector +struct objc_selector8 { union { @@ -41,6 +42,36 @@ struct objc_selector const char * types; }; +/** + * Structure used to store selectors in the list. + */ +struct objc_selector +{ + union + { + /** + * The name of this selector. Used for unregistered selectors. + */ + const char *name; + /** + * The index of this selector in the selector table. When a selector + * is registered with the runtime, its name is replaced by an index + * uniquely identifying this selector. The index is used for dispatch. + */ + uintptr_t index; + }; + + /** + * The Objective-C type encoding of the message identified by this selector. + */ + const char * types; + + /** + * Hash value for this selector (calculated from its name and type) + */ + _Atomic uint32_t hash; +}; + /** * Returns the untyped variant of a selector. */ diff --git a/selector_table.c b/selector_table.c index 01e196fd..1f4965fe 100644 --- a/selector_table.c +++ b/selector_table.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "lock.h" #include "objc/runtime.h" #include "method_list.h" @@ -205,11 +206,11 @@ static int selector_equal(const void *k, } /** - * Hash a selector. + * Calculate the hash for the selector. Should match with the implementation + * in the compiler. */ -static inline uint32_t hash_selector(const void *s) +static inline uint32_t selector_create_hash(SEL sel) { - SEL sel = (SEL)s; uint32_t hash = 5381; const char *str = sel_getNameNonUnique(sel); uint32_t c; @@ -238,6 +239,28 @@ static inline uint32_t hash_selector(const void *s) return hash; } +/** + * Hash a selector + */ +static inline uint32_t hash_selector(const void *s) +{ + struct objc_selector * sel = (struct objc_selector *)s; + uint32_t hash = atomic_load_explicit(&sel->hash, memory_order_relaxed); + + if (hash == 0) { + // selectors can be created from methods or from older ABIs and + // won't have a hash yet. So calculate and cache it. + // Use atomic operations because this may not be happening + // under any lock. + hash = selector_create_hash(sel); + atomic_store_explicit(&sel->hash, hash, memory_order_relaxed); + } + else { + assert(hash == selector_create_hash(sel)); + } + return hash; +} + #define MAP_TABLE_NAME selector #define MAP_TABLE_SINGLE_THREAD #define MAP_TABLE_COMPARE_FUNCTION selector_identical @@ -289,7 +312,7 @@ static SEL selector_lookup(const char *name, const char *types) } static inline void add_selector_to_table(SEL aSel, int32_t uid, uint32_t idx) { - DEBUG_LOG("Sel %s uid: %d, idx: %d, hash: %d\n", sel_getNameNonUnique(aSel), uid, idx, hash_selector(aSel)); + DEBUG_LOG("Sel %s uid: %d, idx: %d, hash: %08x\n", sel_getNameNonUnique(aSel), uid, idx, aSel->hash); struct sel_type_list *typeList = (struct sel_type_list *)selector_pool_alloc(); typeList->value = aSel->name; @@ -333,6 +356,7 @@ static inline void register_selector_locked(SEL aSel) untyped = selector_pool_alloc(); untyped->name = aSel->name; untyped->types = 0; + untyped->hash = 0; DEBUG_LOG("Registering selector %d %s\n", (int)idx, sel_getNameNonUnique(aSel)); add_selector_to_table(untyped, idx, idx); // If we are in type dependent dispatch mode, the uid for the typed @@ -405,6 +429,7 @@ static SEL objc_register_selector_copy(SEL aSel, BOOL copyArgs) copy = selector_pool_alloc(); copy->name = aSel->name; copy->types = (NULL == aSel->types) ? NULL : aSel->types; + copy->hash = aSel->hash; if (copyArgs) { SEL untyped = selector_lookup(aSel->name, 0); @@ -610,6 +635,23 @@ PRIVATE void objc_register_selector_array(SEL selectors, unsigned long count) objc_register_selector(&selectors[i]); } } +PRIVATE void objc_register_selector_array8(struct objc_selector8 * selectors, unsigned long count) +{ + // GCC is broken and always sets the count to 0, so we ignore count until + // we can throw stupid and buggy compilers in the bin. + for (unsigned long i = 0; (NULL != selectors[i].name); i++) + { + SEL copy = selector_pool_alloc(); + copy->name = selectors[i].name; + copy->types = selectors[i].types; + copy->hash = selector_create_hash(copy); + SEL registered = objc_register_selector(copy); + + // the selector from the metadata needs to updated to the unique + // id instead of the name + selectors[i].name = registered->name; + } +} /**