objc-opt.mm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  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. objc-opt.mm
  25. Management of optimizations in the dyld shared cache
  26. */
  27. #include "objc-private.h"
  28. #if !SUPPORT_PREOPT
  29. // Preoptimization not supported on this platform.
  30. struct objc_selopt_t;
  31. bool isPreoptimized(void)
  32. {
  33. return false;
  34. }
  35. bool noMissingWeakSuperclasses(void)
  36. {
  37. return false;
  38. }
  39. bool header_info::isPreoptimized() const
  40. {
  41. return false;
  42. }
  43. bool header_info::hasPreoptimizedSelectors() const
  44. {
  45. return false;
  46. }
  47. bool header_info::hasPreoptimizedClasses() const
  48. {
  49. return false;
  50. }
  51. bool header_info::hasPreoptimizedProtocols() const
  52. {
  53. return false;
  54. }
  55. objc_selopt_t *preoptimizedSelectors(void)
  56. {
  57. return nil;
  58. }
  59. bool sharedCacheSupportsProtocolRoots(void)
  60. {
  61. return false;
  62. }
  63. Protocol *getPreoptimizedProtocol(const char *name)
  64. {
  65. return nil;
  66. }
  67. unsigned int getPreoptimizedClassUnreasonableCount()
  68. {
  69. return 0;
  70. }
  71. Class getPreoptimizedClass(const char *name)
  72. {
  73. return nil;
  74. }
  75. Class* copyPreoptimizedClasses(const char *name, int *outCount)
  76. {
  77. *outCount = 0;
  78. return nil;
  79. }
  80. header_info *preoptimizedHinfoForHeader(const headerType *mhdr)
  81. {
  82. return nil;
  83. }
  84. header_info_rw *getPreoptimizedHeaderRW(const struct header_info *const hdr)
  85. {
  86. return nil;
  87. }
  88. void preopt_init(void)
  89. {
  90. disableSharedCacheOptimizations();
  91. if (PrintPreopt) {
  92. _objc_inform("PREOPTIMIZATION: is DISABLED "
  93. "(not supported on ths platform)");
  94. }
  95. }
  96. // !SUPPORT_PREOPT
  97. #else
  98. // SUPPORT_PREOPT
  99. #include <objc-shared-cache.h>
  100. using objc_opt::objc_stringhash_offset_t;
  101. using objc_opt::objc_protocolopt_t;
  102. using objc_opt::objc_protocolopt2_t;
  103. using objc_opt::objc_clsopt_t;
  104. using objc_opt::objc_headeropt_ro_t;
  105. using objc_opt::objc_headeropt_rw_t;
  106. using objc_opt::objc_opt_t;
  107. __BEGIN_DECLS
  108. // preopt: the actual opt used at runtime (nil or &_objc_opt_data)
  109. // _objc_opt_data: opt data possibly written by dyld
  110. // opt is initialized to ~0 to detect incorrect use before preopt_init()
  111. static const objc_opt_t *opt = (objc_opt_t *)~0;
  112. static bool preoptimized;
  113. extern const objc_opt_t _objc_opt_data; // in __TEXT, __objc_opt_ro
  114. /***********************************************************************
  115. * Return YES if we have a valid optimized shared cache.
  116. **********************************************************************/
  117. bool isPreoptimized(void)
  118. {
  119. return preoptimized;
  120. }
  121. /***********************************************************************
  122. * Return YES if the shared cache does not have any classes with
  123. * missing weak superclasses.
  124. **********************************************************************/
  125. bool noMissingWeakSuperclasses(void)
  126. {
  127. if (!preoptimized) return NO; // might have missing weak superclasses
  128. return opt->flags & objc_opt::NoMissingWeakSuperclasses;
  129. }
  130. /***********************************************************************
  131. * Return YES if this image's dyld shared cache optimizations are valid.
  132. **********************************************************************/
  133. bool header_info::isPreoptimized() const
  134. {
  135. // preoptimization disabled for some reason
  136. if (!preoptimized) return NO;
  137. // image not from shared cache, or not fixed inside shared cache
  138. if (!info()->optimizedByDyld()) return NO;
  139. return YES;
  140. }
  141. bool header_info::hasPreoptimizedSelectors() const
  142. {
  143. // preoptimization disabled for some reason
  144. if (!preoptimized) return NO;
  145. return info()->optimizedByDyld() || info()->optimizedByDyldClosure();
  146. }
  147. bool header_info::hasPreoptimizedClasses() const
  148. {
  149. // preoptimization disabled for some reason
  150. if (!preoptimized) return NO;
  151. return info()->optimizedByDyld() || info()->optimizedByDyldClosure();
  152. }
  153. bool header_info::hasPreoptimizedProtocols() const
  154. {
  155. // preoptimization disabled for some reason
  156. if (!preoptimized) return NO;
  157. return info()->optimizedByDyld() || info()->optimizedByDyldClosure();
  158. }
  159. objc_selopt_t *preoptimizedSelectors(void)
  160. {
  161. return opt ? opt->selopt() : nil;
  162. }
  163. bool sharedCacheSupportsProtocolRoots(void)
  164. {
  165. return (opt != nil) && (opt->protocolopt2() != nil);
  166. }
  167. Protocol *getSharedCachePreoptimizedProtocol(const char *name)
  168. {
  169. // Look in the new table if we have it
  170. if (objc_protocolopt2_t *protocols2 = opt ? opt->protocolopt2() : nil) {
  171. // Note, we have to pass the lambda directly here as otherwise we would try
  172. // message copy and autorelease.
  173. return (Protocol *)protocols2->getProtocol(name, [](const void* hi) -> bool {
  174. return ((header_info *)hi)->isLoaded();
  175. });
  176. }
  177. objc_protocolopt_t *protocols = opt ? opt->protocolopt() : nil;
  178. if (!protocols) return nil;
  179. return (Protocol *)protocols->getProtocol(name);
  180. }
  181. Protocol *getPreoptimizedProtocol(const char *name)
  182. {
  183. // Try table from dyld closure first. It was built to ignore the dupes it
  184. // knows will come from the cache, so anything left in here was there when
  185. // we launched
  186. Protocol *result = nil;
  187. // Note, we have to pass the lambda directly here as otherwise we would try
  188. // message copy and autorelease.
  189. _dyld_for_each_objc_protocol(name, [&result](void* protocolPtr, bool isLoaded, bool* stop) {
  190. // Skip images which aren't loaded. This supports the case where dyld
  191. // might soft link an image from the main binary so its possibly not
  192. // loaded yet.
  193. if (!isLoaded)
  194. return;
  195. // Found a loaded image with this class name, so stop the search
  196. result = (Protocol *)protocolPtr;
  197. *stop = true;
  198. });
  199. if (result) return result;
  200. return getSharedCachePreoptimizedProtocol(name);
  201. }
  202. unsigned int getPreoptimizedClassUnreasonableCount()
  203. {
  204. objc_clsopt_t *classes = opt ? opt->clsopt() : nil;
  205. if (!classes) return 0;
  206. // This is an overestimate: each set of duplicates
  207. // gets double-counted in `capacity` as well.
  208. return classes->capacity + classes->duplicateCount();
  209. }
  210. Class getPreoptimizedClass(const char *name)
  211. {
  212. objc_clsopt_t *classes = opt ? opt->clsopt() : nil;
  213. if (!classes) return nil;
  214. // Try table from dyld closure first. It was built to ignore the dupes it
  215. // knows will come from the cache, so anything left in here was there when
  216. // we launched
  217. Class result = nil;
  218. // Note, we have to pass the lambda directly here as otherwise we would try
  219. // message copy and autorelease.
  220. _dyld_for_each_objc_class(name, [&result](void* classPtr, bool isLoaded, bool* stop) {
  221. // Skip images which aren't loaded. This supports the case where dyld
  222. // might soft link an image from the main binary so its possibly not
  223. // loaded yet.
  224. if (!isLoaded)
  225. return;
  226. // Found a loaded image with this class name, so stop the search
  227. result = (Class)classPtr;
  228. *stop = true;
  229. });
  230. if (result) return result;
  231. void *cls;
  232. void *hi;
  233. uint32_t count = classes->getClassAndHeader(name, cls, hi);
  234. if (count == 1 && ((header_info *)hi)->isLoaded()) {
  235. // exactly one matching class, and its image is loaded
  236. return (Class)cls;
  237. }
  238. else if (count > 1) {
  239. // more than one matching class - find one that is loaded
  240. void *clslist[count];
  241. void *hilist[count];
  242. classes->getClassesAndHeaders(name, clslist, hilist);
  243. for (uint32_t i = 0; i < count; i++) {
  244. if (((header_info *)hilist[i])->isLoaded()) {
  245. return (Class)clslist[i];
  246. }
  247. }
  248. }
  249. // no match that is loaded
  250. return nil;
  251. }
  252. Class* copyPreoptimizedClasses(const char *name, int *outCount)
  253. {
  254. *outCount = 0;
  255. objc_clsopt_t *classes = opt ? opt->clsopt() : nil;
  256. if (!classes) return nil;
  257. void *cls;
  258. void *hi;
  259. uint32_t count = classes->getClassAndHeader(name, cls, hi);
  260. if (count == 0) return nil;
  261. Class *result = (Class *)calloc(count, sizeof(Class));
  262. if (count == 1 && ((header_info *)hi)->isLoaded()) {
  263. // exactly one matching class, and its image is loaded
  264. result[(*outCount)++] = (Class)cls;
  265. return result;
  266. }
  267. else if (count > 1) {
  268. // more than one matching class - find those that are loaded
  269. void *clslist[count];
  270. void *hilist[count];
  271. classes->getClassesAndHeaders(name, clslist, hilist);
  272. for (uint32_t i = 0; i < count; i++) {
  273. if (((header_info *)hilist[i])->isLoaded()) {
  274. result[(*outCount)++] = (Class)clslist[i];
  275. }
  276. }
  277. if (*outCount == 0) {
  278. // found multiple classes with that name, but none are loaded
  279. free(result);
  280. result = nil;
  281. }
  282. return result;
  283. }
  284. // no match that is loaded
  285. return nil;
  286. }
  287. namespace objc_opt {
  288. struct objc_headeropt_ro_t {
  289. uint32_t count;
  290. uint32_t entsize;
  291. header_info headers[0]; // sorted by mhdr address
  292. header_info *get(const headerType *mhdr)
  293. {
  294. ASSERT(entsize == sizeof(header_info));
  295. int32_t start = 0;
  296. int32_t end = count;
  297. while (start <= end) {
  298. int32_t i = (start+end)/2;
  299. header_info *hi = headers+i;
  300. if (mhdr == hi->mhdr()) return hi;
  301. else if (mhdr < hi->mhdr()) end = i-1;
  302. else start = i+1;
  303. }
  304. #if DEBUG
  305. for (uint32_t i = 0; i < count; i++) {
  306. header_info *hi = headers+i;
  307. if (mhdr == hi->mhdr()) {
  308. _objc_fatal("failed to find header %p (%d/%d)",
  309. mhdr, i, count);
  310. }
  311. }
  312. #endif
  313. return nil;
  314. }
  315. };
  316. struct objc_headeropt_rw_t {
  317. uint32_t count;
  318. uint32_t entsize;
  319. header_info_rw headers[0]; // sorted by mhdr address
  320. };
  321. };
  322. header_info *preoptimizedHinfoForHeader(const headerType *mhdr)
  323. {
  324. #if !__OBJC2__
  325. // fixme old ABI shared cache doesn't prepare these properly
  326. return nil;
  327. #endif
  328. objc_headeropt_ro_t *hinfos = opt ? opt->headeropt_ro() : nil;
  329. if (hinfos) return hinfos->get(mhdr);
  330. else return nil;
  331. }
  332. header_info_rw *getPreoptimizedHeaderRW(const struct header_info *const hdr)
  333. {
  334. #if !__OBJC2__
  335. // fixme old ABI shared cache doesn't prepare these properly
  336. return nil;
  337. #endif
  338. objc_headeropt_ro_t *hinfoRO = opt ? opt->headeropt_ro() : nil;
  339. objc_headeropt_rw_t *hinfoRW = opt ? opt->headeropt_rw() : nil;
  340. if (!hinfoRO || !hinfoRW) {
  341. _objc_fatal("preoptimized header_info missing for %s (%p %p %p)",
  342. hdr->fname(), hdr, hinfoRO, hinfoRW);
  343. }
  344. int32_t index = (int32_t)(hdr - hinfoRO->headers);
  345. ASSERT(hinfoRW->entsize == sizeof(header_info_rw));
  346. return &hinfoRW->headers[index];
  347. }
  348. void preopt_init(void)
  349. {
  350. // Get the memory region occupied by the shared cache.
  351. size_t length;
  352. const uintptr_t start = (uintptr_t)_dyld_get_shared_cache_range(&length);
  353. if (start) {
  354. objc::dataSegmentsRanges.add(start, start + length);
  355. }
  356. // `opt` not set at compile time in order to detect too-early usage
  357. const char *failure = nil;
  358. opt = &_objc_opt_data;
  359. if (DisablePreopt) {
  360. // OBJC_DISABLE_PREOPTIMIZATION is set
  361. // If opt->version != VERSION then you continue at your own risk.
  362. failure = "(by OBJC_DISABLE_PREOPTIMIZATION)";
  363. }
  364. else if (opt->version != objc_opt::VERSION) {
  365. // This shouldn't happen. You probably forgot to edit objc-sel-table.s.
  366. // If dyld really did write the wrong optimization version,
  367. // then we must halt because we don't know what bits dyld twiddled.
  368. _objc_fatal("bad objc preopt version (want %d, got %d)",
  369. objc_opt::VERSION, opt->version);
  370. }
  371. else if (!opt->selopt() || !opt->headeropt_ro()) {
  372. // One of the tables is missing.
  373. failure = "(dyld shared cache is absent or out of date)";
  374. }
  375. if (failure) {
  376. // All preoptimized selector references are invalid.
  377. preoptimized = NO;
  378. opt = nil;
  379. disableSharedCacheOptimizations();
  380. if (PrintPreopt) {
  381. _objc_inform("PREOPTIMIZATION: is DISABLED %s", failure);
  382. }
  383. }
  384. else {
  385. // Valid optimization data written by dyld shared cache
  386. preoptimized = YES;
  387. if (PrintPreopt) {
  388. _objc_inform("PREOPTIMIZATION: is ENABLED "
  389. "(version %d)", opt->version);
  390. }
  391. }
  392. }
  393. __END_DECLS
  394. // SUPPORT_PREOPT
  395. #endif