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 "PathOverrides.h"
45 PathOverrides gPathOverrides
;
49 // based on ANSI-C strstr()
50 static const char* strrstr(const char* str
, const char* sub
)
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 )
61 void PathOverrides::setFallbackPathHandling(FallbackPathMode mode
)
63 _fallbackPathMode
= mode
;
66 void PathOverrides::setEnvVars(const char* envp
[], const MachOFile
* mainExe
, const char* mainExePath
)
68 for (const char** p
= envp
; *p
!= NULL
; p
++) {
71 if ( mainExe
!= nullptr )
72 setMainExecutable(mainExe
, mainExePath
);
75 void PathOverrides::setMainExecutable(const dyld3::MachOFile
* mainExe
, const char* mainExePath
)
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
) {
87 // libdyld is never unloaded
88 PathOverrides::~PathOverrides()
93 uint32_t PathOverrides::envVarCount() const
96 if ( _dylibPathOverrides
!= nullptr )
98 if ( _frameworkPathOverrides
!= nullptr )
100 if ( _frameworkPathFallbacks
!= nullptr )
102 if ( _dylibPathFallbacks
!= nullptr )
104 if ( _insertedDylibs
!= nullptr )
106 if ( _imageSuffix
!= nullptr )
108 if ( _rootPath
!= nullptr )
113 void PathOverrides::forEachInsertedDylib(void (^handler
)(const char* dylibPath
)) const
115 if ( _insertedDylibs
!= nullptr ) {
116 forEachInColonList(_insertedDylibs
, ^(const char* path
, bool &stop
) {
122 void PathOverrides::handleEnvVar(const char* key
, const char* value
, void (^handler
)(const char* envVar
)) const
124 if ( value
== nullptr )
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
);
134 void PathOverrides::forEachEnvVar(void (^handler
)(const char* envVar
)) const
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
);
145 const char* PathOverrides::addString(const char* str
)
147 if ( _pathPool
== nullptr )
148 _pathPool
= PathPool::allocate();
149 return _pathPool
->add(str
);
152 void PathOverrides::setString(const char*& var
, const char* value
)
154 if ( var
== nullptr ) {
155 var
= addString(value
);
158 // string already in use, build new appended string
159 char tmp
[strlen(var
)+strlen(value
)+2];
163 var
= addString(tmp
);
166 void PathOverrides::addEnvVar(const char* keyEqualsValue
)
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]);
176 else if ( strncmp(keyEqualsValue
, "DYLD_FRAMEWORK_PATH", 19) == 0 ) {
177 setString(_frameworkPathOverrides
, &keyEqualsValue
[20]);
179 else if ( strncmp(keyEqualsValue
, "DYLD_FALLBACK_FRAMEWORK_PATH", 28) == 0 ) {
180 setString(_frameworkPathFallbacks
, &keyEqualsValue
[29]);
182 else if ( strncmp(keyEqualsValue
, "DYLD_FALLBACK_LIBRARY_PATH", 26) == 0 ) {
183 setString(_dylibPathFallbacks
, &keyEqualsValue
[27]);
185 else if ( strncmp(keyEqualsValue
, "DYLD_INSERT_LIBRARIES", 21) == 0 ) {
186 setString(_insertedDylibs
, &keyEqualsValue
[22]);
188 else if ( strncmp(keyEqualsValue
, "DYLD_IMAGE_SUFFIX", 17) == 0 ) {
189 setString(_imageSuffix
, &keyEqualsValue
[18]);
191 else if ( strncmp(keyEqualsValue
, "DYLD_ROOT_PATH", 14) == 0 ) {
192 setString(_rootPath
, &keyEqualsValue
[15]);
197 void PathOverrides::forEachInColonList(const char* list
, void (^handler
)(const char* path
, bool& stop
))
199 char buffer
[strlen(list
)+1];
200 const char* t
= list
;
202 for (const char* s
=list
; *s
!= '\0'; ++s
) {
206 memcpy(buffer
, t
, len
);
208 handler(buffer
, stop
);
216 void PathOverrides::forEachDylibFallback(Platform platform
, void (^handler
)(const char* fallbackDir
, bool& stop
)) const
218 __block
bool stop
= false;
219 if ( _dylibPathFallbacks
!= nullptr ) {
220 forEachInColonList(_dylibPathFallbacks
, ^(const char* pth
, bool& innerStop
) {
221 handler(pth
, innerStop
);
227 switch ( platform
) {
228 case Platform::macOS
:
229 switch ( _fallbackPathMode
) {
230 case FallbackPathMode::classic
:
232 handler("/usr/local/lib", stop
);
236 case FallbackPathMode::restricted
:
237 handler("/usr/lib", stop
);
239 case FallbackPathMode::none
:
244 case Platform::watchOS
:
246 case Platform::bridgeOS
:
247 case Platform::unknown
:
248 if ( _fallbackPathMode
!= FallbackPathMode::none
) {
249 handler("/usr/local/lib", stop
);
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
);
265 void PathOverrides::forEachFrameworkFallback(Platform platform
, void (^handler
)(const char* fallbackDir
, bool& stop
)) const
267 __block
bool stop
= false;
268 if ( _frameworkPathFallbacks
!= nullptr ) {
269 forEachInColonList(_frameworkPathFallbacks
, ^(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 case FallbackPathMode::restricted
:
287 handler("/System/Library/Frameworks", stop
);
289 case FallbackPathMode::none
:
294 case Platform::watchOS
:
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
);
311 // copy path and add suffix to result
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
319 void PathOverrides::addSuffix(const char* path
, const char* suffix
, char* result
) const
321 strcpy(result
, path
);
324 char* start
= strrchr(result
, '/');
330 // find last dot after last slash
331 char* dot
= strrchr(start
, '.');
334 strcat(&dot
[strlen(suffix
)], &path
[dot
-result
]);
337 strcat(result
, suffix
);
341 void PathOverrides::forEachImageSuffix(const char* path
, bool isFallbackPath
, bool& stop
, void (^handler
)(const char* possiblePath
, bool isFallbackPath
, bool& stop
)) const
343 if ( _imageSuffix
== nullptr ) {
344 handler(path
, isFallbackPath
, stop
);
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
);
355 handler(path
, isFallbackPath
, stop
);
359 void PathOverrides::forEachPathVariant(const char* initialPath
, void (^handler
)(const char* possiblePath
, bool isFallbackPath
, bool& stop
), Platform platform
) const
361 __block
bool stop
= false;
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
);
373 strcat(npath
, frameworkPartialPath
);
374 forEachImageSuffix(npath
, false, innerStop
, handler
);
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
);
389 strcat(npath
, libraryLeafName
);
390 forEachImageSuffix(npath
, false, innerStop
, handler
);
400 forEachImageSuffix(initialPath
, false, stop
, handler
);
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];
413 strcat(npath
, frameworkPartialPath
);
414 forEachImageSuffix(npath
, usesDefaultFallbackPaths
, innerStop
, handler
);
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
);
439 // Find framework path
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
447 // Returns nullptr if not a framework path
449 const char* PathOverrides::getFrameworkPartialPath(const char* path
) const
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
)
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
;
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
;
483 const char* PathOverrides::getLibraryLeafName(const char* path
)
485 const char* start
= strrchr(path
, '/');
486 if ( start
!= nullptr )
494 //////////////////////////// PathPool ////////////////////////////////////////
497 PathPool
* PathPool::allocate()
500 ::vm_allocate(mach_task_self(), &addr
, kAllocationSize
, VM_FLAGS_ANYWHERE
);
501 PathPool
* p
= (PathPool
*)addr
;
503 p
->_current
= &(p
->_buffer
[0]);
504 p
->_bytesFree
= kAllocationSize
- sizeof(PathPool
);
508 void PathPool::deallocate(PathPool
* pool
) {
510 PathPool
* next
= pool
->_next
;
511 ::vm_deallocate(mach_task_self(), (vm_address_t
)pool
, kAllocationSize
);
516 const char* PathPool::add(const char* path
)
518 size_t len
= strlen(path
) + 1;
519 if ( len
< _bytesFree
) {
520 char* result
= _current
;
521 strcpy(_current
, path
);
526 if ( _next
== nullptr )
528 return _next
->add(path
);
531 void PathPool::forEachPath(void (^handler
)(const char* path
))
533 for (const char* s
= _buffer
; s
< _current
; ++s
) {
538 if ( _next
!= nullptr )
539 _next
->forEachPath(handler
);
544 } // namespace closure