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