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:
45typedef void *LoaderPlatformLibraryHandle;
46static 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
55static inline const char *LoaderPlatformLibraryOpenError(const std::string &path) {
56 (void)path;
57 return dlerror();
58}
59
60static inline void LoaderPlatformLibraryClose(LoaderPlatformLibraryHandle library) { dlclose(library); }
61
62static 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
68static 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
82static 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
93static 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
106static 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:
131typedef HMODULE LoaderPlatformLibraryHandle;
132static 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
150static 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
158static inline void LoaderPlatformLibraryClose(LoaderPlatformLibraryHandle library) { FreeLibrary(library); }
159
160static 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
166static 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
177static inline LoaderPlatformLibraryHandle LoaderPlatformLibraryOpen(const std::string &path) {
178// Stub func
179#error("Unknown platform, undefined dynamic library routines resulting");
180 (void)path;
181}
182
183static inline const char *LoaderPlatformLibraryOpenError(const std::string &path) {
184 // Stub func
185 (void)path;
186}
187
188static inline void LoaderPlatformLibraryClose(LoaderPlatformLibraryHandle library) {
189 // Stub func
190 (void)library;
191}
192
193static inline void *LoaderPlatformLibraryGetProcAddr(LoaderPlatformLibraryHandle library, const std::string &name) {
194 // Stub func
195 void(library);
196 void(name);
197}
198
199static inline const char *LoaderPlatformLibraryGetProcAddrError(const std::string &name) {
200 // Stub func
201 (void)name;
202}
203
204#endif
205