]>
Commit | Line | Data |
---|---|---|
39a8cd10 A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
2fd3f4e8 | 3 | * Copyright (c) 2009-2012 Apple Inc. All rights reserved. |
39a8cd10 A |
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> | |
412ebb8e A |
26 | #include <stdio.h> |
27 | #include <Availability.h> | |
28 | ||
39a8cd10 A |
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 | ||
412ebb8e | 38 | |
39a8cd10 A |
39 | namespace dyld { |
40 | ||
412ebb8e | 41 | |
39a8cd10 A |
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> | |
2fd3f4e8 | 44 | const uint8_t* mappedAddress(const uint8_t* cache, const uint8_t* cacheEnd, uint64_t addr) |
39a8cd10 A |
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())) ) { | |
2fd3f4e8 A |
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; | |
39a8cd10 A |
56 | } |
57 | } | |
58 | return NULL; | |
59 | } | |
60 | ||
61 | // call the callback block on each segment in this image | |
62 | template <typename A> | |
2fd3f4e8 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)) | |
39a8cd10 A |
65 | { |
66 | typedef typename A::P P; | |
67 | typedef typename A::P::E E; | |
2fd3f4e8 A |
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; | |
39a8cd10 A |
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>)); | |
2fd3f4e8 A |
76 | if ( (machHeader+ mh->sizeofcmds()) > cacheEnd ) |
77 | return -1; | |
39a8cd10 A |
78 | const uint32_t cmd_count = mh->ncmds(); |
79 | const macho_load_command<P>* cmd = cmds; | |
2fd3f4e8 A |
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; | |
39a8cd10 A |
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; | |
412ebb8e A |
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 | |
2fd3f4e8 A |
103 | if ( (fileOffset+sizem) > (uint64_t)(cacheEnd-cache) ) |
104 | sizem = (cacheEnd-cache)-fileOffset; | |
412ebb8e | 105 | } |
2fd3f4e8 A |
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); | |
39a8cd10 A |
112 | } |
113 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
114 | } | |
2fd3f4e8 | 115 | return 0; |
39a8cd10 | 116 | } |
412ebb8e | 117 | |
39a8cd10 A |
118 | |
119 | // call walkSegments on each image in the cache | |
120 | template <typename A> | |
2fd3f4e8 | 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)) |
39a8cd10 | 122 | { |
2fd3f4e8 A |
123 | // Sanity check there is at least a header |
124 | if ( (size > 0) && (size < 0x7000) ) | |
125 | return -1; | |
412ebb8e A |
126 | typedef typename A::P::E E; |
127 | typedef typename A::P P; | |
2fd3f4e8 A |
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; | |
39a8cd10 A |
155 | for (uint32_t i=0; i < header->imagesCount(); ++i) { |
156 | const char* dylibPath = (char*)cache + dylibs[i].pathFileOffset(); | |
2fd3f4e8 A |
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; | |
39a8cd10 A |
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. | |
2fd3f4e8 A |
180 | extern 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)) { | |
39a8cd10 A |
182 | const uint8_t* cache = (uint8_t*)shared_cache_file; |
183 | if ( strcmp((char*)cache, "dyld_v1 i386") == 0 ) | |
2fd3f4e8 | 184 | return dyld::walkImages<x86>(cache, shared_cache_size, callback); |
39a8cd10 | 185 | else if ( strcmp((char*)cache, "dyld_v1 x86_64") == 0 ) |
2fd3f4e8 | 186 | return dyld::walkImages<x86_64>(cache, shared_cache_size, callback); |
412ebb8e | 187 | else if ( strcmp((char*)cache, "dyld_v1 armv5") == 0 ) |
2fd3f4e8 | 188 | return dyld::walkImages<arm>(cache, shared_cache_size, callback); |
412ebb8e | 189 | else if ( strcmp((char*)cache, "dyld_v1 armv6") == 0 ) |
2fd3f4e8 | 190 | return dyld::walkImages<arm>(cache, shared_cache_size, callback); |
412ebb8e | 191 | else if ( strcmp((char*)cache, "dyld_v1 armv7") == 0 ) |
2fd3f4e8 | 192 | return dyld::walkImages<arm>(cache, shared_cache_size, callback); |
832b6fce | 193 | else if ( strncmp((char*)cache, "dyld_v1 armv7", 14) == 0 ) |
2fd3f4e8 | 194 | return dyld::walkImages<arm>(cache, shared_cache_size, callback); |
39a8cd10 A |
195 | else |
196 | return -1; | |
197 | } | |
198 | ||
2fd3f4e8 A |
199 | |
200 | // implement old version by calling new version | |
201 | int 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 | ||
412ebb8e A |
208 | // implement non-block version by calling block version |
209 | int 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 | |
219 | int 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 | |
229 | int 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 |