dyld-832.7.1.tar.gz
[apple/dyld.git] / dyld3 / PathOverrides.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
26 #include <stdint.h>
27 #include <string.h>
28 #include <assert.h>
29 #include <uuid/uuid.h>
30 #include <mach/mach.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <limits.h>
34 #include <sys/errno.h>
35 #include <unistd.h>
36 #include <TargetConditionals.h>
37
38 #include <initializer_list>
39
40 #include "PathOverrides.h"
41
42
43 namespace dyld3 {
44 namespace closure {
45
46 #if BUILDING_LIBDYLD
47 PathOverrides gPathOverrides;
48 #endif
49
50
51 // based on ANSI-C strstr()
52 static const char* strrstr(const char* str, const char* sub)
53 {
54 const size_t sublen = strlen(sub);
55 for(const char* p = &str[strlen(str)]; p != str; --p) {
56 if ( strncmp(p, sub, sublen) == 0 )
57 return p;
58 }
59 return NULL;
60 }
61
62
63 void PathOverrides::setFallbackPathHandling(FallbackPathMode mode)
64 {
65 _fallbackPathMode = mode;
66 }
67
68 void PathOverrides::setEnvVars(const char* envp[], const MachOFile* mainExe, const char* mainExePath)
69 {
70 for (const char** p = envp; *p != NULL; p++) {
71 addEnvVar(*p, false);
72 }
73 if ( mainExe != nullptr )
74 setMainExecutable(mainExe, mainExePath);
75 }
76
77 void PathOverrides::setMainExecutable(const dyld3::MachOFile* mainExe, const char* mainExePath)
78 {
79 assert(mainExe != nullptr);
80 assert(mainExe->isMainExecutable());
81 // process any LC_DYLD_ENVIRONMENT load commands in main executable
82 mainExe->forDyldEnv(^(const char* envVar, bool& stop) {
83 addEnvVar(envVar, true);
84 });
85 }
86
87
88 #if !BUILDING_LIBDYLD
89 // libdyld is never unloaded
90 PathOverrides::~PathOverrides()
91 {
92 }
93 #endif
94
95 void PathOverrides::forEachInsertedDylib(void (^handler)(const char* dylibPath, bool &stop)) const
96 {
97 if ( _insertedDylibs != nullptr && _insertedDylibs[0] != '\0' ) {
98 forEachInColonList(_insertedDylibs, nullptr, ^(const char* path, bool &stop) {
99 handler(path, stop);
100 });
101 }
102 }
103
104 void PathOverrides::handleEnvVar(const char* key, const char* value, void (^handler)(const char* envVar)) const
105 {
106 if ( value == nullptr )
107 return;
108 size_t allocSize = strlen(key) + strlen(value) + 2;
109 char buffer[allocSize];
110 strlcpy(buffer, key, allocSize);
111 strlcat(buffer, "=", allocSize);
112 strlcat(buffer, value, allocSize);
113 handler(buffer);
114 }
115
116 // Note, this method only returns variables set on the environment, not those from the load command
117 void PathOverrides::forEachEnvVar(void (^handler)(const char* envVar)) const
118 {
119 handleEnvVar("DYLD_LIBRARY_PATH", _dylibPathOverridesEnv, handler);
120 handleEnvVar("DYLD_FRAMEWORK_PATH", _frameworkPathOverridesEnv, handler);
121 handleEnvVar("DYLD_FALLBACK_FRAMEWORK_PATH", _frameworkPathFallbacksEnv, handler);
122 handleEnvVar("DYLD_FALLBACK_LIBRARY_PATH", _dylibPathFallbacksEnv, handler);
123 handleEnvVar("DYLD_INSERT_LIBRARIES", _insertedDylibs, handler);
124 handleEnvVar("DYLD_IMAGE_SUFFIX", _imageSuffix, handler);
125 handleEnvVar("DYLD_ROOT_PATH", _rootPath, handler);
126 }
127
128 // Note, this method only returns variables set on the executable load command, not those from the environment
129 void PathOverrides::forEachExecutableEnvVar(void (^handler)(const char* envVar)) const
130 {
131 handleEnvVar("DYLD_LIBRARY_PATH", _dylibPathOverridesExeLC, handler);
132 handleEnvVar("DYLD_FRAMEWORK_PATH", _frameworkPathOverridesExeLC, handler);
133 handleEnvVar("DYLD_FALLBACK_FRAMEWORK_PATH", _frameworkPathFallbacksExeLC, handler);
134 handleEnvVar("DYLD_FALLBACK_LIBRARY_PATH", _dylibPathFallbacksExeLC, handler);
135 }
136
137 const char* PathOverrides::addString(const char* str)
138 {
139 if ( _pathPool == nullptr )
140 _pathPool = PathPool::allocate();
141 return _pathPool->add(str);
142 }
143
144 void PathOverrides::setString(const char*& var, const char* value)
145 {
146 if ( var == nullptr ) {
147 var = addString(value);
148 return;
149 }
150 // string already in use, build new appended string
151 char tmp[strlen(var)+strlen(value)+2];
152 strcpy(tmp, var);
153 strcat(tmp, ":");
154 strcat(tmp, value);
155 var = addString(tmp);
156 }
157
158 void PathOverrides::addEnvVar(const char* keyEqualsValue, bool forExecutable)
159 {
160 // We have to make a copy of the env vars because the dyld
161 // semantics is that the env vars are only looked at once
162 // at launch (using setenv() at runtime does not change dyld behavior).
163 const char* equals = strchr(keyEqualsValue, '=');
164 if ( equals != NULL ) {
165 if ( strncmp(keyEqualsValue, "DYLD_LIBRARY_PATH", 17) == 0 ) {
166 setString(forExecutable ? _dylibPathOverridesExeLC : _dylibPathOverridesEnv, &keyEqualsValue[18]);
167 }
168 else if ( strncmp(keyEqualsValue, "DYLD_FRAMEWORK_PATH", 19) == 0 ) {
169 setString(forExecutable ? _frameworkPathOverridesExeLC : _frameworkPathOverridesEnv, &keyEqualsValue[20]);
170 }
171 else if ( strncmp(keyEqualsValue, "DYLD_FALLBACK_FRAMEWORK_PATH", 28) == 0 ) {
172 setString(forExecutable ? _frameworkPathFallbacksExeLC : _frameworkPathFallbacksEnv, &keyEqualsValue[29]);
173 }
174 else if ( strncmp(keyEqualsValue, "DYLD_FALLBACK_LIBRARY_PATH", 26) == 0 ) {
175 setString(forExecutable ? _dylibPathFallbacksExeLC : _dylibPathFallbacksEnv, &keyEqualsValue[27]);
176 }
177 else if ( strncmp(keyEqualsValue, "DYLD_INSERT_LIBRARIES", 21) == 0 ) {
178 setString(_insertedDylibs, &keyEqualsValue[22]);
179 }
180 else if ( strncmp(keyEqualsValue, "DYLD_IMAGE_SUFFIX", 17) == 0 ) {
181 setString(_imageSuffix, &keyEqualsValue[18]);
182 }
183 else if ( strncmp(keyEqualsValue, "DYLD_ROOT_PATH", 14) == 0 ) {
184 setString(_rootPath, &keyEqualsValue[15]);
185 }
186 }
187 }
188
189 void PathOverrides::forEachInColonList(const char* list1, const char* list2, void (^handler)(const char* path, bool& stop))
190 {
191 for (const char* list : { list1, list2 }) {
192 if (list == nullptr)
193 continue;
194 char buffer[strlen(list)+1];
195 const char* t = list;
196 bool stop = false;
197 for (const char* s=list; *s != '\0'; ++s) {
198 if (*s != ':')
199 continue;
200 size_t len = s - t;
201 memcpy(buffer, t, len);
202 buffer[len] = '\0';
203 handler(buffer, stop);
204 if ( stop )
205 return;
206 t = s+1;
207 }
208 handler(t, stop);
209 if (stop)
210 return;
211 }
212 }
213
214 void PathOverrides::forEachDylibFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const
215 {
216 __block bool stop = false;
217 if ( (_dylibPathFallbacksEnv != nullptr) || (_dylibPathFallbacksExeLC != nullptr) ) {
218 forEachInColonList(_dylibPathFallbacksEnv, _dylibPathFallbacksExeLC, ^(const char* pth, bool& innerStop) {
219 handler(pth, innerStop);
220 if ( innerStop )
221 stop = true;
222 });
223 }
224 else {
225 switch ( platform ) {
226 case Platform::macOS:
227 switch ( _fallbackPathMode ) {
228 case FallbackPathMode::classic:
229 // "$HOME/lib"
230 handler("/usr/local/lib", stop);
231 if ( stop )
232 break;
233 [[clang::fallthrough]];
234 case FallbackPathMode::restricted:
235 handler("/usr/lib", stop);
236 break;
237 case FallbackPathMode::none:
238 break;
239 }
240 break;
241 case Platform::iOS:
242 case Platform::watchOS:
243 case Platform::tvOS:
244 case Platform::bridgeOS:
245 case Platform::driverKit:
246 case Platform::unknown:
247 if ( _fallbackPathMode != FallbackPathMode::none ) {
248 handler("/usr/local/lib", stop);
249 if ( stop )
250 break;
251 }
252 // fall into /usr/lib case
253 [[clang::fallthrough]];
254 case Platform::iOSMac:
255 case Platform::iOS_simulator:
256 case Platform::watchOS_simulator:
257 case Platform::tvOS_simulator:
258 if ( _fallbackPathMode != FallbackPathMode::none )
259 handler("/usr/lib", stop);
260 break;
261 }
262 }
263 }
264
265 void PathOverrides::forEachFrameworkFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const
266 {
267 __block bool stop = false;
268 if ( (_frameworkPathFallbacksEnv != nullptr) || (_frameworkPathFallbacksExeLC != nullptr) ) {
269 forEachInColonList(_frameworkPathFallbacksEnv, _frameworkPathFallbacksExeLC, ^(const char* pth, bool& innerStop) {
270 handler(pth, innerStop);
271 if ( innerStop )
272 stop = true;
273 });
274 }
275 else {
276 switch ( platform ) {
277 case Platform::macOS:
278 switch ( _fallbackPathMode ) {
279 case FallbackPathMode::classic:
280 // "$HOME/Library/Frameworks"
281 handler("/Library/Frameworks", stop);
282 if ( stop )
283 break;
284 // "/Network/Library/Frameworks"
285 // fall thru
286 [[clang::fallthrough]];
287 case FallbackPathMode::restricted:
288 handler("/System/Library/Frameworks", stop);
289 break;
290 case FallbackPathMode::none:
291 break;
292 }
293 break;
294 case Platform::iOS:
295 case Platform::watchOS:
296 case Platform::tvOS:
297 case Platform::bridgeOS:
298 case Platform::iOSMac:
299 case Platform::iOS_simulator:
300 case Platform::watchOS_simulator:
301 case Platform::tvOS_simulator:
302 case Platform::driverKit:
303 case Platform::unknown:
304 if ( _fallbackPathMode != FallbackPathMode::none )
305 handler("/System/Library/Frameworks", stop);
306 break;
307 }
308 }
309 }
310
311
312 //
313 // copy path and add suffix to result
314 //
315 // /path/foo.dylib _debug => /path/foo_debug.dylib
316 // foo.dylib _debug => foo_debug.dylib
317 // foo _debug => foo_debug
318 // /path/bar _debug => /path/bar_debug
319 // /path/bar.A.dylib _debug => /path/bar.A_debug.dylib
320 //
321 void PathOverrides::addSuffix(const char* path, const char* suffix, char* result) const
322 {
323 strcpy(result, path);
324
325 // find last slash
326 char* start = strrchr(result, '/');
327 if ( start != NULL )
328 start++;
329 else
330 start = result;
331
332 // find last dot after last slash
333 char* dot = strrchr(start, '.');
334 if ( dot != NULL ) {
335 strcpy(dot, suffix);
336 strcat(&dot[strlen(suffix)], &path[dot-result]);
337 }
338 else {
339 strcat(result, suffix);
340 }
341 }
342
343 void PathOverrides::forEachImageSuffix(const char* path, bool isFallbackPath, bool pathIsInDyldCacheWhichCannotBeOverridden, bool& stop, void (^handler)(const char* possiblePath, bool isFallbackPath, bool& stop)) const
344 {
345 if ( (_imageSuffix == nullptr) || pathIsInDyldCacheWhichCannotBeOverridden ) {
346 handler(path, isFallbackPath, stop);
347 }
348 else {
349 forEachInColonList(_imageSuffix, nullptr, ^(const char* suffix, bool& innerStop) {
350 char npath[strlen(path)+strlen(suffix)+8];
351 addSuffix(path, suffix, npath);
352 handler(npath, isFallbackPath, innerStop);
353 if ( innerStop )
354 stop = true;
355 });
356 if ( !stop )
357 handler(path, isFallbackPath, stop);
358 }
359 }
360
361 void PathOverrides::forEachPathVariant(const char* initialPath, bool pathIsInDyldCacheWhichCannotBeOverridden, void (^handler)(const char* possiblePath, bool isFallbackPath, bool& stop), Platform platform) const
362 {
363 __block bool stop = false;
364
365
366 if ( !pathIsInDyldCacheWhichCannotBeOverridden ) {
367 // check for overrides
368 const char* frameworkPartialPath = getFrameworkPartialPath(initialPath);
369 if ( frameworkPartialPath != nullptr ) {
370 const size_t frameworkPartialPathLen = strlen(frameworkPartialPath);
371 // look at each DYLD_FRAMEWORK_PATH directory
372 if ( (_frameworkPathOverridesEnv != nullptr) || (_frameworkPathOverridesExeLC != nullptr) ) {
373 forEachInColonList(_frameworkPathOverridesEnv, _frameworkPathOverridesExeLC, ^(const char* frDir, bool &innerStop) {
374 char npath[strlen(frDir)+frameworkPartialPathLen+8];
375 strcpy(npath, frDir);
376 strcat(npath, "/");
377 strcat(npath, frameworkPartialPath);
378 forEachImageSuffix(npath, false, pathIsInDyldCacheWhichCannotBeOverridden, innerStop, handler);
379 if ( innerStop )
380 stop = true;
381 });
382 }
383 }
384 else {
385 const char* libraryLeafName = getLibraryLeafName(initialPath);
386 const size_t libraryLeafNameLen = strlen(libraryLeafName);
387 // look at each DYLD_LIBRARY_PATH directory
388 if ( (_dylibPathOverridesEnv != nullptr) || (_dylibPathOverridesExeLC != nullptr) ) {
389 forEachInColonList(_dylibPathOverridesEnv, _dylibPathOverridesExeLC, ^(const char* libDir, bool &innerStop) {
390 char npath[strlen(libDir)+libraryLeafNameLen+8];
391 strcpy(npath, libDir);
392 strcat(npath, "/");
393 strcat(npath, libraryLeafName);
394 forEachImageSuffix(npath, false, pathIsInDyldCacheWhichCannotBeOverridden, innerStop, handler);
395 if ( innerStop )
396 stop = true;
397 });
398 }
399 }
400 if ( stop )
401 return;
402 }
403
404 // try rootpath
405 bool searchiOSSupport = (platform == Platform::iOSMac);
406 #if (TARGET_OS_OSX && TARGET_CPU_ARM64)
407 if ( platform == Platform::iOS ) {
408 searchiOSSupport = true;
409 // <rdar://problem/58959974> some old Almond apps reference old WebKit location
410 if ( strcmp(initialPath, "/System/Library/PrivateFrameworks/WebKit.framework/WebKit") == 0 )
411 initialPath = "/System/Library/Frameworks/WebKit.framework/WebKit";
412 }
413 #endif
414 if ( searchiOSSupport ) {
415 char rtpath[strlen("/System/iOSSupport")+strlen(initialPath)+8];
416 strcpy(rtpath, "/System/iOSSupport");
417 strcat(rtpath, initialPath);
418 forEachImageSuffix(rtpath, false, pathIsInDyldCacheWhichCannotBeOverridden, stop, handler);
419 if ( stop )
420 return;
421 }
422
423 // try original path
424 forEachImageSuffix(initialPath, false, pathIsInDyldCacheWhichCannotBeOverridden, stop, handler);
425 if ( stop )
426 return;
427
428 // check fallback paths
429 if ( const char* frameworkPartialPath = getFrameworkPartialPath(initialPath) ) {
430 const size_t frameworkPartialPathLen = strlen(frameworkPartialPath);
431 // look at each DYLD_FALLBACK_FRAMEWORK_PATH directory
432 bool usesDefaultFallbackPaths = (_frameworkPathFallbacksEnv == nullptr) && (_frameworkPathFallbacksExeLC == nullptr);
433 forEachFrameworkFallback(platform, ^(const char* dir, bool& innerStop) {
434 char npath[strlen(dir)+frameworkPartialPathLen+8];
435 strcpy(npath, dir);
436 strcat(npath, "/");
437 strcat(npath, frameworkPartialPath);
438 forEachImageSuffix(npath, usesDefaultFallbackPaths, pathIsInDyldCacheWhichCannotBeOverridden, innerStop, handler);
439 if ( innerStop )
440 stop = true;
441 });
442
443 }
444 else {
445 const char* libraryLeafName = getLibraryLeafName(initialPath);
446 const size_t libraryLeafNameLen = strlen(libraryLeafName);
447 // look at each DYLD_FALLBACK_LIBRARY_PATH directory
448 bool usesDefaultFallbackPaths = (_dylibPathFallbacksEnv == nullptr) && (_dylibPathFallbacksExeLC == nullptr);
449 forEachDylibFallback(platform, ^(const char* dir, bool& innerStop) {
450 char libpath[strlen(dir)+libraryLeafNameLen+8];
451 strcpy(libpath, dir);
452 strcat(libpath, "/");
453 strcat(libpath, libraryLeafName);
454 forEachImageSuffix(libpath, usesDefaultFallbackPaths, pathIsInDyldCacheWhichCannotBeOverridden, innerStop, handler);
455 if ( innerStop )
456 stop = true;
457 });
458 }
459 }
460
461
462 //
463 // Find framework path
464 //
465 // /path/foo.framework/foo => foo.framework/foo
466 // /path/foo.framework/Versions/A/foo => foo.framework/Versions/A/foo
467 // /path/foo.framework/Frameworks/bar.framework/bar => bar.framework/bar
468 // /path/foo.framework/Libraries/bar.dylb => NULL
469 // /path/foo.framework/bar => NULL
470 //
471 // Returns nullptr if not a framework path
472 //
473 const char* PathOverrides::getFrameworkPartialPath(const char* path) const
474 {
475 const char* dirDot = strrstr(path, ".framework/");
476 if ( dirDot != nullptr ) {
477 const char* dirStart = dirDot;
478 for ( ; dirStart >= path; --dirStart) {
479 if ( (*dirStart == '/') || (dirStart == path) ) {
480 const char* frameworkStart = &dirStart[1];
481 if ( dirStart == path )
482 --frameworkStart;
483 size_t len = dirDot - frameworkStart;
484 char framework[len+1];
485 strncpy(framework, frameworkStart, len);
486 framework[len] = '\0';
487 const char* leaf = strrchr(path, '/');
488 if ( leaf != nullptr ) {
489 if ( strcmp(framework, &leaf[1]) == 0 ) {
490 return frameworkStart;
491 }
492 if ( _imageSuffix != nullptr ) {
493 // some debug frameworks have install names that end in _debug
494 if ( strncmp(framework, &leaf[1], len) == 0 ) {
495 if ( strcmp( _imageSuffix, &leaf[len+1]) == 0 )
496 return frameworkStart;
497 }
498 }
499 }
500 }
501 }
502 }
503 return nullptr;
504 }
505
506
507 const char* PathOverrides::getLibraryLeafName(const char* path)
508 {
509 const char* start = strrchr(path, '/');
510 if ( start != nullptr )
511 return &start[1];
512 else
513 return path;
514 }
515
516
517
518 //////////////////////////// PathPool ////////////////////////////////////////
519
520
521 PathPool* PathPool::allocate()
522 {
523 vm_address_t addr;
524 ::vm_allocate(mach_task_self(), &addr, kAllocationSize, VM_FLAGS_ANYWHERE);
525 PathPool* p = (PathPool*)addr;
526 p->_next = nullptr;
527 p->_current = &(p->_buffer[0]);
528 p->_bytesFree = kAllocationSize - sizeof(PathPool);
529 return p;
530 }
531
532 void PathPool::deallocate(PathPool* pool) {
533 do {
534 PathPool* next = pool->_next;
535 ::vm_deallocate(mach_task_self(), (vm_address_t)pool, kAllocationSize);
536 pool = next;
537 } while (pool);
538 }
539
540 const char* PathPool::add(const char* path)
541 {
542 size_t len = strlen(path) + 1;
543 if ( len < _bytesFree ) {
544 char* result = _current;
545 strcpy(_current, path);
546 _current += len;
547 _bytesFree -= len;
548 return result;
549 }
550 if ( _next == nullptr )
551 _next = allocate();
552 return _next->add(path);
553 }
554
555 void PathPool::forEachPath(void (^handler)(const char* path)) const
556 {
557 for (const char* s = _buffer; s < _current; ++s) {
558 handler(s);
559 s += strlen(s);
560 }
561
562 if ( _next != nullptr )
563 _next->forEachPath(handler);
564 }
565
566 bool PathPool::contains(const char* path) const
567 {
568 for (const char* s = _buffer; s < _current; ++s) {
569 if ( strcmp(s, path) == 0 )
570 return true;
571 s += strlen(s);
572 }
573
574 if ( _next != nullptr )
575 return _next->contains(path);
576
577 return false;
578 }
579
580
581
582 } // namespace closure
583 } // namespace dyld3
584
585
586
587
588