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 "MachOFileAbstraction.hpp"
28 #include "DyldSharedCache.h"
29 #include "CacheBuilder.h"
30 #include "Diagnostics.h"
31 #include "IMPCaches.hpp"
33 CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions
& options
, const dyld3::closure::FileSystem
& fileSystem
)
35 , _fileSystem(fileSystem
)
36 , _fullAllocatedBuffer(0)
37 , _diagnostics(options
.loggingPrefix
, options
.verbose
)
38 , _allocatedBufferSize(0)
42 CacheBuilder::~CacheBuilder() {
46 std::string
CacheBuilder::errorMessage()
48 return _diagnostics
.errorMessage();
51 void CacheBuilder::copyRawSegments()
53 const bool log
= false;
54 const bool logCFConstants
= false;
56 forEachDylibInfo(^(const DylibInfo
& dylib
, Diagnostics
& dylibDiag
) {
57 for (const SegmentMappingInfo
& info
: dylib
.cacheLocation
) {
58 if (log
) fprintf(stderr
, "copy %s segment %s (0x%08X bytes) from %p to %p (logical addr 0x%llX) for %s\n",
59 _options
.archs
->name(), info
.segName
, info
.copySegmentSize
, info
.srcSegment
, info
.dstSegment
, info
.dstCacheUnslidAddress
, dylib
.input
->mappedFile
.runtimePath
.c_str());
60 ::memcpy(info
.dstSegment
, info
.srcSegment
, info
.copySegmentSize
);
64 // Copy the coalesced __TEXT sections
65 const uint64_t numCoalescedSections
= sizeof(CacheCoalescedText::SupportedSections
) / sizeof(*CacheCoalescedText::SupportedSections
);
66 dispatch_apply(numCoalescedSections
, DISPATCH_APPLY_AUTO
, ^(size_t index
) {
67 const CacheCoalescedText::StringSection
& cacheStringSection
= _coalescedText
.getSectionData(CacheCoalescedText::SupportedSections
[index
]);
68 if (log
) fprintf(stderr
, "copy %s __TEXT_COAL section %s (0x%08X bytes) to %p (logical addr 0x%llX)\n",
69 _options
.archs
->name(), CacheCoalescedText::SupportedSections
[index
],
70 cacheStringSection
.bufferSize
, cacheStringSection
.bufferAddr
, cacheStringSection
.bufferVMAddr
);
71 for (const auto& stringAndOffset
: cacheStringSection
.stringsToOffsets
)
72 ::memcpy(cacheStringSection
.bufferAddr
+ stringAndOffset
.second
, stringAndOffset
.first
.data(), stringAndOffset
.first
.size() + 1);
75 // Copy the coalesced CF sections
76 if ( _coalescedText
.cfStrings
.bufferSize
!= 0 ) {
77 uint8_t* dstBuffer
= _coalescedText
.cfStrings
.bufferAddr
;
78 uint64_t dstBufferVMAddr
= _coalescedText
.cfStrings
.bufferVMAddr
;
79 forEachDylibInfo(^(const DylibInfo
& dylib
, Diagnostics
& dylibDiag
) {
80 const char* segmentName
= "__OBJC_CONST";
81 const char* sectionName
= "__cfstring";
82 const DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset
& sectionData
= dylib
.textCoalescer
.getSectionCoalescer(segmentName
, sectionName
);
83 if ( sectionData
.empty() )
86 uint64_t sectionContentSize
= 0;
87 const void* sectionContent
= dylib
.input
->mappedFile
.mh
->findSectionContent(segmentName
, sectionName
, sectionContentSize
);
88 assert(sectionContent
!= nullptr);
89 assert(sectionContentSize
!= 0);
90 for (const auto& dylibOffsetAndCacheOffset
: sectionData
) {
91 uint64_t dylibOffset
= dylibOffsetAndCacheOffset
.first
;
92 uint64_t cacheOffset
= dylibOffsetAndCacheOffset
.second
;
93 if (logCFConstants
) fprintf(stderr
, "copy %s %s section %s (0x%08X bytes) to %p (logical addr 0x%llX)\n",
94 _options
.archs
->name(), segmentName
, sectionName
,
95 (uint32_t)DyldSharedCache::ConstantClasses::cfStringAtomSize
, dstBuffer
+ cacheOffset
, dstBufferVMAddr
+ cacheOffset
);
96 ::memcpy(dstBuffer
+ cacheOffset
, (const uint8_t*)sectionContent
+ dylibOffset
, (size_t)DyldSharedCache::ConstantClasses::cfStringAtomSize
);
102 void CacheBuilder::adjustAllImagesForNewSegmentLocations(uint64_t cacheBaseAddress
,
103 ASLR_Tracker
& aslrTracker
, LOH_Tracker
* lohTracker
,
104 const CacheBuilder::CacheCoalescedText
* coalescedText
)
106 // Note this cannot to be done in parallel because the LOH Tracker and aslr tracker are not thread safe
107 __block
bool badDylib
= false;
108 forEachDylibInfo(^(const DylibInfo
& dylib
, Diagnostics
& dylibDiag
) {
109 if ( dylibDiag
.hasError() )
111 adjustDylibSegments(dylib
, dylibDiag
, cacheBaseAddress
, aslrTracker
,
112 lohTracker
, coalescedText
);
113 if ( dylibDiag
.hasError() )
117 if ( badDylib
&& !_diagnostics
.hasError() ) {
118 _diagnostics
.error("One or more binaries has an error which prevented linking. See other errors.");
123 CacheBuilder::ASLR_Tracker::~ASLR_Tracker()
125 if ( _bitmap
!= nullptr )
127 #if BUILDING_APP_CACHE_UTIL
128 if ( _cacheLevels
!= nullptr )
129 ::free(_cacheLevels
);
133 void CacheBuilder::ASLR_Tracker::setDataRegion(const void* rwRegionStart
, size_t rwRegionSize
)
135 _pageCount
= (unsigned)(rwRegionSize
+_pageSize
-1)/_pageSize
;
136 _regionStart
= (uint8_t*)rwRegionStart
;
137 _regionEnd
= (uint8_t*)rwRegionStart
+ rwRegionSize
;
138 _bitmap
= (bool*)calloc(_pageCount
*(_pageSize
/kMinimumFixupAlignment
)*sizeof(bool), 1);
139 #if BUILDING_APP_CACHE_UTIL
140 size_t cacheLevelsSize
= (_pageCount
*(_pageSize
/kMinimumFixupAlignment
)*sizeof(uint8_t));
141 _cacheLevels
= (uint8_t*)malloc(cacheLevelsSize
);
142 memset(_cacheLevels
, (int)~0U, cacheLevelsSize
);
146 void CacheBuilder::ASLR_Tracker::add(void* loc
, uint8_t level
)
150 uint8_t* p
= (uint8_t*)loc
;
151 assert(p
>= _regionStart
);
152 assert(p
< _regionEnd
);
153 _bitmap
[(p
-_regionStart
)/kMinimumFixupAlignment
] = true;
155 #if BUILDING_APP_CACHE_UTIL
156 if ( level
!= (uint8_t)~0U ) {
157 _cacheLevels
[(p
-_regionStart
)/kMinimumFixupAlignment
] = level
;
162 void CacheBuilder::ASLR_Tracker::remove(void* loc
)
166 uint8_t* p
= (uint8_t*)loc
;
167 assert(p
>= _regionStart
);
168 assert(p
< _regionEnd
);
169 _bitmap
[(p
-_regionStart
)/kMinimumFixupAlignment
] = false;
172 bool CacheBuilder::ASLR_Tracker::has(void* loc
, uint8_t* level
) const
176 uint8_t* p
= (uint8_t*)loc
;
177 assert(p
>= _regionStart
);
178 assert(p
< _regionEnd
);
180 if ( _bitmap
[(p
-_regionStart
)/kMinimumFixupAlignment
] ) {
181 #if BUILDING_APP_CACHE_UTIL
182 if ( level
!= nullptr ) {
183 uint8_t levelValue
= _cacheLevels
[(p
-_regionStart
)/kMinimumFixupAlignment
];
184 if ( levelValue
!= (uint8_t)~0U )
193 void CacheBuilder::ASLR_Tracker::setHigh8(void* p
, uint8_t high8
)
195 _high8Map
[p
] = high8
;
198 void CacheBuilder::ASLR_Tracker::setAuthData(void* p
, uint16_t diversity
, bool hasAddrDiv
, uint8_t key
)
200 _authDataMap
[p
] = {diversity
, hasAddrDiv
, key
};
203 void CacheBuilder::ASLR_Tracker::setRebaseTarget32(void*p
, uint32_t targetVMAddr
)
205 _rebaseTarget32
[p
] = targetVMAddr
;
208 void CacheBuilder::ASLR_Tracker::setRebaseTarget64(void*p
, uint64_t targetVMAddr
)
210 _rebaseTarget64
[p
] = targetVMAddr
;
213 bool CacheBuilder::ASLR_Tracker::hasHigh8(void* p
, uint8_t* highByte
) const
215 auto pos
= _high8Map
.find(p
);
216 if ( pos
== _high8Map
.end() )
218 *highByte
= pos
->second
;
222 bool CacheBuilder::ASLR_Tracker::hasAuthData(void* p
, uint16_t* diversity
, bool* hasAddrDiv
, uint8_t* key
) const
224 auto pos
= _authDataMap
.find(p
);
225 if ( pos
== _authDataMap
.end() )
227 *diversity
= pos
->second
.diversity
;
228 *hasAddrDiv
= pos
->second
.addrDiv
;
229 *key
= pos
->second
.key
;
233 bool CacheBuilder::ASLR_Tracker::hasRebaseTarget32(void* p
, uint32_t* vmAddr
) const
235 auto pos
= _rebaseTarget32
.find(p
);
236 if ( pos
== _rebaseTarget32
.end() )
238 *vmAddr
= pos
->second
;
242 bool CacheBuilder::ASLR_Tracker::hasRebaseTarget64(void* p
, uint64_t* vmAddr
) const
244 auto pos
= _rebaseTarget64
.find(p
);
245 if ( pos
== _rebaseTarget64
.end() )
247 *vmAddr
= pos
->second
;
251 std::vector
<void*> CacheBuilder::ASLR_Tracker::getRebaseTargets() const {
252 std::vector
<void*> targets
;
253 for (const auto& target
: _rebaseTarget32
)
254 targets
.push_back(target
.first
);
255 for (const auto& target
: _rebaseTarget64
)
256 targets
.push_back(target
.first
);
260 //////////////////////////// DylibTextCoalescer ////////////////////////////////////
262 bool CacheBuilder::DylibTextCoalescer::segmentWasCoalesced(std::string_view segmentName
) const {
263 if (segmentName
.size() > 16)
264 segmentName
= segmentName
.substr(0, 16);
266 if ( segmentName
== "__OBJC_CONST" ) {
267 return !cfStrings
.empty();
273 bool CacheBuilder::DylibTextCoalescer::sectionWasCoalesced(std::string_view segmentName
,
274 std::string_view sectionName
) const {
275 if (segmentName
.size() > 16)
276 segmentName
= segmentName
.substr(0, 16);
277 if (sectionName
.size() > 16)
278 sectionName
= sectionName
.substr(0, 16);
280 if ( segmentName
== "__TEXT" ) {
281 std::map
<std::string_view
, const DylibSectionOffsetToCacheSectionOffset
*> supportedSections
= {
282 { "__objc_classname", &objcClassNames
},
283 { "__objc_methname", &objcMethNames
},
284 { "__objc_methtype", &objcMethTypes
}
286 auto it
= supportedSections
.find(sectionName
);
287 if (it
== supportedSections
.end())
289 return !it
->second
->empty();
292 if ( segmentName
== "__OBJC_CONST" ) {
293 if ( sectionName
== "__cfstring" ) {
294 return !cfStrings
.empty();
301 CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset
&
302 CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view segmentName
, std::string_view sectionName
) {
303 if (segmentName
.size() > 16)
304 segmentName
= segmentName
.substr(0, 16);
305 if (sectionName
.size() > 16)
306 sectionName
= sectionName
.substr(0, 16);
308 if ( segmentName
== "__TEXT" ) {
309 std::map
<std::string_view
, DylibSectionOffsetToCacheSectionOffset
*> supportedSections
= {
310 { "__objc_classname", &objcClassNames
},
311 { "__objc_methname", &objcMethNames
},
312 { "__objc_methtype", &objcMethTypes
}
314 auto it
= supportedSections
.find(sectionName
);
315 assert(it
!= supportedSections
.end());
319 if ( segmentName
== "__OBJC_CONST" ) {
320 if ( sectionName
== "__cfstring" ) {
328 const CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset
&
329 CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view segmentName
, std::string_view sectionName
) const {
330 if (segmentName
.size() > 16)
331 segmentName
= segmentName
.substr(0, 16);
332 if (sectionName
.size() > 16)
333 sectionName
= sectionName
.substr(0, 16);
335 if ( segmentName
== "__TEXT" ) {
336 std::map
<std::string_view
, const DylibSectionOffsetToCacheSectionOffset
*> supportedSections
= {
337 { "__objc_classname", &objcClassNames
},
338 { "__objc_methname", &objcMethNames
},
339 { "__objc_methtype", &objcMethTypes
}
341 auto it
= supportedSections
.find(sectionName
);
342 assert(it
!= supportedSections
.end());
346 if ( segmentName
== "__OBJC_CONST" ) {
347 if ( sectionName
== "__cfstring" ) {
355 //////////////////////////// CacheCoalescedText ////////////////////////////////////
356 const char* CacheBuilder::CacheCoalescedText::SupportedSections
[] = {
362 void CacheBuilder::CacheCoalescedText::
363 parseCoalescableText(const dyld3::MachOAnalyzer
* ma
,
364 DylibTextCoalescer
& textCoalescer
,
365 const IMPCaches::SelectorMap
& selectors
,
366 IMPCaches::HoleMap
& selectorsHoleMap
) {
367 static const bool log
= false;
369 // We can only remove sections if we know we have split seg v2 to point to it
370 // Otherwise, a PC relative load in the __TEXT segment wouldn't know how to point to the new strings
371 // which are no longer in the same segment
372 uint32_t splitSegSize
= 0;
373 const void* splitSegStart
= ma
->getSplitSeg(splitSegSize
);
377 if ((*(const uint8_t*)splitSegStart
) != DYLD_CACHE_ADJ_V2_FORMAT
)
380 // We can only remove sections from the end of a segment, so cache them all and walk backwards.
381 __block
std::vector
<std::pair
<std::string
, dyld3::MachOAnalyzer::SectionInfo
>> textSectionInfos
;
382 ma
->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
§Info
, bool malformedSectionRange
, bool &stop
) {
383 if (strcmp(sectInfo
.segInfo
.segName
, "__TEXT") != 0)
385 assert(!malformedSectionRange
);
386 textSectionInfos
.push_back({ sectInfo
.sectName
, sectInfo
});
389 const std::set
<std::string_view
> supportedSections(std::begin(SupportedSections
), std::end(SupportedSections
));
390 int64_t slide
= ma
->getSlide();
392 bool isSelectorsSection
= false;
393 for (auto sectionInfoIt
= textSectionInfos
.rbegin(); sectionInfoIt
!= textSectionInfos
.rend(); ++sectionInfoIt
) {
394 const std::string
& sectionName
= sectionInfoIt
->first
;
395 const dyld3::MachOAnalyzer::SectionInfo
& sectInfo
= sectionInfoIt
->second
;
397 isSelectorsSection
= (sectionName
== "__objc_methname");
399 // If we find a section we can't handle then stop here. Hopefully we coalesced some from the end.
400 if (supportedSections
.find(sectionName
) == supportedSections
.end())
403 StringSection
& cacheStringSection
= getSectionData(sectionName
);
405 DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset
& sectionStringData
= textCoalescer
.getSectionCoalescer("__TEXT", sectionName
);
407 // Walk the strings in this section
408 const uint8_t* content
= (uint8_t*)(sectInfo
.sectAddr
+ slide
);
409 const char* s
= (char*)content
;
410 const char* end
= s
+ sectInfo
.sectSize
;
412 std::string_view str
= s
;
413 int cacheSectionOffset
= 0;
415 auto it
= cacheStringSection
.stringsToOffsets
.find(str
);
416 if (it
!= cacheStringSection
.stringsToOffsets
.end()) {
417 // Debugging only. If we didn't include the string then we saved that many bytes
418 cacheStringSection
.savedSpace
+= str
.size() + 1;
419 cacheSectionOffset
= it
->second
;
420 } else if (isSelectorsSection
) {
421 // If we are in the selectors section, we need to move
422 // the selectors in the selector map to their correct addresses,
423 // and fill the holes with the rest
425 #if BUILDING_APP_CACHE_UTIL
426 cacheSectionOffset
= cacheStringSection
.bufferSize
;
428 const IMPCaches::SelectorMap::UnderlyingMap
& map
= selectors
.map
;
429 IMPCaches::SelectorMap::UnderlyingMap::const_iterator selectorsIterator
= map
.find(str
);
430 if (selectorsIterator
!= map
.end()) {
431 cacheSectionOffset
= selectorsIterator
->second
->offset
;
433 cacheSectionOffset
= selectorsHoleMap
.addStringOfSize((unsigned)str
.size() + 1);
436 cacheStringSection
.stringsToOffsets
[str
] = cacheSectionOffset
;
437 uint32_t sizeAtLeast
= cacheSectionOffset
+ (uint32_t)str
.size() + 1;
438 if (cacheStringSection
.bufferSize
< sizeAtLeast
) {
439 cacheStringSection
.bufferSize
= sizeAtLeast
;
442 auto itAndInserted
= cacheStringSection
.stringsToOffsets
.insert({ str
, cacheStringSection
.bufferSize
});
443 cacheSectionOffset
= itAndInserted
.first
->second
;
444 assert(itAndInserted
.second
);
446 cacheStringSection
.bufferSize
+= str
.size() + 1;
448 printf("Selector: %s -> %s\n", ma
->installName(), s
);
452 // Now keep track of this offset in our source dylib as pointing to this offset
453 uint32_t sourceSectionOffset
= (uint32_t)((uint64_t)s
- (uint64_t)content
);
454 sectionStringData
[sourceSectionOffset
] = cacheSectionOffset
;
460 void CacheBuilder::CacheCoalescedText::parseCFConstants(const dyld3::MachOAnalyzer
*ma
,
461 DylibTextCoalescer
&textCoalescer
) {
462 static const bool log
= false;
464 // FIXME: Re-enable this once we can correctly patch the shared cache
471 // We only support chained fixups as we need to rewrite binds/rebases after applying split seg
472 // and that is much easier with chained fixups than opcodes
473 if ( !ma
->hasChainedFixupsLoadCommand() )
476 // FIXME: Support DYLD_CHAINED_PTR_ARM64E_USERLAND once ld64 moves to it.
477 const uint16_t pointerFormat
= ma
->chainedPointerFormat();
478 if ( pointerFormat
!= DYLD_CHAINED_PTR_ARM64E
)
481 // We can only remove sections if we know we have split seg v2 to point to it
482 // Otherwise, a PC relative load in the __TEXT segment wouldn't know how to point to the new constants
483 // which are no longer in the same segment
484 if ( !ma
->isSplitSegV2() )
487 // We can only remove sections from the end of a segment, so cache them all and walk backwards.
488 __block
std::vector
<std::pair
<std::string
, dyld3::MachOAnalyzer::SectionInfo
>> dataSectionInfos
;
489 __block
uint64_t cstringStartVMAddr
= 0;
490 __block
uint64_t cstringEndVMAddr
= 0;
491 ma
->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
§Info
, bool malformedSectionRange
, bool &stop
) {
492 if ( malformedSectionRange
)
494 if ( strcmp(sectInfo
.segInfo
.segName
, "__OBJC_CONST") == 0 ) {
495 dataSectionInfos
.push_back({ sectInfo
.sectName
, sectInfo
});
498 if ( strcmp(sectInfo
.segInfo
.segName
, "__TEXT") == 0 ) {
499 if ( strcmp(sectInfo
.sectName
, "__cstring") == 0 ) {
500 if ( ( (sectInfo
.sectFlags
& SECTION_TYPE
) == S_CSTRING_LITERALS
) ) {
501 cstringStartVMAddr
= sectInfo
.sectAddr
;
502 cstringEndVMAddr
= cstringStartVMAddr
+ sectInfo
.sectSize
;
508 // We need to clear the chained pointer fixups for the whole segment, so can only
509 // process any type of CF object if we can process them all
510 if ( dataSectionInfos
.size() != 1 )
513 if ( dataSectionInfos
.front().first
!= "__cfstring" )
516 if ( cstringStartVMAddr
== 0 )
519 const dyld3::MachOAnalyzer::SectionInfo
& cfStringsSection
= dataSectionInfos
.back().second
;
521 // A CFString is layed out in memory as
524 // uint32_t encoding;
526 // uintptr_t cstringData;
527 // uintptr_t cstringLength;
529 const uint64_t cstringDataOffset
= 16;
530 const char* className
= cfStrings
.isaClassName
;
531 if ( cfStringsSection
.sectSize
% (uint32_t)DyldSharedCache::ConstantClasses::cfStringAtomSize
) {
532 // We don't support padding or any kind on the section
536 uint64_t baseAddress
= ma
->preferredLoadAddress();
538 uint64_t startVMOffset
= cfStringsSection
.sectAddr
- baseAddress
;
539 uint64_t endVMOffset
= startVMOffset
+ cfStringsSection
.sectSize
;
541 __block Diagnostics diag
;
543 // Make sure no symbols are pointing in to this section
544 __block
bool hasSymbols
= false;
545 ma
->forEachGlobalSymbol(diag
, ^(const char *symbolName
, uint64_t n_value
, uint8_t n_type
, uint8_t n_sect
, uint16_t n_desc
, bool &stop
) {
546 uint64_t vmOffset
= n_value
- baseAddress
;
547 if ( vmOffset
< startVMOffset
)
549 if ( vmOffset
>= endVMOffset
)
551 // In range of our section
555 if ( diag
.hasError() )
560 ma
->forEachLocalSymbol(diag
, ^(const char *symbolName
, uint64_t n_value
, uint8_t n_type
, uint8_t n_sect
, uint16_t n_desc
, bool &stop
) {
561 uint64_t vmOffset
= n_value
- baseAddress
;
562 if ( vmOffset
< startVMOffset
)
564 if ( vmOffset
>= endVMOffset
)
566 // In range of our section
570 if ( diag
.hasError() )
575 ma
->forEachExportedSymbol(diag
, ^(const char *symbolName
, uint64_t imageOffset
, uint64_t flags
, uint64_t other
, const char *importName
, bool &stop
) {
576 if ( imageOffset
< startVMOffset
)
578 if ( imageOffset
>= endVMOffset
)
580 // In range of our section
584 if ( diag
.hasError() )
589 __block
std::vector
<const char*> dependentPaths
;
590 ma
->forEachDependentDylib(^(const char *loadPath
, bool isWeak
, bool isReExport
,
591 bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool &stop
) {
592 dependentPaths
.push_back(loadPath
);
595 // Find all the binds to the ISA class. These delineate the atoms
596 // In CoreFoundation itself, we are looking for rebases to the ISA
597 __block
std::vector
<uint64_t> atomOffsets
;
599 bool dylibExportsISA
= strcmp(ma
->installName(), cfStrings
.isaInstallName
) == 0;
600 if ( !dylibExportsISA
) {
601 // This dylib doens't export the class, so look for binds to the ISA
602 __block
std::vector
<std::pair
<const char*, int>> bindTargetSymbols
;
603 ma
->forEachChainedFixupTarget(diag
, ^(int libraryOrdinal
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
) {
604 bindTargetSymbols
.push_back({ symbolName
, libraryOrdinal
});
607 __block
bool foundBadBind
= false;
608 ma
->withChainStarts(diag
, ma
->chainStartsOffset(), ^(const dyld_chained_starts_in_image
* startsInfo
) {
611 ma
->forEachFixupInAllChains(diag
, startsInfo
, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk
* fixupLoc
,
612 const dyld_chained_starts_in_segment
* segInfo
, bool& stopFixups
) {
613 // Skip anything not in this section
614 uint64_t vmOffset
= (uint8_t*)fixupLoc
- (uint8_t*)ma
;
615 if ( vmOffset
< startVMOffset
)
617 if ( vmOffset
>= endVMOffset
)
620 uint32_t bindOrdinal
;
622 if ( fixupLoc
->isBind(pointerFormat
, bindOrdinal
, ptrAddend
) ) {
623 if ( ptrAddend
!= 0 ) {
628 if ( bindOrdinal
>= bindTargetSymbols
.size() ) {
633 if ( strcmp(bindTargetSymbols
[bindOrdinal
].first
, className
) != 0 ) {
638 int libOrdinal
= bindTargetSymbols
[bindOrdinal
].second
;
639 if ( libOrdinal
<= 0 ) {
644 int depIndex
= libOrdinal
- 1;
645 if ( depIndex
>= dependentPaths
.size() ) {
650 const char* depLoadPath
= dependentPaths
[depIndex
];
651 // All dylibs must find the ISA in the same place
652 if ( strcmp(cfStrings
.isaInstallName
, depLoadPath
) != 0 ) {
657 atomOffsets
.push_back(vmOffset
);
664 if ( atomOffsets
.empty() )
668 if ( diag
.hasError() )
671 // Find all the rebases in the atoms, which correpond to pointers strings
672 __block
std::map
<uint64_t, uint64_t> sectionRebases
;
673 ma
->withChainStarts(diag
, ma
->chainStartsOffset(), ^(const dyld_chained_starts_in_image
* startsInfo
) {
674 ma
->forEachFixupInAllChains(diag
, startsInfo
, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk
* fixupLoc
, const dyld_chained_starts_in_segment
* segInfo
, bool& stopFixups
) {
675 // Skip anything not in this section
676 uint64_t vmOffset
= (uint8_t*)fixupLoc
- (uint8_t*)ma
;
677 if ( vmOffset
< startVMOffset
)
679 if ( vmOffset
>= endVMOffset
)
682 uint64_t rebaseTargetRuntimeOffset
;
683 if ( fixupLoc
->isRebase(pointerFormat
, 0, rebaseTargetRuntimeOffset
) ) {
684 if ( dylibExportsISA
&& (rebaseTargetRuntimeOffset
== cfStrings
.isaVMOffset
) ) {
685 atomOffsets
.push_back(vmOffset
);
687 sectionRebases
[vmOffset
] = rebaseTargetRuntimeOffset
;
692 if ( diag
.hasError() )
695 // Every atom should have a single rebase to a cstring
696 if ( sectionRebases
.size() != atomOffsets
.size() )
699 std::sort(atomOffsets
.begin(), atomOffsets
.end());
700 for (uint64_t atomOffset
: atomOffsets
) {
701 auto it
= sectionRebases
.find(atomOffset
+ cstringDataOffset
);
702 if ( it
== sectionRebases
.end() )
706 CFSection
& stringSection
= this->cfStrings
;
707 DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset
& sectionData
= textCoalescer
.getSectionCoalescer("__OBJC_CONST", "__cfstring");
708 for (uint64_t atomOffset
: atomOffsets
) {
710 printf("%s: found __cfstring at: 0x%llx\n", ma
->installName(), atomOffset
);
712 // Now keep track of this offset in our source dylib as pointing to this offset
713 uint32_t sourceSectionOffset
= (uint32_t)(atomOffset
- startVMOffset
);
714 uint32_t cacheSectionOffset
= stringSection
.bufferSize
;
715 sectionData
[sourceSectionOffset
] = cacheSectionOffset
;
716 stringSection
.bufferSize
+= (uint32_t)DyldSharedCache::ConstantClasses::cfStringAtomSize
;
720 void CacheBuilder::CacheCoalescedText::clear() {
721 *this = CacheBuilder::CacheCoalescedText();
725 CacheBuilder::CacheCoalescedText::StringSection
& CacheBuilder::CacheCoalescedText::getSectionData(std::string_view sectionName
) {
726 if (sectionName
.size() > 16)
727 sectionName
= sectionName
.substr(0, 16);
728 std::map
<std::string_view
, StringSection
*> supportedSections
= {
729 { "__objc_classname", &objcClassNames
},
730 { "__objc_methname", &objcMethNames
},
731 { "__objc_methtype", &objcMethTypes
}
733 auto it
= supportedSections
.find(sectionName
);
734 assert(it
!= supportedSections
.end());
739 const CacheBuilder::CacheCoalescedText::StringSection
& CacheBuilder::CacheCoalescedText::getSectionData(std::string_view sectionName
) const {
740 if (sectionName
.size() > 16)
741 sectionName
= sectionName
.substr(0, 16);
742 std::map
<std::string_view
, const StringSection
*> supportedSections
= {
743 { "__objc_classname", &objcClassNames
},
744 { "__objc_methname", &objcMethNames
},
745 { "__objc_methtype", &objcMethTypes
}
747 auto it
= supportedSections
.find(sectionName
);
748 assert(it
!= supportedSections
.end());
752 uint64_t CacheBuilder::CacheCoalescedText::getSectionVMAddr(std::string_view segmentName
,
753 std::string_view sectionName
) const {
754 if (segmentName
.size() > 16)
755 segmentName
= segmentName
.substr(0, 16);
756 if (sectionName
.size() > 16)
757 sectionName
= sectionName
.substr(0, 16);
759 if ( segmentName
== "__TEXT" ) {
760 return getSectionData(sectionName
).bufferVMAddr
;
763 if ( segmentName
== "__OBJC_CONST" ) {
764 if ( sectionName
== "__cfstring" ) {
765 return cfStrings
.bufferVMAddr
;
772 uint8_t* CacheBuilder::CacheCoalescedText::getSectionBufferAddr(std::string_view segmentName
,
773 std::string_view sectionName
) const {
774 if (segmentName
.size() > 16)
775 segmentName
= segmentName
.substr(0, 16);
776 if (sectionName
.size() > 16)
777 sectionName
= sectionName
.substr(0, 16);
779 if ( segmentName
== "__TEXT" ) {
780 return getSectionData(sectionName
).bufferAddr
;
783 if ( segmentName
== "__OBJC_CONST" ) {
784 if ( sectionName
== "__cfstring" ) {
785 return cfStrings
.bufferAddr
;
792 uint64_t CacheBuilder::CacheCoalescedText::getSectionObjcTag(std::string_view segmentName
,
793 std::string_view sectionName
) const {
794 if (segmentName
.size() > 16)
795 segmentName
= segmentName
.substr(0, 16);
796 if (sectionName
.size() > 16)
797 sectionName
= sectionName
.substr(0, 16);
799 if ( segmentName
== "__TEXT" ) {
800 // Nothing has a tag in __TEXT
804 if ( segmentName
== "__OBJC_CONST" ) {
805 if ( sectionName
== "__cfstring" ) {
806 // This is defined by objc as the tag we put in the high bits
807 // FIXME: Get a tag from objc
808 // return 1ULL << 63;