yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Gangzheng TongUse LOAD_LIBRARY_SEARCH_DEFAULT_DIRS for LoadLibraryExW (#8491)c8d189bd5

master
11.0 KiB396 linesraw
1// slang-platform.cpp
2
3#define _CRT_SECURE_NO_WARNINGS
4
5#include "slang-platform.h"
6
7#include "slang-common.h"
8#include "slang-io.h"
9
10#ifdef _WIN32
11#include <windows.h>
12#else
13#include "slang-string.h"
14
15#include <dlfcn.h>
16#endif
17
18#if SLANG_HAS_BACKTRACE
19#include <execinfo.h>
20#endif
21
22#if SLANG_LINUX_FAMILY
23#include <limits.h>
24#include <unistd.h>
25#endif
26
27namespace Slang
28{
29// SharedLibrary
30
31/* static */ SlangResult SharedLibrary::load(const char* path, SharedLibrary::Handle& handleOut)
32{
33    StringBuilder builder;
34    calcPlatformPath(UnownedStringSlice(path), builder);
35    return loadWithPlatformPath(builder.begin(), handleOut);
36}
37
38/* static */ void SharedLibrary::calcPlatformPath(
39    const UnownedStringSlice& path,
40    StringBuilder& outPath)
41{
42    // Work out the shared library name
43    String parent = Path::getParentDirectory(path);
44    String filename = Path::getFileName(path);
45
46    if (parent.getLength() > 0)
47    {
48        // Work out the filename platform name (as in add .dll say on windows)
49        StringBuilder platformFileNameBuilder;
50        SharedLibrary::appendPlatformFileName(filename.getUnownedSlice(), platformFileNameBuilder);
51
52        Path::combineIntoBuilder(
53            parent.getUnownedSlice(),
54            platformFileNameBuilder.getUnownedSlice(),
55            outPath);
56    }
57    else if (filename.getLength() > 0)
58    {
59        appendPlatformFileName(filename.getUnownedSlice(), outPath);
60    }
61}
62
63/* static */ String SharedLibrary::calcPlatformPath(const UnownedStringSlice& path)
64{
65    StringBuilder builder;
66    calcPlatformPath(path, builder);
67    return builder.toString();
68}
69
70#ifdef _WIN32
71
72// Make sure SlangResult match for common standard window HRESULT
73SLANG_COMPILE_TIME_ASSERT(E_FAIL == SLANG_FAIL);
74SLANG_COMPILE_TIME_ASSERT(E_NOINTERFACE == SLANG_E_NO_INTERFACE);
75SLANG_COMPILE_TIME_ASSERT(E_HANDLE == SLANG_E_INVALID_HANDLE);
76SLANG_COMPILE_TIME_ASSERT(E_NOTIMPL == SLANG_E_NOT_IMPLEMENTED);
77SLANG_COMPILE_TIME_ASSERT(E_INVALIDARG == SLANG_E_INVALID_ARG);
78SLANG_COMPILE_TIME_ASSERT(E_OUTOFMEMORY == SLANG_E_OUT_OF_MEMORY);
79
80/* static */ SlangResult PlatformUtil::getInstancePath(StringBuilder& out)
81{
82    wchar_t path[_MAX_PATH];
83    ::GetModuleFileName(::GetModuleHandle(NULL), path, SLANG_COUNT_OF(path));
84    String pathString = String::fromWString(path);
85
86    // We don't want the instance name, just the path to it
87    out.clear();
88    out.append(Path::getParentDirectory(pathString));
89
90    return out.getLength() > 0 ? SLANG_OK : SLANG_FAIL;
91}
92
93/* static */ SlangResult PlatformUtil::appendResult(SlangResult res, StringBuilder& builderOut)
94{
95    if (SLANG_FAILED(res) && res != SLANG_FAIL)
96    {
97        LPWSTR buffer = nullptr;
98        FormatMessage(
99            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
100            nullptr,
101            res,
102            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
103            (LPWSTR)&buffer,
104            0,
105            nullptr);
106
107        if (buffer)
108        {
109            builderOut << " ";
110            // Convert to string
111            builderOut.append(String::fromWString(buffer));
112            LocalFree(buffer);
113            return SLANG_OK;
114        }
115    }
116    return SLANG_FAIL;
117}
118
119/* static */ SlangResult SharedLibrary::loadWithPlatformPath(
120    char const* platformFileName,
121    SharedLibrary::Handle& handleOut)
122{
123    handleOut = nullptr;
124    if (!platformFileName || strlen(platformFileName) == 0)
125    {
126        if (!GetModuleHandleExW(0, nullptr, (HMODULE*)&handleOut))
127            return SLANG_FAIL;
128        return SLANG_OK;
129    }
130
131    // We try to search the DLL in two different attempts.
132    // First attempt - LoadLibraryExW()
133    // If it failed to find one, we will use LoadLibraryW() to search over all PATH.
134    // Search order: 1) The directory that contains the DLL (LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR).
135    //                  This directory is searched only for dependencies of the DLL being loaded.
136    //               2) Application directory
137    //               3) User directories (AddDllDirectory/SetDllDirectory)
138    //               4) System32
139    //               5) PATH environment variable (by the 2nd attempt with LoadLibraryW())
140    // https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw
141    // https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi/nf-libloaderapi-loadlibraryw
142    String platformFileNameStr(platformFileName);
143    OSString wideFileName = platformFileNameStr.toWString();
144    HMODULE handle = LoadLibraryExW(wideFileName, nullptr, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
145
146    if (!handle)
147        handle = LoadLibraryW(wideFileName);
148    // If still not found, return an error.
149    if (!handle)
150    {
151        const DWORD lastError = GetLastError();
152        switch (lastError)
153        {
154        case ERROR_MOD_NOT_FOUND:
155        case ERROR_PATH_NOT_FOUND:
156        case ERROR_FILE_NOT_FOUND:
157            {
158                return SLANG_E_NOT_FOUND;
159            }
160        case ERROR_INVALID_ACCESS:
161        case ERROR_ACCESS_DENIED:
162        case ERROR_INVALID_DATA:
163            {
164                return SLANG_E_CANNOT_OPEN;
165            }
166        default:
167            break;
168        }
169        // Turn to Result, if not one of the well known errors
170        return HRESULT_FROM_WIN32(lastError);
171    }
172    handleOut = (Handle)handle;
173    return SLANG_OK;
174}
175
176/* static */ void SharedLibrary::unload(Handle handle)
177{
178    SLANG_ASSERT(handle);
179    ::FreeLibrary((HMODULE)handle);
180}
181
182/* static */ void* SharedLibrary::findSymbolAddressByName(Handle handle, char const* name)
183{
184    SLANG_ASSERT(handle);
185    return reinterpret_cast<void*>(GetProcAddress((HMODULE)handle, name));
186}
187
188/* static */ void SharedLibrary::appendPlatformFileName(
189    const UnownedStringSlice& name,
190    StringBuilder& dst)
191{
192    dst.append(name);
193    dst.append(".dll");
194}
195
196#else // _WIN32
197/* static */ SlangResult PlatformUtil::getInstancePath([[maybe_unused]] StringBuilder& out)
198{
199#if defined(__linux__) || defined(__CYGWIN__)
200    char path[PATH_MAX];
201    ssize_t len = readlink("/proc/self/exe", path, sizeof(path) - 1);
202    if (len == -1)
203    {
204        return SLANG_FAIL;
205    }
206
207    path[len] = '\0';
208    String pathString(path);
209
210    // We don't want the instance name, just the path to it
211    out.clear();
212    out.append(Path::getParentDirectory(pathString));
213
214    return out.getLength() > 0 ? SLANG_OK : SLANG_FAIL;
215#else
216    return SLANG_E_NOT_IMPLEMENTED;
217#endif
218}
219
220/* static */ SlangResult PlatformUtil::appendResult(
221    [[maybe_unused]] SlangResult res,
222    [[maybe_unused]] StringBuilder& builderOut)
223{
224    return SLANG_E_NOT_IMPLEMENTED;
225}
226
227/* static */ SlangResult SharedLibrary::loadWithPlatformPath(
228    char const* platformFileName,
229    Handle& handleOut)
230{
231    handleOut = nullptr;
232    // Work around
233    // https://github.com/microsoft/DirectXShaderCompiler/issues/5119 and
234    // https://github.com/doitsujin/dxvk/issues/3330
235    // libdxcompiler.so invokes UB on dlclose, the dxvk libs break GDB when
236    // closed
237    const auto unclosableLibNames = {"libdxcompiler", "libdxvk_d3d11", "libdxvk_dxgi"};
238    bool isUnclosable = false;
239    for (auto n : unclosableLibNames)
240    {
241        if (strncmp(platformFileName, n, strlen(n)) == 0)
242        {
243            isUnclosable = true;
244            break;
245        }
246    }
247    if (strlen(platformFileName) == 0)
248        platformFileName = nullptr;
249    const auto mode = RTLD_NOW | RTLD_GLOBAL | (isUnclosable ? RTLD_NODELETE : 0);
250    void* h = dlopen(platformFileName, mode);
251    if (!h)
252    {
253#if 0
254        // We can't output the error message here, because it will cause output when testing what code gen is available
255		if(auto msg = dlerror())
256		{
257			fprintf(stderr, "error: %s\n", msg);
258		}
259#endif
260        return SLANG_FAIL;
261    }
262    handleOut = (Handle)h;
263    return SLANG_OK;
264}
265
266/* static */ void SharedLibrary::unload(Handle handle)
267{
268    SLANG_ASSERT(handle);
269    dlclose(handle);
270}
271
272/* static */ void* SharedLibrary::findSymbolAddressByName(Handle handle, char const* name)
273{
274    return dlsym((void*)handle, name);
275}
276
277/* static */ void SharedLibrary::appendPlatformFileName(
278    const UnownedStringSlice& name,
279    StringBuilder& dst)
280{
281#if __CYGWIN__
282    dst.append(name);
283    dst.append(".dll");
284#elif SLANG_APPLE_FAMILY
285    dst.append("lib");
286    dst.append(name);
287    dst.append(".dylib");
288#elif SLANG_LINUX_FAMILY
289    if (!name.startsWith("lib"))
290        dst.append("lib");
291    dst.append(name);
292    if (name.indexOf(UnownedStringSlice(".so.")) == -1)
293        dst.append(".so");
294#else
295    // Just guess we can do with the name on it's own
296    dst.append(name);
297#endif
298}
299
300#endif // _WIN32
301
302
303/* static */ SlangResult PlatformUtil::getEnvironmentVariable(
304    const UnownedStringSlice& name,
305    StringBuilder& out)
306{
307    const char* value = getenv(String(name).getBuffer());
308    if (value)
309    {
310        out.append(value);
311        return SLANG_OK;
312    }
313    return SLANG_E_NOT_FOUND;
314}
315
316/* static */ PlatformKind PlatformUtil::getPlatformKind()
317{
318#if SLANG_WINRT
319    return PlatformKind::WinRT;
320#elif SLANG_XBOXONE
321    return PlatformKind::XBoxOne;
322#elif SLANG_WIN64
323    return PlatformKind::Win64;
324#elif SLANG_X360
325    return PlatformKind::X360;
326#elif SLANG_WIN32
327    return PlatformKind::Win32;
328#elif SLANG_ANDROID
329    return PlatformKind::Android;
330#elif SLANG_LINUX
331    return PlatformKind::Linux;
332#elif SLANG_IOS
333    return PlatformKind::IOS;
334#elif SLANG_OSX
335    return PlatformKind::OSX;
336#elif SLANG_PS3
337    return PlatformKind::PS3;
338#elif SLANG_SLANG_PS4
339    return PlatformKind::PS4;
340#elif SLANG_PSP2
341    return PlatformKind::PSP2;
342#elif SLANG_WIIU
343    return PlatformKind::WIIU;
344#else
345    return PlatformKind::Unknown;
346#endif
347}
348
349static const PlatformFlags s_familyFlags[int(PlatformFamily::CountOf)] = {
350    0,                                                               // Unknown
351    PlatformFlag::WinRT | PlatformFlag::Win32 | PlatformFlag::Win64, // Windows
352    PlatformFlag::WinRT | PlatformFlag::Win32 | PlatformFlag::Win64 | PlatformFlag::X360 |
353        PlatformFlag::XBoxOne,                   // Microsoft
354    PlatformFlag::Linux | PlatformFlag::Android, // Linux
355    PlatformFlag::IOS | PlatformFlag::OSX,       // Apple
356    PlatformFlag::Linux | PlatformFlag::Android | PlatformFlag::IOS | PlatformFlag::OSX, // Unix
357};
358
359/* static */ PlatformFlags PlatformUtil::getPlatformFlags(PlatformFamily family)
360{
361    return s_familyFlags[int(family)];
362}
363
364/* static */ SlangResult PlatformUtil::outputDebugMessage([[maybe_unused]] const char* text)
365{
366#ifdef _WIN32
367    String textStr(text);
368    OutputDebugStringW(textStr.toWString());
369    return SLANG_OK;
370#else
371    return SLANG_E_NOT_AVAILABLE;
372#endif
373}
374
375/* static */ void PlatformUtil::backtrace()
376{
377#if SLANG_HAS_BACKTRACE
378    // Print stack trace for debugging assistance
379    void* stackTrace[64];
380    int stackDepth = ::backtrace(stackTrace, 64);
381    char** symbols = ::backtrace_symbols(stackTrace, stackDepth);
382    if (symbols)
383    {
384        for (int i = 0; i < stackDepth; ++i)
385        {
386            fprintf(stdout, "%s\n", symbols[i]);
387        }
388        free(symbols);
389    }
390    fprintf(stdout, "\n");
391#else
392    fprintf(stdout, "Stack trace not available on this platform.\n");
393#endif
394}
395
396} // namespace Slang