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