dyld-750.5.tar.gz
[apple/dyld.git] / dyld3 / shared-cache / CacheBuilder.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 #include <assert.h>
26
27 #include "MachOFileAbstraction.hpp"
28 #include "DyldSharedCache.h"
29 #include "CacheBuilder.h"
30 #include "Diagnostics.h"
31
32
33 CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem)
34 : _options(options)
35 , _fileSystem(fileSystem)
36 , _fullAllocatedBuffer(0)
37 , _diagnostics(options.loggingPrefix, options.verbose)
38 , _allocatedBufferSize(0)
39 {
40 }
41
42
43 std::string CacheBuilder::errorMessage()
44 {
45 return _diagnostics.errorMessage();
46 }
47
48 void CacheBuilder::copyRawSegments()
49 {
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);
57 }
58 });
59
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);
69 });
70 }
71
72 void CacheBuilder::adjustAllImagesForNewSegmentLocations()
73 {
74 __block std::vector<Diagnostics> diags;
75 diags.resize(_sortedDylibs.size());
76
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]);
81 }
82 for (const Diagnostics& diag : diags) {
83 if ( diag.hasError() ) {
84 _diagnostics.error("%s", diag.errorMessage().c_str());
85 break;
86 }
87 }
88 }
89
90
91 CacheBuilder::ASLR_Tracker::~ASLR_Tracker()
92 {
93 if ( _bitmap != nullptr )
94 ::free(_bitmap);
95 }
96
97 void CacheBuilder::ASLR_Tracker::setDataRegion(const void* rwRegionStart, size_t rwRegionSize)
98 {
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);
103 }
104
105 void CacheBuilder::ASLR_Tracker::add(void* loc)
106 {
107 if (!_enabled)
108 return;
109 uint8_t* p = (uint8_t*)loc;
110 assert(p >= _regionStart);
111 assert(p < _regionEnd);
112 _bitmap[(p-_regionStart)/4] = true;
113 }
114
115 void CacheBuilder::ASLR_Tracker::remove(void* loc)
116 {
117 if (!_enabled)
118 return;
119 uint8_t* p = (uint8_t*)loc;
120 assert(p >= _regionStart);
121 assert(p < _regionEnd);
122 _bitmap[(p-_regionStart)/4] = false;
123 }
124
125 bool CacheBuilder::ASLR_Tracker::has(void* loc)
126 {
127 if (!_enabled)
128 return true;
129 uint8_t* p = (uint8_t*)loc;
130 assert(p >= _regionStart);
131 assert(p < _regionEnd);
132 return _bitmap[(p-_regionStart)/4];
133 }
134
135 void CacheBuilder::ASLR_Tracker::setHigh8(void* p, uint8_t high8)
136 {
137 _high8Map[p] = high8;
138 }
139
140 void CacheBuilder::ASLR_Tracker::setAuthData(void* p, uint16_t diversity, bool hasAddrDiv, uint8_t key)
141 {
142 _authDataMap[p] = {diversity, hasAddrDiv, key};
143 }
144
145 void CacheBuilder::ASLR_Tracker::setRebaseTarget32(void*p, uint32_t targetVMAddr)
146 {
147 _rebaseTarget32[p] = targetVMAddr;
148 }
149
150 void CacheBuilder::ASLR_Tracker::setRebaseTarget64(void*p, uint64_t targetVMAddr)
151 {
152 _rebaseTarget64[p] = targetVMAddr;
153 }
154
155 bool CacheBuilder::ASLR_Tracker::hasHigh8(void* p, uint8_t* highByte)
156 {
157 auto pos = _high8Map.find(p);
158 if ( pos == _high8Map.end() )
159 return false;
160 *highByte = pos->second;
161 return true;
162 }
163
164 bool CacheBuilder::ASLR_Tracker::hasAuthData(void* p, uint16_t* diversity, bool* hasAddrDiv, uint8_t* key)
165 {
166 auto pos = _authDataMap.find(p);
167 if ( pos == _authDataMap.end() )
168 return false;
169 *diversity = pos->second.diversity;
170 *hasAddrDiv = pos->second.addrDiv;
171 *key = pos->second.key;
172 return true;
173 }
174
175 bool CacheBuilder::ASLR_Tracker::hasRebaseTarget32(void* p, uint32_t* vmAddr)
176 {
177 auto pos = _rebaseTarget32.find(p);
178 if ( pos == _rebaseTarget32.end() )
179 return false;
180 *vmAddr = pos->second;
181 return true;
182 }
183
184 bool CacheBuilder::ASLR_Tracker::hasRebaseTarget64(void* p, uint64_t* vmAddr)
185 {
186 auto pos = _rebaseTarget64.find(p);
187 if ( pos == _rebaseTarget64.end() )
188 return false;
189 *vmAddr = pos->second;
190 return true;
191 }
192
193 //////////////////////////// DylibTextCoalescer ////////////////////////////////////
194
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 }
202 };
203 auto it = supportedSections.find(sectionName);
204 if (it == supportedSections.end())
205 return false;
206 return !it->second->empty();
207 }
208
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 }
216 };
217 auto it = supportedSections.find(sectionName);
218 assert(it != supportedSections.end());
219 return *it->second;
220 }
221
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 }
229 };
230 auto it = supportedSections.find(sectionName);
231 assert(it != supportedSections.end());
232 return *it->second;
233 }
234
235 //////////////////////////// CacheCoalescedText ////////////////////////////////////
236 const char* CacheBuilder::CacheCoalescedText::SupportedSections[] = {
237 "__objc_classname",
238 "__objc_methname",
239 "__objc_methtype",
240 };
241
242 void CacheBuilder::CacheCoalescedText::parseCoalescableText(const dyld3::MachOAnalyzer *ma,
243 DylibTextCoalescer& textCoalescer) {
244 static const bool log = false;
245
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);
251 if (!splitSegStart)
252 return;
253
254 if ((*(const uint8_t*)splitSegStart) != DYLD_CACHE_ADJ_V2_FORMAT)
255 return;
256
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 &sectInfo, bool malformedSectionRange, bool &stop) {
260 if (strcmp(sectInfo.segInfo.segName, "__TEXT") != 0)
261 return;
262 assert(!malformedSectionRange);
263 textSectionInfos.push_back({ sectInfo.sectName, sectInfo });
264 });
265
266 const std::set<std::string_view> supportedSections(std::begin(SupportedSections), std::end(SupportedSections));
267 int64_t slide = ma->getSlide();
268
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;
272
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())
275 break;
276
277 StringSection& cacheStringSection = getSectionData(sectionName);
278
279 DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& sectionStringData = textCoalescer.getSectionCoalescer(sectionName);
280
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;
285 while ( s < end ) {
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;
291 if (log)
292 printf("Selector: %s -> %s\n", ma->installName(), s);
293 } else {
294 // Debugging only. If we didn't include the string then we saved that many bytes
295 cacheStringSection.savedSpace += str.size() + 1;
296 }
297
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;
302 s += str.size() + 1;
303 }
304 }
305 }
306
307 void CacheBuilder::CacheCoalescedText::clear() {
308 *this = CacheBuilder::CacheCoalescedText();
309 }
310
311
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 }
319 };
320 auto it = supportedSections.find(sectionName);
321 assert(it != supportedSections.end());
322 return *it->second;
323 }
324
325
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 }
333 };
334 auto it = supportedSections.find(sectionName);
335 assert(it != supportedSections.end());
336 return *it->second;
337 }