]> git.saurik.com Git - apple/dyld.git/blob - launch-cache/dsc_extractor.cpp
dyld-360.14.tar.gz
[apple/dyld.git] / launch-cache / dsc_extractor.cpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2011 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 <stdlib.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
29 #include <string.h>
30 #include <fcntl.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <sys/mman.h>
34 #include <sys/syslimits.h>
35 #include <libkern/OSByteOrder.h>
36 #include <mach-o/fat.h>
37 #include <mach-o/arch.h>
38 #include <mach-o/loader.h>
39 #include <Availability.h>
40
41 #define NO_ULEB
42 #include "Architectures.hpp"
43 #include "MachOFileAbstraction.hpp"
44 #include "CacheFileAbstraction.hpp"
45
46 #include "dsc_iterator.h"
47 #include "dsc_extractor.h"
48 #include "MachOTrie.hpp"
49
50 #include <vector>
51 #include <set>
52 #include <map>
53 #include <unordered_map>
54 #include <algorithm>
55 #include <dispatch/dispatch.h>
56
57 struct seg_info
58 {
59 seg_info(const char* n, uint64_t o, uint64_t s)
60 : segName(n), offset(o), sizem(s) { }
61 const char* segName;
62 uint64_t offset;
63 uint64_t sizem;
64 };
65
66 class CStringHash {
67 public:
68 size_t operator()(const char* __s) const {
69 size_t __h = 0;
70 for ( ; *__s; ++__s)
71 __h = 5 * __h + *__s;
72 return __h;
73 };
74 };
75 class CStringEquals {
76 public:
77 bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
78 };
79 typedef std::unordered_map<const char*, std::vector<seg_info>, CStringHash, CStringEquals> NameToSegments;
80
81 // Filter to find individual symbol re-exports in trie
82 class NotReExportSymbol {
83 public:
84 NotReExportSymbol(const std::set<int> &rd) :_reexportDeps(rd) {}
85 bool operator()(const mach_o::trie::Entry &entry) const {
86 bool result = isSymbolReExport(entry);
87 if (result) {
88 // <rdar://problem/17671438> Xcode 6 leaks in dyld_shared_cache_extract_dylibs
89 ::free((void*)entry.name);
90 const_cast<mach_o::trie::Entry*>(&entry)->name = NULL;
91 }
92 return result;
93 }
94 private:
95 bool isSymbolReExport(const mach_o::trie::Entry &entry) const {
96 if ( (entry.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_REGULAR )
97 return true;
98 if ( (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) == 0 )
99 return true;
100 // If the symbol comes from a dylib that is re-exported, this is not an individual symbol re-export
101 if ( _reexportDeps.count(entry.other) != 0 )
102 return true;
103 return false;
104 }
105 const std::set<int> &_reexportDeps;
106 };
107
108
109 template <typename A>
110 int optimize_linkedit(macho_header<typename A::P>* mh, uint64_t textOffsetInCache, const void* mapped_cache, uint64_t* newSize)
111 {
112 typedef typename A::P P;
113 typedef typename A::P::E E;
114 typedef typename A::P::uint_t pint_t;
115
116 // update header flags
117 mh->set_flags(mh->flags() & 0x7FFFFFFF); // remove in-cache bit
118
119 // update load commands
120 uint64_t cumulativeFileSize = 0;
121 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
122 const uint32_t cmd_count = mh->ncmds();
123 const macho_load_command<P>* cmd = cmds;
124 macho_segment_command<P>* linkEditSegCmd = NULL;
125 macho_symtab_command<P>* symtab = NULL;
126 macho_dysymtab_command<P>* dynamicSymTab = NULL;
127 macho_linkedit_data_command<P>* functionStarts = NULL;
128 macho_linkedit_data_command<P>* dataInCode = NULL;
129 uint32_t exportsTrieOffset = 0;
130 uint32_t exportsTrieSize = 0;
131 std::set<int> reexportDeps;
132 int depIndex = 0;
133 for (uint32_t i = 0; i < cmd_count; ++i) {
134 switch ( cmd->cmd() ) {
135 case macho_segment_command<P>::CMD:
136 {
137 // update segment/section file offsets
138 macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
139 segCmd->set_fileoff(cumulativeFileSize);
140 macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
141 macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
142 for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
143 if ( sect->offset() != 0 )
144 sect->set_offset((uint32_t)(cumulativeFileSize+sect->addr()-segCmd->vmaddr()));
145 }
146 if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
147 linkEditSegCmd = segCmd;
148 }
149 cumulativeFileSize += segCmd->filesize();
150 }
151 break;
152 case LC_DYLD_INFO_ONLY:
153 {
154 // zero out all dyld info
155 macho_dyld_info_command<P>* dyldInfo = (macho_dyld_info_command<P>*)cmd;
156 exportsTrieOffset = dyldInfo->export_off();
157 exportsTrieSize = dyldInfo->export_size();
158 dyldInfo->set_rebase_off(0);
159 dyldInfo->set_rebase_size(0);
160 dyldInfo->set_bind_off(0);
161 dyldInfo->set_bind_size(0);
162 dyldInfo->set_weak_bind_off(0);
163 dyldInfo->set_weak_bind_size(0);
164 dyldInfo->set_lazy_bind_off(0);
165 dyldInfo->set_lazy_bind_size(0);
166 dyldInfo->set_export_off(0);
167 dyldInfo->set_export_size(0);
168 }
169 break;
170 case LC_SYMTAB:
171 symtab = (macho_symtab_command<P>*)cmd;
172 break;
173 case LC_DYSYMTAB:
174 dynamicSymTab = (macho_dysymtab_command<P>*)cmd;
175 break;
176 case LC_FUNCTION_STARTS:
177 functionStarts = (macho_linkedit_data_command<P>*)cmd;
178 break;
179 case LC_DATA_IN_CODE:
180 dataInCode = (macho_linkedit_data_command<P>*)cmd;
181 break;
182 case LC_LOAD_DYLIB:
183 case LC_LOAD_WEAK_DYLIB:
184 case LC_REEXPORT_DYLIB:
185 case LC_LOAD_UPWARD_DYLIB:
186 ++depIndex;
187 if ( cmd->cmd() == LC_REEXPORT_DYLIB ) {
188 reexportDeps.insert(depIndex);
189 }
190 break;
191 }
192 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
193 }
194
195 // rebuild symbol table
196 if ( linkEditSegCmd == NULL ) {
197 fprintf(stderr, "__LINKEDIT not found\n");
198 return -1;
199 }
200 if ( symtab == NULL ) {
201 fprintf(stderr, "LC_SYMTAB not found\n");
202 return -1;
203 }
204 if ( dynamicSymTab == NULL ) {
205 fprintf(stderr, "LC_DYSYMTAB not found\n");
206 return -1;
207 }
208
209 const uint64_t newFunctionStartsOffset = linkEditSegCmd->fileoff();
210 uint32_t functionStartsSize = 0;
211 if ( functionStarts != NULL ) {
212 // copy function starts from original cache file to new mapped dylib file
213 functionStartsSize = functionStarts->datasize();
214 memcpy((char*)mh + newFunctionStartsOffset, (char*)mapped_cache + functionStarts->dataoff(), functionStartsSize);
215 }
216 const uint64_t newDataInCodeOffset = (newFunctionStartsOffset + functionStartsSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align
217 uint32_t dataInCodeSize = 0;
218 if ( dataInCode != NULL ) {
219 // copy data-in-code info from original cache file to new mapped dylib file
220 dataInCodeSize = dataInCode->datasize();
221 memcpy((char*)mh + newDataInCodeOffset, (char*)mapped_cache + dataInCode->dataoff(), dataInCodeSize);
222 }
223
224 std::vector<mach_o::trie::Entry> exports;
225 if ( exportsTrieSize != 0 ) {
226 const uint8_t* exportsStart = ((uint8_t*)mapped_cache) + exportsTrieOffset;
227 const uint8_t* exportsEnd = &exportsStart[exportsTrieSize];
228 mach_o::trie::parseTrie(exportsStart, exportsEnd, exports);
229 exports.erase(std::remove_if(exports.begin(), exports.end(), NotReExportSymbol(reexportDeps)), exports.end());
230 }
231
232 // look for local symbol info in unmapped part of shared cache
233 dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)mapped_cache;
234 macho_nlist<P>* localNlists = NULL;
235 uint32_t localNlistCount = 0;
236 const char* localStrings = NULL;
237 const char* localStringsEnd = NULL;
238 if ( header->mappingOffset() > offsetof(dyld_cache_header,localSymbolsSize) ) {
239 dyldCacheLocalSymbolsInfo<E>* localInfo = (dyldCacheLocalSymbolsInfo<E>*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset());
240 dyldCacheLocalSymbolEntry<E>* entries = (dyldCacheLocalSymbolEntry<E>*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset() + localInfo->entriesOffset());
241 macho_nlist<P>* allLocalNlists = (macho_nlist<P>*)(((uint8_t*)localInfo) + localInfo->nlistOffset());
242 const uint32_t entriesCount = localInfo->entriesCount();
243 for (uint32_t i=0; i < entriesCount; ++i) {
244 if ( entries[i].dylibOffset() == textOffsetInCache ) {
245 uint32_t localNlistStart = entries[i].nlistStartIndex();
246 localNlistCount = entries[i].nlistCount();
247 localNlists = &allLocalNlists[localNlistStart];
248 localStrings = ((char*)localInfo) + localInfo->stringsOffset();
249 localStringsEnd = &localStrings[localInfo->stringsSize()];
250 break;
251 }
252 }
253 }
254 // compute number of symbols in new symbol table
255 const macho_nlist<P>* const mergedSymTabStart = (macho_nlist<P>*)(((uint8_t*)mapped_cache) + symtab->symoff());
256 const macho_nlist<P>* const mergedSymTabend = &mergedSymTabStart[symtab->nsyms()];
257 uint32_t newSymCount = symtab->nsyms();
258 if ( localNlists != NULL ) {
259 newSymCount = localNlistCount;
260 for (const macho_nlist<P>* s = mergedSymTabStart; s != mergedSymTabend; ++s) {
261 // skip any locals in cache
262 if ( (s->n_type() & (N_TYPE|N_EXT)) == N_SECT )
263 continue;
264 ++newSymCount;
265 }
266 }
267
268 // add room for N_INDR symbols for re-exported symbols
269 newSymCount += exports.size();
270
271 // copy symbol entries and strings from original cache file to new mapped dylib file
272 const uint64_t newSymTabOffset = (newDataInCodeOffset + dataInCodeSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align
273 const uint64_t newIndSymTabOffset = newSymTabOffset + newSymCount*sizeof(macho_nlist<P>);
274 const uint64_t newStringPoolOffset = newIndSymTabOffset + dynamicSymTab->nindirectsyms()*sizeof(uint32_t);
275 macho_nlist<P>* const newSymTabStart = (macho_nlist<P>*)(((uint8_t*)mh) + newSymTabOffset);
276 char* const newStringPoolStart = (char*)mh + newStringPoolOffset;
277 const uint32_t* mergedIndSymTab = (uint32_t*)((char*)mapped_cache + dynamicSymTab->indirectsymoff());
278 const char* mergedStringPoolStart = (char*)mapped_cache + symtab->stroff();
279 const char* mergedStringPoolEnd = &mergedStringPoolStart[symtab->strsize()];
280 macho_nlist<P>* t = newSymTabStart;
281 int poolOffset = 0;
282 uint32_t symbolsCopied = 0;
283 newStringPoolStart[poolOffset++] = '\0'; // first pool entry is always empty string
284 for (const macho_nlist<P>* s = mergedSymTabStart; s != mergedSymTabend; ++s) {
285 // if we have better local symbol info, skip any locals here
286 if ( (localNlists != NULL) && ((s->n_type() & (N_TYPE|N_EXT)) == N_SECT) )
287 continue;
288 *t = *s;
289 t->set_n_strx(poolOffset);
290 const char* symName = &mergedStringPoolStart[s->n_strx()];
291 if ( symName > mergedStringPoolEnd )
292 symName = "<corrupt symbol name>";
293 strcpy(&newStringPoolStart[poolOffset], symName);
294 poolOffset += (strlen(symName) + 1);
295 ++t;
296 ++symbolsCopied;
297 }
298 // <rdar://problem/16529213> recreate N_INDR symbols in extracted dylibs for debugger
299 for (std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) {
300 strcpy(&newStringPoolStart[poolOffset], it->name);
301 t->set_n_strx(poolOffset);
302 poolOffset += (strlen(it->name) + 1);
303 t->set_n_type(N_INDR | N_EXT);
304 t->set_n_sect(0);
305 t->set_n_desc(0);
306 const char* importName = it->importName;
307 if ( *importName == '\0' )
308 importName = it->name;
309 strcpy(&newStringPoolStart[poolOffset], importName);
310 t->set_n_value(poolOffset);
311 poolOffset += (strlen(importName) + 1);
312 ++t;
313 ++symbolsCopied;
314 }
315 if ( localNlists != NULL ) {
316 // update load command to reflect new count of locals
317 dynamicSymTab->set_ilocalsym(symbolsCopied);
318 dynamicSymTab->set_nlocalsym(localNlistCount);
319 // copy local symbols
320 for (uint32_t i=0; i < localNlistCount; ++i) {
321 const char* localName = &localStrings[localNlists[i].n_strx()];
322 if ( localName > localStringsEnd )
323 localName = "<corrupt local symbol name>";
324 *t = localNlists[i];
325 t->set_n_strx(poolOffset);
326 strcpy(&newStringPoolStart[poolOffset], localName);
327 poolOffset += (strlen(localName) + 1);
328 ++t;
329 ++symbolsCopied;
330 }
331 }
332
333 if ( newSymCount != symbolsCopied ) {
334 fprintf(stderr, "symbol count miscalculation\n");
335 return -1;
336 }
337
338 // pointer align string pool size
339 while ( (poolOffset % sizeof(pint_t)) != 0 )
340 ++poolOffset;
341 // copy indirect symbol table
342 uint32_t* newIndSymTab = (uint32_t*)((char*)mh + newIndSymTabOffset);
343 memcpy(newIndSymTab, mergedIndSymTab, dynamicSymTab->nindirectsyms()*sizeof(uint32_t));
344
345 // update load commands
346 if ( functionStarts != NULL ) {
347 functionStarts->set_dataoff((uint32_t)newFunctionStartsOffset);
348 functionStarts->set_datasize(functionStartsSize);
349 }
350 if ( dataInCode != NULL ) {
351 dataInCode->set_dataoff((uint32_t)newDataInCodeOffset);
352 dataInCode->set_datasize(dataInCodeSize);
353 }
354 symtab->set_nsyms(symbolsCopied);
355 symtab->set_symoff((uint32_t)newSymTabOffset);
356 symtab->set_stroff((uint32_t)newStringPoolOffset);
357 symtab->set_strsize(poolOffset);
358 dynamicSymTab->set_extreloff(0);
359 dynamicSymTab->set_nextrel(0);
360 dynamicSymTab->set_locreloff(0);
361 dynamicSymTab->set_nlocrel(0);
362 dynamicSymTab->set_indirectsymoff((uint32_t)newIndSymTabOffset);
363 linkEditSegCmd->set_filesize(symtab->stroff()+symtab->strsize() - linkEditSegCmd->fileoff());
364 linkEditSegCmd->set_vmsize( (linkEditSegCmd->filesize()+4095) & (-4096) );
365
366 // return new size
367 *newSize = (symtab->stroff()+symtab->strsize()+4095) & (-4096);
368
369 // <rdar://problem/17671438> Xcode 6 leaks in dyld_shared_cache_extract_dylibs
370 for (std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) {
371 ::free((void*)(it->name));
372 }
373
374
375 return 0;
376 }
377
378
379
380 static void make_dirs(const char* file_path)
381 {
382 //printf("make_dirs(%s)\n", file_path);
383 char dirs[strlen(file_path)+1];
384 strcpy(dirs, file_path);
385 char* lastSlash = strrchr(dirs, '/');
386 if ( lastSlash == NULL )
387 return;
388 lastSlash[1] = '\0';
389 struct stat stat_buf;
390 if ( stat(dirs, &stat_buf) != 0 ) {
391 char* afterSlash = &dirs[1];
392 char* slash;
393 while ( (slash = strchr(afterSlash, '/')) != NULL ) {
394 *slash = '\0';
395 ::mkdir(dirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
396 //printf("mkdir(%s)\n", dirs);
397 *slash = '/';
398 afterSlash = slash+1;
399 }
400 }
401 }
402
403
404
405 template <typename A>
406 size_t dylib_maker(const void* mapped_cache, std::vector<uint8_t> &dylib_data, const std::vector<seg_info>& segments) {
407 typedef typename A::P P;
408
409 size_t additionalSize = 0;
410 for(std::vector<seg_info>::const_iterator it=segments.begin(); it != segments.end(); ++it) {
411 additionalSize += it->sizem;
412 }
413
414 dylib_data.reserve(dylib_data.size() + additionalSize);
415
416 uint32_t nfat_archs = 0;
417 uint32_t offsetInFatFile = 4096;
418 uint8_t *base_ptr = &dylib_data.front();
419
420 #define FH reinterpret_cast<fat_header*>(base_ptr)
421 #define FA reinterpret_cast<fat_arch*>(base_ptr + (8 + (nfat_archs - 1) * sizeof(fat_arch)))
422
423 if(dylib_data.size() >= 4096 && OSSwapBigToHostInt32(FH->magic) == FAT_MAGIC) {
424 // have fat header, append new arch to end
425 nfat_archs = OSSwapBigToHostInt32(FH->nfat_arch);
426 offsetInFatFile = OSSwapBigToHostInt32(FA->offset) + OSSwapBigToHostInt32(FA->size);
427 }
428
429 dylib_data.resize(offsetInFatFile);
430 base_ptr = &dylib_data.front();
431
432 FH->magic = OSSwapHostToBigInt32(FAT_MAGIC);
433 FH->nfat_arch = OSSwapHostToBigInt32(++nfat_archs);
434
435 FA->cputype = 0; // filled in later
436 FA->cpusubtype = 0; // filled in later
437 FA->offset = OSSwapHostToBigInt32(offsetInFatFile);
438 FA->size = 0; // filled in later
439 FA->align = OSSwapHostToBigInt32(12);
440
441 // Write regular segments into the buffer
442 uint64_t totalSize = 0;
443 uint64_t textOffsetInCache = 0;
444 for( std::vector<seg_info>::const_iterator it=segments.begin(); it != segments.end(); ++it) {
445
446 if(strcmp(it->segName, "__TEXT") == 0 ) {
447 textOffsetInCache = it->offset;
448 const macho_header<P> *textMH = reinterpret_cast<macho_header<P>*>((uint8_t*)mapped_cache+textOffsetInCache);
449 FA->cputype = OSSwapHostToBigInt32(textMH->cputype());
450 FA->cpusubtype = OSSwapHostToBigInt32(textMH->cpusubtype());
451
452 // if this cputype/subtype already exist in fat header, then return immediately
453 for(uint32_t i=0; i < nfat_archs-1; ++i) {
454 fat_arch *afa = reinterpret_cast<fat_arch*>(base_ptr+8)+i;
455
456 if( afa->cputype == FA->cputype
457 && afa->cpusubtype == FA->cpusubtype) {
458 //fprintf(stderr, "arch already exists in fat dylib\n");
459 dylib_data.resize(offsetInFatFile);
460 return offsetInFatFile;
461 }
462 }
463 }
464
465 //printf("segName=%s, offset=0x%llX, size=0x%0llX\n", it->segName, it->offset, it->sizem);
466 std::copy(((uint8_t*)mapped_cache)+it->offset, ((uint8_t*)mapped_cache)+it->offset+it->sizem, std::back_inserter(dylib_data));
467 base_ptr = &dylib_data.front();
468 totalSize += it->sizem;
469 }
470
471 FA->size = OSSwapHostToBigInt32(totalSize);
472
473 // optimize linkedit
474 uint64_t newSize = dylib_data.size();
475 optimize_linkedit<A>(((macho_header<P>*)(base_ptr+offsetInFatFile)), textOffsetInCache, mapped_cache, &newSize);
476
477 // update fat header with new file size
478 dylib_data.resize(offsetInFatFile+newSize);
479 base_ptr = &dylib_data.front();
480 FA->size = OSSwapHostToBigInt32(newSize);
481 #undef FH
482 #undef FA
483 return offsetInFatFile;
484 }
485
486
487 int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path, const char* extraction_root_path,
488 void (^progress)(unsigned current, unsigned total))
489 {
490 struct stat statbuf;
491 if (stat(shared_cache_file_path, &statbuf)) {
492 fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", shared_cache_file_path);
493 return -1;
494 }
495
496 int cache_fd = open(shared_cache_file_path, O_RDONLY);
497 if (cache_fd < 0) {
498 fprintf(stderr, "Error: failed to open shared cache file at %s\n", shared_cache_file_path);
499 return -1;
500 }
501
502 void* mapped_cache = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0);
503 if (mapped_cache == MAP_FAILED) {
504 fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", shared_cache_file_path, errno);
505 return -1;
506 }
507
508 close(cache_fd);
509
510 // instantiate arch specific dylib maker
511 size_t (*dylib_create_func)(const void*, std::vector<uint8_t>&, const std::vector<seg_info>&) = NULL;
512 if ( strcmp((char*)mapped_cache, "dyld_v1 i386") == 0 )
513 dylib_create_func = dylib_maker<x86>;
514 else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64") == 0 )
515 dylib_create_func = dylib_maker<x86_64>;
516 else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64h") == 0 )
517 dylib_create_func = dylib_maker<x86_64>;
518 else if ( strcmp((char*)mapped_cache, "dyld_v1 armv5") == 0 )
519 dylib_create_func = dylib_maker<arm>;
520 else if ( strcmp((char*)mapped_cache, "dyld_v1 armv6") == 0 )
521 dylib_create_func = dylib_maker<arm>;
522 else if ( strcmp((char*)mapped_cache, "dyld_v1 armv7") == 0 )
523 dylib_create_func = dylib_maker<arm>;
524 else if ( strncmp((char*)mapped_cache, "dyld_v1 armv7", 14) == 0 )
525 dylib_create_func = dylib_maker<arm>;
526 else if ( strcmp((char*)mapped_cache, "dyld_v1 arm64") == 0 )
527 dylib_create_func = dylib_maker<arm64>;
528 else {
529 fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n");
530 munmap(mapped_cache, statbuf.st_size);
531 return -1;
532 }
533
534 // iterate through all images in cache and build map of dylibs and segments
535 __block NameToSegments map;
536 __block int result = dyld_shared_cache_iterate(mapped_cache, (uint32_t)statbuf.st_size, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) {
537 map[dylibInfo->path].push_back(seg_info(segInfo->name, segInfo->fileOffset, segInfo->fileSize));
538 });
539
540 if(result != 0) {
541 fprintf(stderr, "Error: dyld_shared_cache_iterate_segments_with_slide failed.\n");
542 munmap(mapped_cache, statbuf.st_size);
543 return result;
544 }
545
546 // for each dylib instantiate a dylib file
547 dispatch_group_t group = dispatch_group_create();
548 dispatch_semaphore_t sema = dispatch_semaphore_create(2);
549 dispatch_queue_t process_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
550 dispatch_queue_t writer_queue = dispatch_queue_create("dyld writer queue", 0);
551
552 __block unsigned count = 0;
553
554 for ( NameToSegments::iterator it = map.begin(); it != map.end(); ++it) {
555 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
556 dispatch_group_async(group, process_queue, ^{
557
558 char dylib_path[PATH_MAX];
559 strcpy(dylib_path, extraction_root_path);
560 strcat(dylib_path, "/");
561 strcat(dylib_path, it->first);
562
563 //printf("%s with %lu segments\n", dylib_path, it->second.size());
564 // make sure all directories in this path exist
565 make_dirs(dylib_path);
566
567 // open file, create if does not already exist
568 int fd = ::open(dylib_path, O_CREAT | O_EXLOCK | O_RDWR, 0644);
569 if ( fd == -1 ) {
570 fprintf(stderr, "can't open or create dylib file %s, errnor=%d\n", dylib_path, errno);
571 result = -1;
572 return;
573 }
574
575 struct stat statbuf;
576 if (fstat(fd, &statbuf)) {
577 fprintf(stderr, "Error: stat failed for dyld file %s, errnor=%d\n", dylib_path, errno);
578 close(fd);
579 result = -1;
580 return;
581 }
582
583 std::vector<uint8_t> *vec = new std::vector<uint8_t>(statbuf.st_size);
584 if(pread(fd, &vec->front(), vec->size(), 0) != (long)vec->size()) {
585 fprintf(stderr, "can't read dylib file %s, errnor=%d\n", dylib_path, errno);
586 close(fd);
587 result = -1;
588 return;
589 }
590
591 const size_t offset = dylib_create_func(mapped_cache, *vec, it->second);
592
593 dispatch_group_async(group, writer_queue, ^{
594 progress(count++, (unsigned)map.size());
595
596 if(offset != vec->size()) {
597 //Write out the first page, and everything after offset
598 if( pwrite(fd, &vec->front(), 4096, 0) == -1
599 || pwrite(fd, &vec->front() + offset, vec->size() - offset, offset) == -1) {
600 fprintf(stderr, "error writing, errnor=%d\n", errno);
601 result = -1;
602 }
603 }
604
605 delete vec;
606 close(fd);
607 dispatch_semaphore_signal(sema);
608 });
609 });
610 }
611
612 dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
613 dispatch_release(group);
614 dispatch_release(writer_queue);
615
616 munmap(mapped_cache, statbuf.st_size);
617 return result;
618 }
619
620
621
622 int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const char* extraction_root_path)
623 {
624 return dyld_shared_cache_extract_dylibs_progress(shared_cache_file_path, extraction_root_path,
625 ^(unsigned , unsigned) {} );
626 }
627
628
629 #if 0
630 // test program
631 #include <stdio.h>
632 #include <stddef.h>
633 #include <dlfcn.h>
634
635
636 typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path,
637 void (^progress)(unsigned current, unsigned total));
638
639 int main(int argc, const char* argv[])
640 {
641 if ( argc != 3 ) {
642 fprintf(stderr, "usage: dsc_extractor <path-to-cache-file> <path-to-device-dir>\n");
643 return 1;
644 }
645
646 //void* handle = dlopen("/Volumes/my/src/dyld/build/Debug/dsc_extractor.bundle", RTLD_LAZY);
647 void* handle = dlopen("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/lib/dsc_extractor.bundle", RTLD_LAZY);
648 if ( handle == NULL ) {
649 fprintf(stderr, "dsc_extractor.bundle could not be loaded\n");
650 return 1;
651 }
652
653 extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress");
654 if ( proc == NULL ) {
655 fprintf(stderr, "dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n");
656 return 1;
657 }
658
659 int result = (*proc)(argv[1], argv[2], ^(unsigned c, unsigned total) { printf("%d/%d\n", c, total); } );
660 fprintf(stderr, "dyld_shared_cache_extract_dylibs_progress() => %d\n", result);
661 return 0;
662 }
663
664
665 #endif
666
667
668
669