diff --git a/src/ll/api/mod/NativeMod.cpp b/src/ll/api/mod/NativeMod.cpp index 5b463e587e..5907fbc70c 100644 --- a/src/ll/api/mod/NativeMod.cpp +++ b/src/ll/api/mod/NativeMod.cpp @@ -8,20 +8,21 @@ namespace ll::mod { struct NativeMod::Impl { - Handle handle; + sys_utils::HandleT handle; // for ll self + sys_utils::DynamicLibrary lib; }; -NativeMod::NativeMod(Manifest manifest, Handle handle) +NativeMod::NativeMod(Manifest manifest, sys_utils::HandleT handle) : Mod(std::move(manifest)), mImpl(std::make_unique(handle)) {} NativeMod::~NativeMod() = default; -void NativeMod::setHandle(Handle handle) { mImpl->handle = handle; } +sys_utils::DynamicLibrary& NativeMod::getDynamicLibrary() { return mImpl->lib; } -NativeMod::Handle NativeMod::getHandle() const { return mImpl->handle; } +sys_utils::HandleT NativeMod::getHandle() const { return mImpl->handle ? mImpl->handle : (mImpl->lib.handle()); } -std::shared_ptr NativeMod::getByHandle(Handle handle) { +std::shared_ptr NativeMod::getByHandle(sys_utils::HandleT handle) { if (handle == sys_utils::getCurrentModuleHandle()) { return getSelfModIns(); } @@ -33,6 +34,6 @@ std::shared_ptr NativeMod::getByHandle(Handle handle) { return manger->getModByHandle(handle); } -std::shared_ptr NativeMod::current(Handle handle) { return getByHandle(handle); } +std::shared_ptr NativeMod::current(sys_utils::HandleT handle) { return getByHandle(handle); } } // namespace ll::mod diff --git a/src/ll/api/mod/NativeMod.h b/src/ll/api/mod/NativeMod.h index 2d33720e41..5ef10bb3bc 100644 --- a/src/ll/api/mod/NativeMod.h +++ b/src/ll/api/mod/NativeMod.h @@ -18,21 +18,18 @@ class NativeMod : public Mod { struct Impl; std::unique_ptr mImpl; -public: - using Handle = void*; - protected: - void setHandle(Handle handle); + sys_utils::DynamicLibrary& getDynamicLibrary(); public: - NativeMod(Manifest manifest, Handle handle = nullptr); + NativeMod(Manifest manifest, sys_utils::HandleT handle = nullptr); ~NativeMod(); - LLNDAPI Handle getHandle() const; + LLNDAPI sys_utils::HandleT getHandle() const; - LLNDAPI static std::shared_ptr getByHandle(Handle handle); + LLNDAPI static std::shared_ptr getByHandle(sys_utils::HandleT handle); - LLNDAPI static std::shared_ptr current(Handle handle = sys_utils::getCurrentModuleHandle()); + LLNDAPI static std::shared_ptr current(sys_utils::HandleT handle = sys_utils::getCurrentModuleHandle()); }; } // namespace ll::mod diff --git a/src/ll/api/utils/SystemUtils.cpp b/src/ll/api/utils/SystemUtils.cpp index b79aac5579..55c4576a30 100644 --- a/src/ll/api/utils/SystemUtils.cpp +++ b/src/ll/api/utils/SystemUtils.cpp @@ -4,6 +4,7 @@ #include "ll/api/i18n/I18n.h" #include "ll/api/memory/Memory.h" +#include "ll/api/utils/ErrorUtils.h" #include "ll/api/utils/StringUtils.h" #include "ll/core/Config.h" @@ -145,4 +146,22 @@ bool isStdoutSupportAnsi() { } return false; } + +std::optional DynamicLibrary::load(std::filesystem::path const& path) noexcept { + if (lib) { + return std::system_error{{}}; + } + lib = LoadLibrary(path.c_str()); + if (!lib) { + return error_utils::getWinLastError(); + } + return {}; +} +std::optional DynamicLibrary::free() noexcept { + if (!FreeLibrary((HMODULE)lib)) { + return error_utils::getWinLastError(); + } + return {}; +} +void* DynamicLibrary::getAddress(char const* symbol) noexcept { return GetProcAddress((HMODULE)lib, symbol); } } // namespace ll::inline utils::sys_utils diff --git a/src/ll/api/utils/SystemUtils.h b/src/ll/api/utils/SystemUtils.h index 9eda54a684..b20ded7d4d 100644 --- a/src/ll/api/utils/SystemUtils.h +++ b/src/ll/api/utils/SystemUtils.h @@ -14,31 +14,60 @@ namespace ll::inline utils::sys_utils { +using HandleT = void*; + extern "C" struct _IMAGE_DOS_HEADER __ImageBase; // NOLINT(bugprone-reserved-identifier) -[[nodiscard]] inline void* getCurrentModuleHandle() { return &__ImageBase; } +[[nodiscard]] inline HandleT getCurrentModuleHandle() { return &__ImageBase; } + +LLNDAPI HandleT getModuleHandle(void* addr); + +LLNDAPI std::optional getModulePath(HandleT handle, HandleT process = nullptr); + +LLNDAPI std::string getModuleFileName(HandleT handle, HandleT process = nullptr); + +[[nodiscard]] inline std::string getCallerModuleFileName(void* addr = _ReturnAddress()) { + return getModuleFileName(getModuleHandle(addr)); +} +LLNDAPI std::span getImageRange(std::string_view name = ""); LLNDAPI std::string getSystemLocaleName(); LLNDAPI std::string const& getSystemName(); +LLNDAPI std::pair getLocalTime(); // tm & ms + +LLNDAPI bool isStdoutSupportAnsi(); + LLNDAPI bool isWine(); -LLNDAPI std::span getImageRange(std::string_view name = ""); +class DynamicLibrary { + HandleT lib = nullptr; -LLNDAPI void* getModuleHandle(void* addr); +public: + [[nodiscard]] DynamicLibrary() {} + [[nodiscard]] DynamicLibrary(std::filesystem::path const& path) { load(path); } + ~DynamicLibrary() { + if (lib) free(); + } -LLNDAPI std::optional getModulePath(void* handle, void* process = nullptr); + DynamicLibrary(DynamicLibrary&&) noexcept = default; + DynamicLibrary& operator=(DynamicLibrary&&) noexcept = default; + DynamicLibrary(DynamicLibrary const&) = delete; + DynamicLibrary& operator=(DynamicLibrary const&) = delete; -LLNDAPI std::string getModuleFileName(void* handle, void* process = nullptr); + LLAPI std::optional load(std::filesystem::path const& path) noexcept; + LLAPI std::optional free() noexcept; -LLNDAPI std::pair getLocalTime(); // tm & ms + LLNDAPI void* getAddress(char const* symbol) noexcept; -LLNDAPI bool isStdoutSupportAnsi(); + template + T getAddress(char const* symbol) noexcept { + return reinterpret_cast(getAddress(symbol)); + } -[[nodiscard]] inline std::string getCallerModuleFileName(void* addr = _ReturnAddress()) { - return getModuleFileName(getModuleHandle(addr)); -} + [[nodiscard]] constexpr HandleT handle() const noexcept { return lib; } +}; template Fn> [[nodiscard]] inline std::optional adaptFixedSizeToAllocatedResult(Fn&& callback) noexcept { diff --git a/src/ll/core/mod/ModRegistrar.cpp b/src/ll/core/mod/ModRegistrar.cpp index 5e5a6d09a4..94c0d0dc89 100644 --- a/src/ll/core/mod/ModRegistrar.cpp +++ b/src/ll/core/mod/ModRegistrar.cpp @@ -119,11 +119,22 @@ void ModRegistrar::loadAllMods() { } } - std::unordered_set needLoad; + std::unordered_set loadingQueueHash; + std::vector pendingRemoved; + std::queue loadingQueue; + for (auto& [name, manifest] : manifests) { if (manifest.passive == true) { continue; } + loadingQueue.push(name); + loadingQueueHash.emplace(name); + } + + while (!loadingQueue.empty()) { + auto name = std::move(loadingQueue.front()); + loadingQueue.pop(); + auto& manifest = manifests.at(name); if (manifest.dependencies) { bool error = false; for (auto& dependency : *manifest.dependencies) { @@ -139,31 +150,34 @@ void ModRegistrar::loadAllMods() { } } if (error) { - getLogger().error("The dependencies of {0} are missing, will not be loaded"_tr(name)); + getLogger().error("{0} will not be loaded because the dependencies are missing"_tr(name)); + pendingRemoved.emplace_back(name); continue; } for (auto& dependency : *manifest.dependencies) { - needLoad.emplace(dependency.name); + if (loadingQueueHash.emplace(dependency.name).second) { + loadingQueue.push(dependency.name); + } } } - needLoad.emplace(name); if (manifest.optionalDependencies) { for (auto& dependency : *manifest.optionalDependencies) { if (manifests.contains(dependency.name) && checkVersion(manifests.at(dependency.name), dependency)) { - needLoad.emplace(dependency.name); + if (loadingQueueHash.emplace(dependency.name).second) { + loadingQueue.push(dependency.name); + } } } } } - std::vector conflicts; - for (auto& name : needLoad) { + for (auto& name : loadingQueueHash) { auto& manifest = manifests.at(name); if (!manifest.conflicts) { continue; } for (auto& conflict : *manifest.conflicts) { if (manifests.contains(conflict.name) && checkVersion(manifests.at(conflict.name), conflict)) { - conflicts.emplace_back(name); + pendingRemoved.emplace_back(name); getLogger().error("{0} conflicts with {1}"_tr( name, conflict.version @@ -173,20 +187,20 @@ void ModRegistrar::loadAllMods() { } } } - for (auto& name : conflicts) { - needLoad.erase(name); + for (auto& name : pendingRemoved) { + loadingQueueHash.erase(name); } - for (auto& name : needLoad) { + for (auto& name : loadingQueueHash) { auto& manifest = manifests.at(name); if (manifest.dependencies) { - bool deniedByConflict = false; + bool denied = false; for (auto& dependency : *manifest.dependencies) { - if (!needLoad.contains(dependency.name)) { - deniedByConflict = true; + if (!loadingQueueHash.contains(dependency.name)) { + denied = true; } } - if (deniedByConflict) { - getLogger().error("The dependencies of {0} are in conflict, will not be loaded"_tr(name)); + if (denied) { + getLogger().error("{0} will not be loaded because the dependencies can't loaded"_tr(name)); continue; } for (auto& dependency : *manifest.dependencies) { @@ -197,14 +211,15 @@ void ModRegistrar::loadAllMods() { } if (manifest.optionalDependencies) { for (auto& dependency : *manifest.optionalDependencies) { - if (needLoad.contains(dependency.name)) { + if (loadingQueueHash.contains(dependency.name)) { impl->deps.emplaceDependency(name, dependency.name); } } } if (manifest.loadBefore) { for (auto& dependency : *manifest.loadBefore) { - if (needLoad.contains(dependency.name) && checkVersion(manifests.at(dependency.name), dependency)) { + if (loadingQueueHash.contains(dependency.name) + && checkVersion(manifests.at(dependency.name), dependency)) { impl->deps.emplaceDependency(dependency.name, name); } } @@ -212,9 +227,12 @@ void ModRegistrar::loadAllMods() { } auto sort = impl->deps.sort(); for (auto& name : sort.unsorted) { - getLogger().error("The dependencies of {0} are in loops, will not be loaded"_tr(name)); + getLogger().error( + "{0} will not be loaded because the dependency are in loops"_tr( + name + ) + ); } - std::unordered_set loadErrored; for (auto& name : sort.sorted) { auto& manifest = manifests.at(name); @@ -226,7 +244,7 @@ void ModRegistrar::loadAllMods() { } } if (deniedByDepError) { - getLogger().error("The dependencies of {0} is not loaded, will not be loaded"_tr(name)); + getLogger().error("{0} will not be loaded because the dependencies are not loaded"_tr(name)); loadErrored.emplace(name); continue; } diff --git a/src/ll/core/mod/NativeModManager.cpp b/src/ll/core/mod/NativeModManager.cpp index b452aca5da..16b6ec6d91 100644 --- a/src/ll/core/mod/NativeModManager.cpp +++ b/src/ll/core/mod/NativeModManager.cpp @@ -24,7 +24,6 @@ #include "ll/core/LeviLamina.h" #include "errhandlingapi.h" -#include "libloaderapi.h" #include "minwindef.h" #include "processenv.h" #include "winerror.h" @@ -119,35 +118,37 @@ Expected<> NativeModManager::load(Manifest manifest) { SetEnvironmentVariableW(L"PATH", res->c_str()); } auto entry = modDir / string_utils::sv2u8sv(currentLoadingMod->getManifest().entry); - auto lib = LoadLibraryW(entry.c_str()); - if (!lib) { - auto e = error_utils::getWinLastError(); + if (auto e = currentLoadingMod->getDynamicLibrary().load(entry); e) { Expected<> error{makeExceptionError(std::make_exception_ptr(e))}; - if (e.code().value() == 126 || e.code().value() == 127) { + if (e->code().value() == 126 || e->code().value() == 127) { error.error().join(makeStringError(diagnosticDependency(entry))); } return error; } - if (!GetProcAddress(lib, "ll_memory_operator_overrided")) { + auto& lib = currentLoadingMod->getDynamicLibrary(); + if (!lib.getAddress("ll_memory_operator_overrided")) { using namespace i18n_literals; - return makeStringError("The mod is not using the unified memory allocation operator, will not be loaded."_tr()); + return makeStringError( + "{0} will not be loaded because it isn't using the unified memory allocation operator"_tr( + currentLoadingMod->getManifest().name + ) + ); } - currentLoadingMod->setHandle(lib); - // TODO: remove in future - if (auto addr = GetProcAddress(lib, "ll_plugin_load"); addr) { - currentLoadingMod->onLoad(reinterpret_cast(addr)); - currentLoadingMod->onUnload(reinterpret_cast(GetProcAddress(lib, "ll_plugin_unload"))); - currentLoadingMod->onEnable(reinterpret_cast(GetProcAddress(lib, "ll_plugin_enable"))); - currentLoadingMod->onDisable(reinterpret_cast(GetProcAddress(lib, "ll_plugin_disable"))); + // TODO: remove in release + if (auto addr = lib.getAddress("ll_plugin_load"); addr) { + currentLoadingMod->onLoad(addr); + currentLoadingMod->onUnload(lib.getAddress("ll_plugin_unload")); + currentLoadingMod->onEnable(lib.getAddress("ll_plugin_enable")); + currentLoadingMod->onDisable(lib.getAddress("ll_plugin_disable")); } else { - currentLoadingMod->onLoad(reinterpret_cast(GetProcAddress(lib, "ll_mod_load"))); - currentLoadingMod->onUnload(reinterpret_cast(GetProcAddress(lib, "ll_mod_unload"))); - currentLoadingMod->onEnable(reinterpret_cast(GetProcAddress(lib, "ll_mod_enable"))); - currentLoadingMod->onDisable(reinterpret_cast(GetProcAddress(lib, "ll_mod_disable"))); + currentLoadingMod->onLoad(lib.getAddress("ll_mod_load")); + currentLoadingMod->onUnload(lib.getAddress("ll_mod_unload")); + currentLoadingMod->onEnable(lib.getAddress("ll_mod_enable")); + currentLoadingMod->onDisable(lib.getAddress("ll_mod_disable")); } return currentLoadingMod->onLoad().transform([&, this] { addMod(currentLoadingMod->getManifest().name, currentLoadingMod); - handleMap[lib] = currentLoadingMod; + handleMap[lib.handle()] = currentLoadingMod; }); } @@ -160,8 +161,8 @@ Expected<> NativeModManager::unload(std::string_view name) { if (auto res = ptr->onDisable().and_then([&] { return ptr->onUnload(); }); !res) { return res; } - if (!FreeLibrary((HMODULE)ptr->getHandle())) { - return makeExceptionError(std::make_exception_ptr(error_utils::getWinLastError())); + if (auto err = ptr->getDynamicLibrary().free(); err) { + return makeExceptionError(std::make_exception_ptr(*err)); } handleMap.erase(ptr->getHandle()); eraseMod(name);