dyld-732.8.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
52 using dyld3::closure::ImageArray;
53 using dyld3::closure::Image;
54 using dyld3::closure::ImageNum;
55 using dyld3::closure::ClosureBuilder;
56 using dyld3::closure::LaunchClosure;
57 using dyld3::closure::DlopenClosure;
58 using dyld3::closure::PathOverrides;
59 using dyld3::Array;
60
61
62 // mmap() an shared cache file read/only but laid out like it would be at runtime
63 static const DyldSharedCache* mapCacheFile(const char* path)
64 {
65 struct stat statbuf;
66 if ( ::stat(path, &statbuf) ) {
67 fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", path);
68 return nullptr;
69 }
70
71 int cache_fd = ::open(path, O_RDONLY);
72 if (cache_fd < 0) {
73 fprintf(stderr, "Error: failed to open shared cache file at %s\n", path);
74 return nullptr;
75 }
76
77 uint8_t firstPage[4096];
78 if ( ::pread(cache_fd, firstPage, 4096, 0) != 4096 ) {
79 fprintf(stderr, "Error: failed to read shared cache file at %s\n", path);
80 return nullptr;
81 }
82 const dyld_cache_header* header = (dyld_cache_header*)firstPage;
83 const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(firstPage + header->mappingOffset);
84
85 size_t vmSize = (size_t)(mappings[2].address + mappings[2].size - mappings[0].address);
86 vm_address_t result;
87 kern_return_t r = ::vm_allocate(mach_task_self(), &result, vmSize, VM_FLAGS_ANYWHERE);
88 if ( r != KERN_SUCCESS ) {
89 fprintf(stderr, "Error: failed to allocate space to load shared cache file at %s\n", path);
90 return nullptr;
91 }
92 for (int i=0; i < 3; ++i) {
93 void* mapped_cache = ::mmap((void*)(result + mappings[i].address - mappings[0].address), (size_t)mappings[i].size,
94 PROT_READ, MAP_FIXED | MAP_PRIVATE, cache_fd, mappings[i].fileOffset);
95 if (mapped_cache == MAP_FAILED) {
96 fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", path, errno);
97 return nullptr;
98 }
99 }
100 ::close(cache_fd);
101
102 return (DyldSharedCache*)result;
103 }
104
105 static void usage()
106 {
107 printf("dyld_closure_util program to create or view dyld3 closures\n");
108 printf(" mode:\n");
109 printf(" -create_closure <prog-path> # create a closure for the specified main executable\n");
110 printf(" -list_dyld_cache_closures # list all launch closures in the dyld shared cache with size\n");
111 printf(" -list_dyld_cache_dlopen_closures # list all dlopen closures in the dyld shared cache with size\n");
112 printf(" -print_dyld_cache_closure <prog-path> # find closure for specified program in dyld cache and print as JSON\n");
113 printf(" -print_dyld_cache_dylib <dylib-path> # print specified cached dylib as JSON\n");
114 printf(" -print_dyld_cache_dylibs # print all cached dylibs as JSON\n");
115 printf(" -print_dyld_cache_dlopen <path> # print specified dlopen closure as JSON\n");
116 printf(" options:\n");
117 printf(" -cache_file <cache-path> # path to cache file to use (default is current cache)\n");
118 printf(" -build_root <path-prefix> # when building a closure, the path prefix when runtime volume is not current boot volume\n");
119 printf(" -env <var=value> # when building a closure, DYLD_* env vars to assume\n");
120 printf(" -dlopen <path> # for use with -create_closure to simulate that program calling dlopen\n");
121 printf(" -verbose_fixups # for use with -print* options to force printing fixups\n");
122 printf(" -no_at_paths # when building a closure, simulate security not allowing @path expansion\n");
123 printf(" -no_fallback_paths # when building a closure, simulate security not allowing default fallback paths\n");
124 printf(" -allow_insertion_failures # when building a closure, simulate security allowing unloadable DYLD_INSERT_LIBRARIES to be ignored\n");
125 printf(" -force_invalid_cache_version # when building a closure, simulate security the cache version mismatching the builder\n");
126 }
127
128 int main(int argc, const char* argv[])
129 {
130 const char* cacheFilePath = nullptr;
131 const char* inputMainExecutablePath = nullptr;
132 const char* printCacheClosure = nullptr;
133 const char* printCachedDylib = nullptr;
134 const char* printOtherDylib = nullptr;
135 const char* fsRootPath = nullptr;
136 const char* fsOverlayPath = nullptr;
137 bool listCacheClosures = false;
138 bool listCacheDlopenClosures = false;
139 bool printCachedDylibs = false;
140 bool verboseFixups = false;
141 bool allowAtPaths = true;
142 bool allowFallbackPaths = true;
143 bool allowInsertionFailures = false;
144 bool forceInvalidFormatVersion = false;
145 bool printRaw = false;
146 std::vector<const char*> envArgs;
147 std::vector<const char*> dlopens;
148 char fsRootRealPath[PATH_MAX];
149 char fsOverlayRealPath[PATH_MAX];
150
151 if ( argc == 1 ) {
152 usage();
153 return 0;
154 }
155
156 for (int i = 1; i < argc; ++i) {
157 const char* arg = argv[i];
158 if ( strcmp(arg, "-cache_file") == 0 ) {
159 cacheFilePath = argv[++i];
160 if ( cacheFilePath == nullptr ) {
161 fprintf(stderr, "-cache_file option requires path to cache file\n");
162 return 1;
163 }
164 }
165 else if ( strcmp(arg, "-create_closure") == 0 ) {
166 inputMainExecutablePath = argv[++i];
167 if ( inputMainExecutablePath == nullptr ) {
168 fprintf(stderr, "-create_closure option requires a path to an executable\n");
169 return 1;
170 }
171 }
172 else if ( strcmp(arg, "-dlopen") == 0 ) {
173 const char* path = argv[++i];
174 if ( path == nullptr ) {
175 fprintf(stderr, "-dlopen option requires a path to a packed closure list\n");
176 return 1;
177 }
178 dlopens.push_back(path);
179 }
180 else if ( strcmp(arg, "-verbose_fixups") == 0 ) {
181 verboseFixups = true;
182 }
183 else if ( strcmp(arg, "-no_at_paths") == 0 ) {
184 allowAtPaths = false;
185 }
186 else if ( strcmp(arg, "-no_fallback_paths") == 0 ) {
187 allowFallbackPaths = false;
188 }
189 else if ( strcmp(arg, "-allow_insertion_failures") == 0 ) {
190 allowInsertionFailures = true;
191 }
192 else if ( strcmp(arg, "-raw") == 0 ) {
193 printRaw = true;
194 }
195 else if ( strcmp(arg, "-fs_root") == 0 ) {
196 fsRootPath = argv[++i];
197 if ( fsRootPath == nullptr ) {
198 fprintf(stderr, "-fs_root option requires a path\n");
199 return 1;
200 }
201 if ( realpath(fsRootPath, fsRootRealPath) == nullptr ) {
202 fprintf(stderr, "-fs_root option requires a real path\n");
203 return 1;
204 }
205 fsRootPath = fsRootRealPath;
206 }
207 else if ( strcmp(arg, "-fs_overlay") == 0 ) {
208 fsOverlayPath = argv[++i];
209 if ( fsOverlayPath == nullptr ) {
210 fprintf(stderr, "-fs_overlay option requires a path\n");
211 return 1;
212 }
213 if ( realpath(fsOverlayPath, fsOverlayRealPath) == nullptr ) {
214 fprintf(stderr, "-fs_root option requires a real path\n");
215 return 1;
216 }
217 fsOverlayPath = fsOverlayRealPath;
218 }
219 else if ( strcmp(arg, "-force_invalid_cache_version") == 0 ) {
220 forceInvalidFormatVersion = true;
221 }
222 else if ( strcmp(arg, "-list_dyld_cache_closures") == 0 ) {
223 listCacheClosures = true;
224 }
225 else if ( strcmp(arg, "-list_dyld_cache_dlopen_closures") == 0 ) {
226 listCacheDlopenClosures = true;
227 }
228 else if ( strcmp(arg, "-print_dyld_cache_closure") == 0 ) {
229 printCacheClosure = argv[++i];
230 if ( printCacheClosure == nullptr ) {
231 fprintf(stderr, "-print_dyld_cache_closure option requires a path \n");
232 return 1;
233 }
234 }
235 else if ( strcmp(arg, "-print_dyld_cache_dylibs") == 0 ) {
236 printCachedDylibs = true;
237 }
238 else if ( strcmp(arg, "-print_dyld_cache_dylib") == 0 ) {
239 printCachedDylib = argv[++i];
240 if ( printCachedDylib == nullptr ) {
241 fprintf(stderr, "-print_dyld_cache_dylib option requires a path \n");
242 return 1;
243 }
244 }
245 else if ( strcmp(arg, "-print_dyld_cache_dlopen") == 0 ) {
246 printOtherDylib = argv[++i];
247 if ( printOtherDylib == nullptr ) {
248 fprintf(stderr, "-print_dyld_cache_dlopen option requires a path \n");
249 return 1;
250 }
251 }
252 else if ( strcmp(arg, "-env") == 0 ) {
253 const char* envArg = argv[++i];
254 if ( (envArg == nullptr) || (strchr(envArg, '=') == nullptr) ) {
255 fprintf(stderr, "-env option requires KEY=VALUE\n");
256 return 1;
257 }
258 envArgs.push_back(envArg);
259 }
260 else {
261 fprintf(stderr, "unknown option %s\n", arg);
262 return 1;
263 }
264 }
265
266 envArgs.push_back(nullptr);
267
268
269 const DyldSharedCache* dyldCache = nullptr;
270 bool dyldCacheIsLive = true;
271 if ( cacheFilePath != nullptr ) {
272 dyldCache = mapCacheFile(cacheFilePath);
273 dyldCacheIsLive = false;
274 }
275 else {
276 #if __MAC_OS_X_VERSION_MIN_REQUIRED && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
277 fprintf(stderr, "this tool needs to run on macOS 10.13 or later\n");
278 return 1;
279 #else
280 size_t cacheLength;
281 dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength);
282 #endif
283 }
284 dyld3::Platform platform = dyldCache->platform();
285 const dyld3::GradedArchs& archs = dyld3::GradedArchs::forName(dyldCache->archName(), true);
286
287 if ( inputMainExecutablePath != nullptr ) {
288 PathOverrides pathOverrides;
289 pathOverrides.setFallbackPathHandling(allowFallbackPaths ? dyld3::closure::PathOverrides::FallbackPathMode::classic : dyld3::closure::PathOverrides::FallbackPathMode::none);
290 pathOverrides.setEnvVars(&envArgs[0], nullptr, nullptr);
291 STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 3+dlopens.size());
292 STACK_ALLOC_ARRAY(dyld3::LoadedImage, loadedArray, 1024);
293 imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
294 imagesArrays.push_back(dyldCache->otherOSImageArray());
295
296 dyld3::closure::FileSystemPhysical fileSystem(fsRootPath, fsOverlayPath);
297 ClosureBuilder::AtPath atPathHanding = allowAtPaths ? ClosureBuilder::AtPath::all : ClosureBuilder::AtPath::none;
298 ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, fileSystem, dyldCache, dyldCacheIsLive, archs, pathOverrides, atPathHanding, true, nullptr, platform, nullptr);
299 if (forceInvalidFormatVersion)
300 builder.setDyldCacheInvalidFormatVersion();
301
302 const LaunchClosure* mainClosure = builder.makeLaunchClosure(inputMainExecutablePath, allowInsertionFailures);
303 if ( builder.diagnostics().hasError() ) {
304 fprintf(stderr, "dyld_closure_util: %s\n", builder.diagnostics().errorMessage());
305 return 1;
306 }
307 ImageNum nextNum = builder.nextFreeImageNum();
308
309 if ( !dlopens.empty() )
310 printf("[\n");
311 imagesArrays.push_back(mainClosure->images());
312 dyld3::closure::printClosureAsJSON(mainClosure, imagesArrays, verboseFixups, printRaw, dyldCache);
313 ClosureBuilder::buildLoadOrder(loadedArray, imagesArrays, mainClosure);
314
315 for (const char* path : dlopens) {
316 printf(",\n");
317
318 // map unloaded mach-o files for use during closure building
319 for (dyld3::LoadedImage& li : loadedArray) {
320 if ( li.loadedAddress() != nullptr )
321 continue;
322 if ( li.image()->inDyldCache() ) {
323 li.setLoadedAddress((dyld3::MachOLoaded*)((uint8_t*)dyldCache + li.image()->cacheOffset()));
324 }
325 else {
326 Diagnostics diag;
327 char realerPath[MAXPATHLEN];
328 dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, li.image()->path(), archs, platform, realerPath);
329 li.setLoadedAddress((const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent);
330 }
331 }
332
333 ClosureBuilder::AtPath atPathHandingDlopen = allowAtPaths ? ClosureBuilder::AtPath::all : ClosureBuilder::AtPath::onlyInRPaths;
334 ClosureBuilder dlopenBuilder(nextNum, fileSystem, dyldCache, dyldCacheIsLive, archs, pathOverrides, atPathHandingDlopen, true, nullptr, platform, nullptr);
335 if (forceInvalidFormatVersion)
336 dlopenBuilder.setDyldCacheInvalidFormatVersion();
337
338 ImageNum topImageNum;
339 const DlopenClosure* dlopenClosure = dlopenBuilder.makeDlopenClosure(path, mainClosure, loadedArray, 0, false, false, false, &topImageNum);
340 if ( dlopenBuilder.diagnostics().hasError() ) {
341 fprintf(stderr, "dyld_closure_util: %s\n", dlopenBuilder.diagnostics().errorMessage());
342 return 1;
343 }
344 if ( dlopenClosure == nullptr ) {
345 if ( topImageNum == 0 ) {
346 fprintf(stderr, "dyld_closure_util: failed to dlopen %s\n", path);
347 return 1;
348 }
349 printf("{\n \"dyld-cache-image-num\": \"0x%04X\"\n}\n", topImageNum);
350 }
351 else {
352 nextNum += dlopenClosure->images()->imageCount();
353 imagesArrays.push_back(dlopenClosure->images());
354 dyld3::closure::printClosureAsJSON(dlopenClosure, imagesArrays, verboseFixups, printRaw);
355 ClosureBuilder::buildLoadOrder(loadedArray, imagesArrays, dlopenClosure);
356 }
357 }
358 if ( !dlopens.empty() )
359 printf("]\n");
360 }
361 else if ( listCacheClosures ) {
362 dyldCache->forEachLaunchClosure(^(const char* runtimePath, const dyld3::closure::LaunchClosure* closure) {
363 printf("%6lu %s\n", closure->size(), runtimePath);
364 });
365 }
366 else if ( listCacheDlopenClosures ) {
367 dyldCache->forEachDlopenImage(^(const char* runtimePath, const dyld3::closure::Image* image) {
368 printf("%6lu %s\n", image->size(), runtimePath);
369 });
370 }
371 else if ( printCacheClosure ) {
372 const dyld3::closure::LaunchClosure* closure = dyldCache->findClosure(printCacheClosure);
373 if ( closure != nullptr ) {
374 STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 3);
375 imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
376 imagesArrays.push_back(dyldCache->otherOSImageArray());
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 imagesArrays.push_back(dyldCache->otherOSImageArray());
404 dyld3::closure::printImageAsJSON(image, imagesArrays, verboseFixups);
405 }
406 else {
407 fprintf(stderr, "no such image found\n");
408 }
409 }
410
411 return 0;
412 }