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>
33 #include <sys/errno.h>
36 #include "PathOverrides.h"
43 PathOverrides gPathOverrides
;
47 // based on ANSI-C strstr()
48 static const char* strrstr(const char* str
, const char* sub
)
50 const size_t sublen
= strlen(sub
);
51 for(const char* p
= &str
[strlen(str
)]; p
!= str
; --p
) {
52 if ( strncmp(p
, sub
, sublen
) == 0 )
60 void PathOverrides::setEnvVars(const char* envp
[])
62 for (const char** p
= envp
; *p
!= NULL
; p
++) {
68 PathOverrides::PathOverrides(const std::vector
<std::string
>& env
)
70 for (const std::string
& envVar
: env
) {
71 addEnvVar(envVar
.c_str());
77 // libdyld is never unloaded
78 PathOverrides::~PathOverrides()
80 freeArray(_dylibPathOverrides
);
81 freeArray(_frameworkPathOverrides
);
82 freeArray(_frameworkPathFallbacks
);
83 freeArray(_dylibPathFallbacks
);
88 void PathOverrides::handleEnvVar(const char* key
, const char* value
, void (^handler
)(const char* envVar
)) const
90 if ( value
== nullptr )
92 size_t allocSize
= strlen(key
) + strlen(value
) + 2;
93 char buffer
[allocSize
];
94 strlcpy(buffer
, key
, allocSize
);
95 strlcat(buffer
, "=", allocSize
);
96 strlcat(buffer
, value
, allocSize
);
100 void PathOverrides::handleListEnvVar(const char* key
, const char** list
, void (^handler
)(const char* envVar
)) const
102 if ( list
== nullptr )
104 size_t allocSize
= strlen(key
) + 2;
105 for (const char** lp
=list
; *lp
!= nullptr; ++lp
)
106 allocSize
+= strlen(*lp
)+1;
107 char buffer
[allocSize
];
108 strlcpy(buffer
, key
, allocSize
);
109 strlcat(buffer
, "=", allocSize
);
110 bool needColon
= false;
111 for (const char** lp
=list
; *lp
!= nullptr; ++lp
) {
113 strlcat(buffer
, ":", allocSize
);
114 strlcat(buffer
, *lp
, allocSize
);
120 void PathOverrides::forEachEnvVar(void (^handler
)(const char* envVar
)) const
122 handleListEnvVar("DYLD_LIBRARY_PATH", _dylibPathOverrides
, handler
);
123 handleListEnvVar("DYLD_FRAMEWORK_PATH", _frameworkPathOverrides
, handler
);
124 handleListEnvVar("DYLD_FALLBACK_FRAMEWORK_PATH", _frameworkPathFallbacks
, handler
);
125 handleListEnvVar("DYLD_FALLBACK_LIBRARY_PATH", _dylibPathFallbacks
, handler
);
126 handleListEnvVar("DYLD_INSERT_LIBRARIES", _insertedDylibs
, handler
);
127 handleEnvVar( "DYLD_IMAGE_SUFFIX", _imageSuffix
, handler
);
128 handleEnvVar( "DYLD_ROOT_PATH", _rootPath
, handler
);
131 uint32_t PathOverrides::envVarCount() const
134 if ( _dylibPathOverrides
!= nullptr )
136 if ( _frameworkPathOverrides
!= nullptr )
138 if ( _frameworkPathFallbacks
!= nullptr )
140 if ( _dylibPathFallbacks
!= nullptr )
142 if ( _insertedDylibs
!= nullptr )
144 if ( _imageSuffix
!= nullptr )
146 if ( _rootPath
!= nullptr )
151 void PathOverrides::forEachInsertedDylib(void (^handler
)(const char* dylibPath
)) const
153 if ( _insertedDylibs
== nullptr )
155 for (const char** lp
=_insertedDylibs
; *lp
!= nullptr; ++lp
)
159 void PathOverrides::addEnvVar(const char* keyEqualsValue
)
161 const char* equals
= strchr(keyEqualsValue
, '=');
162 if ( equals
!= NULL
) {
163 const char* value
= &equals
[1];
164 const size_t keyLen
= equals
-keyEqualsValue
;
166 strncpy(key
, keyEqualsValue
, keyLen
);
168 if ( strcmp(key
, "DYLD_LIBRARY_PATH") == 0 ) {
169 _dylibPathOverrides
= parseColonListIntoArray(value
);
171 else if ( strcmp(key
, "DYLD_FRAMEWORK_PATH") == 0 ) {
172 _frameworkPathOverrides
= parseColonListIntoArray(value
);
174 else if ( strcmp(key
, "DYLD_FALLBACK_FRAMEWORK_PATH") == 0 ) {
175 _frameworkPathFallbacks
= parseColonListIntoArray(value
);
177 else if ( strcmp(key
, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) {
178 _dylibPathFallbacks
= parseColonListIntoArray(value
);
180 else if ( strcmp(key
, "DYLD_INSERT_LIBRARIES") == 0 ) {
181 _insertedDylibs
= parseColonListIntoArray(value
);
183 else if ( strcmp(key
, "DYLD_IMAGE_SUFFIX") == 0 ) {
184 _imageSuffix
= value
;
186 else if ( strcmp(key
, "DYLD_ROOT_PATH") == 0 ) {
192 void PathOverrides::forEachInColonList(const char* list
, void (^handler
)(const char* path
))
194 char buffer
[strlen(list
)+1];
195 const char* t
= list
;
196 for (const char* s
=list
; *s
!= '\0'; ++s
) {
200 memcpy(buffer
, t
, len
);
208 const char** PathOverrides::parseColonListIntoArray(const char* list
)
210 __block
int count
= 1;
211 forEachInColonList(list
, ^(const char* path
) {
214 const char** array
= (const char**)malloc(count
*sizeof(char*));
215 __block
const char** p
= array
;
216 forEachInColonList(list
, ^(const char* path
) {
223 void PathOverrides::freeArray(const char** array
)
225 if ( array
== nullptr )
228 for (const char** p
=array
; *p
!= nullptr; ++p
) {
234 void PathOverrides::forEachDylibFallback(Platform platform
, void (^handler
)(const char* fallbackDir
, bool& stop
)) const
237 if ( _dylibPathFallbacks
!= nullptr ) {
238 for (const char** fp
=_dylibPathFallbacks
; *fp
!= nullptr; ++fp
) {
245 switch ( platform
) {
246 case Platform::macOS
:
248 handler("/usr/local/lib", stop
); // FIXME: not for restricted processes
250 handler("/usr/lib", stop
);
253 case Platform::watchOS
:
255 case Platform::bridgeOS
:
256 case Platform::unknown
:
257 handler("/usr/local/lib", stop
);
259 handler("/usr/lib", stop
);
265 void PathOverrides::forEachFrameworkFallback(Platform platform
, void (^handler
)(const char* fallbackDir
, bool& stop
)) const
268 if ( _frameworkPathFallbacks
!= nullptr ) {
269 for (const char** fp
=_frameworkPathFallbacks
; *fp
!= nullptr; ++fp
) {
276 switch ( platform
) {
277 case Platform::macOS
:
278 // "$HOME/Library/Frameworks"
279 handler("/Library/Frameworks", stop
); // FIXME: not for restricted processes
280 // "/Network/Library/Frameworks"
282 handler("/System/Library/Frameworks", stop
);
285 case Platform::watchOS
:
287 case Platform::bridgeOS
:
288 case Platform::unknown
:
289 handler("/System/Library/Frameworks", stop
);
295 void PathOverrides::forEachPathVariant(const char* initialPath
,
299 void (^handler
)(const char* possiblePath
, bool& stop
)) const
302 Platform platform
= MachOParser::currentPlatform();
304 __block
bool stop
= false;
306 // check for overrides
307 const char* frameworkPartialPath
= getFrameworkPartialPath(initialPath
);
308 if ( frameworkPartialPath
!= nullptr ) {
309 const size_t frameworkPartialPathLen
= strlen(frameworkPartialPath
);
310 // look at each DYLD_FRAMEWORK_PATH directory
311 if ( _frameworkPathOverrides
!= nullptr ) {
312 for (const char** fp
=_frameworkPathOverrides
; *fp
!= nullptr; ++fp
) {
313 char npath
[strlen(*fp
)+frameworkPartialPathLen
+8];
316 strcat(npath
, frameworkPartialPath
);
317 handler(npath
, stop
);
324 const char* libraryLeafName
= getLibraryLeafName(initialPath
);
325 const size_t libraryLeafNameLen
= strlen(libraryLeafName
);
326 // look at each DYLD_LIBRARY_PATH directory
327 if ( _dylibPathOverrides
!= nullptr ) {
328 for (const char** lp
=_dylibPathOverrides
; *lp
!= nullptr; ++lp
) {
329 char libpath
[strlen(*lp
)+libraryLeafNameLen
+8];
330 strcpy(libpath
, *lp
);
331 strcat(libpath
, "/");
332 strcat(libpath
, libraryLeafName
);
333 handler(libpath
, stop
);
341 handler(initialPath
, stop
);
345 // check fallback paths
346 if ( frameworkPartialPath
!= nullptr ) {
347 const size_t frameworkPartialPathLen
= strlen(frameworkPartialPath
);
348 // look at each DYLD_FALLBACK_FRAMEWORK_PATH directory
349 forEachFrameworkFallback(platform
, ^(const char* dir
, bool& innerStop
) {
350 char npath
[strlen(dir
)+frameworkPartialPathLen
+8];
353 strcat(npath
, frameworkPartialPath
);
354 handler(npath
, innerStop
);
361 const char* libraryLeafName
= getLibraryLeafName(initialPath
);
362 const size_t libraryLeafNameLen
= strlen(libraryLeafName
);
363 // look at each DYLD_FALLBACK_LIBRARY_PATH directory
364 forEachDylibFallback(platform
, ^(const char* dir
, bool& innerStop
) {
365 char libpath
[strlen(dir
)+libraryLeafNameLen
+8];
366 strcpy(libpath
, dir
);
367 strcat(libpath
, "/");
368 strcat(libpath
, libraryLeafName
);
369 handler(libpath
, innerStop
);
378 // Find framework path
380 // /path/foo.framework/foo => foo.framework/foo
381 // /path/foo.framework/Versions/A/foo => foo.framework/Versions/A/foo
382 // /path/foo.framework/Frameworks/bar.framework/bar => bar.framework/bar
383 // /path/foo.framework/Libraries/bar.dylb => NULL
384 // /path/foo.framework/bar => NULL
386 // Returns nullptr if not a framework path
388 const char* PathOverrides::getFrameworkPartialPath(const char* path
) const
390 const char* dirDot
= strrstr(path
, ".framework/");
391 if ( dirDot
!= nullptr ) {
392 const char* dirStart
= dirDot
;
393 for ( ; dirStart
>= path
; --dirStart
) {
394 if ( (*dirStart
== '/') || (dirStart
== path
) ) {
395 const char* frameworkStart
= &dirStart
[1];
396 if ( dirStart
== path
)
398 size_t len
= dirDot
- frameworkStart
;
399 char framework
[len
+1];
400 strncpy(framework
, frameworkStart
, len
);
401 framework
[len
] = '\0';
402 const char* leaf
= strrchr(path
, '/');
403 if ( leaf
!= nullptr ) {
404 if ( strcmp(framework
, &leaf
[1]) == 0 ) {
405 return frameworkStart
;
407 if ( _imageSuffix
!= nullptr ) {
408 // some debug frameworks have install names that end in _debug
409 if ( strncmp(framework
, &leaf
[1], len
) == 0 ) {
410 if ( strcmp( _imageSuffix
, &leaf
[len
+1]) == 0 )
411 return frameworkStart
;
422 const char* PathOverrides::getLibraryLeafName(const char* path
)
424 const char* start
= strrchr(path
, '/');
425 if ( start
!= nullptr )