1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
3 * Copyright (c) 2014 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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.
22 * @APPLE_LICENSE_HEADER_END@
27 #include <sys/errno.h>
28 #include <sys/fcntl.h>
29 #include <mach-o/loader.h>
30 #include <mach-o/fat.h>
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"
42 #define MH_HAS_OBJC 0x40000000
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
)
49 // Leading zero not allowed.
50 if (*string
== '0') return false;
56 if (!isdigit(c
)) break;
58 if (__builtin_smul_overflow(length
, 10, &length
)) return false;
59 if (__builtin_sadd_overflow(length
, c
- '0', &length
)) return false;
62 string
= field
+ length
;
63 return length
> 0 && string
<= end
;
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)
73 if (!string
) return nullptr;
75 // Swift mangling prefix.
76 if (strncmp(string
, isProtocol ?
"_TtP" : "_TtC", 4) != 0) return nullptr;
79 const char *end
= string
+ strlen(string
);
84 if (string
[0] == 's') {
85 // "s" is the Swift module.
90 if (! scanMangledField(string
, end
, prefix
, prefixLength
)) return nullptr;
93 // Class or protocol name.
96 if (! scanMangledField(string
, end
, suffix
, suffixLength
)) return nullptr;
99 // Remainder must be "_".
100 if (strcmp(string
, "_") != 0) return nullptr;
102 // Remainder must be empty.
103 if (string
!= end
) return nullptr;
107 asprintf(&result
, "%.*s.%.*s", prefixLength
,prefix
, suffixLength
,suffix
);
112 class ContentAccessor
{
114 ContentAccessor(const DyldSharedCache
* cache
, Diagnostics
& diag
)
117 _cacheStart
= (uint8_t*)cache
;
118 _cacheUnslideAddr
= cache
->unslidLoadAddress();
119 _slide
= (uint64_t)cache
- _cacheUnslideAddr
;
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
) {
128 void* contentForVMAddr(uint64_t vmaddr
) {
129 vmaddr
= vmAddrForOnDiskVMAddr(vmaddr
);
131 uint64_t offset
= vmaddr
- _cacheUnslideAddr
;
132 return _cacheStart
+ offset
;
137 uint64_t vmAddrForContent(const void* content
) {
138 if ( content
!= nullptr )
139 return _cacheUnslideAddr
+ ((uint8_t*)content
- _cacheStart
);
144 Diagnostics
& diagnostics() { return _diagnostics
; }
147 Diagnostics
& _diagnostics
;
149 uint64_t _cacheUnslideAddr
;
150 uint8_t* _cacheStart
;
154 // Access a section containing a list of pointers
155 template <typename P
, typename T
>
158 typedef typename P
::uint_t pint_t
;
160 PointerSection(ContentAccessor
* cache
, const macho_header
<P
>* mh
,
161 const char* segname
, const char* sectname
)
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) {
168 pint_t
count() const { return _count
; }
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());
175 return (pint_t
)P
::getP(_base
[index
]);
178 pint_t
getSectionVMAddress() const {
179 return (pint_t
)_section
->addr();
182 T
get(pint_t index
) const {
183 return (T
)_cache
->contentForVMAddr(getVMAddress(index
));
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());
191 P
::setP(_base
[index
], value
);
196 for (pint_t i
= 0; i
< _count
; i
++) {
197 pint_t value
= _base
[i
];
199 _base
[i
-shift
] = value
;
205 const_cast<macho_section
<P
>*>(_section
)->set_size(_count
* sizeof(pint_t
));
209 ContentAccessor
* const _cache
;
210 const macho_section
<P
>* const _section
;
216 // Access a section containing an array of structures
217 template <typename P
, typename T
>
221 ArraySection(ContentAccessor
* cache
, const macho_header
<P
>* mh
,
222 const char *segname
, const char *sectname
)
224 _section(mh
->getSection(segname
, sectname
)),
225 _base(_section ?
(T
*)cache
->contentForVMAddr(_section
->addr()) : 0),
226 _count(_section ? _section
->size() / sizeof(T
) : 0) {
229 uint64_t count() const { return _count
; }
231 T
& get(uint64_t index
) const {
232 if (index
>= _count
) {
233 _cache
->diagnostics().error("index out of range in section %s", _section
->sectname());
239 ContentAccessor
* const _cache
;
240 const macho_section
<P
>* const _section
;
242 uint64_t const _count
;
247 #include "objc-shared-cache.h"
248 #include "ObjC1Abstraction.hpp"
249 #include "ObjC2Abstraction.hpp"
256 template <typename P
>
257 class ObjCSelectorUniquer
260 typedef typename P
::uint_t pint_t
;
262 ObjCSelectorUniquer(ContentAccessor
* cache
) : _cache(cache
) { }
264 pint_t
visit(pint_t oldValue
)
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
;
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
;
282 objc_opt
::string_map
& strings() {
283 return _selectorStrings
;
286 size_t count() const { return _count
; }
289 objc_opt
::string_map _selectorStrings
;
290 ContentAccessor
* _cache
;
295 template <typename P
>
296 class ClassListBuilder
299 objc_opt
::string_map _classNames
;
300 objc_opt
::class_map _classes
;
302 HeaderInfoOptimizer
<P
, objc_header_info_ro_t
<P
>>& _hInfos
;
306 ClassListBuilder(HeaderInfoOptimizer
<P
, objc_header_info_ro_t
<P
>>& hinfos
) : _hInfos(hinfos
) { }
308 void visitClass(ContentAccessor
* cache
,
309 const macho_header
<P
>* header
,
310 objc_class_t
<P
>* cls
)
312 if (cls
->isMetaClass(cache
)) return;
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
)));
323 objc_opt
::string_map
& classNames() {
327 objc_opt
::class_map
& classes() {
331 size_t count() const { return _count
; }
334 template <typename P
>
335 class ProtocolOptimizer
338 typedef typename P
::uint_t pint_t
;
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
;
348 friend class ProtocolReferenceWalker
<P
, ProtocolOptimizer
<P
>>;
350 pint_t
visitProtocolReference(ContentAccessor
* cache
, pint_t oldValue
)
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
++;
361 ProtocolOptimizer(Diagnostics
& diag
, HeaderInfoOptimizer
<P
, objc_header_info_ro_t
<P
>>& hinfos
)
362 : _protocolCount(0), _protocolReferenceCount(0), _diagnostics(diag
), _hInfos(hinfos
) {
365 void addProtocols(ContentAccessor
* cache
, const macho_header
<P
>* header
)
367 PointerSection
<P
, objc_protocol_t
<P
> *>
368 protocols(cache
, header
, "__DATA", "__objc_protolist");
370 for (pint_t i
= 0; i
< protocols
.count(); i
++) {
371 objc_protocol_t
<P
> *proto
= protocols
.get(i
);
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");
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
));
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
)));
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
)
399 if (_protocolCount
== 0) return NULL
;
401 if (protocolClassVMAddr
== 0) {
402 return "libobjc's Protocol class symbol not found (metadata not optimized)";
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)";
410 for (auto iter
= _protocols
.begin(); iter
!= _protocols
.end(); ++iter
)
412 objc_protocol_t
<P
>* oldProto
= (objc_protocol_t
<P
>*)
413 cache
->contentForVMAddr(iter
->second
);
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
);
421 uint32_t oldSize
= oldProto
->getSize();
422 memcpy(proto
, oldProto
, oldSize
);
423 if (!proto
->getIsaVMAddr()) {
424 proto
->setIsaVMAddr(protocolClassVMAddr
);
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
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);
438 size_t length
= 1 + strlen(demangledName
);
439 if (roremaining
< length
) {
440 return "libobjc's read-only section is too small (metadata not optimized)";
443 memmove(rodest
, demangledName
, length
);
444 roName
= (const char *)rodest
;
446 roremaining
-= length
;
450 proto
->setDemangledName(cache
, roName
, _diagnostics
);
453 proto
->setIsCanonical();
455 // Redirect the protocol table at our new object.
456 iter
->second
= cache
->vmAddrForContent(proto
);
458 // Add new rebase entries.
459 proto
->addPointers(cache
, aslrTracker
);
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
];
471 void updateReferences(ContentAccessor
* cache
, const macho_header
<P
>* header
)
473 ProtocolReferenceWalker
<P
, ProtocolOptimizer
<P
>> refs(*this);
474 refs
.walk(cache
, header
);
477 objc_opt
::string_map
& protocolNames() {
478 return _protocolNames
;
481 objc_opt
::legacy_protocol_map
& protocols() {
485 objc_opt
::protocol_map
& protocolsAndHeaders() {
486 return _protocolsAndHeaders
;
489 size_t protocolCount() const { return _protocolCount
; }
490 size_t protocolReferenceCount() const { return _protocolReferenceCount
; }
494 static int percent(size_t num
, size_t denom
) {
496 return (int)(num
/ (double)denom
* 100);
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
)
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");
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
;
522 if ( linkeditSeg
== nullptr ) {
523 diag
.warning("__LINKEDIT not found in libojbc.dylib");
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
);
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);
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);
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);
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
);
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
)
583 typedef typename P
::E E
;
584 typedef typename P
::uint_t pint_t
;
586 diag
.verbose("Optimizing objc metadata:\n");
587 diag
.verbose(" cache type is %s\n", forProduction ?
"production" : "development");
589 ContentAccessor
cacheAccessor(cache
, diag
);
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)");
597 // Find libobjc's empty sections and build list of images with objc metadata
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");
610 if ( mh
->getSection("__DATA", "__objc_imageinfo") || mh
->getSection("__OBJC", "__image_info") ) {
611 objcDylibs
.push_back(mh
);
613 // log("installName %s at mhdr 0x%016lx", installName, (uintptr_t)cacheAccessor.vmAddrForContent((void*)mh));
615 if ( optROSection
== nullptr ) {
616 diag
.warning("libobjc's read-only section missing (metadata not optimized)");
619 if ( optPointerListSection
== nullptr ) {
620 diag
.warning("libobjc's pointer list section missing (metadata not optimized)");
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");
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)");
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)");
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)");
650 const objc_opt
::objc_opt_pointerlist_tt
<pint_t
> *optPointerList
= (const objc_opt
::objc_opt_pointerlist_tt
<pint_t
> *)cacheAccessor
.contentForVMAddr(optPointerListSection
->addr());
652 // Write nothing to optROHeader until everything else is written.
653 // If something fails below, libobjc will not use the section.
657 // Make copy of objcList and sort that list.
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 {
665 // Build HeaderInfo list in cache
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
);
673 diag
.warning("%s", err
);
677 for (const macho_header
<P
>* mh
: addressSortedDylibs
) {
678 hinfoROOptimizer
.update(&cacheAccessor
, mh
, aslrTracker
);
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
);
688 diag
.warning("%s", err
);
692 for (const macho_header
<P
>* mh
: addressSortedDylibs
) {
693 hinfoRWOptimizer
.update(&cacheAccessor
, mh
, aslrTracker
);
698 // Update selector references and build selector list
700 // This is SAFE: if we run out of room for the selector table,
701 // the modified binaries are still usable.
703 // Heuristic: choose selectors from libraries with more selector cstring data first.
704 // This tries to localize selector cstring memory.
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"))
713 if (!strcmp(installName
, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation") ||
714 !strcmp(installName
, "/System/Library/Frameworks/Foundation.framework/Foundation"))
716 if (!strcmp(installName
, "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation") ||
717 !strcmp(installName
, "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"))
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"))
722 if (!strcmp(installName
, "/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit"))
724 if (!strcmp(installName
, "/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork") ||
725 !strcmp(installName
, "/System/Library/Frameworks/CFNetwork.framework/CFNetwork"))
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
;
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
)
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
;
749 auto alignPointer
= [](uint8_t* ptr
) -> uint8_t* {
750 return (uint8_t*)(((uintptr_t)ptr
+ 0x7) & ~0x7);
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
);
760 diag
.verbose(" uniqued %6lu selectors\n", uniq
.strings().size());
761 diag
.verbose(" updated %6lu selector references\n", uniq
.count());
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());
767 diag
.warning("%s", err
);
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;
777 diag
.verbose(" selector table occupancy %u/%u (%u%%)\n",
778 seloptOccupied
, seloptCapacity
,
779 (unsigned)(seloptOccupied
/(double)seloptCapacity
*100));
783 // Detect classes that have missing weak-import superclasses.
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.
790 // This is SAFE: the binaries themselves are unmodified.
791 WeakClassDetector
<P
> weakopt
;
792 bool noMissingWeakSuperclasses
= weakopt
.noMissingWeakSuperclasses(&cacheAccessor
,
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.");
811 // Build class table.
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
);
820 diag
.verbose(" recorded % 6ld classes\n", classes
.classNames().size());
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);
827 diag
.warning("%s", err
);
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
);
839 diag
.verbose(" found % 6ld duplicate classes\n",
841 diag
.verbose(" class table occupancy %u/%u (%u%%)\n",
842 clsoptOccupied
, clsoptCapacity
,
843 (unsigned)(clsoptOccupied
/(double)clsoptCapacity
*100));
847 // Sort method lists.
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
);
856 diag
.verbose(" sorted % 6ld method lists\n", methodSorter
.optimized());
859 // Unique protocols and build protocol table.
861 // This is SAFE: no protocol references are updated yet
862 // This must be done AFTER updating method lists.
864 ProtocolOptimizer
<P
> protocolOptimizer(diag
, hinfoROOptimizer
);
865 for (const macho_header
<P
>* mh
: sizeSortedDylibs
) {
866 protocolOptimizer
.addProtocols(&cacheAccessor
, mh
);
869 diag
.verbose(" uniqued % 6ld protocols\n",
870 protocolOptimizer
.protocolCount());
872 pint_t protocolClassVMAddr
= (pint_t
)P
::getP(optPointerList
->protocolClass
);
873 err
= protocolOptimizer
.writeProtocols(&cacheAccessor
,
874 optRWData
, optRWRemaining
,
875 optROData
, optRORemaining
,
876 aslrTracker
, protocolClassVMAddr
);
878 diag
.warning("%s", err
);
882 // Align the buffer again. The new protocols may have added an odd number of name characters
883 optROData
= alignPointer(optROData
);
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);
892 diag
.warning("%s", err
);
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
;
902 diag
.verbose(" protocol table occupancy %u/%u (%u%%)\n",
903 protocoloptOccupied
, protocoloptCapacity
,
904 (unsigned)(protocoloptOccupied
/(double)protocoloptCapacity
*100));
907 // Redirect protocol references to the uniqued protocols.
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
);
914 diag
.verbose(" updated % 6ld protocol references\n", protocolOptimizer
.protocolReferenceCount());
918 // Repair ivar offsets.
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
);
926 diag
.verbose(" updated % 6ld ivar offsets\n", ivarOffsetOptimizer
.optimized());
930 uint32_t headerFlags
= 0;
932 headerFlags
|= objc_opt
::IsProduction
;
934 if (noMissingWeakSuperclasses
) {
935 headerFlags
|= objc_opt
::NoMissingWeakSuperclasses
;
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");
945 if (imageInfoSection
) {
946 objc_image_info
<P
>* info
= (objc_image_info
<P
>*)cacheAccessor
.contentForVMAddr(imageInfoSection
->addr());
947 info
->setOptimizedByDyld();
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()));
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
);
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
);
975 // Now that objc has uniqued the selector references, we can apply the LOHs so that ADRP/LDR -> ADRP/ADD
977 const bool logSelectors
= false;
978 uint64_t lohADRPCount
= 0;
979 uint64_t lohLDRCount
= 0;
981 for (auto& targetAndInstructions
: lohTracker
) {
982 uint64_t targetVMAddr
= targetAndInstructions
.first
;
983 if (!selOptimizer
.isSelectorRefAddress((pint_t
)targetVMAddr
))
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
);
999 if ( (instruction
& 0x9F000000) == 0x90000000 ) {
1001 int64_t pageDistance
= ((selectorStringVMAddr
& ~0xFFF) - (instructionVMAddr
& ~0xFFF));
1002 int64_t newPage21
= pageDistance
>> 12;
1005 if ( (newPage21
> 2097151) || (newPage21
< -2097151) ) {
1007 fprintf(stderr
, "Out of bounds ADRP selector reference target\n");
1008 instructions
.clear();
1015 instruction
= (instruction
& 0x9F00001F) | ((newPage21
<< 29) & 0x60000000) | ((newPage21
<< 3) & 0x00FFFFE0);
1021 if ( (instruction
& 0x3B000000) == 0x39000000 ) {
1022 // LDR/STR. STR shouldn't be possible as this is a selref!
1024 if ( (instruction
& 0xC0C00000) != 0xC0400000 ) {
1025 // Not a load, or dest reg isn't xN, or uses sign extension
1027 fprintf(stderr
, "Bad LDR for selector reference optimisation\n");
1028 instructions
.clear();
1031 if ( (instruction
& 0x04000000) != 0 ) {
1034 fprintf(stderr
, "Bad LDR for selector reference optimisation\n");
1035 instructions
.clear();
1042 uint32_t ldrDestReg
= (instruction
& 0x1F);
1043 uint32_t ldrBaseReg
= ((instruction
>> 5) & 0x1F);
1045 // Convert the LDR to an ADD
1046 instruction
= 0x91000000;
1047 instruction
|= ldrDestReg
;
1048 instruction
|= ldrBaseReg
<< 5;
1049 instruction
|= (selectorStringVMAddr
& 0xFFF) << 10;
1056 if ( (instruction
& 0xFFC00000) == 0x91000000 ) {
1058 // We don't support ADDs.
1060 fprintf(stderr
, "Bad ADD for selector reference optimisation\n");
1061 instructions
.clear();
1066 fprintf(stderr
, "Unknown instruction for selref optimisation\n");
1067 instructions
.clear();
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();
1080 diag
.verbose(" Optimized %lld ADRP LOHs\n", lohADRPCount
);
1081 diag
.verbose(" Optimized %lld LDR LOHs\n", lohLDRCount
);
1088 void SharedCacheBuilder
::optimizeObjC()
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
);
1097 doOptimizeObjC
<Pointer32
<LittleEndian
>>((DyldSharedCache
*)_readExecuteRegion
.buffer
, _options
.optimizeStubs
, _aslrTracker
, _lohTracker
,
1098 _coalescedText
, _missingWeakImports
,
1099 _diagnostics
, _objcReadOnlyBuffer
, _objcReadOnlyBufferSizeUsed
, _objcReadOnlyBufferSizeAllocated
,
1100 _objcReadWriteBuffer
, _objcReadWriteBufferSizeAllocated
, objcRwFileOffset
);
1103 static uint32_t hashTableSize(uint32_t maxElements
, uint32_t perElementData
)
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
;
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
)
1115 return 0xA000 + hashTableSize(selRefCount
, 5) + hashTableSize(classDefCount
, 12) + hashTableSize(protocolDefCount
, 8);
1118 // Space to replace the __objc_opt_rw section.
1119 uint32_t SharedCacheBuilder
::computeReadWriteObjC(uint32_t imageCount
, uint32_t protocolDefCount
)
1121 return 8*imageCount
+ protocolDefCount
*12*(_archLayout
->is64 ?
8 : 4);