]> git.saurik.com Git - apple/dyld.git/blob - dyld3/shared-cache/OptimizerObjC.cpp
71fe9dc7ab59a02c5844836a998642b3b87bf81c
[apple/dyld.git] / dyld3 / shared-cache / OptimizerObjC.cpp
1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2 *
3 * Copyright (c) 2014 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25
26 #include <dirent.h>
27 #include <sys/errno.h>
28 #include <sys/fcntl.h>
29 #include <mach-o/loader.h>
30 #include <mach-o/fat.h>
31 #include <assert.h>
32
33 #include "DyldSharedCache.h"
34 #include "Diagnostics.h"
35 #include "CacheBuilder.h"
36 #include "FileAbstraction.hpp"
37 #include "MachOFileAbstraction.hpp"
38
39
40 // Scan a C++ or Swift length-mangled field.
41 static bool scanMangledField(const char *&string, const char *end,
42 const char *&field, int& length)
43 {
44 // Leading zero not allowed.
45 if (*string == '0') return false;
46
47 length = 0;
48 field = string;
49 while (field < end) {
50 char c = *field;
51 if (!isdigit(c)) break;
52 field++;
53 if (__builtin_smul_overflow(length, 10, &length)) return false;
54 if (__builtin_sadd_overflow(length, c - '0', &length)) return false;
55 }
56
57 string = field + length;
58 return length > 0 && string <= end;
59 }
60
61
62 // copySwiftDemangledName
63 // Returns the pretty form of the given Swift-mangled class or protocol name.
64 // Returns nullptr if the string doesn't look like a mangled Swift name.
65 // The result must be freed with free().
66 static char *copySwiftDemangledName(const char *string, bool isProtocol = false)
67 {
68 if (!string) return nullptr;
69
70 // Swift mangling prefix.
71 if (strncmp(string, isProtocol ? "_TtP" : "_TtC", 4) != 0) return nullptr;
72 string += 4;
73
74 const char *end = string + strlen(string);
75
76 // Module name.
77 const char *prefix;
78 int prefixLength;
79 if (string[0] == 's') {
80 // "s" is the Swift module.
81 prefix = "Swift";
82 prefixLength = 5;
83 string += 1;
84 } else {
85 if (! scanMangledField(string, end, prefix, prefixLength)) return nullptr;
86 }
87
88 // Class or protocol name.
89 const char *suffix;
90 int suffixLength;
91 if (! scanMangledField(string, end, suffix, suffixLength)) return nullptr;
92
93 if (isProtocol) {
94 // Remainder must be "_".
95 if (strcmp(string, "_") != 0) return nullptr;
96 } else {
97 // Remainder must be empty.
98 if (string != end) return nullptr;
99 }
100
101 char *result;
102 asprintf(&result, "%.*s.%.*s", prefixLength,prefix, suffixLength,suffix);
103 return result;
104 }
105
106
107 class ContentAccessor {
108 public:
109 ContentAccessor(const DyldSharedCache* cache, Diagnostics& diag)
110 : _diagnostics(diag)
111 {
112 __block int index = 0;
113 cache->forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
114 _regions[index++] = { (uint8_t*)content, (uint8_t*)content+size, vmAddr, vmAddr+size };
115 });
116 }
117
118 void* contentForVMAddr(uint64_t vmaddr) {
119 for (const Info& info : _regions) {
120 if ( (info.startAddr <= vmaddr) && (vmaddr < info.endAddr) )
121 return (void*)(info.contentStart + vmaddr - info.startAddr);
122 }
123 if ( vmaddr != 0 )
124 _diagnostics.error("invalid vmaddr 0x%0llX in ObjC data", vmaddr);
125 return nullptr;
126 }
127
128 uint64_t vmAddrForContent(const void* content) {
129 for (const Info& info : _regions) {
130 if ( (info.contentStart <= content) && (content < info.contentEnd) )
131 return info.startAddr + ((uint8_t*)content - (uint8_t*)info.contentStart);
132 }
133 _diagnostics.error("invalid content pointer %p in ObjC data", content);
134 return 0;
135 }
136
137 Diagnostics& diagnostics() { return _diagnostics; }
138
139 private:
140 struct Info { uint8_t* contentStart; uint8_t* contentEnd; uint64_t startAddr; uint64_t endAddr; };
141 Diagnostics& _diagnostics;
142 Info _regions[3];
143 };
144
145
146 // Access a section containing a list of pointers
147 template <typename P, typename T>
148 class PointerSection
149 {
150 typedef typename P::uint_t pint_t;
151 public:
152 PointerSection(ContentAccessor* cache, const macho_header<P>* mh,
153 const char* segname, const char* sectname)
154 : _cache(cache),
155 _section(mh->getSection(segname, sectname)),
156 _base(_section ? (pint_t*)cache->contentForVMAddr(_section->addr()) : 0),
157 _count(_section ? (pint_t)(_section->size() / sizeof(pint_t)) : 0) {
158 }
159
160 pint_t count() const { return _count; }
161
162 pint_t getVMAddress(pint_t index) const {
163 if ( index >= _count ) {
164 _cache->diagnostics().error("index out of range in section %s", _section->sectname());
165 return 0;
166 }
167 return (pint_t)P::getP(_base[index]);
168 }
169
170 T get(pint_t index) const {
171 return (T)_cache->contentForVMAddr(getVMAddress(index));
172 }
173
174 void setVMAddress(pint_t index, pint_t value) {
175 if ( index >= _count ) {
176 _cache->diagnostics().error("index out of range in section %s", _section->sectname());
177 return;
178 }
179 P::setP(_base[index], value);
180 }
181
182 void removeNulls() {
183 pint_t shift = 0;
184 for (pint_t i = 0; i < _count; i++) {
185 pint_t value = _base[i];
186 if (value) {
187 _base[i-shift] = value;
188 } else {
189 shift++;
190 }
191 }
192 _count -= shift;
193 const_cast<macho_section<P>*>(_section)->set_size(_count * sizeof(pint_t));
194 }
195
196 private:
197 ContentAccessor* const _cache;
198 const macho_section<P>* const _section;
199 pint_t* const _base;
200 pint_t const _count;
201 };
202
203
204 // Access a section containing an array of structures
205 template <typename P, typename T>
206 class ArraySection
207 {
208 public:
209 ArraySection(ContentAccessor* cache, const macho_header<P>* mh,
210 const char *segname, const char *sectname)
211 : _cache(cache),
212 _section(mh->getSection(segname, sectname)),
213 _base(_section ? (T *)cache->contentForVMAddr(_section->addr()) : 0),
214 _count(_section ? _section->size() / sizeof(T) : 0) {
215 }
216
217 uint64_t count() const { return _count; }
218
219 T& get(uint64_t index) const {
220 if (index >= _count) {
221 _cache->diagnostics().error("index out of range in section %s", _section->sectname());
222 }
223 return _base[index];
224 }
225
226 private:
227 ContentAccessor* const _cache;
228 const macho_section<P>* const _section;
229 T * const _base;
230 uint64_t const _count;
231 };
232
233
234 #define SELOPT_WRITE
235 #include "objc-shared-cache.h"
236 #include "ObjC1Abstraction.hpp"
237 #include "ObjC2Abstraction.hpp"
238
239
240 namespace {
241
242
243
244 template <typename P>
245 class ObjCSelectorUniquer
246 {
247 public:
248 typedef typename P::uint_t pint_t;
249
250 ObjCSelectorUniquer(ContentAccessor* cache) : _cache(cache) { }
251
252 pint_t visit(pint_t oldValue)
253 {
254 _count++;
255 const char *s = (const char *)_cache->contentForVMAddr(oldValue);
256 objc_opt::string_map::iterator element =
257 _selectorStrings.insert(objc_opt::string_map::value_type(s, oldValue)).first;
258 return (pint_t)element->second;
259 }
260
261 objc_opt::string_map& strings() {
262 return _selectorStrings;
263 }
264
265 size_t count() const { return _count; }
266
267 private:
268 objc_opt::string_map _selectorStrings;
269 ContentAccessor* _cache;
270 size_t _count = 0;
271 };
272
273
274 template <typename P>
275 class ClassListBuilder
276 {
277 private:
278 objc_opt::string_map _classNames;
279 objc_opt::class_map _classes;
280 size_t _count = 0;
281 HeaderInfoOptimizer<P, objc_header_info_ro_t<P>>& _hInfos;
282
283 public:
284
285 ClassListBuilder(HeaderInfoOptimizer<P, objc_header_info_ro_t<P>>& hinfos) : _hInfos(hinfos) { }
286
287 void visitClass(ContentAccessor* cache,
288 const macho_header<P>* header,
289 objc_class_t<P>* cls)
290 {
291 if (cls->isMetaClass(cache)) return;
292
293 const char *name = cls->getName(cache);
294 uint64_t name_vmaddr = cache->vmAddrForContent((void*)name);
295 uint64_t cls_vmaddr = cache->vmAddrForContent(cls);
296 uint64_t hinfo_vmaddr = cache->vmAddrForContent(_hInfos.hinfoForHeader(cache, header));
297 _classNames.insert(objc_opt::string_map::value_type(name, name_vmaddr));
298 _classes.insert(objc_opt::class_map::value_type(name, std::pair<uint64_t, uint64_t>(cls_vmaddr, hinfo_vmaddr)));
299 _count++;
300 }
301
302 objc_opt::string_map& classNames() {
303 return _classNames;
304 }
305
306 objc_opt::class_map& classes() {
307 return _classes;
308 }
309
310 size_t count() const { return _count; }
311 };
312
313 template <typename P>
314 class ProtocolOptimizer
315 {
316 private:
317 typedef typename P::uint_t pint_t;
318
319 objc_opt::string_map _protocolNames;
320 objc_opt::protocol_map _protocols;
321 size_t _protocolCount;
322 size_t _protocolReferenceCount;
323 Diagnostics& _diagnostics;
324
325 friend class ProtocolReferenceWalker<P, ProtocolOptimizer<P>>;
326
327 pint_t visitProtocolReference(ContentAccessor* cache, pint_t oldValue)
328 {
329 objc_protocol_t<P>* proto = (objc_protocol_t<P>*)
330 cache->contentForVMAddr(oldValue);
331 pint_t newValue = (pint_t)_protocols[proto->getName(cache)];
332 if (oldValue != newValue) _protocolReferenceCount++;
333 return newValue;
334 }
335
336 public:
337
338 ProtocolOptimizer(Diagnostics& diag)
339 : _protocolCount(0), _protocolReferenceCount(0), _diagnostics(diag) {
340 }
341
342 void addProtocols(ContentAccessor* cache, const macho_header<P>* header)
343 {
344 PointerSection<P, objc_protocol_t<P> *>
345 protocols(cache, header, "__DATA", "__objc_protolist");
346
347 for (pint_t i = 0; i < protocols.count(); i++) {
348 objc_protocol_t<P> *proto = protocols.get(i);
349
350 const char *name = proto->getName(cache);
351 if (_protocolNames.count(name) == 0) {
352 if (proto->getSize() > sizeof(objc_protocol_t<P>)) {
353 _diagnostics.error("objc protocol is too big");
354 return;
355 }
356
357 uint64_t name_vmaddr = cache->vmAddrForContent((void*)name);
358 uint64_t proto_vmaddr = cache->vmAddrForContent(proto);
359 _protocolNames.insert(objc_opt::string_map::value_type(name, name_vmaddr));
360 _protocols.insert(objc_opt::protocol_map::value_type(name, proto_vmaddr));
361 _protocolCount++;
362 }
363 }
364 }
365
366 const char *writeProtocols(ContentAccessor* cache,
367 uint8_t *& rwdest, size_t& rwremaining,
368 uint8_t *& rodest, size_t& roremaining,
369 std::vector<void*>& pointersInData,
370 pint_t protocolClassVMAddr)
371 {
372 if (_protocolCount == 0) return NULL;
373
374 if (protocolClassVMAddr == 0) {
375 return "libobjc's Protocol class symbol not found (metadata not optimized)";
376 }
377
378 size_t rwrequired = _protocolCount * sizeof(objc_protocol_t<P>);
379 if (rwremaining < rwrequired) {
380 return "libobjc's read-write section is too small (metadata not optimized)";
381 }
382
383 for (objc_opt::protocol_map::iterator iter = _protocols.begin();
384 iter != _protocols.end();
385 ++iter)
386 {
387 objc_protocol_t<P>* oldProto = (objc_protocol_t<P>*)
388 cache->contentForVMAddr(iter->second);
389
390 // Create a new protocol object.
391 objc_protocol_t<P>* proto = (objc_protocol_t<P>*)rwdest;
392 rwdest += sizeof(*proto);
393 rwremaining -= sizeof(*proto);
394
395 // Initialize it.
396 uint32_t oldSize = oldProto->getSize();
397 memcpy(proto, oldProto, oldSize);
398 if (!proto->getIsaVMAddr()) {
399 proto->setIsaVMAddr(protocolClassVMAddr);
400 }
401 if (oldSize < sizeof(*proto)) {
402 // Protocol object is old. Populate new fields.
403 proto->setSize(sizeof(objc_protocol_t<P>));
404 // missing extendedMethodTypes is already nil
405 }
406 // Some protocol objects are big enough to have the
407 // demangledName field but don't initialize it.
408 // Initialize it here if it is not already set.
409 if (!proto->getDemangledName(cache)) {
410 const char *roName = proto->getName(cache);
411 char *demangledName = copySwiftDemangledName(roName, true);
412 if (demangledName) {
413 size_t length = 1 + strlen(demangledName);
414 if (roremaining < length) {
415 return "libobjc's read-only section is too small (metadata not optimized)";
416 }
417
418 memmove(rodest, demangledName, length);
419 roName = (const char *)rodest;
420 rodest += length;
421 roremaining -= length;
422
423 free(demangledName);
424 }
425 proto->setDemangledName(cache, roName, _diagnostics);
426 }
427 proto->setFixedUp();
428
429 // Redirect the protocol table at our new object.
430 iter->second = cache->vmAddrForContent(proto);
431
432 // Add new rebase entries.
433 proto->addPointers(pointersInData);
434 }
435
436 return NULL;
437 }
438
439 void updateReferences(ContentAccessor* cache, const macho_header<P>* header)
440 {
441 ProtocolReferenceWalker<P, ProtocolOptimizer<P>> refs(*this);
442 refs.walk(cache, header);
443 }
444
445 objc_opt::string_map& protocolNames() {
446 return _protocolNames;
447 }
448
449 objc_opt::protocol_map& protocols() {
450 return _protocols;
451 }
452
453 size_t protocolCount() const { return _protocolCount; }
454 size_t protocolReferenceCount() const { return _protocolReferenceCount; }
455 };
456
457
458 static int percent(size_t num, size_t denom) {
459 if (denom)
460 return (int)(num / (double)denom * 100);
461 else
462 return 100;
463 }
464
465
466 template <typename P>
467 void optimizeObjC(DyldSharedCache* cache, bool forProduction, std::vector<void*>& pointersForASLR, Diagnostics& diag)
468 {
469 typedef typename P::E E;
470 typedef typename P::uint_t pint_t;
471
472 diag.verbose("Optimizing objc metadata:\n");
473 diag.verbose(" cache type is %s\n", forProduction ? "production" : "development");
474
475 ContentAccessor cacheAccessor(cache, diag);
476
477 size_t headerSize = P::round_up(sizeof(objc_opt::objc_opt_t));
478 if (headerSize != sizeof(objc_opt::objc_opt_t)) {
479 diag.warning("libobjc's optimization structure size is wrong (metadata not optimized)");
480 }
481
482 //
483 // Find libobjc's empty sections and build list of images with objc metadata
484 //
485 __block const macho_section<P> *optROSection = nullptr;
486 __block const macho_section<P> *optRWSection = nullptr;
487 __block const macho_section<P> *optPointerListSection = nullptr;
488 __block std::vector<const macho_header<P>*> objcDylibs;
489 cache->forEachImage(^(const mach_header* machHeader, const char* installName) {
490 const macho_header<P>* mh = (const macho_header<P>*)machHeader;
491 if ( strstr(installName, "/libobjc.") != nullptr ) {
492 optROSection = mh->getSection("__TEXT", "__objc_opt_ro");
493 optRWSection = mh->getSection("__DATA", "__objc_opt_rw");
494 optPointerListSection = mh->getSection("__DATA", "__objc_opt_ptrs");
495 }
496 if ( mh->getSection("__DATA", "__objc_imageinfo") || mh->getSection("__OBJC", "__image_info") ) {
497 objcDylibs.push_back(mh);
498 }
499 // log("installName %s at mhdr 0x%016lx", installName, (uintptr_t)cacheAccessor.vmAddrForContent((void*)mh));
500 });
501 if ( optROSection == nullptr ) {
502 diag.warning("libobjc's read-only section missing (metadata not optimized)");
503 return;
504 }
505 if ( optRWSection == nullptr ) {
506 diag.warning("libobjc's read/write section missing (metadata not optimized)");
507 return;
508 }
509 if ( optPointerListSection == nullptr ) {
510 diag.warning("libobjc's pointer list section missing (metadata not optimized)");
511 return;
512 }
513
514 uint8_t* optROData = (uint8_t*)cacheAccessor.contentForVMAddr(optROSection->addr());
515 if ( optROData == nullptr ) {
516 diag.warning("libobjc's read-only section has bad content");
517 return;
518 }
519 size_t optRORemaining = optROSection->size();
520 uint8_t* optRWData = (uint8_t*)cacheAccessor.contentForVMAddr(optRWSection->addr());
521 size_t optRWRemaining = optRWSection->size();
522 if (optRORemaining < headerSize) {
523 diag.warning("libobjc's read-only section is too small (metadata not optimized)");
524 return;
525 }
526 objc_opt::objc_opt_t* optROHeader = (objc_opt::objc_opt_t *)optROData;
527 optROData += headerSize;
528 optRORemaining -= headerSize;
529 if (E::get32(optROHeader->version) != objc_opt::VERSION) {
530 diag.warning("libobjc's read-only section version is unrecognized (metadata not optimized)");
531 return;
532 }
533
534 if (optPointerListSection->size() < sizeof(objc_opt::objc_opt_pointerlist_tt<pint_t>)) {
535 diag.warning("libobjc's pointer list section is too small (metadata not optimized)");
536 return;
537 }
538 const objc_opt::objc_opt_pointerlist_tt<pint_t> *optPointerList = (const objc_opt::objc_opt_pointerlist_tt<pint_t> *)cacheAccessor.contentForVMAddr(optPointerListSection->addr());
539
540 // Write nothing to optROHeader until everything else is written.
541 // If something fails below, libobjc will not use the section.
542
543
544 //
545 // Make copy of objcList and sort that list.
546 //
547 std::vector<const macho_header<P>*> addressSortedDylibs = objcDylibs;
548 std::sort(addressSortedDylibs.begin(), addressSortedDylibs.end(), [](const macho_header<P>* lmh, const macho_header<P>* rmh) -> bool {
549 return lmh < rmh;
550 });
551
552 //
553 // Build HeaderInfo list in cache
554 //
555 // First the RO header info
556 // log("writing out %d RO dylibs at offset %d", (uint32_t)objcDylibs.size(), (uint32_t)(optROSection->size() - optRORemaining));
557 uint64_t hinfoROVMAddr = optROSection->addr() + optROSection->size() - optRORemaining;
558 HeaderInfoOptimizer<P, objc_header_info_ro_t<P>> hinfoROOptimizer;
559 const char* err = hinfoROOptimizer.init((uint32_t)objcDylibs.size(), optROData, optRORemaining);
560 if (err) {
561 diag.warning("%s", err);
562 return;
563 }
564 else {
565 for (const macho_header<P>* mh : addressSortedDylibs) {
566 hinfoROOptimizer.update(&cacheAccessor, mh, pointersForASLR);
567 }
568 }
569
570 // Then the RW header info
571 // log("writing out %d RW dylibs at offset %d", (uint32_t)objcDylibs.size(), (uint32_t)(optRWSection->size() - optRWRemaining));
572 uint64_t hinfoRWVMAddr = (uint64_t)optRWSection->addr() + (uint64_t)optRWSection->size() - optRWRemaining;
573 HeaderInfoOptimizer<P, objc_header_info_rw_t<P>> hinfoRWOptimizer;
574 err = hinfoRWOptimizer.init((uint32_t)objcDylibs.size(), optRWData, optRWRemaining);
575 if (err) {
576 diag.warning("%s", err);
577 return;
578 }
579 else {
580 for (const macho_header<P>* mh : addressSortedDylibs) {
581 hinfoRWOptimizer.update(&cacheAccessor, mh, pointersForASLR);
582 }
583 }
584
585 //
586 // Update selector references and build selector list
587 //
588 // This is SAFE: if we run out of room for the selector table,
589 // the modified binaries are still usable.
590 //
591 // Heuristic: choose selectors from libraries with more selector cstring data first.
592 // This tries to localize selector cstring memory.
593 //
594 ObjCSelectorUniquer<P> uniq(&cacheAccessor);
595 std::vector<const macho_header<P>*> sizeSortedDylibs = objcDylibs;
596 std::sort(sizeSortedDylibs.begin(), sizeSortedDylibs.end(), [](const macho_header<P>* lmh, const macho_header<P>* rmh) -> bool {
597 const macho_section<P>* lSection = lmh->getSection("__TEXT", "__objc_methname");
598 const macho_section<P>* rSection = rmh->getSection("__TEXT", "__objc_methname");
599 uint64_t lSelectorSize = (lSection ? lSection->size() : 0);
600 uint64_t rSelectorSize = (rSection ? rSection->size() : 0);
601 return lSelectorSize > rSelectorSize;
602 });
603
604 SelectorOptimizer<P, ObjCSelectorUniquer<P> > selOptimizer(uniq);
605 for (const macho_header<P>* mh : sizeSortedDylibs) {
606 LegacySelectorUpdater<P, ObjCSelectorUniquer<P>>::update(&cacheAccessor, mh, uniq);
607 selOptimizer.optimize(&cacheAccessor, mh);
608 }
609
610 diag.verbose(" uniqued %6lu selectors\n", uniq.strings().size());
611 diag.verbose(" updated %6lu selector references\n", uniq.count());
612
613 uint64_t seloptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining;
614 objc_opt::objc_selopt_t *selopt = new(optROData) objc_opt::objc_selopt_t;
615 err = selopt->write(seloptVMAddr, optRORemaining, uniq.strings());
616 if (err) {
617 diag.warning("%s", err);
618 return;
619 }
620 optROData += selopt->size();
621 optRORemaining -= selopt->size();
622 uint32_t seloptCapacity = selopt->capacity;
623 uint32_t seloptOccupied = selopt->occupied;
624 selopt->byteswap(E::little_endian), selopt = nullptr;
625
626 diag.verbose(" selector table occupancy %u/%u (%u%%)\n",
627 seloptOccupied, seloptCapacity,
628 (unsigned)(seloptOccupied/(double)seloptCapacity*100));
629
630
631 //
632 // Detect classes that have missing weak-import superclasses.
633 //
634 // Production only. Development cache does not do this: a replacement
635 // library could omit a class at runtime that was present during
636 // cache construction.
637 //
638 // This is SAFE: the binaries themselves are unmodified.
639 bool noMissingWeakSuperclasses = false; // dev cache can't promise otherwise
640 if (forProduction) {
641 WeakClassDetector<P> weakopt;
642 noMissingWeakSuperclasses =
643 weakopt.noMissingWeakSuperclasses(&cacheAccessor, sizeSortedDylibs);
644
645 // Shared cache does not currently support unbound weak references.
646 // Here we assert that there are none. If support is added later then
647 // this assertion needs to be removed and this path needs to be tested.
648 if (!noMissingWeakSuperclasses) {
649 diag.error("Some Objective-C class has a superclass that is "
650 "weak-import and missing from the cache.");
651 }
652 }
653
654
655 //
656 // Build class table.
657 //
658 // This is SAFE: the binaries themselves are unmodified.
659 ClassListBuilder<P> classes(hinfoROOptimizer);
660 ClassWalker<P, ClassListBuilder<P>> classWalker(classes);
661 for (const macho_header<P>* mh : sizeSortedDylibs) {
662 classWalker.walk(&cacheAccessor, mh);
663 }
664
665 diag.verbose(" recorded % 6ld classes\n", classes.classNames().size());
666
667 uint64_t clsoptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining;
668 objc_opt::objc_clsopt_t *clsopt = new(optROData) objc_opt::objc_clsopt_t;
669 err = clsopt->write(clsoptVMAddr, optRORemaining,
670 classes.classNames(), classes.classes(), false);
671 if (err) {
672 diag.warning("%s", err);
673 return;
674 }
675 optROData += clsopt->size();
676 optRORemaining -= clsopt->size();
677 size_t duplicateCount = clsopt->duplicateCount();
678 uint32_t clsoptCapacity = clsopt->capacity;
679 uint32_t clsoptOccupied = clsopt->occupied;
680 clsopt->byteswap(E::little_endian);
681 clsopt = nullptr;
682
683 diag.verbose(" found % 6ld duplicate classes\n",
684 duplicateCount);
685 diag.verbose(" class table occupancy %u/%u (%u%%)\n",
686 clsoptOccupied, clsoptCapacity,
687 (unsigned)(clsoptOccupied/(double)clsoptCapacity*100));
688
689
690 //
691 // Sort method lists.
692 //
693 // This is SAFE: modified binaries are still usable as unsorted lists.
694 // This must be done AFTER uniquing selectors.
695 MethodListSorter<P> methodSorter;
696 for (const macho_header<P>* mh : sizeSortedDylibs) {
697 methodSorter.optimize(&cacheAccessor, mh);
698 }
699
700 diag.verbose(" sorted % 6ld method lists\n", methodSorter.optimized());
701
702
703 // Unique protocols and build protocol table.
704
705 // This is SAFE: no protocol references are updated yet
706 // This must be done AFTER updating method lists.
707
708 ProtocolOptimizer<P> protocolOptimizer(diag);
709 for (const macho_header<P>* mh : sizeSortedDylibs) {
710 protocolOptimizer.addProtocols(&cacheAccessor, mh);
711 }
712
713 diag.verbose(" uniqued % 6ld protocols\n",
714 protocolOptimizer.protocolCount());
715
716 pint_t protocolClassVMAddr = (pint_t)P::getP(optPointerList->protocolClass);
717 err = protocolOptimizer.writeProtocols(&cacheAccessor,
718 optRWData, optRWRemaining,
719 optROData, optRORemaining,
720 pointersForASLR, protocolClassVMAddr);
721 if (err) {
722 diag.warning("%s", err);
723 return;
724 }
725
726 uint64_t protocoloptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining;
727 objc_opt::objc_protocolopt_t *protocolopt = new (optROData) objc_opt::objc_protocolopt_t;
728 err = protocolopt->write(protocoloptVMAddr, optRORemaining,
729 protocolOptimizer.protocolNames(),
730 protocolOptimizer.protocols(), true);
731 if (err) {
732 diag.warning("%s", err);
733 return;
734 }
735 optROData += protocolopt->size();
736 optRORemaining -= protocolopt->size();
737 uint32_t protocoloptCapacity = protocolopt->capacity;
738 uint32_t protocoloptOccupied = protocolopt->occupied;
739 protocolopt->byteswap(E::little_endian), protocolopt = NULL;
740
741 diag.verbose(" protocol table occupancy %u/%u (%u%%)\n",
742 protocoloptOccupied, protocoloptCapacity,
743 (unsigned)(protocoloptOccupied/(double)protocoloptCapacity*100));
744
745
746 // Redirect protocol references to the uniqued protocols.
747
748 // This is SAFE: the new protocol objects are still usable as-is.
749 for (const macho_header<P>* mh : sizeSortedDylibs) {
750 protocolOptimizer.updateReferences(&cacheAccessor, mh);
751 }
752
753 diag.verbose(" updated % 6ld protocol references\n", protocolOptimizer.protocolReferenceCount());
754
755
756 //
757 // Repair ivar offsets.
758 //
759 // This is SAFE: the runtime always validates ivar offsets at runtime.
760 IvarOffsetOptimizer<P> ivarOffsetOptimizer;
761 for (const macho_header<P>* mh : sizeSortedDylibs) {
762 ivarOffsetOptimizer.optimize(&cacheAccessor, mh);
763 }
764
765 diag.verbose(" updated % 6ld ivar offsets\n", ivarOffsetOptimizer.optimized());
766
767
768 // Collect flags.
769 uint32_t headerFlags = 0;
770 if (forProduction) {
771 headerFlags |= objc_opt::IsProduction;
772 }
773 if (noMissingWeakSuperclasses) {
774 headerFlags |= objc_opt::NoMissingWeakSuperclasses;
775 }
776
777
778 // Success. Mark dylibs as optimized.
779 for (const macho_header<P>* mh : sizeSortedDylibs) {
780 const macho_section<P>* imageInfoSection = mh->getSection("__DATA", "__objc_imageinfo");
781 if (!imageInfoSection) {
782 imageInfoSection = mh->getSection("__OBJC", "__image_info");
783 }
784 if (imageInfoSection) {
785 objc_image_info<P>* info = (objc_image_info<P>*)cacheAccessor.contentForVMAddr(imageInfoSection->addr());
786 info->setOptimizedByDyld();
787 }
788 }
789
790
791 // Success. Update RO header last.
792 E::set32(optROHeader->flags, headerFlags);
793 E::set32(optROHeader->selopt_offset, (uint32_t)(seloptVMAddr - optROSection->addr()));
794 E::set32(optROHeader->clsopt_offset, (uint32_t)(clsoptVMAddr - optROSection->addr()));
795 E::set32(optROHeader->protocolopt_offset, (uint32_t)(protocoloptVMAddr - optROSection->addr()));
796 E::set32(optROHeader->headeropt_ro_offset, (uint32_t)(hinfoROVMAddr - optROSection->addr()));
797 E::set32(optROHeader->headeropt_rw_offset, (uint32_t)(hinfoRWVMAddr - optROSection->addr()));
798
799 // Log statistics.
800 size_t roSize = optROSection->size() - optRORemaining;
801 size_t rwSize = optRWSection->size() - optRWRemaining;
802 diag.verbose(" %lu/%llu bytes (%d%%) used in libobjc read-only optimization section\n",
803 roSize, optROSection->size(), percent(roSize, optROSection->size()));
804 diag.verbose(" %lu/%llu bytes (%d%%) used in libobjc read/write optimization section\n",
805 rwSize, optRWSection->size(), percent(rwSize, optRWSection->size()));
806 diag.verbose(" wrote objc metadata optimization version %d\n", objc_opt::VERSION);
807 }
808
809
810 } // anon namespace
811
812 void optimizeObjC(DyldSharedCache* cache, bool is64, bool customerCache, std::vector<void*>& pointersForASLR, Diagnostics& diag)
813 {
814 if ( is64 )
815 optimizeObjC<Pointer64<LittleEndian>>(cache, customerCache, pointersForASLR, diag);
816 else
817 optimizeObjC<Pointer32<LittleEndian>>(cache, customerCache, pointersForASLR, diag);
818 }
819
820