dyld-625.13.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 }
126
127 int main(int argc, const char* argv[])
128 {
129 const char* cacheFilePath = nullptr;
130 const char* inputMainExecutablePath = nullptr;
131 const char* printCacheClosure = nullptr;
132 const char* printCachedDylib = nullptr;
133 const char* printOtherDylib = nullptr;
134 bool listCacheClosures = false;
135 bool listCacheDlopenClosures = false;
136 bool printCachedDylibs = false;
137 bool verboseFixups = false;
138 bool allowAtPaths = true;
139 bool allowFallbackPaths = true;
140 bool allowInsertionFailures = false;
141 std::vector<std::string> buildtimePrefixes;
142 std::vector<const char*> envArgs;
143 std::vector<const char*> dlopens;
144
145 if ( argc == 1 ) {
146 usage();
147 return 0;
148 }
149
150 for (int i = 1; i < argc; ++i) {
151 const char* arg = argv[i];
152 if ( strcmp(arg, "-cache_file") == 0 ) {
153 cacheFilePath = argv[++i];
154 if ( cacheFilePath == nullptr ) {
155 fprintf(stderr, "-cache_file option requires path to cache file\n");
156 return 1;
157 }
158 }
159 else if ( strcmp(arg, "-create_closure") == 0 ) {
160 inputMainExecutablePath = argv[++i];
161 if ( inputMainExecutablePath == nullptr ) {
162 fprintf(stderr, "-create_closure option requires a path to an executable\n");
163 return 1;
164 }
165 }
166 else if ( strcmp(arg, "-dlopen") == 0 ) {
167 const char* path = argv[++i];
168 if ( path == nullptr ) {
169 fprintf(stderr, "-dlopen option requires a path to a packed closure list\n");
170 return 1;
171 }
172 dlopens.push_back(path);
173 }
174 else if ( strcmp(arg, "-verbose_fixups") == 0 ) {
175 verboseFixups = true;
176 }
177 else if ( strcmp(arg, "-no_at_paths") == 0 ) {
178 allowAtPaths = false;
179 }
180 else if ( strcmp(arg, "-no_fallback_paths") == 0 ) {
181 allowFallbackPaths = false;
182 }
183 else if ( strcmp(arg, "-allow_insertion_failures") == 0 ) {
184 allowInsertionFailures = true;
185 }
186 else if ( strcmp(arg, "-build_root") == 0 ) {
187 const char* buildRootPath = argv[++i];
188 if ( buildRootPath == nullptr ) {
189 fprintf(stderr, "-build_root option requires a path \n");
190 return 1;
191 }
192 buildtimePrefixes.push_back(buildRootPath);
193 }
194 else if ( strcmp(arg, "-list_dyld_cache_closures") == 0 ) {
195 listCacheClosures = true;
196 }
197 else if ( strcmp(arg, "-list_dyld_cache_dlopen_closures") == 0 ) {
198 listCacheDlopenClosures = true;
199 }
200 else if ( strcmp(arg, "-print_dyld_cache_closure") == 0 ) {
201 printCacheClosure = argv[++i];
202 if ( printCacheClosure == nullptr ) {
203 fprintf(stderr, "-print_dyld_cache_closure option requires a path \n");
204 return 1;
205 }
206 }
207 else if ( strcmp(arg, "-print_dyld_cache_dylibs") == 0 ) {
208 printCachedDylibs = true;
209 }
210 else if ( strcmp(arg, "-print_dyld_cache_dylib") == 0 ) {
211 printCachedDylib = argv[++i];
212 if ( printCachedDylib == nullptr ) {
213 fprintf(stderr, "-print_dyld_cache_dylib option requires a path \n");
214 return 1;
215 }
216 }
217 else if ( strcmp(arg, "-print_dyld_cache_dlopen") == 0 ) {
218 printOtherDylib = argv[++i];
219 if ( printOtherDylib == nullptr ) {
220 fprintf(stderr, "-print_dyld_cache_dlopen option requires a path \n");
221 return 1;
222 }
223 }
224 else if ( strcmp(arg, "-env") == 0 ) {
225 const char* envArg = argv[++i];
226 if ( (envArg == nullptr) || (strchr(envArg, '=') == nullptr) ) {
227 fprintf(stderr, "-env option requires KEY=VALUE\n");
228 return 1;
229 }
230 envArgs.push_back(envArg);
231 }
232 else {
233 fprintf(stderr, "unknown option %s\n", arg);
234 return 1;
235 }
236 }
237
238 envArgs.push_back(nullptr);
239
240
241 const DyldSharedCache* dyldCache = nullptr;
242 bool dyldCacheIsLive = true;
243 if ( cacheFilePath != nullptr ) {
244 dyldCache = mapCacheFile(cacheFilePath);
245 dyldCacheIsLive = false;
246 }
247 else {
248 #if __MAC_OS_X_VERSION_MIN_REQUIRED && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
249 fprintf(stderr, "this tool needs to run on macOS 10.13 or later\n");
250 return 1;
251 #else
252 size_t cacheLength;
253 dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength);
254 #endif
255 }
256 dyld3::Platform platform = dyldCache->platform();
257 const char* archName = dyldCache->archName();
258
259 if ( inputMainExecutablePath != nullptr ) {
260 PathOverrides pathOverrides;
261 pathOverrides.setFallbackPathHandling(allowFallbackPaths ? dyld3::closure::PathOverrides::FallbackPathMode::classic : dyld3::closure::PathOverrides::FallbackPathMode::none);
262 pathOverrides.setEnvVars(&envArgs[0], nullptr, nullptr);
263 const char* prefix = ( buildtimePrefixes.empty() ? nullptr : buildtimePrefixes.front().c_str());
264 //dyld3::PathOverrides pathStuff(envArgs);
265 STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 3+dlopens.size());
266 STACK_ALLOC_ARRAY(dyld3::LoadedImage, loadedArray, 1024);
267 imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
268 imagesArrays.push_back(dyldCache->otherOSImageArray());
269
270 dyld3::closure::FileSystemPhysical fileSystem(prefix);
271 ClosureBuilder::AtPath atPathHanding = allowAtPaths ? ClosureBuilder::AtPath::all : ClosureBuilder::AtPath::none;
272 ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, fileSystem, dyldCache, dyldCacheIsLive, pathOverrides, atPathHanding, nullptr, archName, platform, nullptr);
273 const LaunchClosure* mainClosure = builder.makeLaunchClosure(inputMainExecutablePath, allowInsertionFailures);
274 if ( builder.diagnostics().hasError() ) {
275 fprintf(stderr, "dyld_closure_util: %s\n", builder.diagnostics().errorMessage());
276 return 1;
277 }
278 ImageNum nextNum = builder.nextFreeImageNum();
279
280 if ( !dlopens.empty() )
281 printf("[\n");
282 imagesArrays.push_back(mainClosure->images());
283 dyld3::closure::printClosureAsJSON(mainClosure, imagesArrays, verboseFixups);
284 ClosureBuilder::buildLoadOrder(loadedArray, imagesArrays, mainClosure);
285
286 for (const char* path : dlopens) {
287 printf(",\n");
288
289 // map unloaded mach-o files for use during closure building
290 for (dyld3::LoadedImage& li : loadedArray) {
291 if ( li.loadedAddress() != nullptr )
292 continue;
293 if ( li.image()->inDyldCache() ) {
294 li.setLoadedAddress((dyld3::MachOLoaded*)((uint8_t*)dyldCache + li.image()->cacheOffset()));
295 }
296 else {
297 Diagnostics diag;
298 dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, li.image()->path(), archName, platform);
299 li.setLoadedAddress((const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent);
300 }
301 }
302
303 ClosureBuilder::AtPath atPathHandingDlopen = allowAtPaths ? ClosureBuilder::AtPath::all : ClosureBuilder::AtPath::onlyInRPaths;
304 ClosureBuilder dlopenBuilder(nextNum, fileSystem, dyldCache, dyldCacheIsLive, pathOverrides, atPathHandingDlopen, nullptr, archName, platform, nullptr);
305 ImageNum topImageNum;
306 const DlopenClosure* dlopenClosure = dlopenBuilder.makeDlopenClosure(path, mainClosure, loadedArray, 0, false, false, &topImageNum);
307 if ( dlopenBuilder.diagnostics().hasError() ) {
308 fprintf(stderr, "dyld_closure_util: %s\n", dlopenBuilder.diagnostics().errorMessage());
309 return 1;
310 }
311 if ( dlopenClosure == nullptr ) {
312 if ( topImageNum == 0 ) {
313 fprintf(stderr, "dyld_closure_util: failed to dlopen %s\n", path);
314 return 1;
315 }
316 printf("{\n \"dyld-cache-image-num\": \"0x%04X\"\n}\n", topImageNum);
317 }
318 else {
319 nextNum += dlopenClosure->images()->imageCount();
320 imagesArrays.push_back(dlopenClosure->images());
321 dyld3::closure::printClosureAsJSON(dlopenClosure, imagesArrays, verboseFixups);
322 ClosureBuilder::buildLoadOrder(loadedArray, imagesArrays, dlopenClosure);
323 }
324 }
325 if ( !dlopens.empty() )
326 printf("]\n");
327 }
328 else if ( listCacheClosures ) {
329 dyldCache->forEachLaunchClosure(^(const char* runtimePath, const dyld3::closure::LaunchClosure* closure) {
330 printf("%6lu %s\n", closure->size(), runtimePath);
331 });
332 }
333 else if ( listCacheDlopenClosures ) {
334 dyldCache->forEachDlopenImage(^(const char* runtimePath, const dyld3::closure::Image* image) {
335 printf("%6lu %s\n", image->size(), runtimePath);
336 });
337 }
338 else if ( printCacheClosure ) {
339 const dyld3::closure::LaunchClosure* closure = dyldCache->findClosure(printCacheClosure);
340 if ( closure != nullptr ) {
341 STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 3);
342 imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
343 imagesArrays.push_back(dyldCache->otherOSImageArray());
344 imagesArrays.push_back(closure->images());
345 dyld3::closure::printClosureAsJSON(closure, imagesArrays, verboseFixups);
346 }
347 else {
348 fprintf(stderr, "no closure in cache for %s\n", printCacheClosure);
349 }
350 }
351 else if ( printCachedDylibs ) {
352 dyld3::closure::printDyldCacheImagesAsJSON(dyldCache, verboseFixups);
353 }
354 else if ( printCachedDylib != nullptr ) {
355 const dyld3::closure::ImageArray* dylibs = dyldCache->cachedDylibsImageArray();
356 STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 2);
357 imagesArrays.push_back(dylibs);
358 ImageNum num;
359 if ( dylibs->hasPath(printCachedDylib, num) ) {
360 dyld3::closure::printImageAsJSON(dylibs->imageForNum(num), imagesArrays, verboseFixups);
361 }
362 else {
363 fprintf(stderr, "no such image found\n");
364 }
365 }
366 else if ( printOtherDylib != nullptr ) {
367 if ( const dyld3::closure::Image* image = dyldCache->findDlopenOtherImage(printOtherDylib) ) {
368 STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 2);
369 imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
370 imagesArrays.push_back(dyldCache->otherOSImageArray());
371 dyld3::closure::printImageAsJSON(image, imagesArrays, verboseFixups);
372 }
373 else {
374 fprintf(stderr, "no such image found\n");
375 }
376 }
377
378 return 0;
379 }