1 | // Copyright (c) 2017-2023, The Khronos Group Inc. |
2 | // Copyright (c) 2017-2019 Valve Corporation |
3 | // Copyright (c) 2017-2019 LunarG, Inc. |
4 | // |
5 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
6 | // |
7 | // Initial Authors: Mark Young <marky@lunarg.com>, Dave Houlton <daveh@lunarg.com> |
8 | // |
9 | |
10 | #pragma once |
11 | |
12 | #include <cassert> |
13 | #include <sstream> |
14 | #include <string> |
15 | |
16 | #include "xr_dependencies.h" |
17 | #include "platform_utils.hpp" |
18 | |
19 | #if defined(__GNUC__) && __GNUC__ >= 4 |
20 | #define LOADER_EXPORT __attribute__((visibility("default"))) |
21 | #elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) |
22 | #define LOADER_EXPORT __attribute__((visibility("default"))) |
23 | #else |
24 | #define LOADER_EXPORT |
25 | #endif |
26 | |
27 | // Environment variables |
28 | #if defined(XR_OS_LINUX) || defined(XR_OS_APPLE) || defined(XR_OS_ANDROID) |
29 | |
30 | #include <sys/types.h> |
31 | #include <sys/stat.h> |
32 | #include <dlfcn.h> |
33 | #include <unistd.h> |
34 | #include <stdlib.h> |
35 | #include <dirent.h> |
36 | |
37 | #ifndef PATH_MAX |
38 | #define PATH_MAX 1024 |
39 | #endif |
40 | |
41 | #define PATH_SEPARATOR ':' |
42 | #define DIRECTORY_SYMBOL '/' |
43 | |
44 | // Dynamic Loading of libraries: |
45 | typedef void *LoaderPlatformLibraryHandle; |
46 | static inline LoaderPlatformLibraryHandle LoaderPlatformLibraryOpen(const std::string &path) { |
47 | // When loading the library, we use RTLD_LAZY so that not all symbols have to be |
48 | // resolved at this time (which improves performance). Note that if not all symbols |
49 | // can be resolved, this could cause crashes later. |
50 | // For experimenting/debugging: Define the LD_BIND_NOW environment variable to force all |
51 | // symbols to be resolved here. |
52 | return dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); |
53 | } |
54 | |
55 | static inline const char *LoaderPlatformLibraryOpenError(const std::string &path) { |
56 | (void)path; |
57 | return dlerror(); |
58 | } |
59 | |
60 | static inline void LoaderPlatformLibraryClose(LoaderPlatformLibraryHandle library) { dlclose(library); } |
61 | |
62 | static inline void *LoaderPlatformLibraryGetProcAddr(LoaderPlatformLibraryHandle library, const std::string &name) { |
63 | assert(library); |
64 | assert(!name.empty()); |
65 | return dlsym(library, name.c_str()); |
66 | } |
67 | |
68 | static inline const char *LoaderPlatformLibraryGetProcAddrError(const std::string &name) { |
69 | (void)name; |
70 | return dlerror(); |
71 | } |
72 | |
73 | #elif defined(XR_OS_WINDOWS) |
74 | |
75 | #define PATH_SEPARATOR ';' |
76 | #define DIRECTORY_SYMBOL '\\' |
77 | |
78 | // Workaround for MS VS 2010/2013 missing snprintf and vsnprintf |
79 | #if defined(_MSC_VER) && _MSC_VER < 1900 |
80 | #include <stdint.h> |
81 | |
82 | static inline int32_t xr_vsnprintf(char *result_buffer, size_t buffer_size, const char *print_format, va_list varying_list) { |
83 | int32_t copy_count = -1; |
84 | if (buffer_size != 0) { |
85 | copy_count = _vsnprintf_s(result_buffer, buffer_size, _TRUNCATE, print_format, varying_list); |
86 | } |
87 | if (copy_count == -1) { |
88 | copy_count = _vscprintf(print_format, varying_list); |
89 | } |
90 | return copy_count; |
91 | } |
92 | |
93 | static inline int32_t xr_snprintf(char *result_buffer, size_t buffer_size, const char *print_format, ...) { |
94 | va_list varying_list; |
95 | va_start(varying_list, print_format); |
96 | int32_t copy_count = xr_vsnprintf(result_buffer, buffer_size, print_format, varying_list); |
97 | va_end(varying_list); |
98 | return copy_count; |
99 | } |
100 | |
101 | #define snprintf xr_snprintf |
102 | #define vsnprintf xr_vsnprintf |
103 | |
104 | #endif |
105 | |
106 | static inline std::string DescribeError(uint32_t code, bool prefixErrorCode = true) { |
107 | std::string str; |
108 | |
109 | if (prefixErrorCode) { |
110 | char prefixBuffer[64]; |
111 | snprintf(prefixBuffer, sizeof(prefixBuffer), "0x%llx (%lld): " , (uint64_t)code, (int64_t)code); |
112 | str = prefixBuffer; |
113 | } |
114 | |
115 | // Could use FORMAT_MESSAGE_FROM_HMODULE to specify an error source. |
116 | WCHAR errorBufferW[1024]{}; |
117 | const DWORD errorBufferWCapacity = sizeof(errorBufferW) / sizeof(errorBufferW[0]); |
118 | const DWORD length = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, (DWORD)code, |
119 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorBufferW, errorBufferWCapacity, nullptr); |
120 | |
121 | if (length) { // If errorBufferW contains what we are looking for... |
122 | str += wide_to_utf8(errorBufferW); |
123 | } else { |
124 | str = "(unknown)" ; |
125 | } |
126 | |
127 | return str; |
128 | } |
129 | |
130 | // Dynamic Loading: |
131 | typedef HMODULE LoaderPlatformLibraryHandle; |
132 | static inline LoaderPlatformLibraryHandle LoaderPlatformLibraryOpen(const std::string &path) { |
133 | const std::wstring pathW = utf8_to_wide(path); |
134 | |
135 | // Try loading the library the original way first. |
136 | LoaderPlatformLibraryHandle handle = LoadLibraryW(pathW.c_str()); |
137 | |
138 | if (handle == NULL && GetLastError() == ERROR_MOD_NOT_FOUND) { |
139 | const DWORD dwAttrib = GetFileAttributesW(pathW.c_str()); |
140 | const bool fileExists = (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); |
141 | if (fileExists) { |
142 | // If that failed, then try loading it with broader search folders. |
143 | handle = LoadLibraryExW(pathW.c_str(), NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); |
144 | } |
145 | } |
146 | |
147 | return handle; |
148 | } |
149 | |
150 | static inline std::string LoaderPlatformLibraryOpenError(const std::string &path) { |
151 | std::stringstream ss; |
152 | const DWORD dwLastError = GetLastError(); |
153 | const std::string strError = DescribeError(dwLastError); |
154 | ss << "Failed to open dynamic library " << path << " with error " << dwLastError << ": " << strError; |
155 | return ss.str(); |
156 | } |
157 | |
158 | static inline void LoaderPlatformLibraryClose(LoaderPlatformLibraryHandle library) { FreeLibrary(library); } |
159 | |
160 | static inline void *LoaderPlatformLibraryGetProcAddr(LoaderPlatformLibraryHandle library, const std::string &name) { |
161 | assert(library); |
162 | assert(name.size() > 0); |
163 | return reinterpret_cast<void *>(GetProcAddress(library, name.c_str())); |
164 | } |
165 | |
166 | static inline std::string LoaderPlatformLibraryGetProcAddrAddrError(const std::string &name) { |
167 | std::stringstream ss; |
168 | ss << "Failed to find function " << name << " in dynamic library" ; |
169 | return ss.str(); |
170 | } |
171 | |
172 | #else // Not Linux or Windows |
173 | |
174 | #define PATH_SEPARATOR ':' |
175 | #define DIRECTORY_SYMBOL '/' |
176 | |
177 | static inline LoaderPlatformLibraryHandle LoaderPlatformLibraryOpen(const std::string &path) { |
178 | // Stub func |
179 | #error("Unknown platform, undefined dynamic library routines resulting"); |
180 | (void)path; |
181 | } |
182 | |
183 | static inline const char *LoaderPlatformLibraryOpenError(const std::string &path) { |
184 | // Stub func |
185 | (void)path; |
186 | } |
187 | |
188 | static inline void LoaderPlatformLibraryClose(LoaderPlatformLibraryHandle library) { |
189 | // Stub func |
190 | (void)library; |
191 | } |
192 | |
193 | static inline void *LoaderPlatformLibraryGetProcAddr(LoaderPlatformLibraryHandle library, const std::string &name) { |
194 | // Stub func |
195 | void(library); |
196 | void(name); |
197 | } |
198 | |
199 | static inline const char *LoaderPlatformLibraryGetProcAddrError(const std::string &name) { |
200 | // Stub func |
201 | (void)name; |
202 | } |
203 | |
204 | #endif |
205 | |