1/* 2 * Copyright (c) 2012 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-opt.mm 26 Management of optimizations in the dyld shared cache 27*/ 28 29#include "objc-private.h" 30 31 32#if !SUPPORT_PREOPT 33// Preoptimization not supported on this platform. 34 35struct objc_selopt_t; 36 37bool isPreoptimized(void) 38{ 39 return false; 40} 41 42objc_selopt_t *preoptimizedSelectors(void) 43{ 44 return nil; 45} 46 47Class getPreoptimizedClass(const char *name) 48{ 49 return nil; 50} 51 52Class* copyPreoptimizedClasses(const char *name, int *outCount) 53{ 54 *outCount = 0; 55 return nil; 56} 57 58header_info *preoptimizedHinfoForHeader(const headerType *mhdr) 59{ 60 return nil; 61} 62 63void preopt_init(void) 64{ 65 disableSharedCacheOptimizations(); 66 67 if (PrintPreopt) { 68 _objc_inform("PREOPTIMIZATION: is DISABLED " 69 "(not supported on ths platform)"); 70 } 71} 72 73 74// !SUPPORT_PREOPT 75#else 76// SUPPORT_PREOPT 77 78#include <objc-shared-cache.h> 79 80using objc_opt::objc_clsopt_t; 81using objc_opt::objc_headeropt_t; 82using objc_opt::objc_opt_t; 83 84__BEGIN_DECLS 85 86// preopt: the actual opt used at runtime (nil or &_objc_opt_data) 87// _objc_opt_data: opt data possibly written by dyld 88// opt is initialized to ~0 to detect incorrect use before preopt_init() 89 90static const objc_opt_t *opt = (objc_opt_t *)~0; 91static bool preoptimized; 92 93extern const objc_opt_t _objc_opt_data; // in __TEXT, __objc_opt_ro 94 95bool isPreoptimized(void) 96{ 97 return preoptimized; 98} 99 100objc_selopt_t *preoptimizedSelectors(void) 101{ 102 return opt ? opt->selopt() : nil; 103} 104 105Class getPreoptimizedClass(const char *name) 106{ 107 objc_clsopt_t *classes = opt ? opt->clsopt() : nil; 108 if (!classes) return nil; 109 110 void *cls; 111 void *hi; 112 uint32_t count = classes->getClassAndHeader(name, cls, hi); 113 if (count == 1 && ((header_info *)hi)->loaded) { 114 // exactly one matching class, and its image is loaded 115 return (Class)cls; 116 } 117 else if (count > 1) { 118 // more than one matching class - find one that is loaded 119 void *clslist[count]; 120 void *hilist[count]; 121 classes->getClassesAndHeaders(name, clslist, hilist); 122 for (uint32_t i = 0; i < count; i++) { 123 if (((header_info *)hilist[i])->loaded) { 124 return (Class)clslist[i]; 125 } 126 } 127 } 128 129 // no match that is loaded 130 return nil; 131} 132 133 134Class* copyPreoptimizedClasses(const char *name, int *outCount) 135{ 136 *outCount = 0; 137 138 objc_clsopt_t *classes = opt ? opt->clsopt() : nil; 139 if (!classes) return nil; 140 141 void *cls; 142 void *hi; 143 uint32_t count = classes->getClassAndHeader(name, cls, hi); 144 if (count == 0) return nil; 145 146 Class *result = (Class *)_calloc_internal(count, sizeof(Class)); 147 if (count == 1 && ((header_info *)hi)->loaded) { 148 // exactly one matching class, and its image is loaded 149 result[(*outCount)++] = (Class)cls; 150 return result; 151 } 152 else if (count > 1) { 153 // more than one matching class - find those that are loaded 154 void *clslist[count]; 155 void *hilist[count]; 156 classes->getClassesAndHeaders(name, clslist, hilist); 157 for (uint32_t i = 0; i < count; i++) { 158 if (((header_info *)hilist[i])->loaded) { 159 result[(*outCount)++] = (Class)clslist[i]; 160 } 161 } 162 163 if (*outCount == 0) { 164 // found multiple classes with that name, but none are loaded 165 free(result); 166 result = nil; 167 } 168 return result; 169 } 170 171 // no match that is loaded 172 return nil; 173} 174 175namespace objc_opt { 176struct objc_headeropt_t { 177 uint32_t count; 178 uint32_t entsize; 179 header_info headers[0]; // sorted by mhdr address 180 181 header_info *get(const headerType *mhdr) 182 { 183 assert(entsize == sizeof(header_info)); 184 185 int32_t start = 0; 186 int32_t end = count; 187 while (start <= end) { 188 int32_t i = (start+end)/2; 189 header_info *hi = headers+i; 190 if (mhdr == hi->mhdr) return hi; 191 else if (mhdr < hi->mhdr) end = i-1; 192 else start = i+1; 193 } 194 195#if !NDEBUG 196 for (uint32_t i = 0; i < count; i++) { 197 header_info *hi = headers+i; 198 if (mhdr == hi->mhdr) { 199 _objc_fatal("failed to find header %p (%d/%d)", 200 mhdr, i, count); 201 } 202 } 203#endif 204 205 return nil; 206 } 207}; 208}; 209 210 211header_info *preoptimizedHinfoForHeader(const headerType *mhdr) 212{ 213 objc_headeropt_t *hinfos = opt ? opt->headeropt() : nil; 214 if (hinfos) return hinfos->get(mhdr); 215 else return nil; 216} 217 218 219void preopt_init(void) 220{ 221 // `opt` not set at compile time in order to detect too-early usage 222 const char *failure = nil; 223 opt = &_objc_opt_data; 224 225 if (DisablePreopt) { 226 // OBJC_DISABLE_PREOPTIMIZATION is set 227 // If opt->version != VERSION then you continue at your own risk. 228 failure = "(by OBJC_DISABLE_PREOPTIMIZATION)"; 229 } 230 else if (opt->version != objc_opt::VERSION) { 231 // This shouldn't happen. You probably forgot to edit objc-sel-table.s. 232 // If dyld really did write the wrong optimization version, 233 // then we must halt because we don't know what bits dyld twiddled. 234 _objc_fatal("bad objc preopt version (want %d, got %d)", 235 objc_opt::VERSION, opt->version); 236 } 237 else if (!opt->selopt() || !opt->headeropt()) { 238 // One of the tables is missing. 239 failure = "(dyld shared cache is absent or out of date)"; 240 } 241#if SUPPORT_IGNORED_SELECTOR_CONSTANT 242 else if (UseGC) { 243 // GC is on, which renames some selectors 244 // Non-selector optimizations are still valid, but we don't have 245 // any of those yet 246 failure = "(GC is on)"; 247 } 248#endif 249 250 if (failure) { 251 // All preoptimized selector references are invalid. 252 preoptimized = NO; 253 opt = nil; 254 disableSharedCacheOptimizations(); 255 256 if (PrintPreopt) { 257 _objc_inform("PREOPTIMIZATION: is DISABLED %s", failure); 258 } 259 } 260 else { 261 // Valid optimization data written by dyld shared cache 262 preoptimized = YES; 263 264 if (PrintPreopt) { 265 _objc_inform("PREOPTIMIZATION: is ENABLED " 266 "(version %d)", opt->version); 267 } 268 } 269} 270 271 272__END_DECLS 273 274// SUPPORT_PREOPT 275#endif 276