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