]> git.saurik.com Git - apple/dyld.git/blob - dyld3/shared-cache/dyld_closure_util.cpp
dyld-851.27.tar.gz
[apple/dyld.git] / dyld3 / shared-cache / dyld_closure_util.cpp
1 /*
2 * Copyright (c) 2017 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <string.h>
29 #include <dlfcn.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 <mach-o/arch.h>
36 #include <mach-o/loader.h>
37 #include <mach-o/dyld_priv.h>
38 #include <bootstrap.h>
39 #include <mach/mach.h>
40 #include <dispatch/dispatch.h>
41
42 #include <map>
43 #include <vector>
44
45 #include "DyldSharedCache.h"
46 #include "FileUtils.h"
47 #include "StringUtils.h"
48 #include "ClosureBuilder.h"
49 #include "ClosurePrinter.h"
50 #include "ClosureFileSystemPhysical.h"
51 #include "RootsChecker.h"
52
53 using dyld3::closure::ImageArray;
54 using dyld3::closure::Image;
55 using dyld3::closure::ImageNum;
56 using dyld3::closure::ClosureBuilder;
57 using dyld3::closure::LaunchClosure;
58 using dyld3::closure::DlopenClosure;
59 using dyld3::closure::PathOverrides;
60 using dyld3::Array;
61
62
63 // mmap() an shared cache file read/only but laid out like it would be at runtime
64 static const DyldSharedCache* mapCacheFile(const char* path)
65 {
66 struct stat statbuf;
67 if ( ::stat(path, &statbuf) ) {
68 fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", path);
69 return nullptr;
70 }
71
72 int cache_fd = ::open(path, O_RDONLY);
73 if (cache_fd < 0) {
74 fprintf(stderr, "Error: failed to open shared cache file at %s\n", path);
75 return nullptr;
76 }
77
78 uint8_t firstPage[4096];
79 if ( ::pread(cache_fd, firstPage, 4096, 0) != 4096 ) {
80 fprintf(stderr, "Error: failed to read shared cache file at %s\n", path);
81 return nullptr;
82 }
83 const dyld_cache_header* header = (dyld_cache_header*)firstPage;
84 const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(firstPage + header->mappingOffset);
85 const dyld_cache_mapping_info* lastMapping = &mappings[header->mappingCount - 1];
86
87 size_t vmSize = (size_t)(lastMapping->address + lastMapping->size - mappings[0].address);
88 vm_address_t result;
89 kern_return_t r = ::vm_allocate(mach_task_self(), &result, vmSize, VM_FLAGS_ANYWHERE);
90 if ( r != KERN_SUCCESS ) {
91 fprintf(stderr, "Error: failed to allocate space to load shared cache file at %s\n", path);
92 return nullptr;
93 }
94 for (int i=0; i < header->mappingCount; ++i) {
95 void* mapped_cache = ::mmap((void*)(result + mappings[i].address - mappings[0].address), (size_t)mappings[i].size,
96 PROT_READ, MAP_FIXED | MAP_PRIVATE, cache_fd, mappings[i].fileOffset);
97 if (mapped_cache == MAP_FAILED) {
98 fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", path, errno);
99 return nullptr;
100 }
101 }
102 ::close(cache_fd);
103
104 return (DyldSharedCache*)result;
105 }
106
107 static void usage()
108 {
109 printf("dyld_closure_util program to create or view dyld3 closures\n");
110 printf(" mode:\n");
111 printf(" -create_closure <prog-path> # create a closure for the specified main executable\n");
112 printf(" -list_dyld_cache_closures # list all launch closures in the dyld shared cache with size\n");
113 printf(" -list_dyld_cache_dlopen_closures # list all dlopen closures in the dyld shared cache with size\n");
114 printf(" -print_dyld_cache_closure <prog-path> # find closure for specified program in dyld cache and print as JSON\n");
115 printf(" -print_dyld_cache_dylib <dylib-path> # print specified cached dylib as JSON\n");
116 printf(" -print_dyld_cache_dylibs # print all cached dylibs as JSON\n");
117 printf(" -print_dyld_cache_dlopen <path> # print specified dlopen closure as JSON\n");
118 printf(" options:\n");
119 printf(" -cache_file <cache-path> # path to cache file to use (default is current cache)\n");
120 printf(" -build_root <path-prefix> # when building a closure, the path prefix when runtime volume is not current boot volume\n");
121 printf(" -env <var=value> # when building a closure, DYLD_* env vars to assume\n");
122 printf(" -dlopen <path> # for use with -create_closure to simulate that program calling dlopen\n");
123 printf(" -verbose_fixups # for use with -print* options to force printing fixups\n");
124 printf(" -no_at_paths # when building a closure, simulate security not allowing @path expansion\n");
125 printf(" -no_fallback_paths # when building a closure, simulate security not allowing default fallback paths\n");
126 printf(" -allow_insertion_failures # when building a closure, simulate security allowing unloadable DYLD_INSERT_LIBRARIES to be ignored\n");
127 printf(" -force_invalid_cache_version # when building a closure, simulate security the cache version mismatching the builder\n");
128 }
129
130 int main(int argc, const char* argv[])
131 {
132 const char* cacheFilePath = nullptr;
133 const char* inputMainExecutablePath = nullptr;
134 const char* printCacheClosure = nullptr;
135 const char* printCachedDylib = nullptr;
136 const char* printOtherDylib = nullptr;
137 const char* fsRootPath = nullptr;
138 const char* fsOverlayPath = nullptr;
139 bool listCacheClosures = false;
140 bool listCacheDlopenClosures = false;
141 bool printCachedDylibs = false;
142 bool verboseFixups = false;
143 bool allowAtPaths = true;
144 bool allowFallbackPaths = true;
145 bool allowInsertionFailures = false;
146 bool forceInvalidFormatVersion = false;
147 bool printRaw = false;
148 std::vector<const char*> envArgs;
149 std::vector<const char*> dlopens;
150 char fsRootRealPath[PATH_MAX];
151 char fsOverlayRealPath[PATH_MAX];
152
153 if ( argc == 1 ) {
154 usage();
155 return 0;
156 }
157
158 for (int i = 1; i < argc; ++i) {
159 const char* arg = argv[i];
160 if ( strcmp(arg, "-cache_file") == 0 ) {
161 cacheFilePath = argv[++i];
162 if ( cacheFilePath == nullptr ) {
163 fprintf(stderr, "-cache_file option requires path to cache file\n");
164 return 1;
165 }
166 }
167 else if ( strcmp(arg, "-create_closure") == 0 ) {
168 inputMainExecutablePath = argv[++i];
169 if ( inputMainExecutablePath == nullptr ) {
170 fprintf(stderr, "-create_closure option requires a path to an executable\n");
171 return 1;
172 }
173 }
174 else if ( strcmp(arg, "-dlopen") == 0 ) {
175 const char* path = argv[++i];
176 if ( path == nullptr ) {
177 fprintf(stderr, "-dlopen option requires a path to a packed closure list\n");
178 return 1;
179 }
180 dlopens.push_back(path);
181 }
182 else if ( strcmp(arg, "-verbose_fixups") == 0 ) {
183 verboseFixups = true;
184 }
185 else if ( strcmp(arg, "-no_at_paths") == 0 ) {
186 allowAtPaths = false;
187 }
188 else if ( strcmp(arg, "-no_fallback_paths") == 0 ) {
189 allowFallbackPaths = false;
190 }
191 else if ( strcmp(arg, "-allow_insertion_failures") == 0 ) {
192 allowInsertionFailures = true;
193 }
194 else if ( strcmp(arg, "-raw") == 0 ) {
195 printRaw = true;
196 }
197 else if ( strcmp(arg, "-fs_root") == 0 ) {
198 fsRootPath = argv[++i];
199 if ( fsRootPath == nullptr ) {
200 fprintf(stderr, "-fs_root option requires a path\n");
201 return 1;
202 }
203 if ( realpath(fsRootPath, fsRootRealPath) == nullptr ) {
204 fprintf(stderr, "-fs_root option requires a real path\n");
205 return 1;
206 }
207 fsRootPath = fsRootRealPath;
208 }
209 else if ( strcmp(arg, "-fs_overlay") == 0 ) {
210 fsOverlayPath = argv[++i];
211 if ( fsOverlayPath == nullptr ) {
212 fprintf(stderr, "-fs_overlay option requires a path\n");
213 return 1;
214 }
215 if ( realpath(fsOverlayPath, fsOverlayRealPath) == nullptr ) {
216 fprintf(stderr, "-fs_root option requires a real path\n");
217 return 1;
218 }
219 fsOverlayPath = fsOverlayRealPath;
220 }
221 else if ( strcmp(arg, "-force_invalid_cache_version") == 0 ) {
222 forceInvalidFormatVersion = true;
223 }
224 else if ( strcmp(arg, "-list_dyld_cache_closures") == 0 ) {
225 listCacheClosures = true;
226 }
227 else if ( strcmp(arg, "-list_dyld_cache_dlopen_closures") == 0 ) {
228 listCacheDlopenClosures = true;
229 }
230 else if ( strcmp(arg, "-print_dyld_cache_closure") == 0 ) {
231 printCacheClosure = argv[++i];
232 if ( printCacheClosure == nullptr ) {
233 fprintf(stderr, "-print_dyld_cache_closure option requires a path \n");
234 return 1;
235 }
236 }
237 else if ( strcmp(arg, "-print_dyld_cache_dylibs") == 0 ) {
238 printCachedDylibs = true;
239 }
240 else if ( strcmp(arg, "-print_dyld_cache_dylib") == 0 ) {
241 printCachedDylib = argv[++i];
242 if ( printCachedDylib == nullptr ) {
243 fprintf(stderr, "-print_dyld_cache_dylib option requires a path \n");
244 return 1;
245 }
246 }
247 else if ( strcmp(arg, "-print_dyld_cache_dlopen") == 0 ) {
248 printOtherDylib = argv[++i];
249 if ( printOtherDylib == nullptr ) {
250 fprintf(stderr, "-print_dyld_cache_dlopen option requires a path \n");
251 return 1;
252 }
253 }
254 else if ( strcmp(arg, "-env") == 0 ) {
255 const char* envArg = argv[++i];
256 if ( (envArg == nullptr) || (strchr(envArg, '=') == nullptr) ) {
257 fprintf(stderr, "-env option requires KEY=VALUE\n");
258 return 1;
259 }
260 envArgs.push_back(envArg);
261 }
262 else {
263 fprintf(stderr, "unknown option %s\n", arg);
264 return 1;
265 }
266 }
267
268 envArgs.push_back(nullptr);
269
270
271 const DyldSharedCache* dyldCache = nullptr;
272 bool dyldCacheIsLive = true;
273 if ( cacheFilePath != nullptr ) {
274 dyldCache = mapCacheFile(cacheFilePath);
275 dyldCacheIsLive = false;
276 }
277 else {
278 size_t cacheLength;
279 dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength);
280 }
281 dyld3::Platform platform = dyldCache->platform();
282 const dyld3::GradedArchs& archs = dyld3::GradedArchs::forName(dyldCache->archName(), true);
283
284 if ( inputMainExecutablePath != nullptr ) {
285 PathOverrides pathOverrides;
286 pathOverrides.setFallbackPathHandling(allowFallbackPaths ? dyld3::closure::PathOverrides::FallbackPathMode::classic : dyld3::closure::PathOverrides::FallbackPathMode::none);
287 pathOverrides.setEnvVars(&envArgs[0], nullptr, nullptr);
288 STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 3+dlopens.size());
289 STACK_ALLOC_ARRAY(dyld3::LoadedImage, loadedArray, 1024);
290 imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
291 if ( auto others = dyldCache->otherOSImageArray() )
292 imagesArrays.push_back(others);
293
294 dyld3::closure::FileSystemPhysical fileSystem(fsRootPath, fsOverlayPath);
295 dyld3::RootsChecker rootsChecker;
296 ClosureBuilder::AtPath atPathHanding = allowAtPaths ? ClosureBuilder::AtPath::all : ClosureBuilder::AtPath::none;
297 ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, fileSystem, rootsChecker, dyldCache, dyldCacheIsLive, archs, pathOverrides, atPathHanding, true, nullptr, platform, nullptr);
298 if (forceInvalidFormatVersion)
299 builder.setDyldCacheInvalidFormatVersion();
300
301 const LaunchClosure* mainClosure = builder.makeLaunchClosure(inputMainExecutablePath, allowInsertionFailures);
302 if ( builder.diagnostics().hasError() ) {
303 fprintf(stderr, "dyld_closure_util: %s\n", builder.diagnostics().errorMessage());
304 return 1;
305 }
306 ImageNum nextNum = builder.nextFreeImageNum();
307
308 if ( !dlopens.empty() )
309 printf("[\n");
310 imagesArrays.push_back(mainClosure->images());
311 dyld3::closure::printClosureAsJSON(mainClosure, imagesArrays, verboseFixups, printRaw, dyldCache);
312 ClosureBuilder::buildLoadOrder(loadedArray, imagesArrays, mainClosure);
313
314 for (const char* path : dlopens) {
315 printf(",\n");
316
317 // map unloaded mach-o files for use during closure building
318 for (dyld3::LoadedImage& li : loadedArray) {
319 if ( li.loadedAddress() != nullptr )
320 continue;
321 if ( li.image()->inDyldCache() ) {
322 li.setLoadedAddress((dyld3::MachOLoaded*)((uint8_t*)dyldCache + li.image()->cacheOffset()));
323 }
324 else {
325 Diagnostics diag;
326 char realerPath[MAXPATHLEN];
327 dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, li.image()->path(), archs, builder.platform(), realerPath);
328 li.setLoadedAddress((const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent);
329 }
330 }
331
332 ClosureBuilder::AtPath atPathHandingDlopen = allowAtPaths ? ClosureBuilder::AtPath::all : ClosureBuilder::AtPath::onlyInRPaths;
333 ClosureBuilder dlopenBuilder(nextNum, fileSystem, rootsChecker, dyldCache, dyldCacheIsLive, archs, pathOverrides, atPathHandingDlopen, true, nullptr, builder.platform(), nullptr);
334 if (forceInvalidFormatVersion)
335 dlopenBuilder.setDyldCacheInvalidFormatVersion();
336
337 ImageNum topImageNum;
338 const DlopenClosure* dlopenClosure = dlopenBuilder.makeDlopenClosure(path, mainClosure, loadedArray, 0, false, false, false, &topImageNum);
339 if ( dlopenBuilder.diagnostics().hasError() ) {
340 fprintf(stderr, "dyld_closure_util: %s\n", dlopenBuilder.diagnostics().errorMessage());
341 return 1;
342 }
343 if ( dlopenClosure == nullptr ) {
344 if ( topImageNum == 0 ) {
345 fprintf(stderr, "dyld_closure_util: failed to dlopen %s\n", path);
346 return 1;
347 }
348 printf("{\n \"dyld-cache-image-num\": \"0x%04X\"\n}\n", topImageNum);
349 }
350 else {
351 nextNum += dlopenClosure->images()->imageCount();
352 imagesArrays.push_back(dlopenClosure->images());
353 dyld3::closure::printClosureAsJSON(dlopenClosure, imagesArrays, verboseFixups, printRaw);
354 ClosureBuilder::buildLoadOrder(loadedArray, imagesArrays, dlopenClosure);
355 }
356 }
357 if ( !dlopens.empty() )
358 printf("]\n");
359 }
360 else if ( listCacheClosures ) {
361 dyldCache->forEachLaunchClosure(^(const char* runtimePath, const dyld3::closure::LaunchClosure* closure) {
362 printf("%6lu %s\n", closure->size(), runtimePath);
363 });
364 }
365 else if ( listCacheDlopenClosures ) {
366 dyldCache->forEachDlopenImage(^(const char* runtimePath, const dyld3::closure::Image* image) {
367 printf("%6lu %s\n", image->size(), runtimePath);
368 });
369 }
370 else if ( printCacheClosure ) {
371 const dyld3::closure::LaunchClosure* closure = dyldCache->findClosure(printCacheClosure);
372 if ( closure != nullptr ) {
373 STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 3);
374 imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
375 if ( auto others = dyldCache->otherOSImageArray() )
376 imagesArrays.push_back(others);
377 imagesArrays.push_back(closure->images());
378 dyld3::closure::printClosureAsJSON(closure, imagesArrays, verboseFixups, printRaw, dyldCache);
379 }
380 else {
381 fprintf(stderr, "no closure in cache for %s\n", printCacheClosure);
382 }
383 }
384 else if ( printCachedDylibs ) {
385 dyld3::closure::printDyldCacheImagesAsJSON(dyldCache, verboseFixups, printRaw);
386 }
387 else if ( printCachedDylib != nullptr ) {
388 const dyld3::closure::ImageArray* dylibs = dyldCache->cachedDylibsImageArray();
389 STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 2);
390 imagesArrays.push_back(dylibs);
391 ImageNum num;
392 if ( dylibs->hasPath(printCachedDylib, num) ) {
393 dyld3::closure::printImageAsJSON(dylibs->imageForNum(num), imagesArrays, verboseFixups, printRaw, dyldCache);
394 }
395 else {
396 fprintf(stderr, "no such image found\n");
397 }
398 }
399 else if ( printOtherDylib != nullptr ) {
400 if ( const dyld3::closure::Image* image = dyldCache->findDlopenOtherImage(printOtherDylib) ) {
401 STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 2);
402 imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
403 if ( auto others = dyldCache->otherOSImageArray() )
404 imagesArrays.push_back(others);
405 dyld3::closure::printImageAsJSON(image, imagesArrays, verboseFixups);
406 }
407 else {
408 fprintf(stderr, "no such image found\n");
409 }
410 }
411
412 return 0;
413 }