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"
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)
43 std::string
CacheBuilder::errorMessage()
45 return _diagnostics
.errorMessage();
48 void CacheBuilder::copyRawSegments()
50 const bool log
= false;
51 dispatch_apply(_sortedDylibs
.size(), DISPATCH_APPLY_AUTO
, ^(size_t index
) {
52 const DylibInfo
& dylib
= _sortedDylibs
[index
];
53 for (const SegmentMappingInfo
& info
: dylib
.cacheLocation
) {
54 if (log
) fprintf(stderr
, "copy %s segment %s (0x%08X bytes) from %p to %p (logical addr 0x%llX) for %s\n",
55 _options
.archs
->name(), info
.segName
, info
.copySegmentSize
, info
.srcSegment
, info
.dstSegment
, info
.dstCacheUnslidAddress
, dylib
.input
->mappedFile
.runtimePath
.c_str());
56 ::memcpy(info
.dstSegment
, info
.srcSegment
, info
.copySegmentSize
);
60 // Copy the coalesced sections
61 const uint64_t numCoalescedSections
= sizeof(CacheCoalescedText::SupportedSections
) / sizeof(*CacheCoalescedText::SupportedSections
);
62 dispatch_apply(numCoalescedSections
, DISPATCH_APPLY_AUTO
, ^(size_t index
) {
63 const CacheCoalescedText::StringSection
& cacheStringSection
= _coalescedText
.getSectionData(CacheCoalescedText::SupportedSections
[index
]);
64 if (log
) fprintf(stderr
, "copy %s __TEXT_COAL section %s (0x%08X bytes) to %p (logical addr 0x%llX)\n",
65 _options
.archs
->name(), CacheCoalescedText::SupportedSections
[index
],
66 cacheStringSection
.bufferSize
, cacheStringSection
.bufferAddr
, cacheStringSection
.bufferVMAddr
);
67 for (const auto& stringAndOffset
: cacheStringSection
.stringsToOffsets
)
68 ::memcpy(cacheStringSection
.bufferAddr
+ stringAndOffset
.second
, stringAndOffset
.first
.data(), stringAndOffset
.first
.size() + 1);
72 void CacheBuilder::adjustAllImagesForNewSegmentLocations()
74 __block
std::vector
<Diagnostics
> diags
;
75 diags
.resize(_sortedDylibs
.size());
77 // Note this cannot to be done in parallel because the LOH Tracker and aslr tracker are not thread safe
78 for (size_t index
= 0; index
!= _sortedDylibs
.size(); ++index
) {
79 const DylibInfo
& dylib
= _sortedDylibs
[index
];
80 adjustDylibSegments(dylib
, diags
[index
]);
82 for (const Diagnostics
& diag
: diags
) {
83 if ( diag
.hasError() ) {
84 _diagnostics
.error("%s", diag
.errorMessage().c_str());
91 CacheBuilder::ASLR_Tracker::~ASLR_Tracker()
93 if ( _bitmap
!= nullptr )
97 void CacheBuilder::ASLR_Tracker::setDataRegion(const void* rwRegionStart
, size_t rwRegionSize
)
99 _pageCount
= (unsigned)(rwRegionSize
+_pageSize
-1)/_pageSize
;
100 _regionStart
= (uint8_t*)rwRegionStart
;
101 _regionEnd
= (uint8_t*)rwRegionStart
+ rwRegionSize
;
102 _bitmap
= (bool*)calloc(_pageCount
*(_pageSize
/4)*sizeof(bool), 1);
105 void CacheBuilder::ASLR_Tracker::add(void* loc
)
109 uint8_t* p
= (uint8_t*)loc
;
110 assert(p
>= _regionStart
);
111 assert(p
< _regionEnd
);
112 _bitmap
[(p
-_regionStart
)/4] = true;
115 void CacheBuilder::ASLR_Tracker::remove(void* loc
)
119 uint8_t* p
= (uint8_t*)loc
;
120 assert(p
>= _regionStart
);
121 assert(p
< _regionEnd
);
122 _bitmap
[(p
-_regionStart
)/4] = false;
125 bool CacheBuilder::ASLR_Tracker::has(void* loc
)
129 uint8_t* p
= (uint8_t*)loc
;
130 assert(p
>= _regionStart
);
131 assert(p
< _regionEnd
);
132 return _bitmap
[(p
-_regionStart
)/4];
135 void CacheBuilder::ASLR_Tracker::setHigh8(void* p
, uint8_t high8
)
137 _high8Map
[p
] = high8
;
140 void CacheBuilder::ASLR_Tracker::setAuthData(void* p
, uint16_t diversity
, bool hasAddrDiv
, uint8_t key
)
142 _authDataMap
[p
] = {diversity
, hasAddrDiv
, key
};
145 void CacheBuilder::ASLR_Tracker::setRebaseTarget32(void*p
, uint32_t targetVMAddr
)
147 _rebaseTarget32
[p
] = targetVMAddr
;
150 void CacheBuilder::ASLR_Tracker::setRebaseTarget64(void*p
, uint64_t targetVMAddr
)
152 _rebaseTarget64
[p
] = targetVMAddr
;
155 bool CacheBuilder::ASLR_Tracker::hasHigh8(void* p
, uint8_t* highByte
)
157 auto pos
= _high8Map
.find(p
);
158 if ( pos
== _high8Map
.end() )
160 *highByte
= pos
->second
;
164 bool CacheBuilder::ASLR_Tracker::hasAuthData(void* p
, uint16_t* diversity
, bool* hasAddrDiv
, uint8_t* key
)
166 auto pos
= _authDataMap
.find(p
);
167 if ( pos
== _authDataMap
.end() )
169 *diversity
= pos
->second
.diversity
;
170 *hasAddrDiv
= pos
->second
.addrDiv
;
171 *key
= pos
->second
.key
;
175 bool CacheBuilder::ASLR_Tracker::hasRebaseTarget32(void* p
, uint32_t* vmAddr
)
177 auto pos
= _rebaseTarget32
.find(p
);
178 if ( pos
== _rebaseTarget32
.end() )
180 *vmAddr
= pos
->second
;
184 bool CacheBuilder::ASLR_Tracker::hasRebaseTarget64(void* p
, uint64_t* vmAddr
)
186 auto pos
= _rebaseTarget64
.find(p
);
187 if ( pos
== _rebaseTarget64
.end() )
189 *vmAddr
= pos
->second
;
193 //////////////////////////// DylibTextCoalescer ////////////////////////////////////
195 bool CacheBuilder::DylibTextCoalescer::sectionWasCoalesced(std::string_view sectionName
) const {
196 if (sectionName
.size() > 16)
197 sectionName
= sectionName
.substr(0, 16);
198 std::map
<std::string_view
, const DylibSectionOffsetToCacheSectionOffset
*> supportedSections
= {
199 { "__objc_classname", &objcClassNames
},
200 { "__objc_methname", &objcMethNames
},
201 { "__objc_methtype", &objcMethTypes
}
203 auto it
= supportedSections
.find(sectionName
);
204 if (it
== supportedSections
.end())
206 return !it
->second
->empty();
209 CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset
& CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view sectionName
) {
210 if (sectionName
.size() > 16)
211 sectionName
= sectionName
.substr(0, 16);
212 std::map
<std::string_view
, DylibSectionOffsetToCacheSectionOffset
*> supportedSections
= {
213 { "__objc_classname", &objcClassNames
},
214 { "__objc_methname", &objcMethNames
},
215 { "__objc_methtype", &objcMethTypes
}
217 auto it
= supportedSections
.find(sectionName
);
218 assert(it
!= supportedSections
.end());
222 const CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset
& CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view sectionName
) const {
223 if (sectionName
.size() > 16)
224 sectionName
= sectionName
.substr(0, 16);
225 std::map
<std::string_view
, const DylibSectionOffsetToCacheSectionOffset
*> supportedSections
= {
226 { "__objc_classname", &objcClassNames
},
227 { "__objc_methname", &objcMethNames
},
228 { "__objc_methtype", &objcMethTypes
}
230 auto it
= supportedSections
.find(sectionName
);
231 assert(it
!= supportedSections
.end());
235 //////////////////////////// CacheCoalescedText ////////////////////////////////////
236 const char* CacheBuilder::CacheCoalescedText::SupportedSections
[] = {
242 void CacheBuilder::CacheCoalescedText::parseCoalescableText(const dyld3::MachOAnalyzer
*ma
,
243 DylibTextCoalescer
& textCoalescer
) {
244 static const bool log
= false;
246 // We can only remove sections if we know we have split seg v2 to point to it
247 // Otherwise, a PC relative load in the __TEXT segment wouldn't know how to point to the new strings
248 // which are no longer in the same segment
249 uint32_t splitSegSize
= 0;
250 const void* splitSegStart
= ma
->getSplitSeg(splitSegSize
);
254 if ((*(const uint8_t*)splitSegStart
) != DYLD_CACHE_ADJ_V2_FORMAT
)
257 // We can only remove sections from the end of a segment, so cache them all and walk backwards.
258 __block
std::vector
<std::pair
<std::string
, dyld3::MachOAnalyzer::SectionInfo
>> textSectionInfos
;
259 ma
->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
§Info
, bool malformedSectionRange
, bool &stop
) {
260 if (strcmp(sectInfo
.segInfo
.segName
, "__TEXT") != 0)
262 assert(!malformedSectionRange
);
263 textSectionInfos
.push_back({ sectInfo
.sectName
, sectInfo
});
266 const std::set
<std::string_view
> supportedSections(std::begin(SupportedSections
), std::end(SupportedSections
));
267 int64_t slide
= ma
->getSlide();
269 for (auto sectionInfoIt
= textSectionInfos
.rbegin(); sectionInfoIt
!= textSectionInfos
.rend(); ++sectionInfoIt
) {
270 const std::string
& sectionName
= sectionInfoIt
->first
;
271 const dyld3::MachOAnalyzer::SectionInfo
& sectInfo
= sectionInfoIt
->second
;
273 // If we find a section we can't handle then stop here. Hopefully we coalesced some from the end.
274 if (supportedSections
.find(sectionName
) == supportedSections
.end())
277 StringSection
& cacheStringSection
= getSectionData(sectionName
);
279 DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset
& sectionStringData
= textCoalescer
.getSectionCoalescer(sectionName
);
281 // Walk the strings in this section
282 const uint8_t* content
= (uint8_t*)(sectInfo
.sectAddr
+ slide
);
283 const char* s
= (char*)content
;
284 const char* end
= s
+ sectInfo
.sectSize
;
286 std::string_view str
= s
;
287 auto itAndInserted
= cacheStringSection
.stringsToOffsets
.insert({ str
, cacheStringSection
.bufferSize
});
288 if (itAndInserted
.second
) {
289 // If we inserted the string then we need to include it in the total
290 cacheStringSection
.bufferSize
+= str
.size() + 1;
292 printf("Selector: %s -> %s\n", ma
->installName(), s
);
294 // Debugging only. If we didn't include the string then we saved that many bytes
295 cacheStringSection
.savedSpace
+= str
.size() + 1;
298 // Now keep track of this offset in our source dylib as pointing to this offset
299 uint32_t sourceSectionOffset
= (uint32_t)((uint64_t)s
- (uint64_t)content
);
300 uint32_t cacheSectionOffset
= itAndInserted
.first
->second
;
301 sectionStringData
[sourceSectionOffset
] = cacheSectionOffset
;
307 void CacheBuilder::CacheCoalescedText::clear() {
308 *this = CacheBuilder::CacheCoalescedText();
312 CacheBuilder::CacheCoalescedText::StringSection
& CacheBuilder::CacheCoalescedText::getSectionData(std::string_view sectionName
) {
313 if (sectionName
.size() > 16)
314 sectionName
= sectionName
.substr(0, 16);
315 std::map
<std::string_view
, StringSection
*> supportedSections
= {
316 { "__objc_classname", &objcClassNames
},
317 { "__objc_methname", &objcMethNames
},
318 { "__objc_methtype", &objcMethTypes
}
320 auto it
= supportedSections
.find(sectionName
);
321 assert(it
!= supportedSections
.end());
326 const CacheBuilder::CacheCoalescedText::StringSection
& CacheBuilder::CacheCoalescedText::getSectionData(std::string_view sectionName
) const {
327 if (sectionName
.size() > 16)
328 sectionName
= sectionName
.substr(0, 16);
329 std::map
<std::string_view
, const StringSection
*> supportedSections
= {
330 { "__objc_classname", &objcClassNames
},
331 { "__objc_methname", &objcMethNames
},
332 { "__objc_methtype", &objcMethTypes
}
334 auto it
= supportedSections
.find(sectionName
);
335 assert(it
!= supportedSections
.end());