From 53d3f1ae6ce5bd1dab1aec3803ad16ae867c16ed Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Sun, 9 Aug 2020 22:47:33 +0200 Subject: [PATCH] util/library: Cross-platform handler for library loading Adds a utility class and functions to load libraries and symbols from libraries somewhat safely. Libraries are immediately unloaded when the last reference to them is lost, so the shared_ptr should be stored if the library is actually needed. --- CMakeLists.txt | 2 + source/common.hpp | 1 + source/util/util-library.cpp | 107 +++++++++++++++++++++++++++++++++++ source/util/util-library.hpp | 40 +++++++++++++ 4 files changed, 150 insertions(+) create mode 100644 source/util/util-library.cpp create mode 100644 source/util/util-library.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 34c7ae0e..09427103 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -506,6 +506,8 @@ list(APPEND PROJECT_PRIVATE_SOURCE "source/util/utility.hpp" "source/util/utility.cpp" "source/util/util-event.hpp" + "source/util/util-library.cpp" + "source/util/util-library.hpp" "source/util/util-profiler.cpp" "source/util/util-profiler.hpp" "source/util/util-threadpool.cpp" diff --git a/source/common.hpp b/source/common.hpp index fa48b8a6..6ac7b8eb 100644 --- a/source/common.hpp +++ b/source/common.hpp @@ -47,6 +47,7 @@ #include "config.hpp" #include "strings.hpp" #include "version.hpp" +#include "util/util-library.hpp" #include "util/util-math.hpp" #include "util/util-profiler.hpp" #include "util/util-threadpool.hpp" diff --git a/source/util/util-library.cpp b/source/util/util-library.cpp new file mode 100644 index 00000000..e6bc10b2 --- /dev/null +++ b/source/util/util-library.cpp @@ -0,0 +1,107 @@ +// Copyright (c) 2020 Michael Fabian Dirks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "util-library.hpp" +#include + +#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) // Windows +#define ST_WINDOWS +#else +#define ST_UNIX +#endif + +#if defined(ST_WINDOWS) +#include +#elif defined(ST_UNIX) +#include +#endif + +util::library::library(std::filesystem::path file) : _library(nullptr) +{ +#if defined(ST_WINDOWS) + SetLastError(ERROR_SUCCESS); + _library = reinterpret_cast(LoadLibraryW(file.wstring().c_str())); + if (!_library) { + DWORD error = GetLastError(); + if (error != ERROR_PROC_NOT_FOUND) { + PSTR message = NULL; + std::string ex = "Failed to load library."; + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, error, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPSTR)&message, 0, NULL); + if (message) { + ex = message; + LocalFree(message); + throw std::runtime_error(ex); + } + } + throw std::runtime_error("Failed to load library."); + } +#elif defined(ST_UNIX) + _library = dlopen(file.string().c_str(), RTLD_LAZY); + if (!_library) { + if (char* error = dlerror(); error) + throw std::runtime_error(error); + else + throw std::runtime_error("Failed to load library."); + } +#endif +} + +util::library::~library() +{ +#if defined(ST_WINDOWS) + FreeLibrary(reinterpret_cast(_library)); +#elif defined(ST_UNIX) + dlclose(_library); +#endif +} + +void* util::library::load_symbol(std::string name) +{ +#if defined(ST_WINDOWS) + return reinterpret_cast(GetProcAddress(reinterpret_cast(_library), name.c_str())); +#elif defined(ST_UNIX) + return reinterpret_cast(dlsym(_library, name.c_str())); +#endif +} + +static std::unordered_map> libraries; + +std::shared_ptr<::util::library> util::library::load(std::filesystem::path file) +{ + std::string utf8_name = file.string(); + + auto kv = libraries.find(utf8_name); + if (kv != libraries.end()) { + if (auto ptr = kv->second.lock(); ptr) + return ptr; + libraries.erase(kv); + } + + auto ptr = std::make_shared<::util::library>(file); + libraries.emplace(utf8_name, ptr); + + return ptr; +} + +std::shared_ptr<::util::library> util::library::load(std::string name) +{ + return load(std::filesystem::path(name)); +} diff --git a/source/util/util-library.hpp b/source/util/util-library.hpp new file mode 100644 index 00000000..56b0da17 --- /dev/null +++ b/source/util/util-library.hpp @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Michael Fabian Dirks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once +#include +#include +#include + +namespace util { + class library { + void* _library; + + public: + library(std::filesystem::path file); + ~library(); + + void* load_symbol(std::string name); + + static std::shared_ptr<::util::library> load(std::filesystem::path file); + + static std::shared_ptr<::util::library> load(std::string name); + }; +} // namespace util