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