dynamic_library.cpp (4514B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "common/dynamic_library.h" 5 #include "common/assert.h" 6 #include "common/error.h" 7 #include "common/file_system.h" 8 #include "common/log.h" 9 #include "common/path.h" 10 #include "common/small_string.h" 11 #include "common/string_util.h" 12 13 #include "fmt/format.h" 14 #include <cstring> 15 16 #ifdef _WIN32 17 #include "common/windows_headers.h" 18 #else 19 #include <dlfcn.h> 20 #ifdef __APPLE__ 21 #include "common/cocoa_tools.h" 22 #endif 23 #endif 24 25 Log_SetChannel(DynamicLibrary); 26 27 DynamicLibrary::DynamicLibrary() = default; 28 29 DynamicLibrary::DynamicLibrary(const char* filename) 30 { 31 Error error; 32 if (!Open(filename, &error)) 33 ERROR_LOG(error.GetDescription()); 34 } 35 36 DynamicLibrary::DynamicLibrary(DynamicLibrary&& move) : m_handle(move.m_handle) 37 { 38 move.m_handle = nullptr; 39 } 40 41 DynamicLibrary::~DynamicLibrary() 42 { 43 Close(); 44 } 45 46 std::string DynamicLibrary::GetUnprefixedFilename(const char* filename) 47 { 48 #if defined(_WIN32) 49 return std::string(filename) + ".dll"; 50 #elif defined(__APPLE__) 51 return std::string(filename) + ".dylib"; 52 #else 53 return std::string(filename) + ".so"; 54 #endif 55 } 56 57 std::string DynamicLibrary::GetVersionedFilename(const char* libname, int major, int minor, int patch) 58 { 59 #if defined(_WIN32) 60 if (major >= 0 && minor >= 0 && patch >= 0) 61 return fmt::format("{}-{}-{}-{}.dll", libname, major, minor, patch); 62 else if (major >= 0 && minor >= 0) 63 return fmt::format("{}-{}-{}.dll", libname, major, minor); 64 else if (major >= 0) 65 return fmt::format("{}-{}.dll", libname, major); 66 else 67 return fmt::format("{}.dll", libname); 68 #elif defined(__APPLE__) 69 const char* prefix = std::strncmp(libname, "lib", 3) ? "lib" : ""; 70 if (major >= 0 && minor >= 0 && patch >= 0) 71 return fmt::format("{}{}.{}.{}.{}.dylib", prefix, libname, major, minor, patch); 72 else if (major >= 0 && minor >= 0) 73 return fmt::format("{}{}.{}.{}.dylib", prefix, libname, major, minor); 74 else if (major >= 0) 75 return fmt::format("{}{}.{}.dylib", prefix, libname, major); 76 else 77 return fmt::format("{}{}.dylib", prefix, libname); 78 #else 79 const char* prefix = std::strncmp(libname, "lib", 3) ? "lib" : ""; 80 if (major >= 0 && minor >= 0 && patch >= 0) 81 return fmt::format("{}{}.so.{}.{}.{}", prefix, libname, major, minor, patch); 82 else if (major >= 0 && minor >= 0) 83 return fmt::format("{}{}.so.{}.{}", prefix, libname, major, minor); 84 else if (major >= 0) 85 return fmt::format("{}{}.so.{}", prefix, libname, major); 86 else 87 return fmt::format("{}{}.so", prefix, libname); 88 #endif 89 } 90 91 bool DynamicLibrary::Open(const char* filename, Error* error) 92 { 93 #ifdef _WIN32 94 m_handle = reinterpret_cast<void*>(LoadLibraryW(StringUtil::UTF8StringToWideString(filename).c_str())); 95 if (!m_handle) 96 { 97 Error::SetWin32(error, TinyString::from_format("Loading {} failed: ", filename), GetLastError()); 98 return false; 99 } 100 101 return true; 102 #else 103 m_handle = dlopen(filename, RTLD_NOW); 104 if (!m_handle) 105 { 106 #ifdef __APPLE__ 107 // On MacOS, try searching in Frameworks. 108 if (!Path::IsAbsolute(filename)) 109 { 110 std::optional<std::string> bundle_path = CocoaTools::GetBundlePath(); 111 if (bundle_path.has_value()) 112 { 113 std::string frameworks_path = fmt::format("{}/Contents/Frameworks/{}", bundle_path.value(), filename); 114 if (FileSystem::FileExists(frameworks_path.c_str())) 115 { 116 m_handle = dlopen(frameworks_path.c_str(), RTLD_NOW); 117 if (m_handle) 118 { 119 Error::Clear(error); 120 return true; 121 } 122 } 123 } 124 } 125 #endif 126 127 const char* err = dlerror(); 128 Error::SetStringFmt(error, "Loading {} failed: {}", filename, err ? err : "<UNKNOWN>"); 129 return false; 130 } 131 132 return true; 133 #endif 134 } 135 136 void DynamicLibrary::Adopt(void* handle) 137 { 138 AssertMsg(handle, "Handle is valid"); 139 140 Close(); 141 142 m_handle = handle; 143 } 144 145 void DynamicLibrary::Close() 146 { 147 if (!IsOpen()) 148 return; 149 150 #ifdef _WIN32 151 FreeLibrary(reinterpret_cast<HMODULE>(m_handle)); 152 #else 153 dlclose(m_handle); 154 #endif 155 m_handle = nullptr; 156 } 157 158 void* DynamicLibrary::GetSymbolAddress(const char* name) const 159 { 160 #ifdef _WIN32 161 return reinterpret_cast<void*>(GetProcAddress(reinterpret_cast<HMODULE>(m_handle), name)); 162 #else 163 return reinterpret_cast<void*>(dlsym(m_handle, name)); 164 #endif 165 } 166 167 DynamicLibrary& DynamicLibrary::operator=(DynamicLibrary&& move) 168 { 169 Close(); 170 m_handle = move.m_handle; 171 move.m_handle = nullptr; 172 return *this; 173 }