#include <stdint.h>
#include <stdlib.h>
#ifdef SELOPT_WRITE
-#include <ext/hash_map>
+#include <unordered_map>
#endif
/*
DO NOT INCLUDE ANY objc HEADERS HERE
typedef int32_t objc_stringhash_offset_t;
typedef uint8_t objc_stringhash_check_t;
+static uint64_t lookup8( uint8_t *k, size_t length, uint64_t level);
+
#ifdef SELOPT_WRITE
// Perfect hash code is at the end of this file.
}
};
+struct hashstr {
+ size_t operator()(const char *s) const {
+ return (size_t)lookup8((uint8_t *)s, strlen(s), 0);
+ }
+};
+
// cstring => cstring's vmaddress
// (used for selector names and class names)
-typedef __gnu_cxx::hash_map<const char *, uint64_t, __gnu_cxx::hash<const char *>, eqstr> string_map;
+typedef std::unordered_map<const char *, uint64_t, hashstr, eqstr> string_map;
+
+// protocol name => protocol vmaddress
+typedef std::unordered_map<const char *, uint64_t, hashstr, eqstr> protocol_map;
// class name => (class vmaddress, header_info vmaddress)
-typedef __gnu_cxx::hash_multimap<const char *, std::pair<uint64_t, uint64_t>, __gnu_cxx::hash<const char *>, eqstr> class_map;
+typedef std::unordered_multimap<const char *, std::pair<uint64_t, uint64_t>, hashstr, eqstr> class_map;
static perfect_hash make_perfect(const string_map& strings);
#endif
-static uint64_t lookup8( uint8_t *k, size_t length, uint64_t level);
// Precomputed perfect hash table of strings.
// Base class for precomputed selector table and class table.
uint32_t occupied;
uint32_t shift;
uint32_t mask;
- uint32_t zero;
- uint32_t unused; // alignment pad
+ uint32_t unused1; // was zero
+ uint32_t unused2; // alignment pad
uint64_t salt;
uint32_t scramble[256];
objc_stringhash_offset_t *offsets() { return (objc_stringhash_offset_t *)&checkbytes()[capacity]; }
const objc_stringhash_offset_t *offsets() const { return (const objc_stringhash_offset_t *)&checkbytes()[capacity]; }
- uint32_t hash(const char *key) const
+ uint32_t hash(const char *key, size_t keylen) const
{
- uint64_t val = lookup8((uint8_t*)key, strlen(key), salt);
+ uint64_t val = lookup8((uint8_t*)key, keylen, salt);
uint32_t index = (uint32_t)(val>>shift) ^ scramble[tab[val&mask]];
return index;
}
+ uint32_t hash(const char *key) const
+ {
+ return hash(key, strlen(key));
+ }
+
// The check bytes areused to reject strings that aren't in the table
// without paging in the table's cstring data. This checkbyte calculation
// catches 4785/4815 rejects when launching Safari; a perfect checkbyte
// would catch 4796/4815.
- objc_stringhash_check_t checkbyte(const char *key) const
+ objc_stringhash_check_t checkbyte(const char *key, size_t keylen) const
{
return
((key[0] & 0x7) << 5)
|
- (strlen(key) & 0x1f);
+ ((uint8_t)keylen & 0x1f);
}
+ objc_stringhash_check_t checkbyte(const char *key) const
+ {
+ return checkbyte(key, strlen(key));
+ }
+
+
#define INDEX_NOT_FOUND (~(uint32_t)0)
uint32_t getIndex(const char *key) const
{
- uint32_t h = hash(key);
+ size_t keylen = strlen(key);
+ uint32_t h = hash(key, keylen);
// Use check byte to reject without paging in the table's cstrings
objc_stringhash_check_t h_check = checkbytes()[h];
- objc_stringhash_check_t key_check = checkbyte(key);
+ objc_stringhash_check_t key_check = checkbyte(key, keylen);
bool check_fail = (h_check != key_check);
#if ! SELOPT_DEBUG
if (check_fail) return INDEX_NOT_FOUND;
#endif
- const char *result = (const char *)this + offsets()[h];
+ objc_stringhash_offset_t offset = offsets()[h];
+ if (offset == 0) return INDEX_NOT_FOUND;
+ const char *result = (const char *)this + offset;
if (0 != strcmp(key, result)) return INDEX_NOT_FOUND;
#if SELOPT_DEBUG
S32(occupied);
S32(shift);
S32(mask);
- S32(zero);
S64(salt);
}
occupied = phash.occupied;
shift = phash.shift;
mask = phash.mask;
- zero = 0;
- unused = 0;
+ unused1 = 0;
+ unused2 = 0;
salt = phash.salt;
if (size() > remaining) {
tab[i] = phash.tab[i];
}
- // Set offsets to ""
+ // Set offsets to 0
for (uint32_t i = 0; i < phash.capacity; i++) {
- offsets()[i] =
- (objc_stringhash_offset_t)offsetof(objc_stringhash_t, zero);
+ offsets()[i] = 0;
}
// Set checkbytes to 0
for (uint32_t i = 0; i < phash.capacity; i++) {
return "selector section too small (metadata not optimized)";
}
- // Set class offsets to &zero
- objc_stringhash_offset_t zeroOffset =
- (objc_stringhash_offset_t)offsetof(objc_stringhash_t, zero);
+ // Set class offsets to 0
for (uint32_t i = 0; i < capacity; i++) {
- classOffsets()[i].clsOffset = zeroOffset;
- classOffsets()[i].hiOffset = zeroOffset;
+ classOffsets()[i].clsOffset = 0;
+ classOffsets()[i].hiOffset = 0;
}
// Set real class offsets
return "class list busted (metadata not optimized)";
}
- if (classOffsets()[h].clsOffset != zeroOffset) {
+ if (classOffsets()[h].clsOffset != 0) {
// already did this class
continue;
}
#endif
};
+
+
+struct objc_protocolopt_t : objc_stringhash_t {
+ // ...objc_stringhash_t fields...
+ // uint32_t protocolOffsets[capacity]; /* offsets from &capacity to protocol_t */
+
+ objc_stringhash_offset_t *protocolOffsets() { return (objc_stringhash_offset_t *)&offsets()[capacity]; }
+ const objc_stringhash_offset_t *protocolOffsets() const { return (const objc_stringhash_offset_t *)&offsets()[capacity]; }
+
+ void* getProtocol(const char *key) const
+ {
+ uint32_t h = getIndex(key);
+ if (h == INDEX_NOT_FOUND) {
+ return NULL;
+ }
+
+ return (void *)((const char *)this + protocolOffsets()[h]);
+ }
+
+#ifdef SELOPT_WRITE
+
+ size_t size()
+ {
+ return
+ objc_stringhash_t::size() + capacity * sizeof(objc_stringhash_offset_t);
+ }
+
+ void byteswap(bool little_endian)
+ {
+ objc_stringhash_offset_t *o;
+
+ o = protocolOffsets();
+ for (objc_stringhash_offset_t i = 0; i < capacity; i++) {
+ S32(o[i]);
+ }
+
+ objc_stringhash_t::byteswap(little_endian);
+ }
+
+ const char *write(uint64_t base, size_t remaining,
+ string_map& strings, protocol_map& protocols,
+ bool verbose)
+ {
+ const char *err;
+ err = objc_stringhash_t::write(base, remaining, strings);
+ if (err) return err;
+
+ if (size() > remaining) {
+ return "selector section too small (metadata not optimized)";
+ }
+
+ // Set protocol offsets to 0
+ for (uint32_t i = 0; i < capacity; i++) {
+ protocolOffsets()[i] = 0;
+ }
+
+ // Set real protocol offsets
+# define SHIFT (64 - 8*sizeof(objc_stringhash_offset_t))
+ protocol_map::const_iterator c;
+ for (c = protocols.begin(); c != protocols.end(); ++c) {
+ uint32_t h = getIndex(c->first);
+ if (h == INDEX_NOT_FOUND) {
+ return "protocol list busted (metadata not optimized)";
+ }
+
+ int64_t offset = c->second - base;
+ if ((offset<<SHIFT)>>SHIFT != offset) {
+ return "protocol offset too big (metadata not optimized)";
+ }
+
+ protocolOffsets()[h] = (objc_stringhash_offset_t)offset;
+ }
+# undef SHIFT
+
+ return NULL;
+ }
+
+// SELOPT_WRITE
+#endif
+};
+
+
// Precomputed image list.
struct objc_headeropt_t;
struct objc_clsopt_t;
// Edit objc-sel-table.s if you change this value.
-enum { VERSION = 12 };
+enum { VERSION = 13 };
// Top-level optimization structure.
// Edit objc-sel-table.s and OPT_INITIALIZER if you change this structure.
-struct objc_opt_t {
+struct alignas(alignof(void*)) objc_opt_t {
uint32_t version;
int32_t selopt_offset;
int32_t headeropt_offset;
int32_t clsopt_offset;
+ int32_t protocolopt_offset;
const objc_selopt_t* selopt() const {
if (selopt_offset == 0) return NULL;
if (clsopt_offset == 0) return NULL;
return (objc_clsopt_t *)((uint8_t *)this + clsopt_offset);
}
+
+ struct objc_protocolopt_t* protocolopt() const {
+ if (protocolopt_offset == 0) return NULL;
+ return (objc_protocolopt_t *)((uint8_t *)this + protocolopt_offset);
+ }
};
// sizeof(objc_opt_t) must be pointer-aligned
4, 4, 63, 3, 0, 0, 0,0, X256(0), 0, 0, 16, 16, 16, 16 \
/* no objc_headeropt_t */ \
/* no objc_clsopt_t */ \
+ /* no objc_protocolopt_t */ \
}
+// List of offsets in libobjc that the shared cache optimization needs to use.
+template <typename T>
+struct objc_opt_pointerlist_tt {
+ T protocolClass;
+};
+typedef struct objc_opt_pointerlist_tt<uintptr_t> objc_opt_pointerlist_t;
+
+
/*
--------------------------------------------------------------------
mix -- mix 3 64-bit values reversibly.