// slang-platform.cpp #define _CRT_SECURE_NO_WARNINGS #include "slang-platform.h" #include "slang-common.h" #include "slang-io.h" #ifdef _WIN32 #include #else #include "slang-string.h" #include #endif namespace Slang { // SharedLibrary /* static */SlangResult SharedLibrary::load(const char* path, SharedLibrary::Handle& handleOut) { StringBuilder builder; calcPlatformPath(UnownedStringSlice(path), builder); return loadWithPlatformPath(builder.begin(), handleOut); } /* static */void SharedLibrary::calcPlatformPath(const UnownedStringSlice& path, StringBuilder& outPath) { // Work out the shared library name String parent = Path::getParentDirectory(path); String filename = Path::getFileName(path); if (parent.getLength() > 0) { // Work out the filename platform name (as in add .dll say on windows) StringBuilder platformFileNameBuilder; SharedLibrary::appendPlatformFileName(filename.getUnownedSlice(), platformFileNameBuilder); Path::combineIntoBuilder(parent.getUnownedSlice(), platformFileNameBuilder.getUnownedSlice(), outPath); } else if (filename.getLength() > 0) { appendPlatformFileName(filename.getUnownedSlice(), outPath); } } /* static */String SharedLibrary::calcPlatformPath(const UnownedStringSlice& path) { StringBuilder builder; calcPlatformPath(path, builder); return builder.toString(); } #ifdef _WIN32 // Make sure SlangResult match for common standard window HRESULT SLANG_COMPILE_TIME_ASSERT(E_FAIL == SLANG_FAIL); SLANG_COMPILE_TIME_ASSERT(E_NOINTERFACE == SLANG_E_NO_INTERFACE); SLANG_COMPILE_TIME_ASSERT(E_HANDLE == SLANG_E_INVALID_HANDLE); SLANG_COMPILE_TIME_ASSERT(E_NOTIMPL == SLANG_E_NOT_IMPLEMENTED); SLANG_COMPILE_TIME_ASSERT(E_INVALIDARG == SLANG_E_INVALID_ARG); SLANG_COMPILE_TIME_ASSERT(E_OUTOFMEMORY == SLANG_E_OUT_OF_MEMORY); /* static */SlangResult PlatformUtil::getInstancePath(StringBuilder& out) { wchar_t path[_MAX_PATH]; ::GetModuleFileName(::GetModuleHandle(NULL), path, SLANG_COUNT_OF(path)); String pathString = String::fromWString(path); // We don't want the instance name, just the path to it out.clear(); out.append(Path::getParentDirectory(pathString)); return out.getLength() > 0 ? SLANG_OK : SLANG_FAIL; } /* static */SlangResult PlatformUtil::appendResult(SlangResult res, StringBuilder& builderOut) { if (SLANG_FAILED(res) && res != SLANG_FAIL) { LPWSTR buffer = nullptr; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, res, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPWSTR)&buffer, 0, nullptr); if (buffer) { builderOut << " "; // Convert to string builderOut.append(String::fromWString(buffer)); LocalFree(buffer); return SLANG_OK; } } return SLANG_FAIL; } /* static */SlangResult SharedLibrary::loadWithPlatformPath(char const* platformFileName, SharedLibrary::Handle& handleOut) { handleOut = nullptr; if (!platformFileName || strlen(platformFileName) == 0) { if (!GetModuleHandleExA(0, nullptr, (HMODULE*)&handleOut)) return SLANG_FAIL; return SLANG_OK; } // https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi/nf-libloaderapi-loadlibrarya const HMODULE h = LoadLibraryA(platformFileName); if (!h) { const DWORD lastError = GetLastError(); switch (lastError) { case ERROR_MOD_NOT_FOUND: case ERROR_PATH_NOT_FOUND: case ERROR_FILE_NOT_FOUND: { return SLANG_E_NOT_FOUND; } case ERROR_INVALID_ACCESS: case ERROR_ACCESS_DENIED: case ERROR_INVALID_DATA: { return SLANG_E_CANNOT_OPEN; } default: break; } // Turn to Result, if not one of the well known errors return HRESULT_FROM_WIN32(lastError); } handleOut = (Handle)h; return SLANG_OK; } /* static */void SharedLibrary::unload(Handle handle) { SLANG_ASSERT(handle); ::FreeLibrary((HMODULE)handle); } /* static */ void* SharedLibrary::findSymbolAddressByName(Handle handle, char const* name) { SLANG_ASSERT(handle); return GetProcAddress((HMODULE)handle, name); } /* static */void SharedLibrary::appendPlatformFileName(const UnownedStringSlice& name, StringBuilder& dst) { dst.append(name); dst.append(".dll"); } #else // _WIN32 /* static */SlangResult PlatformUtil::getInstancePath([[maybe_unused]] StringBuilder& out) { // On non Windows it's typically hard to get the instance path, so we'll say not implemented. // The meaning is also somewhat more ambiguous - is it the exe or the shared library path? return SLANG_E_NOT_IMPLEMENTED; } /* static */SlangResult PlatformUtil::appendResult([[maybe_unused]] SlangResult res, [[maybe_unused]] StringBuilder& builderOut) { return SLANG_E_NOT_IMPLEMENTED; } /* static */SlangResult SharedLibrary::loadWithPlatformPath(char const* platformFileName, Handle& handleOut) { handleOut = nullptr; // Work around // https://github.com/microsoft/DirectXShaderCompiler/issues/5119 and // https://github.com/doitsujin/dxvk/issues/3330 // libdxcompiler.so invokes UB on dlclose, the dxvk libs break GDB when // closed const auto unclosableLibNames = {"libdxcompiler", "libdxvk_d3d11", "libdxvk_dxgi"}; bool isUnclosable = false; for(auto n : unclosableLibNames) { if(strncmp(platformFileName, n, strlen(n)) == 0) { isUnclosable = true; break; } } if (strlen(platformFileName) == 0) platformFileName = nullptr; const auto mode = RTLD_NOW | RTLD_GLOBAL | (isUnclosable ? RTLD_NODELETE : 0); void *h = dlopen(platformFileName, mode); if (!h) { #if 0 // We can't output the error message here, because it will cause output when testing what code gen is available if(auto msg = dlerror()) { fprintf(stderr, "error: %s\n", msg); } #endif return SLANG_FAIL; } handleOut = (Handle)h; return SLANG_OK; } /* static */void SharedLibrary::unload(Handle handle) { SLANG_ASSERT(handle); dlclose(handle); } /* static */void* SharedLibrary::findSymbolAddressByName(Handle handle, char const* name) { return dlsym((void*)handle, name); } /* static */void SharedLibrary::appendPlatformFileName(const UnownedStringSlice& name, StringBuilder& dst) { #if __CYGWIN__ dst.append(name); dst.append(".dll"); #elif SLANG_APPLE_FAMILY dst.append("lib"); dst.append(name); dst.append(".dylib"); #elif SLANG_LINUX_FAMILY if (!name.startsWith("lib")) dst.append("lib"); dst.append(name); if (name.indexOf(UnownedStringSlice(".so.")) == -1) dst.append(".so"); #else // Just guess we can do with the name on it's own dst.append(name); #endif } #endif // _WIN32 /* static */SlangResult PlatformUtil::getEnvironmentVariable(const UnownedStringSlice& name, StringBuilder& out) { const char* value = getenv(String(name).getBuffer()); if (value) { out.append(value); return SLANG_OK; } return SLANG_E_NOT_FOUND; } /* static */PlatformKind PlatformUtil::getPlatformKind() { #if SLANG_WINRT return PlatformKind::WinRT; #elif SLANG_XBOXONE return PlatformKind::XBoxOne; #elif SLANG_WIN64 return PlatformKind::Win64; #elif SLANG_X360 return PlatformKind::X360; #elif SLANG_WIN32 return PlatformKind::Win32; #elif SLANG_ANDROID return PlatformKind::Android; #elif SLANG_LINUX return PlatformKind::Linux; #elif SLANG_IOS return PlatformKind::IOS; #elif SLANG_OSX return PlatformKind::OSX; #elif SLANG_PS3 return PlatformKind::PS3; #elif SLANG_SLANG_PS4 return PlatformKind::PS4; #elif SLANG_PSP2 return PlatformKind::PSP2; #elif SLANG_WIIU return PlatformKind::WIIU; #else return PlatformKind::Unknown; #endif } static const PlatformFlags s_familyFlags[int(PlatformFamily::CountOf)] = { 0, // Unknown PlatformFlag::WinRT | PlatformFlag::Win32 | PlatformFlag::Win64, // Windows PlatformFlag::WinRT | PlatformFlag::Win32 | PlatformFlag::Win64 | PlatformFlag::X360 | PlatformFlag::XBoxOne, // Microsoft PlatformFlag::Linux | PlatformFlag::Android, // Linux PlatformFlag::IOS | PlatformFlag::OSX, // Apple PlatformFlag::Linux | PlatformFlag::Android | PlatformFlag::IOS | PlatformFlag::OSX, // Unix }; /* static */PlatformFlags PlatformUtil::getPlatformFlags(PlatformFamily family) { return s_familyFlags[int(family)]; } /* static */SlangResult PlatformUtil::outputDebugMessage([[maybe_unused]] const char* text) { #ifdef _WIN32 OutputDebugStringA(text); return SLANG_OK; #else return SLANG_E_NOT_AVAILABLE; #endif } }