/ CoreFoundation / PlugIn.subproj / CFBundle_Executable.c
CFBundle_Executable.c
  1  /*      CFBundle_Executable.c
  2  	Copyright (c) 1999-2017, Apple Inc. and the Swift project authors
  3   
  4  	Portions Copyright (c) 2014-2017, Apple Inc. and the Swift project authors
  5  	Licensed under Apache License v2.0 with Runtime Library Exception
  6  	See http://swift.org/LICENSE.txt for license information
  7  	See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
  8          Responsibility: Tony Parker
  9  */
 10  
 11  #include <CoreFoundation/CFBase.h>
 12  #include <CoreFoundation/CFBundle.h>
 13  #include "CFBundle_Internal.h"
 14  
 15  #if TARGET_OS_IPHONE
 16  #include <dlfcn.h>
 17  #endif
 18  
 19  #if !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID
 20  
 21      #if TARGET_OS_LINUX
 22          #if TARGET_RT_64_BIT
 23              #define _CFBundleFHSArchDirectorySuffix "64"
 24          #else // !TARGET_RT_64_BIT
 25              #define _CFBundleFHSArchDirectorySuffix "32"
 26          #endif // TARGET_RT_64_BIT
 27      #endif // TARGET_OS_LINUX
 28  
 29      CONST_STRING_DECL(_kCFBundleFHSDirectory_bin, "bin");
 30      CONST_STRING_DECL(_kCFBundleFHSDirectory_sbin, "sbin");
 31      CONST_STRING_DECL(_kCFBundleFHSDirectory_lib, "lib");
 32      #if TARGET_OS_LINUX
 33          CONST_STRING_DECL(_kCFBundleFHSDirectory_libWithArchSuffix, "lib" _CFBundleFHSArchDirectorySuffix);
 34      #endif
 35  
 36      #define _CFBundleFHSExecutablesDirectorySuffix CFSTR(".executables")
 37      #define _CFBundleFHSDirectoryCLiteral_libexec "libexec"
 38  
 39  #if TARGET_OS_LINUX
 40      #define _CFBundleFHSDirectoriesInExecutableSearchOrder \
 41          _kCFBundleFHSDirectory_bin, \
 42          _kCFBundleFHSDirectory_sbin, \
 43          _kCFBundleFHSDirectory_libWithArchSuffix, \
 44          _kCFBundleFHSDirectory_lib
 45  #else
 46      #define _CFBundleFHSDirectoriesInExecutableSearchOrder \
 47          _kCFBundleFHSDirectory_bin, \
 48          _kCFBundleFHSDirectory_sbin, \
 49          _kCFBundleFHSDirectory_lib
 50  #endif // TARGET_OS_LINUX
 51  
 52  #endif // !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID
 53  
 54  // This is here because on iPhoneOS with the dyld shared cache, we remove binaries from their
 55  // original locations on disk, so checking whether a binary's path exists is no longer sufficient.
 56  // For performance reasons, we only call dlopen_preflight() after we've verified that the binary
 57  // does not exist at its original path with _CFURLExists().
 58  // See <rdar://problem/6956670>
 59  static Boolean _binaryLoadable(CFURLRef url) {
 60      Boolean loadable = _CFURLExists(url);
 61  #if TARGET_OS_IPHONE
 62      if (!loadable) {
 63          uint8_t path[PATH_MAX];
 64          if (url && CFURLGetFileSystemRepresentation(url, true, path, sizeof(path))) {
 65              loadable = dlopen_preflight((char *)path);
 66          }
 67      }
 68  #endif
 69      return loadable;
 70  }
 71  
 72  static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeName) {
 73      // Given an url to a folder and a name, this returns the url to the executable in that folder with that name, if it exists, and NULL otherwise.  This function deals with appending the ".exe" or ".dll" on Windows.
 74      CFURLRef executableURL = NULL;
 75      if (!urlPath || !exeName) return NULL;
 76      
 77  #if !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32
 78      if (!executableURL) {
 79          executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, kCFURLPOSIXPathStyle, false, urlPath);
 80          if (!_binaryLoadable(executableURL)) {
 81              CFRelease(executableURL);
 82              
 83              CFStringRef sharedLibraryName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@%@"), _CFBundleFHSSharedLibraryFilenamePrefix, exeName, _CFBundleFHSSharedLibraryFilenameSuffix);
 84              
 85              executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, sharedLibraryName, kCFURLPOSIXPathStyle, false, urlPath);
 86              if (!_binaryLoadable(executableURL)) {
 87                  CFRelease(executableURL);
 88                  executableURL = NULL;
 89              }
 90              if (sharedLibraryName) CFRelease(sharedLibraryName);
 91          }
 92      }
 93  #elif TARGET_OS_MAC
 94      const uint8_t *image_suffix = (uint8_t *)__CFgetenvIfNotRestricted("DYLD_IMAGE_SUFFIX");
 95      
 96      if (image_suffix) {
 97          CFStringRef newExeName, imageSuffix;
 98          imageSuffix = CFStringCreateWithCString(kCFAllocatorSystemDefault, (char *)image_suffix, kCFStringEncodingUTF8);
 99          if (CFStringHasSuffix(exeName, CFSTR(".dylib"))) {
100              CFStringRef bareExeName = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, exeName, CFRangeMake(0, CFStringGetLength(exeName)-6));
101              newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@.dylib"), exeName, imageSuffix);
102              CFRelease(bareExeName);
103          } else {
104              newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), exeName, imageSuffix);
105          }
106          executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, newExeName, kCFURLPOSIXPathStyle, false, urlPath);
107          if (executableURL && !_binaryLoadable(executableURL)) {
108              CFRelease(executableURL);
109              executableURL = NULL;
110          }
111          CFRelease(newExeName);
112          CFRelease(imageSuffix);
113      }
114      if (!executableURL) {
115          executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, kCFURLPOSIXPathStyle, false, urlPath);
116          if (executableURL && !_binaryLoadable(executableURL)) {
117              CFRelease(executableURL);
118              executableURL = NULL;
119          }
120      }
121  #elif TARGET_OS_WIN32
122      if (!executableURL) {
123          executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, kCFURLWindowsPathStyle, false, urlPath);
124          if (executableURL && !_binaryLoadable(executableURL)) {
125              CFRelease(executableURL);
126              executableURL = NULL;
127          }
128      }
129      if (!executableURL) {
130          if (!CFStringFindWithOptions(exeName, CFSTR(".dll"), CFRangeMake(0, CFStringGetLength(exeName)), kCFCompareAnchored|kCFCompareBackwards|kCFCompareCaseInsensitive, NULL)) {
131  #if defined(DEBUG)
132              CFStringRef extension = CFSTR("_debug.dll");
133  #else
134              CFStringRef extension = CFSTR(".dll");
135  #endif
136              CFStringRef newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), exeName, extension);
137              executableURL = CFURLCreateWithString(kCFAllocatorSystemDefault, newExeName, urlPath);
138              if (executableURL && !_binaryLoadable(executableURL)) {
139                  CFRelease(executableURL);
140                  executableURL = NULL;
141              }
142              CFRelease(newExeName);
143          }
144      }
145      if (!executableURL) {
146          if (!CFStringFindWithOptions(exeName, CFSTR(".exe"), CFRangeMake(0, CFStringGetLength(exeName)), kCFCompareAnchored|kCFCompareBackwards|kCFCompareCaseInsensitive, NULL)) {
147  #if defined(DEBUG)
148              CFStringRef extension = CFSTR("_debug.exe");
149  #else
150              CFStringRef extension = CFSTR(".exe");
151  #endif
152              CFStringRef newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), exeName, extension);
153              executableURL = CFURLCreateWithString(kCFAllocatorSystemDefault, newExeName, urlPath);
154              if (executableURL && !_binaryLoadable(executableURL)) {
155                  CFRelease(executableURL);
156                  executableURL = NULL;
157              }
158              CFRelease(newExeName);
159          }
160      }
161  #endif
162      return executableURL;
163  }
164  
165  static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURLRef url, CFStringRef executableName, Boolean ignoreCache) {
166      uint8_t version = 0;
167      CFDictionaryRef infoDict = NULL;
168      CFStringRef executablePath = NULL;
169      CFURLRef executableURL = NULL;
170      Boolean foundIt = false;
171      Boolean lookupMainExe = (executableName ? false : true);
172      
173      if (bundle) {
174          infoDict = CFBundleGetInfoDictionary(bundle);
175          version = bundle->_version;
176      } else {
177          infoDict = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault, url, &version);
178      }
179      
180      // If we have a bundle instance and an info dict, see if we have already cached the path
181      if (lookupMainExe && !ignoreCache && bundle && bundle->_executablePath) {
182          __CFLock(&bundle->_lock);
183          executablePath = bundle->_executablePath;
184          if (executablePath) CFRetain(executablePath);
185          __CFUnlock(&bundle->_lock);
186          if (executablePath) {
187              executableURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, executablePath, PLATFORM_PATH_STYLE, false);
188              if (executableURL) {
189                  foundIt = true;
190              }
191              CFRelease(executablePath);
192          }
193      }
194      
195      if (!foundIt) {
196          if (lookupMainExe) executableName = _CFBundleCopyExecutableName(bundle, url, infoDict);
197          if (executableName) {
198  #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
199              Boolean doExecSearch = false;
200  #else
201              Boolean doExecSearch = true;
202  #endif
203              
204  #if !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID
205              if (lookupMainExe && bundle && bundle->_isFHSInstalledBundle) {
206                  // For a FHS installed bundle, the URL points to share/Bundle.resources, and the binary is in:
207                  
208                  CFURLRef sharePath = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url);
209                  CFURLRef prefixPath = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, sharePath);
210                  CFRelease(sharePath);
211  
212                  CFStringRef directories[] = { _CFBundleFHSDirectoriesInExecutableSearchOrder };
213                  size_t directoriesCount = sizeof(directories) / sizeof(directories[0]);
214                  
215                  for (size_t i = 0; i < directoriesCount; i++) {
216                      CFURLRef where = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, prefixPath, directories[i], true);
217                      executableURL = _CFBundleCopyExecutableURLRaw(where, executableName);
218                      CFRelease(where);
219                      
220                      if (executableURL) {
221                          foundIt = true;
222                          break;
223                      }
224                  }
225                  
226                  CFRelease(prefixPath);
227              }
228  #endif // !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID
229              
230              // Now, look for the executable inside the bundle.
231              if (!foundIt && doExecSearch && 0 != version) {
232                  CFURLRef exeDirURL = NULL;
233                  
234  #if !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID
235                  if (bundle && bundle->_isFHSInstalledBundle) {
236                      CFURLRef withoutExtension = CFURLCreateCopyDeletingPathExtension(kCFAllocatorSystemDefault, url);
237                      CFStringRef lastPathComponent = CFURLCopyLastPathComponent(withoutExtension);
238                      
239                      CFURLRef libexec = CFURLCreateWithString(kCFAllocatorSystemDefault, CFSTR("../../" _CFBundleFHSDirectoryCLiteral_libexec), url);
240                      
241                      CFStringRef exeDirName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), lastPathComponent, _CFBundleFHSExecutablesDirectorySuffix);
242                      exeDirURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, libexec, exeDirName, true);
243                      
244                      CFRelease(withoutExtension);
245                      CFRelease(lastPathComponent);
246                      CFRelease(libexec);
247                      CFRelease(exeDirName);
248                  } else
249  #endif // !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID
250                  if (1 == version) {
251                      exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase1, url);
252                  } else if (2 == version) {
253                      exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase2, url);
254                  } else {
255  #if TARGET_OS_WIN32 || !DEPLOYMENT_RUNTIME_OBJC
256                      // On Windows and on targets that support FHS bundles, if the bundle URL is foo.resources, then the executable is at the same level as the .resources directory
257                      CFStringRef extension = CFURLCopyPathExtension(url);
258                      if (extension && CFEqual(extension, _CFBundleSiblingResourceDirectoryExtension)) {
259                          exeDirURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url);
260                      } else {
261                          exeDirURL = (CFURLRef)CFRetain(url);
262                      }
263                      if (extension) CFRelease(extension);
264  #else
265                      exeDirURL = (CFURLRef)CFRetain(url);
266  #endif
267                  }
268                  
269                  // Historical note: This used to search the directories "Mac OS X", "MacOSClassic", then "MacOS8". As of 10.13 we only look in "MacOS".
270                  CFURLRef exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, _CFBundleGetPlatformExecutablesSubdirectoryName(), kCFURLPOSIXPathStyle, true, exeDirURL);
271                  executableURL = _CFBundleCopyExecutableURLRaw(exeSubdirURL, executableName);
272                  CFRelease(exeSubdirURL);
273                  
274                  if (!executableURL) executableURL = _CFBundleCopyExecutableURLRaw(exeDirURL, executableName);
275                  CFRelease(exeDirURL);
276              }
277              
278              // If this was an old bundle, or we did not find the executable in the Executables subdirectory, look directly in the bundle wrapper.
279              if (!executableURL) executableURL = _CFBundleCopyExecutableURLRaw(url, executableName);
280              
281  #if TARGET_OS_WIN32
282              // Windows only: If we still haven't found the exe, look in the Executables folder.
283              // But only for the main bundle exe
284              if (lookupMainExe && !executableURL) {
285                  CFURLRef exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, CFSTR("../../Executables"), url);
286                  executableURL = _CFBundleCopyExecutableURLRaw(exeDirURL, executableName);
287                  CFRelease(exeDirURL);
288              }
289  #endif
290              
291              if (lookupMainExe && !ignoreCache && bundle && executableURL) {
292                  // We found it.  Cache the path.
293                  CFURLRef absURL = CFURLCopyAbsoluteURL(executableURL);
294                  executablePath = CFURLCopyFileSystemPath(absURL, PLATFORM_PATH_STYLE);
295                  CFRelease(absURL);
296                  __CFLock(&bundle->_lock);
297                  bundle->_executablePath = (CFStringRef)CFRetain(executablePath);
298                  __CFUnlock(&bundle->_lock);
299                  CFRelease(executablePath);
300              }
301              if (lookupMainExe && bundle && !executableURL) bundle->_binaryType = __CFBundleNoBinary;
302              if (lookupMainExe) CFRelease(executableName);
303          }
304      }
305      if (!bundle && infoDict) CFRelease(infoDict);
306      return executableURL;
307  }
308  
309  static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) {
310      //!!! need to handle frameworks, NT; need to integrate with NSBundle - drd
311      UniChar buff[CFMaxPathSize];
312      CFIndex buffLen;
313      CFURLRef url = NULL;
314      CFStringRef outstr;
315  
316  #if TARGET_OS_ANDROID
317      const char *fixedUserHome = __CFgetenv("CFFIXED_USER_HOME");
318      if (fixedUserHome) {
319          outstr = CFStringCreateWithCString(kCFAllocatorSystemDefault, fixedUserHome, kCFStringEncodingUTF8);
320          if (outstr) {
321              url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, outstr, PLATFORM_PATH_STYLE, true);
322              CFRelease(outstr);
323              if (url) {
324                  return url;
325              }
326          }
327      }
328  #endif
329      
330      buffLen = CFStringGetLength(str);
331      if (buffLen > CFMaxPathSize) buffLen = CFMaxPathSize;
332      CFStringGetCharacters(str, CFRangeMake(0, buffLen), buff);
333      
334  #if TARGET_OS_WIN32
335      // Is this a .dll or .exe?
336      if (buffLen >= 5 && (_wcsnicmp((wchar_t *)&(buff[buffLen-4]), L".dll", 4) == 0 || _wcsnicmp((wchar_t *)&(buff[buffLen-4]), L".exe", 4) == 0)) {
337          CFIndex extensionLength = CFStringGetLength(_CFBundleSiblingResourceDirectoryExtension);
338          buffLen -= 4;
339          // If this is an _debug, we should strip that before looking for the bundle
340          if (buffLen >= 7 && (_wcsnicmp((wchar_t *)&buff[buffLen-6], L"_debug", 6) == 0)) buffLen -= 6;
341          
342          if (buffLen + 1 + extensionLength < CFMaxPathSize) {
343              buff[buffLen] = '.';
344              buffLen ++;
345              CFStringGetCharacters(_CFBundleSiblingResourceDirectoryExtension, CFRangeMake(0, extensionLength), buff + buffLen);
346              buffLen += extensionLength;
347              outstr = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, buff, buffLen, kCFAllocatorNull);
348              url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, outstr, PLATFORM_PATH_STYLE, true);
349              if (!_CFURLExists(url)) {
350                  CFRelease(url);
351                  url = NULL;
352              }
353              CFRelease(outstr);
354          }
355      }
356  #endif
357      
358      if (!url) {
359          buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen);  // Remove exe name
360          
361          if (buffLen > 0) {
362              // See if this is a new bundle.  If it is, we have to remove more path components.
363              CFIndex startOfLastDir = _CFStartOfLastPathComponent(buff, buffLen);
364              if (startOfLastDir > 0 && startOfLastDir < buffLen) {
365                  CFStringRef lastDirName = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, &(buff[startOfLastDir]), buffLen - startOfLastDir);
366                  
367                  if (CFEqual(lastDirName, _CFBundleGetPlatformExecutablesSubdirectoryName())) {
368                      // This is a new bundle.  Back off a few more levels
369                      if (buffLen > 0) {
370                          // Remove platform folder
371                          buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen);
372                      }
373                      if (buffLen > 0) {
374                          // Remove executables folder (if present)
375                          CFIndex startOfNextDir = _CFStartOfLastPathComponent(buff, buffLen);
376                          if (startOfNextDir > 0 && startOfNextDir < buffLen) {
377                              CFStringRef nextDirName = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, &(buff[startOfNextDir]), buffLen - startOfNextDir);
378                              if (CFEqual(nextDirName, _CFBundleExecutablesDirectoryName)) buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen);
379                              CFRelease(nextDirName);
380                          }
381                      }
382                      if (buffLen > 0) {
383                          // Remove support files folder
384                          buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen);
385                      }
386                  }
387                  CFRelease(lastDirName);
388              }
389          }
390          
391          if (buffLen > 0) {
392              outstr = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, buff, buffLen, kCFAllocatorNull);
393              url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, outstr, PLATFORM_PATH_STYLE, true);
394              CFRelease(outstr);
395          }
396      }
397      return url;
398  }
399  
400  static CFURLRef _CFBundleCopyResolvedURLForExecutableURL(CFURLRef url) {
401      // this is necessary so that we match any sanitization CFURL may perform on the result of _CFBundleCopyBundleURLForExecutableURL()
402      CFURLRef absoluteURL, url1, url2, outURL = NULL;
403      CFStringRef str, str1, str2;
404      absoluteURL = CFURLCopyAbsoluteURL(url);
405      str = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
406      if (str) {
407          UniChar buff[CFMaxPathSize];
408          CFIndex buffLen = CFStringGetLength(str), len1;
409          if (buffLen > CFMaxPathSize) buffLen = CFMaxPathSize;
410          CFStringGetCharacters(str, CFRangeMake(0, buffLen), buff);
411          len1 = _CFLengthAfterDeletingLastPathComponent(buff, buffLen);
412          if (len1 > 0 && len1 + 1 < buffLen) {
413              str1 = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, buff, len1);
414              CFIndex skipSlashCount = 1;
415  #if TARGET_OS_WIN32
416              // On Windows, _CFLengthAfterDeletingLastPathComponent will return a value of 3 if the path is at the root (e.g. C:\). This includes the \, which is not the case for URLs with subdirectories
417              if (len1 == 3 && buff[1] == ':' && buff[2] == '\\') {
418                  skipSlashCount = 0;
419              }
420  #endif
421              str2 = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, buff + len1 + skipSlashCount, buffLen - len1 - skipSlashCount);
422              if (str1 && str2) {
423                  url1 = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str1, PLATFORM_PATH_STYLE, true);
424                  if (url1) {
425                      url2 = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, str2, PLATFORM_PATH_STYLE, false, url1);
426                      if (url2) {
427                          outURL = CFURLCopyAbsoluteURL(url2);
428                          CFRelease(url2);
429                      }
430                      CFRelease(url1);
431                  }
432              }
433              if (str1) CFRelease(str1);
434              if (str2) CFRelease(str2);
435          }
436          CFRelease(str);
437      }
438      if (!outURL) {
439          outURL = absoluteURL;
440      } else {
441          CFRelease(absoluteURL);
442      }
443      return outURL;
444  }
445  
446  // MARK: - Exported Functions
447  
448  CF_EXPORT CFURLRef _CFBundleCopyExecutableURLInDirectory(CFURLRef url) {
449      return _CFBundleCopyExecutableURLInDirectory2(NULL, url, NULL, true);
450  }
451  
452  CF_EXPORT CFURLRef _CFBundleCopyOtherExecutableURLInDirectory(CFURLRef url) {
453      // As of 10.13, there does not appear to be anyone actually invoking this function (either in the OS or other apps). The search list is also pretty far out of date.
454      // Therefore we will just return the same result as _CFBundleCopyExecutableURLInDirectory.
455      return _CFBundleCopyExecutableURLInDirectory(url);
456  }
457  
458  CF_EXPORT CFURLRef CFBundleCopyExecutableURL(CFBundleRef bundle) {
459      return _CFBundleCopyExecutableURLInDirectory2(bundle, bundle->_url, NULL, false);
460  }
461  
462  CF_EXPORT CFURLRef CFBundleCopyAuxiliaryExecutableURL(CFBundleRef bundle, CFStringRef executableName) {
463      return _CFBundleCopyExecutableURLInDirectory2(bundle, bundle->_url, executableName, true);
464  }
465  
466  CF_EXPORT CFURLRef _CFBundleCopyBundleURLForExecutableURL(CFURLRef url) {
467      CFURLRef resolvedURL, outurl = NULL;
468      CFStringRef str;
469      resolvedURL = _CFBundleCopyResolvedURLForExecutableURL(url);
470      str = CFURLCopyFileSystemPath(resolvedURL, PLATFORM_PATH_STYLE);
471      if (str) {
472          outurl = _CFBundleCopyBundleURLForExecutablePath(str);
473          CFRelease(str);
474      }
475      CFRelease(resolvedURL);
476      return outurl;
477  }
478  
479  CF_EXPORT CFBundleRef _CFBundleCreateWithExecutableURLIfLooksLikeBundle(CFAllocatorRef allocator, CFURLRef url) {
480      CFBundleRef bundle = NULL;
481      CFURLRef bundleURL = _CFBundleCopyBundleURLForExecutableURL(url), resolvedURL = _CFBundleCopyResolvedURLForExecutableURL(url);
482      if (bundleURL && resolvedURL) {
483          // We used to call _CFBundleCreateIfLooksLikeBundle here, but switched to the regular CFBundleCreate because we want this to return a result for certain flat bundles as well.
484          // It is assumed that users of this SPI do not want this bundle to persist forever, so we use the Unique version of CFBundleCreate.
485          bundle = _CFBundleCreateUnique(allocator, bundleURL);
486          if (bundle) {
487              CFURLRef executableURL = _CFBundleCopyExecutableURLInDirectory2(bundle, bundle->_url, NULL, true);
488              char buff1[CFMaxPathSize], buff2[CFMaxPathSize];
489              if (!executableURL || !CFURLGetFileSystemRepresentation(resolvedURL, true, (uint8_t *)buff1, CFMaxPathSize) || !CFURLGetFileSystemRepresentation(executableURL, true, (uint8_t *)buff2, CFMaxPathSize) || 0 != strcmp(buff1, buff2)) {
490                  CFRelease(bundle);
491                  bundle = NULL;
492              }
493              if (executableURL) CFRelease(executableURL);
494          }
495      }
496      if (bundleURL) CFRelease(bundleURL);
497      if (resolvedURL) CFRelease(resolvedURL);
498      return bundle;
499  }
500  
501  CF_EXPORT CFBundleRef _CFBundleCreateWithExecutableURLIfMightBeBundle(CFAllocatorRef allocator, CFURLRef url) {
502      CFBundleRef result = _CFBundleCreateWithExecutableURLIfLooksLikeBundle(allocator, url);
503      
504      // This function applies additional requirements on a bundle to return a result
505      // The above makes sure that:
506      //  0. CFBundleCreate must succeed using a URL derived from the executable URL
507      //  1. The bundle must have an executableURL, and it must match the passed in executable URL
508      
509      // This function additionally requires that
510      //  2. If flat, the bundle must have a non-empty Info.plist. (15663535)
511      if (result) {
512          uint8_t localVersion = _CFBundleEffectiveLayoutVersion(result);
513          if (3 == localVersion || 4 == localVersion) {
514              CFDictionaryRef infoPlist = CFBundleGetInfoDictionary(result);
515              if (!infoPlist || (infoPlist && CFDictionaryGetCount(infoPlist) == 0)) {
516                  CFRelease(result);
517                  result = NULL;
518              }
519          }
520      }
521      return result;
522  }