dyld-732.8.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
37 #include <initializer_list>
38
39 #include "PathOverrides.h"
40
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 ) {
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 original path
405 forEachImageSuffix(initialPath, false, pathIsInDyldCacheWhichCannotBeOverridden, stop, handler);
406 if ( stop )
407 return;
408
409 // check fallback paths
410 if ( const char* frameworkPartialPath = getFrameworkPartialPath(initialPath) ) {
411 const size_t frameworkPartialPathLen = strlen(frameworkPartialPath);
412 // look at each DYLD_FALLBACK_FRAMEWORK_PATH directory
413 bool usesDefaultFallbackPaths = (_frameworkPathFallbacksEnv == nullptr) && (_frameworkPathFallbacksExeLC == nullptr);
414 forEachFrameworkFallback(platform, ^(const char* dir, bool& innerStop) {
415 char npath[strlen(dir)+frameworkPartialPathLen+8];
416 strcpy(npath, dir);
417 strcat(npath, "/");
418 strcat(npath, frameworkPartialPath);
419 forEachImageSuffix(npath, usesDefaultFallbackPaths, pathIsInDyldCacheWhichCannotBeOverridden, innerStop, handler);
420 if ( innerStop )
421 stop = true;
422 });
423
424 }
425 else {
426 const char* libraryLeafName = getLibraryLeafName(initialPath);
427 const size_t libraryLeafNameLen = strlen(libraryLeafName);
428 // look at each DYLD_FALLBACK_LIBRARY_PATH directory
429 bool usesDefaultFallbackPaths = (_dylibPathFallbacksEnv == nullptr) && (_dylibPathFallbacksExeLC == nullptr);
430 forEachDylibFallback(platform, ^(const char* dir, bool& innerStop) {
431 char libpath[strlen(dir)+libraryLeafNameLen+8];
432 strcpy(libpath, dir);
433 strcat(libpath, "/");
434 strcat(libpath, libraryLeafName);
435 forEachImageSuffix(libpath, usesDefaultFallbackPaths, pathIsInDyldCacheWhichCannotBeOverridden, innerStop, handler);
436 if ( innerStop )
437 stop = true;
438 });
439 }
440 }
441
442
443 //
444 // Find framework path
445 //
446 // /path/foo.framework/foo => foo.framework/foo
447 // /path/foo.framework/Versions/A/foo => foo.framework/Versions/A/foo
448 // /path/foo.framework/Frameworks/bar.framework/bar => bar.framework/bar
449 // /path/foo.framework/Libraries/bar.dylb => NULL
450 // /path/foo.framework/bar => NULL
451 //
452 // Returns nullptr if not a framework path
453 //
454 const char* PathOverrides::getFrameworkPartialPath(const char* path) const
455 {
456 const char* dirDot = strrstr(path, ".framework/");
457 if ( dirDot != nullptr ) {
458 const char* dirStart = dirDot;
459 for ( ; dirStart >= path; --dirStart) {
460 if ( (*dirStart == '/') || (dirStart == path) ) {
461 const char* frameworkStart = &dirStart[1];
462 if ( dirStart == path )
463 --frameworkStart;
464 size_t len = dirDot - frameworkStart;
465 char framework[len+1];
466 strncpy(framework, frameworkStart, len);
467 framework[len] = '\0';
468 const char* leaf = strrchr(path, '/');
469 if ( leaf != nullptr ) {
470 if ( strcmp(framework, &leaf[1]) == 0 ) {
471 return frameworkStart;
472 }
473 if ( _imageSuffix != nullptr ) {
474 // some debug frameworks have install names that end in _debug
475 if ( strncmp(framework, &leaf[1], len) == 0 ) {
476 if ( strcmp( _imageSuffix, &leaf[len+1]) == 0 )
477 return frameworkStart;
478 }
479 }
480 }
481 }
482 }
483 }
484 return nullptr;
485 }
486
487
488 const char* PathOverrides::getLibraryLeafName(const char* path)
489 {
490 const char* start = strrchr(path, '/');
491 if ( start != nullptr )
492 return &start[1];
493 else
494 return path;
495 }
496
497
498
499 //////////////////////////// PathPool ////////////////////////////////////////
500
501
502 PathPool* PathPool::allocate()
503 {
504 vm_address_t addr;
505 ::vm_allocate(mach_task_self(), &addr, kAllocationSize, VM_FLAGS_ANYWHERE);
506 PathPool* p = (PathPool*)addr;
507 p->_next = nullptr;
508 p->_current = &(p->_buffer[0]);
509 p->_bytesFree = kAllocationSize - sizeof(PathPool);
510 return p;
511 }
512
513 void PathPool::deallocate(PathPool* pool) {
514 do {
515 PathPool* next = pool->_next;
516 ::vm_deallocate(mach_task_self(), (vm_address_t)pool, kAllocationSize);
517 pool = next;
518 } while (pool);
519 }
520
521 const char* PathPool::add(const char* path)
522 {
523 size_t len = strlen(path) + 1;
524 if ( len < _bytesFree ) {
525 char* result = _current;
526 strcpy(_current, path);
527 _current += len;
528 _bytesFree -= len;
529 return result;
530 }
531 if ( _next == nullptr )
532 _next = allocate();
533 return _next->add(path);
534 }
535
536 void PathPool::forEachPath(void (^handler)(const char* path))
537 {
538 for (const char* s = _buffer; s < _current; ++s) {
539 handler(s);
540 s += strlen(s);
541 }
542
543 if ( _next != nullptr )
544 _next->forEachPath(handler);
545 }
546
547
548
549 } // namespace closure
550 } // namespace dyld3
551
552
553
554
555