1/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 2 * 3 * Copyright (c) 2009-2012 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 <Availability.h> 28 29 30#include "dsc_iterator.h" 31#include "dyld_cache_format.h" 32#define NO_ULEB 33#include "Architectures.hpp" 34#include "MachOFileAbstraction.hpp" 35#include "CacheFileAbstraction.hpp" 36 37 38 39namespace dyld { 40 41 42 // convert an address in the shared region where the cache would normally be mapped, into an address where the cache is currently mapped 43 template <typename E> 44 const uint8_t* mappedAddress(const uint8_t* cache, const uint8_t* cacheEnd, uint64_t addr) 45 { 46 const dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)cache; 47 const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&cache[header->mappingOffset()]; 48 for (uint32_t i=0; i < header->mappingCount(); ++i) { 49 if ( (mappings[i].address() <= addr) && (addr < (mappings[i].address() + mappings[i].size())) ) { 50 uint32_t cacheOffset = mappings[i].file_offset() + addr - mappings[i].address(); 51 const uint8_t* result = &cache[cacheOffset]; 52 if ( result < cacheEnd ) 53 return result; 54 else 55 return NULL; 56 } 57 } 58 return NULL; 59 } 60 61 // call the callback block on each segment in this image 62 template <typename A> 63 int walkSegments(const uint8_t* cache, const uint8_t* cacheEnd, const uint8_t* firstSeg, const char* dylibPath, const uint8_t* machHeader, 64 void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) 65 { 66 typedef typename A::P P; 67 typedef typename A::P::E E; 68 dyld_shared_cache_dylib_info dylibInfo; 69 dyld_shared_cache_segment_info segInfo; 70 dylibInfo.version = 1; 71 dylibInfo.isAlias = (dylibPath < (char*)firstSeg); // paths for aliases are store between cache header and first segment 72 dylibInfo.machHeader = machHeader; 73 dylibInfo.path = dylibPath; 74 const macho_header<P>* mh = (const macho_header<P>*)machHeader; 75 const macho_load_command<P>* const cmds = (macho_load_command<P>*)(machHeader + sizeof(macho_header<P>)); 76 if ( (machHeader+ mh->sizeofcmds()) > cacheEnd ) 77 return -1; 78 const uint32_t cmd_count = mh->ncmds(); 79 const macho_load_command<P>* cmd = cmds; 80 // scan for LC_UUID 81 dylibInfo.uuid = NULL; 82 for (uint32_t i = 0; i < cmd_count; ++i) { 83 if ( cmd->cmd() == LC_UUID ) { 84 const uuid_command* uc = (const uuid_command*)cmd; 85 dylibInfo.uuid = &uc->uuid; 86 break; 87 } 88 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); 89 } 90 // callback for each LC_SEGMENT 91 cmd = cmds; 92 for (uint32_t i = 0; i < cmd_count; ++i) { 93 if ( cmd->cmd() == macho_segment_command<P>::CMD ) { 94 macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd; 95 uint64_t fileOffset = segCmd->fileoff(); 96 // work around until <rdar://problem/7022345> is fixed 97 if ( fileOffset == 0 ) { 98 fileOffset = (machHeader - cache); 99 } 100 uint64_t sizem = segCmd->vmsize(); 101 if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { 102 // clip LINKEDIT size if bigger than cache file 103 if ( (fileOffset+sizem) > (uint64_t)(cacheEnd-cache) ) 104 sizem = (cacheEnd-cache)-fileOffset; 105 } 106 segInfo.version = 1; 107 segInfo.name = segCmd->segname(); 108 segInfo.fileOffset = fileOffset; 109 segInfo.fileSize = sizem; 110 segInfo.address = segCmd->vmaddr(); 111 callback(&dylibInfo, &segInfo); 112 } 113 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); 114 } 115 return 0; 116 } 117 118 119 // call walkSegments on each image in the cache 120 template <typename A> 121 int walkImages(const uint8_t* cache, uint32_t size, void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) 122 { 123 // Sanity check there is at least a header 124 if ( (size > 0) && (size < 0x7000) ) 125 return -1; 126 typedef typename A::P::E E; 127 typedef typename A::P P; 128 const dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)cache; 129 const dyldCacheImageInfo<E>* dylibs = (dyldCacheImageInfo<E>*)&cache[header->imagesOffset()]; 130 const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&cache[header->mappingOffset()]; 131 uint64_t greatestMappingOffset = 0; 132 for (uint32_t i=0; i < header->mappingCount(); ++i) { 133 if ( (size != 0) && (mappings[i].file_offset() > size) ) 134 return -1; 135 uint64_t endOffset = mappings[i].file_offset()+mappings[i].size(); 136 if ( (size != 0) && (endOffset > size) ) 137 return -1; 138 if ( endOffset > greatestMappingOffset ) 139 greatestMappingOffset = endOffset; 140 } 141 const uint8_t* cacheEnd = &cache[size]; 142 if ( size == 0 ) { 143 // Zero size means old API is being used, assume all mapped 144 cacheEnd = &cache[greatestMappingOffset]; 145 } 146 else { 147 // verifiy mappings are not bigger than size 148 if ( size < greatestMappingOffset ) 149 return -1; 150 } 151 // verify all image infos are mapped 152 if ( (const uint8_t*)&dylibs[header->imagesCount()] > cacheEnd ) 153 return -1; 154 const uint8_t* firstSeg = NULL; 155 for (uint32_t i=0; i < header->imagesCount(); ++i) { 156 const char* dylibPath = (char*)cache + dylibs[i].pathFileOffset(); 157 if ( (const uint8_t*)dylibPath > cacheEnd ) 158 return -1; 159 const uint8_t* machHeader = mappedAddress<E>(cache, cacheEnd, dylibs[i].address()); 160 if ( machHeader == NULL ) 161 return -1; 162 if ( machHeader > cacheEnd ) 163 return -1; 164 if ( firstSeg == NULL ) 165 firstSeg = machHeader; 166 int result = walkSegments<A>(cache, cacheEnd, firstSeg, dylibPath, machHeader, callback); 167 if ( result != 0 ) 168 return result; 169 } 170 return 0; 171 } 172 173} 174 175 176// Given a pointer to an in-memory copy of a dyld shared cache file, 177// this routine will call the callback block once for each segment 178// in each dylib in the shared cache file. 179// Returns -1 if there was an error, otherwise 0. 180extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, 181 void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) { 182 const uint8_t* cache = (uint8_t*)shared_cache_file; 183 if ( strcmp((char*)cache, "dyld_v1 i386") == 0 ) 184 return dyld::walkImages<x86>(cache, shared_cache_size, callback); 185 else if ( strcmp((char*)cache, "dyld_v1 x86_64") == 0 ) 186 return dyld::walkImages<x86_64>(cache, shared_cache_size, callback); 187 else if ( strcmp((char*)cache, "dyld_v1 armv5") == 0 ) 188 return dyld::walkImages<arm>(cache, shared_cache_size, callback); 189 else if ( strcmp((char*)cache, "dyld_v1 armv6") == 0 ) 190 return dyld::walkImages<arm>(cache, shared_cache_size, callback); 191 else if ( strcmp((char*)cache, "dyld_v1 armv7") == 0 ) 192 return dyld::walkImages<arm>(cache, shared_cache_size, callback); 193 else if ( strncmp((char*)cache, "dyld_v1 armv7", 14) == 0 ) 194 return dyld::walkImages<arm>(cache, shared_cache_size, callback); 195 else 196 return -1; 197} 198 199 200// implement old version by calling new version 201int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) 202{ 203 return dyld_shared_cache_iterate(shared_cache_file, 0, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) { 204 callback(dylibInfo->path, segInfo->name, segInfo->fileOffset, segInfo->fileSize, segInfo->address, 0); 205 }); 206} 207 208// implement non-block version by calling block version 209int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t func, void* userData) 210{ 211 return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName, 212 uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) { 213 (*func)(dylibName, segName, offset, size, mappedddress, slide, userData); 214 }); 215} 216 217 218// implement non-slide version by wrapping slide version in block 219int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback) 220{ 221 dyld_shared_cache_iterator_slide_t wrapper_cb = ^(const char* dylibName, const char* segName, uint64_t offset, 222 uint64_t size, uint64_t mappedddress, uint64_t slide) { 223 callback(dylibName, segName, offset, size, mappedddress); 224 }; 225 return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, wrapper_cb); 226} 227 228// implement non-slide,non-block version by wrapping slide version in block 229int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t func, void* userData) 230{ 231 return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName, 232 uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) { 233 (*func)(dylibName, segName, offset, size, mappedddress, userData); 234 }); 235} 236 237