dyld-750.5.tar.gz
[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 "SharedCacheBuilder.h"
36 #include "FileAbstraction.hpp"
37 #include "MachOFileAbstraction.hpp"
38 #include "MachOLoaded.h"
39 #include "MachOAnalyzer.h"
40
41 #ifndef MH_HAS_OBJC
42 #define MH_HAS_OBJC 0x40000000
43 #endif
44
45 // Scan a C++ or Swift length-mangled field.
46 static bool scanMangledField(const char *&string, const char *end,
47 const char *&field, int& length)
48 {
49 // Leading zero not allowed.
50 if (*string == '0') return false;
51
52 length = 0;
53 field = string;
54 while (field < end) {
55 char c = *field;
56 if (!isdigit(c)) break;
57 field++;
58 if (__builtin_smul_overflow(length, 10, &length)) return false;
59 if (__builtin_sadd_overflow(length, c - '0', &length)) return false;
60 }
61
62 string = field + length;
63 return length > 0 && string <= end;
64 }
65
66
67 // copySwiftDemangledName
68 // Returns the pretty form of the given Swift-mangled class or protocol name.
69 // Returns nullptr if the string doesn't look like a mangled Swift name.
70 // The result must be freed with free().
71 static char *copySwiftDemangledName(const char *string, bool isProtocol = false)
72 {
73 if (!string) return nullptr;
74
75 // Swift mangling prefix.
76 if (strncmp(string, isProtocol ? "_TtP" : "_TtC", 4) != 0) return nullptr;
77 string += 4;
78
79 const char *end = string + strlen(string);
80
81 // Module name.
82 const char *prefix;
83 int prefixLength;
84 if (string[0] == 's') {
85 // "s" is the Swift module.
86 prefix = "Swift";
87 prefixLength = 5;
88 string += 1;
89 } else {
90 if (! scanMangledField(string, end, prefix, prefixLength)) return nullptr;
91 }
92
93 // Class or protocol name.
94 const char *suffix;
95 int suffixLength;
96 if (! scanMangledField(string, end, suffix, suffixLength)) return nullptr;
97
98 if (isProtocol) {
99 // Remainder must be "_".
100 if (strcmp(string, "_") != 0) return nullptr;
101 } else {
102 // Remainder must be empty.
103 if (string != end) return nullptr;
104 }
105
106 char *result;
107 asprintf(&result, "%.*s.%.*s", prefixLength,prefix, suffixLength,suffix);
108 return result;
109 }
110
111
112 class ContentAccessor {
113 public:
114 ContentAccessor(const DyldSharedCache* cache, Diagnostics& diag)
115 : _diagnostics(diag)
116 {
117 _cacheStart = (uint8_t*)cache;
118 _cacheUnslideAddr = cache->unslidLoadAddress();
119 _slide = (uint64_t)cache - _cacheUnslideAddr;
120 }
121
122 // Converts from an on disk vmAddr to the real vmAddr
123 // That is, for a chained fixup, decodes the chain, for a non-chained fixup, does nothing.
124 uint64_t vmAddrForOnDiskVMAddr(uint64_t vmaddr) {
125 return vmaddr;
126 }
127
128 void* contentForVMAddr(uint64_t vmaddr) {
129 vmaddr = vmAddrForOnDiskVMAddr(vmaddr);
130 if ( vmaddr != 0 ) {
131 uint64_t offset = vmaddr - _cacheUnslideAddr;
132 return _cacheStart + offset;
133 } else
134 return nullptr;
135 }
136
137 uint64_t vmAddrForContent(const void* content) {
138 if ( content != nullptr )
139 return _cacheUnslideAddr + ((uint8_t*)content - _cacheStart);
140 else
141 return 0;
142 }
143
144 Diagnostics& diagnostics() { return _diagnostics; }
145
146 private:
147 Diagnostics& _diagnostics;
148 uint64_t _slide;
149 uint64_t _cacheUnslideAddr;
150 uint8_t* _cacheStart;
151 };
152
153
154 // Access a section containing a list of pointers
155 template <typename P, typename T>
156 class PointerSection
157 {
158 typedef typename P::uint_t pint_t;
159 public:
160 PointerSection(ContentAccessor* cache, const macho_header<P>* mh,
161 const char* segname, const char* sectname)
162 : _cache(cache),
163 _section(mh->getSection(segname, sectname)),
164 _base(_section ? (pint_t*)cache->contentForVMAddr(_section->addr()) : 0),
165 _count(_section ? (pint_t)(_section->size() / sizeof(pint_t)) : 0) {
166 }
167
168 pint_t count() const { return _count; }
169
170 pint_t getVMAddress(pint_t index) const {
171 if ( index >= _count ) {
172 _cache->diagnostics().error("index out of range in section %s", _section->sectname());
173 return 0;
174 }
175 return (pint_t)P::getP(_base[index]);
176 }
177
178 pint_t getSectionVMAddress() const {
179 return (pint_t)_section->addr();
180 }
181
182 T get(pint_t index) const {
183 return (T)_cache->contentForVMAddr(getVMAddress(index));
184 }
185
186 void setVMAddress(pint_t index, pint_t value) {
187 if ( index >= _count ) {
188 _cache->diagnostics().error("index out of range in section %s", _section->sectname());
189 return;
190 }
191 P::setP(_base[index], value);
192 }
193
194 void removeNulls() {
195 pint_t shift = 0;
196 for (pint_t i = 0; i < _count; i++) {
197 pint_t value = _base[i];
198 if (value) {
199 _base[i-shift] = value;
200 } else {
201 shift++;
202 }
203 }
204 _count -= shift;
205 const_cast<macho_section<P>*>(_section)->set_size(_count * sizeof(pint_t));
206 }
207
208 private:
209 ContentAccessor* const _cache;
210 const macho_section<P>* const _section;
211 pint_t* const _base;
212 pint_t const _count;
213 };
214
215
216 // Access a section containing an array of structures
217 template <typename P, typename T>
218 class ArraySection
219 {
220 public:
221 ArraySection(ContentAccessor* cache, const macho_header<P>* mh,
222 const char *segname, const char *sectname)
223 : _cache(cache),
224 _section(mh->getSection(segname, sectname)),
225 _base(_section ? (T *)cache->contentForVMAddr(_section->addr()) : 0),
226 _count(_section ? _section->size() / sizeof(T) : 0) {
227 }
228
229 uint64_t count() const { return _count; }
230
231 T& get(uint64_t index) const {
232 if (index >= _count) {
233 _cache->diagnostics().error("index out of range in section %s", _section->sectname());
234 }
235 return _base[index];
236 }
237
238 private:
239 ContentAccessor* const _cache;
240 const macho_section<P>* const _section;
241 T * const _base;
242 uint64_t const _count;
243 };
244
245
246 #define SELOPT_WRITE
247 #include "objc-shared-cache.h"
248 #include "ObjC1Abstraction.hpp"
249 #include "ObjC2Abstraction.hpp"
250
251
252 namespace {
253
254
255
256 template <typename P>
257 class ObjCSelectorUniquer
258 {
259 public:
260 typedef typename P::uint_t pint_t;
261
262 ObjCSelectorUniquer(ContentAccessor* cache) : _cache(cache) { }
263
264 pint_t visit(pint_t oldValue)
265 {
266 _count++;
267 const char *s = (const char *)_cache->contentForVMAddr(oldValue);
268 oldValue = (pint_t)_cache->vmAddrForOnDiskVMAddr(oldValue);
269 objc_opt::string_map::iterator element =
270 _selectorStrings.insert(objc_opt::string_map::value_type(s, oldValue)).first;
271 return (pint_t)element->second;
272 }
273
274 void visitCoalescedStrings(const CacheBuilder::CacheCoalescedText& coalescedText) {
275 const CacheBuilder::CacheCoalescedText::StringSection& methodNames = coalescedText.getSectionData("__objc_methname");
276 for (const auto& stringAndOffset : methodNames.stringsToOffsets) {
277 uint64_t vmAddr = methodNames.bufferVMAddr + stringAndOffset.second;
278 _selectorStrings[stringAndOffset.first.data()] = vmAddr;
279 }
280 }
281
282 objc_opt::string_map& strings() {
283 return _selectorStrings;
284 }
285
286 size_t count() const { return _count; }
287
288 private:
289 objc_opt::string_map _selectorStrings;
290 ContentAccessor* _cache;
291 size_t _count = 0;
292 };
293
294
295 template <typename P>
296 class ClassListBuilder
297 {
298 private:
299 objc_opt::string_map _classNames;
300 objc_opt::class_map _classes;
301 size_t _count = 0;
302 HeaderInfoOptimizer<P, objc_header_info_ro_t<P>>& _hInfos;
303
304 public:
305
306 ClassListBuilder(HeaderInfoOptimizer<P, objc_header_info_ro_t<P>>& hinfos) : _hInfos(hinfos) { }
307
308 void visitClass(ContentAccessor* cache,
309 const macho_header<P>* header,
310 objc_class_t<P>* cls)
311 {
312 if (cls->isMetaClass(cache)) return;
313
314 const char *name = cls->getName(cache);
315 uint64_t name_vmaddr = cache->vmAddrForContent((void*)name);
316 uint64_t cls_vmaddr = cache->vmAddrForContent(cls);
317 uint64_t hinfo_vmaddr = cache->vmAddrForContent(_hInfos.hinfoForHeader(cache, header));
318 _classNames.insert(objc_opt::string_map::value_type(name, name_vmaddr));
319 _classes.insert(objc_opt::class_map::value_type(name, std::pair<uint64_t, uint64_t>(cls_vmaddr, hinfo_vmaddr)));
320 _count++;
321 }
322
323 objc_opt::string_map& classNames() {
324 return _classNames;
325 }
326
327 objc_opt::class_map& classes() {
328 return _classes;
329 }
330
331 size_t count() const { return _count; }
332 };
333
334 template <typename P>
335 class ProtocolOptimizer
336 {
337 private:
338 typedef typename P::uint_t pint_t;
339
340 objc_opt::string_map _protocolNames;
341 objc_opt::legacy_protocol_map _protocols;
342 objc_opt::protocol_map _protocolsAndHeaders;
343 size_t _protocolCount;
344 size_t _protocolReferenceCount;
345 Diagnostics& _diagnostics;
346 HeaderInfoOptimizer<P, objc_header_info_ro_t<P>>& _hInfos;
347
348 friend class ProtocolReferenceWalker<P, ProtocolOptimizer<P>>;
349
350 pint_t visitProtocolReference(ContentAccessor* cache, pint_t oldValue)
351 {
352 objc_protocol_t<P>* proto = (objc_protocol_t<P>*)
353 cache->contentForVMAddr(oldValue);
354 pint_t newValue = (pint_t)_protocols[proto->getName(cache)];
355 if (oldValue != newValue) _protocolReferenceCount++;
356 return newValue;
357 }
358
359 public:
360
361 ProtocolOptimizer(Diagnostics& diag, HeaderInfoOptimizer<P, objc_header_info_ro_t<P>>& hinfos)
362 : _protocolCount(0), _protocolReferenceCount(0), _diagnostics(diag), _hInfos(hinfos) {
363 }
364
365 void addProtocols(ContentAccessor* cache, const macho_header<P>* header)
366 {
367 PointerSection<P, objc_protocol_t<P> *>
368 protocols(cache, header, "__DATA", "__objc_protolist");
369
370 for (pint_t i = 0; i < protocols.count(); i++) {
371 objc_protocol_t<P> *proto = protocols.get(i);
372
373 const char *name = proto->getName(cache);
374 if (_protocolNames.count(name) == 0) {
375 if (proto->getSize() > sizeof(objc_protocol_t<P>)) {
376 _diagnostics.error("objc protocol is too big");
377 return;
378 }
379 uint64_t name_vmaddr = cache->vmAddrForContent((void*)name);
380 uint64_t proto_vmaddr = cache->vmAddrForContent(proto);
381 _protocolNames.insert(objc_opt::string_map::value_type(name, name_vmaddr));
382 _protocols.insert(objc_opt::legacy_protocol_map::value_type(name, proto_vmaddr));
383 _protocolCount++;
384 }
385
386 // Note down which header this protocol came from. We'll fill in the proto_vmaddr here later
387 // once we've chosen a single definition for the protocol with this name.
388 uint64_t hinfo_vmaddr = cache->vmAddrForContent(_hInfos.hinfoForHeader(cache, header));
389 _protocolsAndHeaders.insert(objc_opt::class_map::value_type(name, std::pair<uint64_t, uint64_t>(0, hinfo_vmaddr)));
390 }
391 }
392
393 const char *writeProtocols(ContentAccessor* cache,
394 uint8_t *& rwdest, size_t& rwremaining,
395 uint8_t *& rodest, size_t& roremaining,
396 CacheBuilder::ASLR_Tracker& aslrTracker,
397 pint_t protocolClassVMAddr)
398 {
399 if (_protocolCount == 0) return NULL;
400
401 if (protocolClassVMAddr == 0) {
402 return "libobjc's Protocol class symbol not found (metadata not optimized)";
403 }
404
405 size_t rwrequired = _protocolCount * sizeof(objc_protocol_t<P>);
406 if (rwremaining < rwrequired) {
407 return "libobjc's read-write section is too small (metadata not optimized)";
408 }
409
410 for (auto iter = _protocols.begin(); iter != _protocols.end(); ++iter)
411 {
412 objc_protocol_t<P>* oldProto = (objc_protocol_t<P>*)
413 cache->contentForVMAddr(iter->second);
414
415 // Create a new protocol object.
416 objc_protocol_t<P>* proto = (objc_protocol_t<P>*)rwdest;
417 rwdest += sizeof(*proto);
418 rwremaining -= sizeof(*proto);
419
420 // Initialize it.
421 uint32_t oldSize = oldProto->getSize();
422 memcpy(proto, oldProto, oldSize);
423 if (!proto->getIsaVMAddr()) {
424 proto->setIsaVMAddr(protocolClassVMAddr);
425 }
426 if (oldSize < sizeof(*proto)) {
427 // Protocol object is old. Populate new fields.
428 proto->setSize(sizeof(objc_protocol_t<P>));
429 // missing extendedMethodTypes is already nil
430 }
431 // Some protocol objects are big enough to have the
432 // demangledName field but don't initialize it.
433 // Initialize it here if it is not already set.
434 if (!proto->getDemangledName(cache)) {
435 const char *roName = proto->getName(cache);
436 char *demangledName = copySwiftDemangledName(roName, true);
437 if (demangledName) {
438 size_t length = 1 + strlen(demangledName);
439 if (roremaining < length) {
440 return "libobjc's read-only section is too small (metadata not optimized)";
441 }
442
443 memmove(rodest, demangledName, length);
444 roName = (const char *)rodest;
445 rodest += length;
446 roremaining -= length;
447
448 free(demangledName);
449 }
450 proto->setDemangledName(cache, roName, _diagnostics);
451 }
452 proto->setFixedUp();
453 proto->setIsCanonical();
454
455 // Redirect the protocol table at our new object.
456 iter->second = cache->vmAddrForContent(proto);
457
458 // Add new rebase entries.
459 proto->addPointers(cache, aslrTracker);
460 }
461
462 // Now that we've chosen the canonical protocols, set the duplicate headers to
463 // point to their protocols.
464 for (auto iter = _protocolsAndHeaders.begin(); iter != _protocolsAndHeaders.end(); ++iter) {
465 iter->second.first = _protocols[iter->first];
466 }
467
468 return NULL;
469 }
470
471 void updateReferences(ContentAccessor* cache, const macho_header<P>* header)
472 {
473 ProtocolReferenceWalker<P, ProtocolOptimizer<P>> refs(*this);
474 refs.walk(cache, header);
475 }
476
477 objc_opt::string_map& protocolNames() {
478 return _protocolNames;
479 }
480
481 objc_opt::legacy_protocol_map& protocols() {
482 return _protocols;
483 }
484
485 objc_opt::protocol_map& protocolsAndHeaders() {
486 return _protocolsAndHeaders;
487 }
488
489 size_t protocolCount() const { return _protocolCount; }
490 size_t protocolReferenceCount() const { return _protocolReferenceCount; }
491 };
492
493
494 static int percent(size_t num, size_t denom) {
495 if (denom)
496 return (int)(num / (double)denom * 100);
497 else
498 return 100;
499 }
500
501 template <typename P>
502 void addObjcSegments(Diagnostics& diag, DyldSharedCache* cache, const mach_header* libobjcMH,
503 uint8_t* objcReadOnlyBuffer, uint64_t objcReadOnlyBufferSizeAllocated,
504 uint8_t* objcReadWriteBuffer, uint64_t objcReadWriteBufferSizeAllocated,
505 uint32_t objcRwFileOffset)
506 {
507 // validate there is enough free space to add the load commands
508 const dyld3::MachOAnalyzer* libobjcMA = ((dyld3::MachOAnalyzer*)libobjcMH);
509 uint32_t freeSpace = libobjcMA->loadCommandsFreeSpace();
510 const uint32_t segSize = sizeof(macho_segment_command<P>);
511 if ( freeSpace < 2*segSize ) {
512 diag.warning("not enough space in libojbc.dylib to add load commands for objc optimization regions");
513 return;
514 }
515
516 // find location of LINKEDIT LC_SEGMENT load command, we need to insert new segments before it
517 __block uint8_t* linkeditSeg = nullptr;
518 libobjcMA->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
519 if ( strcmp(info.segName, "__LINKEDIT") == 0 )
520 linkeditSeg = (uint8_t*)libobjcMH + info.loadCommandOffset;
521 });
522 if ( linkeditSeg == nullptr ) {
523 diag.warning("__LINKEDIT not found in libojbc.dylib");
524 return;
525 }
526
527 // move load commands to make room to insert two new ones before LINKEDIT segment load command
528 uint8_t* endOfLoadCommands = (uint8_t*)libobjcMH + sizeof(macho_header<P>) + libobjcMH->sizeofcmds;
529 uint32_t remainingSize = (uint32_t)(endOfLoadCommands - linkeditSeg);
530 memmove(linkeditSeg+2*segSize, linkeditSeg, remainingSize);
531
532 // insert new segments
533 macho_segment_command<P>* roSeg = (macho_segment_command<P>*)(linkeditSeg);
534 macho_segment_command<P>* rwSeg = (macho_segment_command<P>*)(linkeditSeg+sizeof(macho_segment_command<P>));
535 roSeg->set_cmd(macho_segment_command<P>::CMD);
536 roSeg->set_cmdsize(segSize);
537 roSeg->set_segname("__OBJC_RO");
538 roSeg->set_vmaddr(cache->unslidLoadAddress() + objcReadOnlyBuffer - (uint8_t*)cache);
539 roSeg->set_vmsize(objcReadOnlyBufferSizeAllocated);
540 roSeg->set_fileoff(objcReadOnlyBuffer - (uint8_t*)cache);
541 roSeg->set_filesize(objcReadOnlyBufferSizeAllocated);
542 roSeg->set_maxprot(VM_PROT_READ);
543 roSeg->set_initprot(VM_PROT_READ);
544 roSeg->set_nsects(0);
545 roSeg->set_flags(0);
546 rwSeg->set_cmd(macho_segment_command<P>::CMD);
547 rwSeg->set_cmdsize(segSize);
548 rwSeg->set_segname("__OBJC_RW");
549 rwSeg->set_vmaddr(cache->unslidLoadAddress() + objcReadWriteBuffer - (uint8_t*)cache);
550 rwSeg->set_vmsize(objcReadWriteBufferSizeAllocated);
551 rwSeg->set_fileoff(objcRwFileOffset);
552 rwSeg->set_filesize(objcReadWriteBufferSizeAllocated);
553 rwSeg->set_maxprot(VM_PROT_WRITE|VM_PROT_READ);
554 rwSeg->set_initprot(VM_PROT_WRITE|VM_PROT_READ);
555 rwSeg->set_nsects(0);
556 rwSeg->set_flags(0);
557
558 // update mach_header to account for new load commands
559 macho_header<P>* mh = (macho_header<P>*)libobjcMH;
560 mh->set_sizeofcmds(mh->sizeofcmds() + 2*segSize);
561 mh->set_ncmds(mh->ncmds()+2);
562
563 // fix up table at start of dyld cache that has pointer into install name for libobjc
564 dyld_cache_image_info* images = (dyld_cache_image_info*)((uint8_t*)cache + cache->header.imagesOffset);
565 uint64_t libobjcUnslidAddress = cache->unslidLoadAddress() + ((uint8_t*)libobjcMH - (uint8_t*)cache);
566 for (uint32_t i=0; i < cache->header.imagesCount; ++i) {
567 if ( images[i].address == libobjcUnslidAddress ) {
568 images[i].pathFileOffset += (2*segSize);
569 break;
570 }
571 }
572 }
573
574
575 template <typename P>
576 void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::ASLR_Tracker& aslrTracker,
577 CacheBuilder::LOH_Tracker& lohTracker, const CacheBuilder::CacheCoalescedText& coalescedText,
578 const std::map<void*, std::string>& missingWeakImports, Diagnostics& diag,
579 uint8_t* objcReadOnlyBuffer, uint64_t objcReadOnlyBufferSizeUsed, uint64_t objcReadOnlyBufferSizeAllocated,
580 uint8_t* objcReadWriteBuffer, uint64_t objcReadWriteBufferSizeAllocated,
581 uint32_t objcRwFileOffset)
582 {
583 typedef typename P::E E;
584 typedef typename P::uint_t pint_t;
585
586 diag.verbose("Optimizing objc metadata:\n");
587 diag.verbose(" cache type is %s\n", forProduction ? "production" : "development");
588
589 ContentAccessor cacheAccessor(cache, diag);
590
591 size_t headerSize = P::round_up(sizeof(objc_opt::objc_opt_t));
592 if (headerSize != sizeof(objc_opt::objc_opt_t)) {
593 diag.warning("libobjc's optimization structure size is wrong (metadata not optimized)");
594 }
595
596 //
597 // Find libobjc's empty sections and build list of images with objc metadata
598 //
599 __block const mach_header* libobjcMH = nullptr;
600 __block const macho_section<P> *optROSection = nullptr;
601 __block const macho_section<P> *optPointerListSection = nullptr;
602 __block std::vector<const macho_header<P>*> objcDylibs;
603 cache->forEachImage(^(const mach_header* machHeader, const char* installName) {
604 const macho_header<P>* mh = (const macho_header<P>*)machHeader;
605 if ( strstr(installName, "/libobjc.") != nullptr ) {
606 libobjcMH = (mach_header*)mh;
607 optROSection = mh->getSection("__TEXT", "__objc_opt_ro");
608 optPointerListSection = mh->getSection("__DATA", "__objc_opt_ptrs");
609 }
610 if ( mh->getSection("__DATA", "__objc_imageinfo") || mh->getSection("__OBJC", "__image_info") ) {
611 objcDylibs.push_back(mh);
612 }
613 // log("installName %s at mhdr 0x%016lx", installName, (uintptr_t)cacheAccessor.vmAddrForContent((void*)mh));
614 });
615 if ( optROSection == nullptr ) {
616 diag.warning("libobjc's read-only section missing (metadata not optimized)");
617 return;
618 }
619 if ( optPointerListSection == nullptr ) {
620 diag.warning("libobjc's pointer list section missing (metadata not optimized)");
621 return;
622 }
623 // point optROData into space allocated in dyld cache
624 uint8_t* optROData = objcReadOnlyBuffer + objcReadOnlyBufferSizeUsed;
625 size_t optRORemaining = objcReadOnlyBufferSizeAllocated - objcReadOnlyBufferSizeUsed;
626 *((uint32_t*)optROData) = objc_opt::VERSION;
627 if ( optROData == nullptr ) {
628 diag.warning("libobjc's read-only section has bad content");
629 return;
630 }
631
632 uint8_t* optRWData = objcReadWriteBuffer;
633 size_t optRWRemaining = objcReadWriteBufferSizeAllocated;
634 if (optRORemaining < headerSize) {
635 diag.warning("libobjc's read-only section is too small (metadata not optimized)");
636 return;
637 }
638 objc_opt::objc_opt_t* optROHeader = (objc_opt::objc_opt_t *)optROData;
639 optROData += headerSize;
640 optRORemaining -= headerSize;
641 if (E::get32(optROHeader->version) != objc_opt::VERSION) {
642 diag.warning("libobjc's read-only section version is unrecognized (metadata not optimized)");
643 return;
644 }
645
646 if (optPointerListSection->size() < sizeof(objc_opt::objc_opt_pointerlist_tt<pint_t>)) {
647 diag.warning("libobjc's pointer list section is too small (metadata not optimized)");
648 return;
649 }
650 const objc_opt::objc_opt_pointerlist_tt<pint_t> *optPointerList = (const objc_opt::objc_opt_pointerlist_tt<pint_t> *)cacheAccessor.contentForVMAddr(optPointerListSection->addr());
651
652 // Write nothing to optROHeader until everything else is written.
653 // If something fails below, libobjc will not use the section.
654
655
656 //
657 // Make copy of objcList and sort that list.
658 //
659 std::vector<const macho_header<P>*> addressSortedDylibs = objcDylibs;
660 std::sort(addressSortedDylibs.begin(), addressSortedDylibs.end(), [](const macho_header<P>* lmh, const macho_header<P>* rmh) -> bool {
661 return lmh < rmh;
662 });
663
664 //
665 // Build HeaderInfo list in cache
666 //
667 // First the RO header info
668 // log("writing out %d RO dylibs at offset %d", (uint32_t)objcDylibs.size(), (uint32_t)(optROSection->size() - optRORemaining));
669 uint64_t hinfoROVMAddr = cacheAccessor.vmAddrForContent(optROData);
670 HeaderInfoOptimizer<P, objc_header_info_ro_t<P>> hinfoROOptimizer;
671 const char* err = hinfoROOptimizer.init((uint32_t)objcDylibs.size(), optROData, optRORemaining);
672 if (err) {
673 diag.warning("%s", err);
674 return;
675 }
676 else {
677 for (const macho_header<P>* mh : addressSortedDylibs) {
678 hinfoROOptimizer.update(&cacheAccessor, mh, aslrTracker);
679 }
680 }
681
682 // Then the RW header info
683 // log("writing out %d RW dylibs at offset %d", (uint32_t)objcDylibs.size(), (uint32_t)(optRWSection->size() - optRWRemaining));
684 uint64_t hinfoRWVMAddr = cacheAccessor.vmAddrForContent(optRWData);
685 HeaderInfoOptimizer<P, objc_header_info_rw_t<P>> hinfoRWOptimizer;
686 err = hinfoRWOptimizer.init((uint32_t)objcDylibs.size(), optRWData, optRWRemaining);
687 if (err) {
688 diag.warning("%s", err);
689 return;
690 }
691 else {
692 for (const macho_header<P>* mh : addressSortedDylibs) {
693 hinfoRWOptimizer.update(&cacheAccessor, mh, aslrTracker);
694 }
695 }
696
697 //
698 // Update selector references and build selector list
699 //
700 // This is SAFE: if we run out of room for the selector table,
701 // the modified binaries are still usable.
702 //
703 // Heuristic: choose selectors from libraries with more selector cstring data first.
704 // This tries to localize selector cstring memory.
705 //
706 ObjCSelectorUniquer<P> uniq(&cacheAccessor);
707 std::vector<const macho_header<P>*> sizeSortedDylibs = objcDylibs;
708 std::sort(sizeSortedDylibs.begin(), sizeSortedDylibs.end(), [](const macho_header<P>* lmh, const macho_header<P>* rmh) -> bool {
709 // Sort a select few heavy hitters first.
710 auto getPriority = [](const char* installName) -> int {
711 if (!strcmp(installName, "/usr/lib/libobjc.A.dylib"))
712 return 0;
713 if (!strcmp(installName, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation") ||
714 !strcmp(installName, "/System/Library/Frameworks/Foundation.framework/Foundation"))
715 return 1;
716 if (!strcmp(installName, "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation") ||
717 !strcmp(installName, "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"))
718 return 2;
719 // Note we don't sort iOSMac UIKitCore early as we want iOSMac after macOS.
720 if (!strcmp(installName, "/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore"))
721 return 3;
722 if (!strcmp(installName, "/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit"))
723 return 4;
724 if (!strcmp(installName, "/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork") ||
725 !strcmp(installName, "/System/Library/Frameworks/CFNetwork.framework/CFNetwork"))
726 return 5;
727 return INT_MAX;
728 };
729
730 // Sort by priority first
731 int priorityA = getPriority(((const dyld3::MachOFile*)lmh)->installName());
732 int priorityB = getPriority(((const dyld3::MachOFile*)rmh)->installName());
733 if (priorityA != priorityB)
734 return priorityA < priorityB;
735
736 // Sort mac before iOSMac
737 bool isIOSMacA = strncmp(((const dyld3::MachOFile*)lmh)->installName(), "/System/iOSSupport/", 19) == 0;
738 bool isIOSMacB = strncmp(((const dyld3::MachOFile*)rmh)->installName(), "/System/iOSSupport/", 19) == 0;
739 if (isIOSMacA != isIOSMacB)
740 return !isIOSMacA;
741
742 const macho_section<P>* lSection = lmh->getSection("__TEXT", "__objc_methname");
743 const macho_section<P>* rSection = rmh->getSection("__TEXT", "__objc_methname");
744 uint64_t lSelectorSize = (lSection ? lSection->size() : 0);
745 uint64_t rSelectorSize = (rSection ? rSection->size() : 0);
746 return lSelectorSize > rSelectorSize;
747 });
748
749 auto alignPointer = [](uint8_t* ptr) -> uint8_t* {
750 return (uint8_t*)(((uintptr_t)ptr + 0x7) & ~0x7);
751 };
752
753 SelectorOptimizer<P, ObjCSelectorUniquer<P> > selOptimizer(uniq);
754 selOptimizer.visitCoalescedStrings(coalescedText);
755 for (const macho_header<P>* mh : sizeSortedDylibs) {
756 LegacySelectorUpdater<P, ObjCSelectorUniquer<P>>::update(&cacheAccessor, mh, uniq);
757 selOptimizer.optimize(&cacheAccessor, mh);
758 }
759
760 diag.verbose(" uniqued %6lu selectors\n", uniq.strings().size());
761 diag.verbose(" updated %6lu selector references\n", uniq.count());
762
763 uint64_t seloptVMAddr = cacheAccessor.vmAddrForContent(optROData);
764 objc_opt::objc_selopt_t *selopt = new(optROData) objc_opt::objc_selopt_t;
765 err = selopt->write(seloptVMAddr, optRORemaining, uniq.strings());
766 if (err) {
767 diag.warning("%s", err);
768 return;
769 }
770 optROData += selopt->size();
771 optROData = alignPointer(optROData);
772 optRORemaining -= selopt->size();
773 uint32_t seloptCapacity = selopt->capacity;
774 uint32_t seloptOccupied = selopt->occupied;
775 selopt->byteswap(E::little_endian), selopt = nullptr;
776
777 diag.verbose(" selector table occupancy %u/%u (%u%%)\n",
778 seloptOccupied, seloptCapacity,
779 (unsigned)(seloptOccupied/(double)seloptCapacity*100));
780
781
782 //
783 // Detect classes that have missing weak-import superclasses.
784 //
785 // Production shared caches don't support roots so we can set this and know
786 // there will definitely not be missing weak superclasses at runtime.
787 // Development shared caches can set this bit as the objc runtime only trusts
788 // this bit if there are no roots at runtime.
789 //
790 // This is SAFE: the binaries themselves are unmodified.
791 WeakClassDetector<P> weakopt;
792 bool noMissingWeakSuperclasses = weakopt.noMissingWeakSuperclasses(&cacheAccessor,
793 missingWeakImports,
794 sizeSortedDylibs);
795
796 if (forProduction) {
797 // Shared cache does not currently support unbound weak references.
798 // Here we assert that there are none. If support is added later then
799 // this assertion needs to be removed and this path needs to be tested.
800 // FIXME: The internal cache also isn't going to notice that an on-disk
801 // dylib could resolve a weak bind from the shared cache. Should we just
802 // error on all caches, regardless of dev/customer?
803 if (!noMissingWeakSuperclasses) {
804 diag.error("Some Objective-C class has a superclass that is "
805 "weak-import and missing from the cache.");
806 }
807 }
808
809
810 //
811 // Build class table.
812 //
813 // This is SAFE: the binaries themselves are unmodified.
814 ClassListBuilder<P> classes(hinfoROOptimizer);
815 ClassWalker<P, ClassListBuilder<P>> classWalker(classes);
816 for (const macho_header<P>* mh : sizeSortedDylibs) {
817 classWalker.walk(&cacheAccessor, mh);
818 }
819
820 diag.verbose(" recorded % 6ld classes\n", classes.classNames().size());
821
822 uint64_t clsoptVMAddr = cacheAccessor.vmAddrForContent(optROData);
823 objc_opt::objc_clsopt_t *clsopt = new(optROData) objc_opt::objc_clsopt_t;
824 err = clsopt->write(clsoptVMAddr, optRORemaining,
825 classes.classNames(), classes.classes(), false);
826 if (err) {
827 diag.warning("%s", err);
828 return;
829 }
830 optROData += clsopt->size();
831 optROData = alignPointer(optROData);
832 optRORemaining -= clsopt->size();
833 size_t duplicateCount = clsopt->duplicateCount();
834 uint32_t clsoptCapacity = clsopt->capacity;
835 uint32_t clsoptOccupied = clsopt->occupied;
836 clsopt->byteswap(E::little_endian);
837 clsopt = nullptr;
838
839 diag.verbose(" found % 6ld duplicate classes\n",
840 duplicateCount);
841 diag.verbose(" class table occupancy %u/%u (%u%%)\n",
842 clsoptOccupied, clsoptCapacity,
843 (unsigned)(clsoptOccupied/(double)clsoptCapacity*100));
844
845
846 //
847 // Sort method lists.
848 //
849 // This is SAFE: modified binaries are still usable as unsorted lists.
850 // This must be done AFTER uniquing selectors.
851 MethodListSorter<P> methodSorter;
852 for (const macho_header<P>* mh : sizeSortedDylibs) {
853 methodSorter.optimize(&cacheAccessor, mh);
854 }
855
856 diag.verbose(" sorted % 6ld method lists\n", methodSorter.optimized());
857
858
859 // Unique protocols and build protocol table.
860
861 // This is SAFE: no protocol references are updated yet
862 // This must be done AFTER updating method lists.
863
864 ProtocolOptimizer<P> protocolOptimizer(diag, hinfoROOptimizer);
865 for (const macho_header<P>* mh : sizeSortedDylibs) {
866 protocolOptimizer.addProtocols(&cacheAccessor, mh);
867 }
868
869 diag.verbose(" uniqued % 6ld protocols\n",
870 protocolOptimizer.protocolCount());
871
872 pint_t protocolClassVMAddr = (pint_t)P::getP(optPointerList->protocolClass);
873 err = protocolOptimizer.writeProtocols(&cacheAccessor,
874 optRWData, optRWRemaining,
875 optROData, optRORemaining,
876 aslrTracker, protocolClassVMAddr);
877 if (err) {
878 diag.warning("%s", err);
879 return;
880 }
881
882 // Align the buffer again. The new protocols may have added an odd number of name characters
883 optROData = alignPointer(optROData);
884
885 // New protocol table which tracks loaded images.
886 uint64_t protocoloptVMAddr = cacheAccessor.vmAddrForContent(optROData);
887 objc_opt::objc_protocolopt2_t *protocolopt = new (optROData) objc_opt::objc_protocolopt2_t;
888 err = protocolopt->write(protocoloptVMAddr, optRORemaining,
889 protocolOptimizer.protocolNames(),
890 protocolOptimizer.protocolsAndHeaders(), false);
891 if (err) {
892 diag.warning("%s", err);
893 return;
894 }
895 optROData += protocolopt->size();
896 optROData = alignPointer(optROData);
897 optRORemaining -= protocolopt->size();
898 uint32_t protocoloptCapacity = protocolopt->capacity;
899 uint32_t protocoloptOccupied = protocolopt->occupied;
900 protocolopt->byteswap(E::little_endian), protocolopt = NULL;
901
902 diag.verbose(" protocol table occupancy %u/%u (%u%%)\n",
903 protocoloptOccupied, protocoloptCapacity,
904 (unsigned)(protocoloptOccupied/(double)protocoloptCapacity*100));
905
906
907 // Redirect protocol references to the uniqued protocols.
908
909 // This is SAFE: the new protocol objects are still usable as-is.
910 for (const macho_header<P>* mh : sizeSortedDylibs) {
911 protocolOptimizer.updateReferences(&cacheAccessor, mh);
912 }
913
914 diag.verbose(" updated % 6ld protocol references\n", protocolOptimizer.protocolReferenceCount());
915
916
917 //
918 // Repair ivar offsets.
919 //
920 // This is SAFE: the runtime always validates ivar offsets at runtime.
921 IvarOffsetOptimizer<P> ivarOffsetOptimizer;
922 for (const macho_header<P>* mh : sizeSortedDylibs) {
923 ivarOffsetOptimizer.optimize(&cacheAccessor, mh);
924 }
925
926 diag.verbose(" updated % 6ld ivar offsets\n", ivarOffsetOptimizer.optimized());
927
928
929 // Collect flags.
930 uint32_t headerFlags = 0;
931 if (forProduction) {
932 headerFlags |= objc_opt::IsProduction;
933 }
934 if (noMissingWeakSuperclasses) {
935 headerFlags |= objc_opt::NoMissingWeakSuperclasses;
936 }
937
938
939 // Success. Mark dylibs as optimized.
940 for (const macho_header<P>* mh : sizeSortedDylibs) {
941 const macho_section<P>* imageInfoSection = mh->getSection("__DATA", "__objc_imageinfo");
942 if (!imageInfoSection) {
943 imageInfoSection = mh->getSection("__OBJC", "__image_info");
944 }
945 if (imageInfoSection) {
946 objc_image_info<P>* info = (objc_image_info<P>*)cacheAccessor.contentForVMAddr(imageInfoSection->addr());
947 info->setOptimizedByDyld();
948 }
949 }
950
951
952 // Success. Update __objc_opt_ro section in libobjc.dylib to contain offsets to generated optimization structures
953 objc_opt::objc_opt_t* libROHeader = (objc_opt::objc_opt_t *)cacheAccessor.contentForVMAddr(optROSection->addr());
954 E::set32(libROHeader->flags, headerFlags);
955 E::set32(libROHeader->selopt_offset, (uint32_t)(seloptVMAddr - optROSection->addr()));
956 E::set32(libROHeader->clsopt_offset, (uint32_t)(clsoptVMAddr - optROSection->addr()));
957 E::set32(libROHeader->unused_protocolopt_offset, 0);
958 E::set32(libROHeader->headeropt_ro_offset, (uint32_t)(hinfoROVMAddr - optROSection->addr()));
959 E::set32(libROHeader->headeropt_rw_offset, (uint32_t)(hinfoRWVMAddr - optROSection->addr()));
960 E::set32(libROHeader->protocolopt_offset, (uint32_t)(protocoloptVMAddr - optROSection->addr()));
961
962 // Log statistics.
963 size_t roSize = objcReadOnlyBufferSizeAllocated - optRORemaining;
964 size_t rwSize = objcReadWriteBufferSizeAllocated - optRWRemaining;
965 diag.verbose(" %lu/%llu bytes (%d%%) used in shared cache read-only optimization region\n",
966 roSize, objcReadOnlyBufferSizeAllocated, percent(roSize, objcReadOnlyBufferSizeAllocated));
967 diag.verbose(" %lu/%llu bytes (%d%%) used in shared cache read/write optimization region\n",
968 rwSize, objcReadWriteBufferSizeAllocated, percent(rwSize, objcReadWriteBufferSizeAllocated));
969 diag.verbose(" wrote objc metadata optimization version %d\n", objc_opt::VERSION);
970
971 // Add segments to libobjc.dylib that cover cache builder allocated r/o and r/w regions
972 addObjcSegments<P>(diag, cache, libobjcMH, objcReadOnlyBuffer, objcReadOnlyBufferSizeAllocated, objcReadWriteBuffer, objcReadWriteBufferSizeAllocated, objcRwFileOffset);
973
974
975 // Now that objc has uniqued the selector references, we can apply the LOHs so that ADRP/LDR -> ADRP/ADD
976 if (forProduction) {
977 const bool logSelectors = false;
978 uint64_t lohADRPCount = 0;
979 uint64_t lohLDRCount = 0;
980
981 for (auto& targetAndInstructions : lohTracker) {
982 uint64_t targetVMAddr = targetAndInstructions.first;
983 if (!selOptimizer.isSelectorRefAddress((pint_t)targetVMAddr))
984 continue;
985
986 std::set<void*>& instructions = targetAndInstructions.second;
987 // We do 2 passes over the instructions. The first to validate them and the second
988 // to actually update them.
989 for (unsigned pass = 0; pass != 2; ++pass) {
990 uint32_t adrpCount = 0;
991 uint32_t ldrCount = 0;
992 for (void* instructionAddress : instructions) {
993 uint32_t& instruction = *(uint32_t*)instructionAddress;
994 uint64_t instructionVMAddr = cacheAccessor.vmAddrForContent(&instruction);
995 uint64_t selRefContent = *(uint64_t*)cacheAccessor.contentForVMAddr(targetVMAddr);
996 const char* selectorString = (const char*)cacheAccessor.contentForVMAddr(selRefContent);
997 uint64_t selectorStringVMAddr = cacheAccessor.vmAddrForContent(selectorString);
998
999 if ( (instruction & 0x9F000000) == 0x90000000 ) {
1000 // ADRP
1001 int64_t pageDistance = ((selectorStringVMAddr & ~0xFFF) - (instructionVMAddr & ~0xFFF));
1002 int64_t newPage21 = pageDistance >> 12;
1003
1004 if (pass == 0) {
1005 if ( (newPage21 > 2097151) || (newPage21 < -2097151) ) {
1006 if (logSelectors)
1007 fprintf(stderr, "Out of bounds ADRP selector reference target\n");
1008 instructions.clear();
1009 break;
1010 }
1011 ++adrpCount;
1012 }
1013
1014 if (pass == 1) {
1015 instruction = (instruction & 0x9F00001F) | ((newPage21 << 29) & 0x60000000) | ((newPage21 << 3) & 0x00FFFFE0);
1016 ++lohADRPCount;
1017 }
1018 continue;
1019 }
1020
1021 if ( (instruction & 0x3B000000) == 0x39000000 ) {
1022 // LDR/STR. STR shouldn't be possible as this is a selref!
1023 if (pass == 0) {
1024 if ( (instruction & 0xC0C00000) != 0xC0400000 ) {
1025 // Not a load, or dest reg isn't xN, or uses sign extension
1026 if (logSelectors)
1027 fprintf(stderr, "Bad LDR for selector reference optimisation\n");
1028 instructions.clear();
1029 break;
1030 }
1031 if ( (instruction & 0x04000000) != 0 ) {
1032 // Loading a float
1033 if (logSelectors)
1034 fprintf(stderr, "Bad LDR for selector reference optimisation\n");
1035 instructions.clear();
1036 break;
1037 }
1038 ++ldrCount;
1039 }
1040
1041 if (pass == 1) {
1042 uint32_t ldrDestReg = (instruction & 0x1F);
1043 uint32_t ldrBaseReg = ((instruction >> 5) & 0x1F);
1044
1045 // Convert the LDR to an ADD
1046 instruction = 0x91000000;
1047 instruction |= ldrDestReg;
1048 instruction |= ldrBaseReg << 5;
1049 instruction |= (selectorStringVMAddr & 0xFFF) << 10;
1050
1051 ++lohLDRCount;
1052 }
1053 continue;
1054 }
1055
1056 if ( (instruction & 0xFFC00000) == 0x91000000 ) {
1057 // ADD imm12
1058 // We don't support ADDs.
1059 if (logSelectors)
1060 fprintf(stderr, "Bad ADD for selector reference optimisation\n");
1061 instructions.clear();
1062 break;
1063 }
1064
1065 if (logSelectors)
1066 fprintf(stderr, "Unknown instruction for selref optimisation\n");
1067 instructions.clear();
1068 break;
1069 }
1070 if (pass == 0) {
1071 // If we didn't see at least one ADRP/LDR in pass one then don't optimize this location
1072 if ((adrpCount == 0) || (ldrCount == 0)) {
1073 instructions.clear();
1074 break;
1075 }
1076 }
1077 }
1078 }
1079
1080 diag.verbose(" Optimized %lld ADRP LOHs\n", lohADRPCount);
1081 diag.verbose(" Optimized %lld LDR LOHs\n", lohLDRCount);
1082 }
1083 }
1084
1085
1086 } // anon namespace
1087
1088 void SharedCacheBuilder::optimizeObjC()
1089 {
1090 uint32_t objcRwFileOffset = (uint32_t)((_objcReadWriteBuffer - _readWriteRegion.buffer) + _readWriteRegion.cacheFileOffset);
1091 if ( _archLayout->is64 )
1092 doOptimizeObjC<Pointer64<LittleEndian>>((DyldSharedCache*)_readExecuteRegion.buffer, _options.optimizeStubs, _aslrTracker, _lohTracker,
1093 _coalescedText, _missingWeakImports,
1094 _diagnostics, _objcReadOnlyBuffer, _objcReadOnlyBufferSizeUsed, _objcReadOnlyBufferSizeAllocated,
1095 _objcReadWriteBuffer, _objcReadWriteBufferSizeAllocated, objcRwFileOffset);
1096 else
1097 doOptimizeObjC<Pointer32<LittleEndian>>((DyldSharedCache*)_readExecuteRegion.buffer, _options.optimizeStubs, _aslrTracker, _lohTracker,
1098 _coalescedText, _missingWeakImports,
1099 _diagnostics, _objcReadOnlyBuffer, _objcReadOnlyBufferSizeUsed, _objcReadOnlyBufferSizeAllocated,
1100 _objcReadWriteBuffer, _objcReadWriteBufferSizeAllocated, objcRwFileOffset);
1101 }
1102
1103 static uint32_t hashTableSize(uint32_t maxElements, uint32_t perElementData)
1104 {
1105 uint32_t elementsWithPadding = maxElements*11/10; // if close to power of 2, perfect hash may fail, so don't get within 10% of that
1106 uint32_t powTwoCapacity = 1 << (32 - __builtin_clz(elementsWithPadding - 1));
1107 uint32_t headerSize = 4*(8+256);
1108 return headerSize + powTwoCapacity/2 + powTwoCapacity + powTwoCapacity*perElementData;
1109 }
1110
1111 // The goal here is to allocate space in the dyld shared cache (while it is being laid out) that will contain
1112 // the objc structures that previously were in the __objc_opt_ro section.
1113 uint32_t SharedCacheBuilder::computeReadOnlyObjC(uint32_t selRefCount, uint32_t classDefCount, uint32_t protocolDefCount)
1114 {
1115 return 0xA000 + hashTableSize(selRefCount, 5) + hashTableSize(classDefCount, 12) + hashTableSize(protocolDefCount, 8);
1116 }
1117
1118 // Space to replace the __objc_opt_rw section.
1119 uint32_t SharedCacheBuilder::computeReadWriteObjC(uint32_t imageCount, uint32_t protocolDefCount)
1120 {
1121 return 8*imageCount + protocolDefCount*12*(_archLayout->is64 ? 8 : 4);
1122 }