2 * Copyright (c) 2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
29 #include <uuid/uuid.h>
30 #include <mach/mach.h>
34 #include <sys/errno.h>
37 #include <initializer_list>
39 #include "PathOverrides.h"
47 PathOverrides gPathOverrides
;
51 // based on ANSI-C strstr()
52 static const char* strrstr(const char* str
, const char* sub
)
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 )
63 void PathOverrides::setFallbackPathHandling(FallbackPathMode mode
)
65 _fallbackPathMode
= mode
;
68 void PathOverrides::setEnvVars(const char* envp
[], const MachOFile
* mainExe
, const char* mainExePath
)
70 for (const char** p
= envp
; *p
!= NULL
; p
++) {
73 if ( mainExe
!= nullptr )
74 setMainExecutable(mainExe
, mainExePath
);
77 void PathOverrides::setMainExecutable(const dyld3::MachOFile
* mainExe
, const char* mainExePath
)
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);
89 // libdyld is never unloaded
90 PathOverrides::~PathOverrides()
95 void PathOverrides::forEachInsertedDylib(void (^handler
)(const char* dylibPath
, bool &stop
)) const
97 if ( _insertedDylibs
!= nullptr ) {
98 forEachInColonList(_insertedDylibs
, nullptr, ^(const char* path
, bool &stop
) {
104 void PathOverrides::handleEnvVar(const char* key
, const char* value
, void (^handler
)(const char* envVar
)) const
106 if ( value
== nullptr )
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
);
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
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
);
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
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
);
137 const char* PathOverrides::addString(const char* str
)
139 if ( _pathPool
== nullptr )
140 _pathPool
= PathPool::allocate();
141 return _pathPool
->add(str
);
144 void PathOverrides::setString(const char*& var
, const char* value
)
146 if ( var
== nullptr ) {
147 var
= addString(value
);
150 // string already in use, build new appended string
151 char tmp
[strlen(var
)+strlen(value
)+2];
155 var
= addString(tmp
);
158 void PathOverrides::addEnvVar(const char* keyEqualsValue
, bool forExecutable
)
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]);
168 else if ( strncmp(keyEqualsValue
, "DYLD_FRAMEWORK_PATH", 19) == 0 ) {
169 setString(forExecutable
? _frameworkPathOverridesExeLC
: _frameworkPathOverridesEnv
, &keyEqualsValue
[20]);
171 else if ( strncmp(keyEqualsValue
, "DYLD_FALLBACK_FRAMEWORK_PATH", 28) == 0 ) {
172 setString(forExecutable
? _frameworkPathFallbacksExeLC
: _frameworkPathFallbacksEnv
, &keyEqualsValue
[29]);
174 else if ( strncmp(keyEqualsValue
, "DYLD_FALLBACK_LIBRARY_PATH", 26) == 0 ) {
175 setString(forExecutable
? _dylibPathFallbacksExeLC
: _dylibPathFallbacksEnv
, &keyEqualsValue
[27]);
177 else if ( strncmp(keyEqualsValue
, "DYLD_INSERT_LIBRARIES", 21) == 0 ) {
178 setString(_insertedDylibs
, &keyEqualsValue
[22]);
180 else if ( strncmp(keyEqualsValue
, "DYLD_IMAGE_SUFFIX", 17) == 0 ) {
181 setString(_imageSuffix
, &keyEqualsValue
[18]);
183 else if ( strncmp(keyEqualsValue
, "DYLD_ROOT_PATH", 14) == 0 ) {
184 setString(_rootPath
, &keyEqualsValue
[15]);
189 void PathOverrides::forEachInColonList(const char* list1
, const char* list2
, void (^handler
)(const char* path
, bool& stop
))
191 for (const char* list
: { list1
, list2
}) {
194 char buffer
[strlen(list
)+1];
195 const char* t
= list
;
197 for (const char* s
=list
; *s
!= '\0'; ++s
) {
201 memcpy(buffer
, t
, len
);
203 handler(buffer
, stop
);
214 void PathOverrides::forEachDylibFallback(Platform platform
, void (^handler
)(const char* fallbackDir
, bool& stop
)) const
216 __block
bool stop
= false;
217 if ( (_dylibPathFallbacksEnv
!= nullptr) || (_dylibPathFallbacksExeLC
!= nullptr) ) {
218 forEachInColonList(_dylibPathFallbacksEnv
, _dylibPathFallbacksExeLC
, ^(const char* pth
, bool& innerStop
) {
219 handler(pth
, innerStop
);
225 switch ( platform
) {
226 case Platform::macOS
:
227 switch ( _fallbackPathMode
) {
228 case FallbackPathMode::classic
:
230 handler("/usr/local/lib", stop
);
233 [[clang::fallthrough]];
234 case FallbackPathMode::restricted
:
235 handler("/usr/lib", stop
);
237 case FallbackPathMode::none
:
242 case Platform::watchOS
:
244 case Platform::bridgeOS
:
245 case Platform::driverKit
:
246 case Platform::unknown
:
247 if ( _fallbackPathMode
!= FallbackPathMode::none
) {
248 handler("/usr/local/lib", stop
);
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
);
265 void PathOverrides::forEachFrameworkFallback(Platform platform
, void (^handler
)(const char* fallbackDir
, bool& stop
)) const
267 __block
bool stop
= false;
268 if ( (_frameworkPathFallbacksEnv
!= nullptr) || (_frameworkPathFallbacksExeLC
!= nullptr) ) {
269 forEachInColonList(_frameworkPathFallbacksEnv
, _frameworkPathFallbacksExeLC
, ^(const char* pth
, bool& innerStop
) {
270 handler(pth
, innerStop
);
276 switch ( platform
) {
277 case Platform::macOS
:
278 switch ( _fallbackPathMode
) {
279 case FallbackPathMode::classic
:
280 // "$HOME/Library/Frameworks"
281 handler("/Library/Frameworks", stop
);
284 // "/Network/Library/Frameworks"
286 [[clang::fallthrough]];
287 case FallbackPathMode::restricted
:
288 handler("/System/Library/Frameworks", stop
);
290 case FallbackPathMode::none
:
295 case Platform::watchOS
:
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
);
313 // copy path and add suffix to result
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
321 void PathOverrides::addSuffix(const char* path
, const char* suffix
, char* result
) const
323 strcpy(result
, path
);
326 char* start
= strrchr(result
, '/');
332 // find last dot after last slash
333 char* dot
= strrchr(start
, '.');
336 strcat(&dot
[strlen(suffix
)], &path
[dot
-result
]);
339 strcat(result
, suffix
);
343 void PathOverrides::forEachImageSuffix(const char* path
, bool isFallbackPath
, bool pathIsInDyldCacheWhichCannotBeOverridden
, bool& stop
, void (^handler
)(const char* possiblePath
, bool isFallbackPath
, bool& stop
)) const
345 if ( (_imageSuffix
== nullptr) || pathIsInDyldCacheWhichCannotBeOverridden
) {
346 handler(path
, isFallbackPath
, stop
);
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
);
357 handler(path
, isFallbackPath
, stop
);
361 void PathOverrides::forEachPathVariant(const char* initialPath
, bool pathIsInDyldCacheWhichCannotBeOverridden
, void (^handler
)(const char* possiblePath
, bool isFallbackPath
, bool& stop
), Platform platform
) const
363 __block
bool stop
= false;
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
);
377 strcat(npath
, frameworkPartialPath
);
378 forEachImageSuffix(npath
, false, pathIsInDyldCacheWhichCannotBeOverridden
, innerStop
, handler
);
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
);
393 strcat(npath
, libraryLeafName
);
394 forEachImageSuffix(npath
, false, pathIsInDyldCacheWhichCannotBeOverridden
, innerStop
, handler
);
405 forEachImageSuffix(initialPath
, false, pathIsInDyldCacheWhichCannotBeOverridden
, stop
, handler
);
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];
418 strcat(npath
, frameworkPartialPath
);
419 forEachImageSuffix(npath
, usesDefaultFallbackPaths
, pathIsInDyldCacheWhichCannotBeOverridden
, innerStop
, handler
);
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
);
444 // Find framework path
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
452 // Returns nullptr if not a framework path
454 const char* PathOverrides::getFrameworkPartialPath(const char* path
) const
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
)
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
;
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
;
488 const char* PathOverrides::getLibraryLeafName(const char* path
)
490 const char* start
= strrchr(path
, '/');
491 if ( start
!= nullptr )
499 //////////////////////////// PathPool ////////////////////////////////////////
502 PathPool
* PathPool::allocate()
505 ::vm_allocate(mach_task_self(), &addr
, kAllocationSize
, VM_FLAGS_ANYWHERE
);
506 PathPool
* p
= (PathPool
*)addr
;
508 p
->_current
= &(p
->_buffer
[0]);
509 p
->_bytesFree
= kAllocationSize
- sizeof(PathPool
);
513 void PathPool::deallocate(PathPool
* pool
) {
515 PathPool
* next
= pool
->_next
;
516 ::vm_deallocate(mach_task_self(), (vm_address_t
)pool
, kAllocationSize
);
521 const char* PathPool::add(const char* path
)
523 size_t len
= strlen(path
) + 1;
524 if ( len
< _bytesFree
) {
525 char* result
= _current
;
526 strcpy(_current
, path
);
531 if ( _next
== nullptr )
533 return _next
->add(path
);
536 void PathPool::forEachPath(void (^handler
)(const char* path
))
538 for (const char* s
= _buffer
; s
< _current
; ++s
) {
543 if ( _next
!= nullptr )
544 _next
->forEachPath(handler
);
549 } // namespace closure