@@ -387,6 +387,269 @@ void free_string_list(const struct loader_instance *inst, struct loader_string_l
387387 memset (string_list , 0 , sizeof (struct loader_string_list ));
388388}
389389
390+ // In place modify the passed in path to do the following:
391+ // If HAVE_REALPATH is defined, then this simply calls realpath() so its behavior is defined by realpath()
392+ // Else:
393+ // * Windows-only: Replace forward slashes with backwards slashes (platform correct directory separator)
394+ // * Replace contiguous directory separators with a single directory separator
395+ // * Replace "/./" separator with "/" (where / is the platform correct directory separator)
396+ // * Replace "/<directory_name>/../" with just "/" (where / is the platform correct directory separator)
397+ VkResult normalize_path (const struct loader_instance * inst , char * * passed_in_path ) {
398+ // passed_in_path doesn't point to anything, can't modify inplace so just return
399+ if (passed_in_path == NULL ) {
400+ return VK_SUCCESS ;
401+ }
402+
403+ // POSIX systems has the realpath() function to do this for us, fallback to basic normalization on other platforms
404+ #if defined(HAVE_REALPATH )
405+ char * path = loader_instance_heap_calloc (inst , PATH_MAX , VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE );
406+ if (NULL == path ) {
407+ return VK_ERROR_OUT_OF_HOST_MEMORY ;
408+ }
409+ char * ret = realpath (* passed_in_path , path );
410+ if (NULL == ret ) {
411+ // error path
412+ int error_code = errno ;
413+ loader_log (inst , VULKAN_LOADER_DEBUG_BIT , 0 ,
414+ "normalize_path: Call to realpath() failed with error code %d when given the path %s" , error_code ,
415+ * passed_in_path );
416+ loader_instance_heap_free (inst , path );
417+ } else {
418+ // Replace string pointed to by passed_in_path with the one given to us by realpath()
419+ loader_instance_heap_free (inst , * passed_in_path );
420+ * passed_in_path = path ;
421+ }
422+ return VK_SUCCESS ;
423+
424+ // Windows has GetFullPathName which does essentially the same thing. Note that we call GetFullPathNameA because the path has
425+ // already been converted from the wide char format when it was initially gotten
426+ #elif defined(WIN32 )
427+ VkResult res = VK_SUCCESS ;
428+ DWORD path_len = (DWORD )strlen (* passed_in_path ) + 1 ;
429+ char * path = loader_instance_heap_calloc (inst , (size_t )path_len , VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE );
430+ if (NULL == path ) {
431+ res = VK_ERROR_OUT_OF_HOST_MEMORY ;
432+ goto out ;
433+ }
434+ DWORD actual_len = GetFullPathNameA (* passed_in_path , path_len , path , NULL );
435+ if (actual_len == 0 ) {
436+ size_t last_error = (size_t )GetLastError ();
437+ loader_log (inst , VULKAN_LOADER_DEBUG_BIT , 0 ,
438+ "normalize_path: Call to GetFullPathNameA() failed with error code %llu when given the path %s" , last_error ,
439+ * passed_in_path );
440+ res = VK_ERROR_INITIALIZATION_FAILED ;
441+ goto out ;
442+ }
443+
444+ // If path_len wasn't big enough, need to realloc and call again
445+ // actual_len doesn't include null terminator
446+ if (actual_len + 1 > path_len ) {
447+ path = loader_instance_heap_realloc (inst , path , path_len , actual_len + 1 , VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE );
448+ if (NULL == path ) {
449+ res = VK_ERROR_OUT_OF_HOST_MEMORY ;
450+ goto out ;
451+ }
452+ // store the updated allocation size (sans null terminator)
453+ path_len = actual_len + 1 ;
454+
455+ actual_len = GetFullPathNameA (* passed_in_path , path_len , path , NULL );
456+ if (actual_len == 0 ) {
457+ size_t last_error = (size_t )GetLastError ();
458+ loader_log (inst , VULKAN_LOADER_DEBUG_BIT , 0 ,
459+ "normalize_path: Call to GetFullPathNameA() failed with error code %llu when given the path %s" , last_error ,
460+ * passed_in_path );
461+ res = VK_ERROR_INITIALIZATION_FAILED ;
462+ goto out ;
463+ // actual_len doesn't include null terminator
464+ } else if (actual_len + 1 != path_len ) {
465+ loader_log (inst , VULKAN_LOADER_DEBUG_BIT , 0 ,
466+ "normalize_path: Call to GetFullPathNameA() with too small of a buffer when given the path %s after the "
467+ "initial call to GetFullPathNameA() failed for the same reason. Buffer size is %llu, actual size is %llu" ,
468+ * passed_in_path , (size_t )path_len , (size_t )actual_len );
469+ res = VK_ERROR_INITIALIZATION_FAILED ;
470+ goto out ;
471+ }
472+ }
473+ // Replace string pointed to by passed_in_path with the one given to us by realpath()
474+ loader_instance_heap_free (inst , * passed_in_path );
475+ * passed_in_path = path ;
476+ out :
477+ if (VK_SUCCESS != res ) {
478+ if (NULL != path ) {
479+ loader_instance_heap_free (inst , path );
480+ }
481+ }
482+ return res ;
483+
484+ #else
485+ (void )inst ;
486+ char * path = * passed_in_path ;
487+ size_t path_len = strlen (path ) + 1 ;
488+
489+ size_t output_index = 0 ;
490+ // Iterate through the string up to the last character, excluding the null terminator
491+ for (size_t i = 0 ; i < path_len - 1 ; i ++ ) {
492+ if (i + 1 < path_len && path [i ] == DIRECTORY_SYMBOL && path [i + 1 ] == DIRECTORY_SYMBOL ) {
493+ continue ;
494+ } else if (i + 2 < path_len && path [i ] == DIRECTORY_SYMBOL && path [i + 1 ] == '.' && path [i + 2 ] == DIRECTORY_SYMBOL ) {
495+ i += 1 ;
496+ } else {
497+ path [output_index ++ ] = path [i ];
498+ }
499+ }
500+ // Add null terminator and set the new length
501+ path [output_index ++ ] = '\0' ;
502+ path_len = output_index ;
503+
504+ // Loop while there are still ..'s in the path. Easiest implementation resolves them one by one, which requires quadratic
505+ // iteration through the string
506+ char * directory_stack = loader_stack_alloc (path_len );
507+ if (directory_stack == NULL ) {
508+ return VK_ERROR_OUT_OF_HOST_MEMORY ;
509+ }
510+
511+ size_t top_of_stack = 0 ;
512+
513+ // Iterate through the path, push characters as we see them, if we find a "..", pop off the top of the directory stack until the
514+ // current directory is gone.
515+ for (size_t i = 0 ; i < path_len - 1 ; i ++ ) {
516+ // if the next part of path is "/../" we need to pop from the directory stack until we hit the previous directory symbol.
517+ if (i + 3 < path_len && path [i ] == DIRECTORY_SYMBOL && path [i + 1 ] == '.' && path [i + 2 ] == '.' && path_len &&
518+ path [i + 3 ] == DIRECTORY_SYMBOL ) {
519+ // Pop until we hit the next directory symbol in the stack
520+ while (top_of_stack > 0 && directory_stack [top_of_stack - 1 ] != DIRECTORY_SYMBOL ) {
521+ top_of_stack -- ;
522+ directory_stack [top_of_stack ] = '\0' ;
523+ }
524+ // Amend the directory stack so that the top isn't a directory separator
525+ if (top_of_stack > 0 && directory_stack [top_of_stack - 1 ] == DIRECTORY_SYMBOL ) {
526+ top_of_stack -- ;
527+ directory_stack [top_of_stack ] = '\0' ;
528+ }
529+ i += 2 ; // need to skip the second dot & directory separator
530+ } else {
531+ // push characters as we come across them
532+ directory_stack [top_of_stack ++ ] = path [i ];
533+ }
534+ }
535+
536+ // Can't forget the null terminator
537+ directory_stack [top_of_stack ] = '\0' ;
538+
539+ // We now have the path without any ..'s, so just copy it out
540+ loader_strncpy (path , path_len , directory_stack , path_len );
541+ path [top_of_stack ] = '\0' ;
542+ path_len = top_of_stack + 1 ;
543+
544+ return VK_SUCCESS ;
545+ #endif
546+ }
547+
548+ // Queries the path to the library that lib_handle & gipa are assoicated with, allocating a string to hold it and returning it in
549+ // out_path
550+ VkResult get_library_path_of_dl_handle (const struct loader_instance * inst , loader_platform_dl_handle lib_handle ,
551+ PFN_vkGetInstanceProcAddr gipa , char * * out_path ) {
552+ #if COMMON_UNIX_PLATFORMS
553+ (void )lib_handle ;
554+ Dl_info dl_info = {0 };
555+ if (dladdr (gipa , & dl_info ) != 0 && NULL != dl_info .dli_fname ) {
556+ return loader_copy_to_new_str (inst , dl_info .dli_fname , out_path );
557+ }
558+ return VK_SUCCESS ;
559+
560+ #elif defined(WIN32 )
561+ (void )gipa ;
562+ size_t module_file_name_len = MAX_PATH ; // start with reasonably large buffer
563+ wchar_t * buffer_utf16 = (wchar_t * )loader_stack_alloc (module_file_name_len * sizeof (wchar_t ));
564+ DWORD ret = GetModuleFileNameW (lib_handle , buffer_utf16 , (DWORD )module_file_name_len );
565+ if (ret == 0 ) {
566+ return VK_SUCCESS ;
567+ }
568+ while (GetLastError () == ERROR_INSUFFICIENT_BUFFER ) {
569+ module_file_name_len *= 2 ;
570+ buffer_utf16 = (wchar_t * )loader_stack_alloc (module_file_name_len * sizeof (wchar_t ));
571+ ret = GetModuleFileNameW (lib_handle , buffer_utf16 , (DWORD )module_file_name_len );
572+ if (ret == 0 ) {
573+ return VK_SUCCESS ;
574+ }
575+ }
576+
577+ // Need to convert from utf16 to utf8
578+ int buffer_utf8_size = WideCharToMultiByte (CP_UTF8 , 0 , buffer_utf16 , -1 , NULL , 0 , NULL , NULL );
579+ if (buffer_utf8_size <= 0 ) {
580+ return VK_SUCCESS ;
581+ }
582+
583+ char * buffer_utf8 = loader_instance_heap_calloc (inst , buffer_utf8_size , VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE );
584+ if (NULL == buffer_utf8 ) {
585+ return VK_ERROR_OUT_OF_HOST_MEMORY ;
586+ }
587+ if (WideCharToMultiByte (CP_UTF8 , 0 , buffer_utf16 , -1 , buffer_utf8 , buffer_utf8_size , NULL , NULL ) != buffer_utf8_size ) {
588+ return VK_SUCCESS ;
589+ }
590+
591+ // Successfully got the 'real' path to the library.
592+ * out_path = buffer_utf8 ;
593+ return VK_SUCCESS ;
594+
595+ #else
596+ // Do nothing, platform doesn't handle getting the path to a library
597+ #endif
598+ }
599+
600+ // Find and replace the path that was loaded using the lib_name path with the real path of the library. This is done to provide
601+ // accurate logging info for users.
602+ // This function prints a warning if there is a mismatch between the lib_name path and the real path.
603+ VkResult fixup_library_binary_path (const struct loader_instance * inst , char * * lib_name , loader_platform_dl_handle lib_handle ,
604+ PFN_vkGetInstanceProcAddr gipa ) {
605+ if (lib_name == NULL ) {
606+ // do nothing as we got an invalid lib_path pointer
607+ return VK_SUCCESS ;
608+ }
609+
610+ bool system_path = true;
611+ size_t lib_name_len = strlen (* lib_name ) + 1 ;
612+ for (size_t i = 0 ; i < lib_name_len ; i ++ ) {
613+ if ((* lib_name )[i ] == DIRECTORY_SYMBOL ) {
614+ system_path = false;
615+ break ;
616+ }
617+ }
618+
619+ if (!system_path ) {
620+ // The OS path we get for a binary is normalized, so we need to normalize the path passed into LoadLibrary/dlopen so that
621+ // mismatches are minimized. EG, do not warn when we give dlopen/LoadLibrary "/foo/./bar" but get "/foo/bar" as the loaded
622+ // binary path from the OS.
623+ VkResult res = normalize_path (inst , lib_name );
624+ if (res == VK_ERROR_OUT_OF_HOST_MEMORY ) {
625+ return res ;
626+ }
627+ }
628+ char * os_determined_lib_name = NULL ;
629+ VkResult res = get_library_path_of_dl_handle (inst , lib_handle , gipa , & os_determined_lib_name );
630+ if (res == VK_ERROR_OUT_OF_HOST_MEMORY ) {
631+ return res ;
632+ }
633+
634+ if (NULL != os_determined_lib_name ) {
635+ if (0 != strcmp (os_determined_lib_name , * lib_name )) {
636+ // Paths do not match, so we need to replace lib_name with the real path
637+ if (!system_path ) {
638+ // Only warn when the library_path is relative or absolute, not system. EG lib_name had no directory separators
639+ loader_log (inst , VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT , 0 ,
640+ "Path to given binary %s was found to differ from OS loaded path %s" , * lib_name , os_determined_lib_name );
641+ }
642+ loader_instance_heap_free (inst , * lib_name );
643+ * lib_name = os_determined_lib_name ;
644+ } else {
645+ // Paths match, so just need to free temporary allocation
646+ loader_instance_heap_free (inst , os_determined_lib_name );
647+ }
648+ }
649+
650+ return res ;
651+ }
652+
390653// Given string of three part form "maj.min.pat" convert to a vulkan version number.
391654// Also can understand four part form "variant.major.minor.patch" if provided.
392655uint32_t loader_parse_version_string (char * vers_str ) {
@@ -1996,6 +2259,11 @@ VkResult loader_scanned_icd_add(const struct loader_instance *inst, struct loade
19962259 }
19972260 icd_tramp_list -> count ++ ;
19982261
2262+ // Uses OS calls to find the 'true' path to the binary, for more accurate logging later on.
2263+ res = fixup_library_binary_path (inst , & (new_scanned_icd -> lib_name ), new_scanned_icd -> handle , fp_get_proc_addr );
2264+ if (res == VK_ERROR_OUT_OF_HOST_MEMORY ) {
2265+ goto out ;
2266+ }
19992267out :
20002268 if (res != VK_SUCCESS ) {
20012269 if (NULL != handle ) {
@@ -4807,6 +5075,11 @@ VkResult loader_create_instance_chain(const VkInstanceCreateInfo *pCreateInfo, c
48075075
48085076 chain_info .u .pLayerInfo = & layer_instance_link_info [num_activated_layers ];
48095077
5078+ res = fixup_library_binary_path (inst , & (layer_prop -> lib_name ), layer_prop -> lib_handle , cur_gipa );
5079+ if (res == VK_ERROR_OUT_OF_HOST_MEMORY ) {
5080+ return res ;
5081+ }
5082+
48105083 activated_layers [num_activated_layers ].name = layer_prop -> info .layerName ;
48115084 activated_layers [num_activated_layers ].manifest = layer_prop -> manifest_file_name ;
48125085 activated_layers [num_activated_layers ].library = layer_prop -> lib_name ;
0 commit comments