1 /*
2  *
3  * Copyright (c) 2015-2022 The Khronos Group Inc.
4  * Copyright (c) 2015-2022 Valve Corporation
5  * Copyright (c) 2015-2022 LunarG, Inc.
6  * Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
7  * Copyright (c) 2023-2023 RasterGrid Kft.
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *     http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  * Author: Ian Elliot <ian@lunarg.com>
22  * Author: Jon Ashburn <jon@lunarg.com>
23  * Author: Lenny Komow <lenny@lunarg.com>
24  * Author: Charles Giessen <charles@lunarg.com>
25  *
26  */
27 #pragma once
28 
29 #if defined(__FreeBSD__) || defined(__OpenBSD__)
30 #include <sys/types.h>
31 #include <sys/select.h>
32 #endif
33 
34 #include <assert.h>
35 #include <float.h>
36 #include <stdbool.h>
37 #include <stdint.h>
38 #include <stdio.h>
39 #include <string.h>
40 
41 #if defined(__Fuchsia__)
42 #include "dlopen_fuchsia.h"
43 #endif  // defined(__Fuchsia__)
44 
45 // Set of platforms with a common set of functionality which is queried throughout the program
46 #if defined(__linux__) || defined(__APPLE__) || defined(__Fuchsia__) || defined(__QNX__) || defined(__FreeBSD__) || \
47     defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__GNU__)
48 #define COMMON_UNIX_PLATFORMS 1
49 #else
50 #define COMMON_UNIX_PLATFORMS 0
51 #endif
52 
53 #if COMMON_UNIX_PLATFORMS
54 #include <unistd.h>
55 // Note: The following file is for dynamic loading:
56 #include <dlfcn.h>
57 #include <pthread.h>
58 #include <stdlib.h>
59 #include <libgen.h>
60 
61 #elif defined(_WIN32)
62 // WinBase.h defines CreateSemaphore and synchapi.h defines CreateEvent
63 //  undefine them to avoid conflicts with VkLayerDispatchTable struct members.
64 #if defined(CreateSemaphore)
65 #undef CreateSemaphore
66 #endif
67 #if defined(CreateEvent)
68 #undef CreateEvent
69 #endif
70 #include <stdio.h>
71 #include <io.h>
72 #include <shlwapi.h>
73 #include <direct.h>
74 #endif  // defined(_WIN32)
75 
76 #include "stack_allocation.h"
77 
78 #if defined(APPLE_STATIC_LOADER) && !defined(__APPLE__)
79 #error "APPLE_STATIC_LOADER can only be defined on Apple platforms!"
80 #endif
81 
82 #if defined(APPLE_STATIC_LOADER)
83 #define LOADER_EXPORT
84 #elif defined(__GNUC__) && __GNUC__ >= 4
85 #define LOADER_EXPORT __attribute__((visibility("default")))
86 #elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)
87 #define LOADER_EXPORT __attribute__((visibility("default")))
88 #else
89 #define LOADER_EXPORT
90 #endif
91 
92 #define MAX_STRING_SIZE 1024
93 
94 // This is defined in vk_layer.h, but if there's problems we need to create the define
95 // here.
96 #if !defined(MAX_NUM_UNKNOWN_EXTS)
97 #define MAX_NUM_UNKNOWN_EXTS 250
98 #endif
99 
100 // Environment Variable information
101 #define VK_ICD_FILENAMES_ENV_VAR "VK_ICD_FILENAMES"  // Deprecated in v1.3.207 loader
102 #define VK_DRIVER_FILES_ENV_VAR "VK_DRIVER_FILES"
103 #define VK_LAYER_PATH_ENV_VAR "VK_LAYER_PATH"
104 // Support added in v1.3.207 loader
105 #define VK_ADDITIONAL_DRIVER_FILES_ENV_VAR "VK_ADD_DRIVER_FILES"
106 #define VK_ADDITIONAL_LAYER_PATH_ENV_VAR "VK_ADD_LAYER_PATH"
107 // Support added in v1.3.234 loader
108 #define VK_LAYERS_ENABLE_ENV_VAR "VK_LOADER_LAYERS_ENABLE"
109 #define VK_LAYERS_DISABLE_ENV_VAR "VK_LOADER_LAYERS_DISABLE"
110 #define VK_LAYERS_ALLOW_ENV_VAR "VK_LOADER_LAYERS_ALLOW"
111 #define VK_DRIVERS_SELECT_ENV_VAR "VK_LOADER_DRIVERS_SELECT"
112 #define VK_DRIVERS_DISABLE_ENV_VAR "VK_LOADER_DRIVERS_DISABLE"
113 #define VK_LOADER_DISABLE_ALL_LAYERS_VAR_1 "~all~"
114 #define VK_LOADER_DISABLE_ALL_LAYERS_VAR_2 "*"
115 #define VK_LOADER_DISABLE_ALL_LAYERS_VAR_3 "**"
116 #define VK_LOADER_DISABLE_IMPLICIT_LAYERS_VAR "~implicit~"
117 #define VK_LOADER_DISABLE_EXPLICIT_LAYERS_VAR "~explicit~"
118 
119 // Override layer information
120 #define VK_OVERRIDE_LAYER_NAME "VK_LAYER_LUNARG_override"
121 
122 // Loader Settings filename
123 #define VK_LOADER_SETTINGS_FILENAME "vk_loader_settings.json"
124 
125 #define LAYERS_PATH_ENV "VK_LAYER_PATH"
126 #define ENABLED_LAYERS_ENV "VK_INSTANCE_LAYERS"
127 
128 #if COMMON_UNIX_PLATFORMS
129 /* Linux-specific common code: */
130 
131 // VK Library Filenames, Paths, etc.:
132 #define PATH_SEPARATOR ':'
133 #define DIRECTORY_SYMBOL '/'
134 
135 #define VULKAN_DIR "vulkan/"
136 #define VULKAN_ICDCONF_DIR "icd.d"
137 #define VULKAN_ICD_DIR "icd"
138 #define VULKAN_SETTINGSCONF_DIR "settings.d"
139 #define VULKAN_ELAYERCONF_DIR "explicit_layer.d"
140 #define VULKAN_ILAYERCONF_DIR "implicit_layer.d"
141 #define VULKAN_LAYER_DIR "layer"
142 
143 #define VK_DRIVERS_INFO_RELATIVE_DIR VULKAN_DIR VULKAN_ICDCONF_DIR
144 #define VK_SETTINGS_INFO_RELATIVE_DIR VULKAN_DIR VULKAN_SETTINGSCONF_DIR
145 #define VK_ELAYERS_INFO_RELATIVE_DIR VULKAN_DIR VULKAN_ELAYERCONF_DIR
146 #define VK_ILAYERS_INFO_RELATIVE_DIR VULKAN_DIR VULKAN_ILAYERCONF_DIR
147 
148 #define VK_DRIVERS_INFO_REGISTRY_LOC ""
149 #define VK_ELAYERS_INFO_REGISTRY_LOC ""
150 #define VK_ILAYERS_INFO_REGISTRY_LOC ""
151 #define VK_SETTINGS_INFO_REGISTRY_LOC ""
152 
153 #if defined(__QNX__)
154 #define SYSCONFDIR "/etc"
155 #endif
156 
157 // C99:
158 #define PRINTF_SIZE_T_SPECIFIER "%zu"
159 
160 // Dynamic Loading of libraries:
161 typedef void *loader_platform_dl_handle;
162 
163 // Threads:
164 typedef pthread_t loader_platform_thread;
165 
166 // Thread IDs:
167 typedef pthread_t loader_platform_thread_id;
168 
169 // Thread mutex:
170 typedef pthread_mutex_t loader_platform_thread_mutex;
171 
172 typedef pthread_cond_t loader_platform_thread_cond;
173 
174 #elif defined(_WIN32)
175 /* Windows-specific common code: */
176 // VK Library Filenames, Paths, etc.:
177 #define PATH_SEPARATOR ';'
178 #define DIRECTORY_SYMBOL '\\'
179 #define DEFAULT_VK_REGISTRY_HIVE HKEY_LOCAL_MACHINE
180 #define DEFAULT_VK_REGISTRY_HIVE_STR "HKEY_LOCAL_MACHINE"
181 #define SECONDARY_VK_REGISTRY_HIVE HKEY_CURRENT_USER
182 #define SECONDARY_VK_REGISTRY_HIVE_STR "HKEY_CURRENT_USER"
183 
184 #define VK_DRIVERS_INFO_RELATIVE_DIR ""
185 #define VK_SETTINGS_INFO_RELATIVE_DIR ""
186 #define VK_ELAYERS_INFO_RELATIVE_DIR ""
187 #define VK_ILAYERS_INFO_RELATIVE_DIR ""
188 
189 #define VK_VARIANT_REG_STR ""
190 #define VK_VARIANT_REG_STR_W L""
191 
192 #define VK_DRIVERS_INFO_REGISTRY_LOC "SOFTWARE\\Khronos\\Vulkan" VK_VARIANT_REG_STR "\\Drivers"
193 #define VK_ELAYERS_INFO_REGISTRY_LOC "SOFTWARE\\Khronos\\Vulkan" VK_VARIANT_REG_STR "\\ExplicitLayers"
194 #define VK_ILAYERS_INFO_REGISTRY_LOC "SOFTWARE\\Khronos\\Vulkan" VK_VARIANT_REG_STR "\\ImplicitLayers"
195 #define VK_SETTINGS_INFO_REGISTRY_LOC "SOFTWARE\\Khronos\\Vulkan" VK_VARIANT_REG_STR "\\LoaderSettings"
196 
197 #define PRINTF_SIZE_T_SPECIFIER "%Iu"
198 
199 // Dynamic Loading:
200 typedef HMODULE loader_platform_dl_handle;
201 
202 // Threads:
203 typedef HANDLE loader_platform_thread;
204 
205 // Thread IDs:
206 typedef DWORD loader_platform_thread_id;
207 
208 // Thread mutex:
209 typedef CRITICAL_SECTION loader_platform_thread_mutex;
210 
211 typedef CONDITION_VARIABLE loader_platform_thread_cond;
212 
213 #else
214 
215 #warning The "vk_loader_platform.h" file must be modified for this OS.
216 
217 // NOTE: In order to support another OS, an #elif needs to be added (above the
218 // "#else // defined(_WIN32)") for that OS, and OS-specific versions of the
219 // contents of this file must be created, or extend one of the existing OS specific
220 // sections with the necessary changes.
221 
222 #endif
223 
224 // controls whether loader_platform_close_library() closes the libraries or not - controlled by an environment variables
225 extern bool loader_disable_dynamic_library_unloading;
226 
227 // Returns true if the DIRECTORY_SYMBOL is contained within path
loader_platform_is_path(const char *path)228 static inline bool loader_platform_is_path(const char *path) { return strchr(path, DIRECTORY_SYMBOL) != NULL; }
229 
230 // The once init functionality is not used when building a DLL on Windows. This is because there is no way to clean up the
231 // resources allocated by anything allocated by once init. This isn't a problem for static libraries, but it is for dynamic
232 // ones. When building a DLL, we use DllMain() instead to allow properly cleaning up resources.
233 
234 #if defined(APPLE_STATIC_LOADER)
loader_platform_thread_once_fn(pthread_once_t *ctl, void (*func)(void))235 static inline void loader_platform_thread_once_fn(pthread_once_t *ctl, void (*func)(void)) {
236     assert(func != NULL);
237     assert(ctl != NULL);
238     pthread_once(ctl, func);
239 }
240 #define LOADER_PLATFORM_THREAD_ONCE_DECLARATION(var) pthread_once_t var = PTHREAD_ONCE_INIT;
241 #define LOADER_PLATFORM_THREAD_ONCE_EXTERN_DEFINITION(var) extern pthread_once_t var;
242 #define LOADER_PLATFORM_THREAD_ONCE(ctl, func) loader_platform_thread_once_fn(ctl, func);
243 #else
244 #define LOADER_PLATFORM_THREAD_ONCE_DECLARATION(var)
245 #define LOADER_PLATFORM_THREAD_ONCE_EXTERN_DEFINITION(var)
246 #define LOADER_PLATFORM_THREAD_ONCE(ctl, func)
247 
248 #endif
249 
250 #if COMMON_UNIX_PLATFORMS
251 
252 // File IO
loader_platform_file_exists(const char *path)253 static inline bool loader_platform_file_exists(const char *path) {
254     if (access(path, F_OK))
255         return false;
256     else
257         return true;
258 }
259 
260 // Returns true if the given string appears to be a relative or absolute
261 // path, as opposed to a bare filename.
loader_platform_is_path_absolute(const char *path)262 static inline bool loader_platform_is_path_absolute(const char *path) {
263     if (path[0] == '/')
264         return true;
265     else
266         return false;
267 }
268 
loader_platform_dirname(char *path)269 static inline char *loader_platform_dirname(char *path) { return dirname(path); }
270 
271 // loader_platform_executable_path finds application path + name.
272 // Path cannot be longer than 1024, returns NULL if it is greater than that.
273 #if defined(__linux__) || defined(__GNU__)
loader_platform_executable_path(char *buffer, size_t size)274 static inline char *loader_platform_executable_path(char *buffer, size_t size) {
275     ssize_t count = readlink("/proc/self/exe", buffer, size);
276     if (count == -1) return NULL;
277     if (count == 0) return NULL;
278     buffer[count] = '\0';
279     return buffer;
280 }
281 #elif defined(__APPLE__)
282 #include <TargetConditionals.h>
283 // TARGET_OS_IPHONE isn't just iOS it's also iOS/tvOS/watchOS. See TargetConditionals.h documentation.
284 #if TARGET_OS_IPHONE
loader_platform_executable_path(char *buffer, size_t size)285 static inline char *loader_platform_executable_path(char *buffer, size_t size) {
286     (void)size;
287     buffer[0] = '\0';
288     return buffer;
289 }
290 #endif
291 #if TARGET_OS_OSX
292 #include <libproc.h>
loader_platform_executable_path(char *buffer, size_t size)293 static inline char *loader_platform_executable_path(char *buffer, size_t size) {
294     // proc_pidpath takes a uint32_t for the buffer size
295     if (size > UINT32_MAX) {
296         return NULL;
297     }
298     pid_t pid = getpid();
299     int ret = proc_pidpath(pid, buffer, (uint32_t)size);
300     if (ret <= 0) {
301         return NULL;
302     }
303     buffer[ret] = '\0';
304     return buffer;
305 }
306 #endif
307 #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
308 #include <sys/sysctl.h>
loader_platform_executable_path(char *buffer, size_t size)309 static inline char *loader_platform_executable_path(char *buffer, size_t size) {
310     int mib[] = {
311         CTL_KERN,
312 #if defined(__NetBSD__)
313         KERN_PROC_ARGS,
314         -1,
315         KERN_PROC_PATHNAME,
316 #else
317         KERN_PROC,
318         KERN_PROC_PATHNAME,
319         -1,
320 #endif
321     };
322     if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buffer, &size, NULL, 0) < 0) {
323         return NULL;
324     }
325 
326     return buffer;
327 }
328 #elif defined(__Fuchsia__) || defined(__OpenBSD__)
loader_platform_executable_path(char *buffer, size_t size)329 static inline char *loader_platform_executable_path(char *buffer, size_t size) { return NULL; }
330 #elif defined(__QNX__)
331 
332 #define SYSCONFDIR "/etc"
333 
334 #include <fcntl.h>
335 #include <sys/stat.h>
336 
loader_platform_executable_path(char *buffer, size_t size)337 static inline char *loader_platform_executable_path(char *buffer, size_t size) {
338     int fd = open("/proc/self/exefile", O_RDONLY);
339     size_t rdsize;
340 
341     if (fd == -1) {
342         return NULL;
343     }
344 
345     rdsize = read(fd, buffer, size);
346     if (rdsize == size) {
347         return NULL;
348     }
349     buffer[rdsize] = 0x00;
350     close(fd);
351 
352     return buffer;
353 }
354 #endif  // defined (__QNX__)
355 
356 // Compatibility with compilers that don't support __has_feature
357 #if !defined(__has_feature)
358 #define __has_feature(x) 0
359 #endif
360 
361 #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
362 #define LOADER_ADDRESS_SANITIZER_ACTIVE  // TODO: Add proper build flag for ASAN support
363 #endif
364 
365 // When loading the library, we use RTLD_LAZY so that not all symbols have to be
366 // resolved at this time (which improves performance). Note that if not all symbols
367 // can be resolved, this could cause crashes later. Use the LD_BIND_NOW environment
368 // variable to force all symbols to be resolved here.
369 #define LOADER_DLOPEN_MODE (RTLD_LAZY | RTLD_LOCAL)
370 
371 #if defined(__Fuchsia__)
loader_platform_open_driver(const char *libPath)372 static inline loader_platform_dl_handle loader_platform_open_driver(const char *libPath) {
373     return dlopen_fuchsia(libPath, LOADER_DLOPEN_MODE, true);
374 }
loader_platform_open_library(const char *libPath)375 static inline loader_platform_dl_handle loader_platform_open_library(const char *libPath) {
376     return dlopen_fuchsia(libPath, LOADER_DLOPEN_MODE, false);
377 }
378 #else
loader_platform_open_library(const char *libPath)379 static inline loader_platform_dl_handle loader_platform_open_library(const char *libPath) {
380     return dlopen(libPath, LOADER_DLOPEN_MODE);
381 }
382 #endif
383 
loader_platform_open_library_error(const char *libPath)384 static inline const char *loader_platform_open_library_error(const char *libPath) {
385     (void)libPath;
386 #if defined(__Fuchsia__)
387     return dlerror_fuchsia();
388 #else
389     return dlerror();
390 #endif
391 }
loader_platform_close_library(loader_platform_dl_handle library)392 static inline void loader_platform_close_library(loader_platform_dl_handle library) {
393     if (!loader_disable_dynamic_library_unloading) {
394         dlclose(library);
395     } else {
396         (void)library;
397     }
398 }
loader_platform_get_proc_address(loader_platform_dl_handle library, const char *name)399 static inline void *loader_platform_get_proc_address(loader_platform_dl_handle library, const char *name) {
400     assert(library);
401     assert(name);
402     return dlsym(library, name);
403 }
loader_platform_get_proc_address_error(const char *name)404 static inline const char *loader_platform_get_proc_address_error(const char *name) {
405     (void)name;
406     return dlerror();
407 }
408 
409 // Thread mutex:
loader_platform_thread_create_mutex(loader_platform_thread_mutex *pMutex)410 static inline void loader_platform_thread_create_mutex(loader_platform_thread_mutex *pMutex) { pthread_mutex_init(pMutex, NULL); }
loader_platform_thread_lock_mutex(loader_platform_thread_mutex *pMutex)411 static inline void loader_platform_thread_lock_mutex(loader_platform_thread_mutex *pMutex) { pthread_mutex_lock(pMutex); }
loader_platform_thread_unlock_mutex(loader_platform_thread_mutex *pMutex)412 static inline void loader_platform_thread_unlock_mutex(loader_platform_thread_mutex *pMutex) { pthread_mutex_unlock(pMutex); }
loader_platform_thread_delete_mutex(loader_platform_thread_mutex *pMutex)413 static inline void loader_platform_thread_delete_mutex(loader_platform_thread_mutex *pMutex) { pthread_mutex_destroy(pMutex); }
414 
thread_safe_strtok(char *str, const char *delim, char **saveptr)415 static inline void *thread_safe_strtok(char *str, const char *delim, char **saveptr) { return strtok_r(str, delim, saveptr); }
416 
loader_fopen(const char *fileName, const char *mode)417 static inline FILE *loader_fopen(const char *fileName, const char *mode) { return fopen(fileName, mode); }
loader_strncat(char *dest, size_t dest_sz, const char *src, size_t count)418 static inline char *loader_strncat(char *dest, size_t dest_sz, const char *src, size_t count) {
419     (void)dest_sz;
420     return strncat(dest, src, count);
421 }
loader_strncpy(char *dest, size_t dest_sz, const char *src, size_t count)422 static inline char *loader_strncpy(char *dest, size_t dest_sz, const char *src, size_t count) {
423     (void)dest_sz;
424     return strncpy(dest, src, count);
425 }
426 
427 #elif defined(_WIN32)
428 
429 // Get the key for the plug n play driver registry
430 // The string returned by this function should NOT be freed
LoaderPnpDriverRegistry()431 static inline const char *LoaderPnpDriverRegistry() {
432     BOOL is_wow;
433     IsWow64Process(GetCurrentProcess(), &is_wow);
434     return is_wow ? "Vulkan" VK_VARIANT_REG_STR "DriverNameWow" : "Vulkan" VK_VARIANT_REG_STR "DriverName";
435 }
LoaderPnpDriverRegistryWide()436 static inline const wchar_t *LoaderPnpDriverRegistryWide() {
437     BOOL is_wow;
438     IsWow64Process(GetCurrentProcess(), &is_wow);
439     return is_wow ? L"Vulkan" VK_VARIANT_REG_STR_W L"DriverNameWow" : L"Vulkan" VK_VARIANT_REG_STR_W L"DriverName";
440 }
441 
442 // Get the key for the plug 'n play explicit layer registry
443 // The string returned by this function should NOT be freed
LoaderPnpELayerRegistry()444 static inline const char *LoaderPnpELayerRegistry() {
445     BOOL is_wow;
446     IsWow64Process(GetCurrentProcess(), &is_wow);
447     return is_wow ? "Vulkan" VK_VARIANT_REG_STR "ExplicitLayersWow" : "Vulkan" VK_VARIANT_REG_STR "ExplicitLayers";
448 }
LoaderPnpELayerRegistryWide()449 static inline const wchar_t *LoaderPnpELayerRegistryWide() {
450     BOOL is_wow;
451     IsWow64Process(GetCurrentProcess(), &is_wow);
452     return is_wow ? L"Vulkan" VK_VARIANT_REG_STR_W L"ExplicitLayersWow" : L"Vulkan" VK_VARIANT_REG_STR_W L"ExplicitLayers";
453 }
454 
455 // Get the key for the plug 'n play implicit layer registry
456 // The string returned by this function should NOT be freed
LoaderPnpILayerRegistry()457 static inline const char *LoaderPnpILayerRegistry() {
458     BOOL is_wow;
459     IsWow64Process(GetCurrentProcess(), &is_wow);
460     return is_wow ? "Vulkan" VK_VARIANT_REG_STR "ImplicitLayersWow" : "Vulkan" VK_VARIANT_REG_STR "ImplicitLayers";
461 }
LoaderPnpILayerRegistryWide()462 static inline const wchar_t *LoaderPnpILayerRegistryWide() {
463     BOOL is_wow;
464     IsWow64Process(GetCurrentProcess(), &is_wow);
465     return is_wow ? L"Vulkan" VK_VARIANT_REG_STR_W L"ImplicitLayersWow" : L"Vulkan" VK_VARIANT_REG_STR_W L"ImplicitLayers";
466 }
467 
468 // File IO
loader_platform_file_exists(const char *path)469 static inline bool loader_platform_file_exists(const char *path) {
470     int path_utf16_size = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
471     if (path_utf16_size <= 0) {
472         return false;
473     }
474     wchar_t *path_utf16 = (wchar_t *)loader_stack_alloc(path_utf16_size * sizeof(wchar_t));
475     if (MultiByteToWideChar(CP_UTF8, 0, path, -1, path_utf16, path_utf16_size) != path_utf16_size) {
476         return false;
477     }
478     if (_waccess(path_utf16, 0) == -1)
479         return false;
480     else
481         return true;
482 }
483 
484 // Returns true if the given string appears to be a relative or absolute
485 // path, as opposed to a bare filename.
loader_platform_is_path_absolute(const char *path)486 static inline bool loader_platform_is_path_absolute(const char *path) {
487     if (!path || !*path) {
488         return false;
489     }
490     if (*path == DIRECTORY_SYMBOL || path[1] == ':') {
491         return true;
492     }
493     return false;
494 }
495 
496 // WIN32 runtime doesn't have dirname().
loader_platform_dirname(char *path)497 static inline char *loader_platform_dirname(char *path) {
498     char *current, *next;
499 
500     // TODO/TBD: Do we need to deal with the Windows's ":" character?
501 
502     for (current = path; *current != '\0'; current = next) {
503         next = strchr(current, DIRECTORY_SYMBOL);
504         if (next == NULL) {
505             if (current != path) *(current - 1) = '\0';
506             return path;
507         } else {
508             // Point one character past the DIRECTORY_SYMBOL:
509             next++;
510         }
511     }
512     return path;
513 }
514 
loader_platform_executable_path(char *buffer, size_t size)515 static inline char *loader_platform_executable_path(char *buffer, size_t size) {
516     wchar_t *buffer_utf16 = (wchar_t *)loader_stack_alloc(size * sizeof(wchar_t));
517     DWORD ret = GetModuleFileNameW(NULL, buffer_utf16, (DWORD)size);
518     if (ret == 0) {
519         return NULL;
520     }
521     if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
522         return NULL;
523     }
524     int buffer_utf8_size = WideCharToMultiByte(CP_UTF8, 0, buffer_utf16, -1, NULL, 0, NULL, NULL);
525     if (buffer_utf8_size <= 0 || (size_t)buffer_utf8_size > size) {
526         return NULL;
527     }
528     if (WideCharToMultiByte(CP_UTF8, 0, buffer_utf16, -1, buffer, buffer_utf8_size, NULL, NULL) != buffer_utf8_size) {
529         return NULL;
530     }
531     return buffer;
532 }
533 
534 // Dynamic Loading:
loader_platform_open_library(const char *lib_path)535 static inline loader_platform_dl_handle loader_platform_open_library(const char *lib_path) {
536     int lib_path_utf16_size = MultiByteToWideChar(CP_UTF8, 0, lib_path, -1, NULL, 0);
537     if (lib_path_utf16_size <= 0) {
538         return NULL;
539     }
540     wchar_t *lib_path_utf16 = (wchar_t *)loader_stack_alloc(lib_path_utf16_size * sizeof(wchar_t));
541     if (MultiByteToWideChar(CP_UTF8, 0, lib_path, -1, lib_path_utf16, lib_path_utf16_size) != lib_path_utf16_size) {
542         return NULL;
543     }
544     // Try loading the library the original way first.
545     loader_platform_dl_handle lib_handle = LoadLibraryW(lib_path_utf16);
546     if (lib_handle == NULL && GetLastError() == ERROR_MOD_NOT_FOUND) {
547         // If that failed, then try loading it with broader search folders.
548         lib_handle = LoadLibraryExW(lib_path_utf16, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
549     }
550     return lib_handle;
551 }
loader_platform_open_library_error(const char *libPath)552 static inline const char *loader_platform_open_library_error(const char *libPath) {
553     static char errorMsg[512];
554     (void)snprintf(errorMsg, 511, "Failed to open dynamic library \"%s\" with error %lu", libPath, GetLastError());
555     return errorMsg;
556 }
loader_platform_close_library(loader_platform_dl_handle library)557 static inline void loader_platform_close_library(loader_platform_dl_handle library) {
558     if (!loader_disable_dynamic_library_unloading) {
559         FreeLibrary(library);
560     } else {
561         (void)library;
562     }
563 }
loader_platform_get_proc_address(loader_platform_dl_handle library, const char *name)564 static inline void *loader_platform_get_proc_address(loader_platform_dl_handle library, const char *name) {
565     assert(library);
566     assert(name);
567     return (void *)GetProcAddress(library, name);
568 }
loader_platform_get_proc_address_error(const char *name)569 static inline const char *loader_platform_get_proc_address_error(const char *name) {
570     static char errorMsg[120];
571     (void)snprintf(errorMsg, 119, "Failed to find function \"%s\" in dynamic library", name);
572     return errorMsg;
573 }
574 
575 // Thread mutex:
loader_platform_thread_create_mutex(loader_platform_thread_mutex *pMutex)576 static inline void loader_platform_thread_create_mutex(loader_platform_thread_mutex *pMutex) { InitializeCriticalSection(pMutex); }
loader_platform_thread_lock_mutex(loader_platform_thread_mutex *pMutex)577 static inline void loader_platform_thread_lock_mutex(loader_platform_thread_mutex *pMutex) { EnterCriticalSection(pMutex); }
loader_platform_thread_unlock_mutex(loader_platform_thread_mutex *pMutex)578 static inline void loader_platform_thread_unlock_mutex(loader_platform_thread_mutex *pMutex) { LeaveCriticalSection(pMutex); }
loader_platform_thread_delete_mutex(loader_platform_thread_mutex *pMutex)579 static inline void loader_platform_thread_delete_mutex(loader_platform_thread_mutex *pMutex) { DeleteCriticalSection(pMutex); }
580 
thread_safe_strtok(char *str, const char *delimiters, char **context)581 static inline void *thread_safe_strtok(char *str, const char *delimiters, char **context) {
582     return strtok_s(str, delimiters, context);
583 }
584 
loader_fopen(const char *fileName, const char *mode)585 static inline FILE *loader_fopen(const char *fileName, const char *mode) {
586     FILE *file = NULL;
587     errno_t err = fopen_s(&file, fileName, mode);
588     if (err != 0) return NULL;
589     return file;
590 }
591 
loader_strncat(char *dest, size_t dest_sz, const char *src, size_t count)592 static inline char *loader_strncat(char *dest, size_t dest_sz, const char *src, size_t count) {
593     errno_t err = strncat_s(dest, dest_sz, src, count);
594     if (err != 0) return NULL;
595     return dest;
596 }
597 
loader_strncpy(char *dest, size_t dest_sz, const char *src, size_t count)598 static inline char *loader_strncpy(char *dest, size_t dest_sz, const char *src, size_t count) {
599     errno_t err = strncpy_s(dest, dest_sz, src, count);
600     if (err != 0) return NULL;
601     return dest;
602 }
603 
604 #else  // defined(_WIN32)
605 
606 #warning The "vk_loader_platform.h" file must be modified for this OS.
607 
608 // NOTE: In order to support another OS, an #elif needs to be added (above the
609 // "#else // defined(_WIN32)") for that OS, and OS-specific versions of the
610 // contents of this file must be created.
611 
612 // NOTE: Other OS-specific changes are also needed for this OS.  Search for
613 // files with "WIN32" in it, as a quick way to find files that must be changed.
614 
615 #endif  // defined(_WIN32)
616