Skip to content

Commit

Permalink
fix: fix #1559
Browse files Browse the repository at this point in the history
  • Loading branch information
OEOTYAN committed Aug 3, 2024
1 parent 2bd2bea commit c55d750
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 66 deletions.
13 changes: 7 additions & 6 deletions src/ll/api/mod/NativeMod.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Impl>(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> NativeMod::getByHandle(Handle handle) {
std::shared_ptr<NativeMod> NativeMod::getByHandle(sys_utils::HandleT handle) {
if (handle == sys_utils::getCurrentModuleHandle()) {
return getSelfModIns();
}
Expand All @@ -33,6 +34,6 @@ std::shared_ptr<NativeMod> NativeMod::getByHandle(Handle handle) {
return manger->getModByHandle(handle);
}

std::shared_ptr<NativeMod> NativeMod::current(Handle handle) { return getByHandle(handle); }
std::shared_ptr<NativeMod> NativeMod::current(sys_utils::HandleT handle) { return getByHandle(handle); }

} // namespace ll::mod
13 changes: 5 additions & 8 deletions src/ll/api/mod/NativeMod.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,18 @@ class NativeMod : public Mod {
struct Impl;
std::unique_ptr<Impl> 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<NativeMod> getByHandle(Handle handle);
LLNDAPI static std::shared_ptr<NativeMod> getByHandle(sys_utils::HandleT handle);

LLNDAPI static std::shared_ptr<NativeMod> current(Handle handle = sys_utils::getCurrentModuleHandle());
LLNDAPI static std::shared_ptr<NativeMod> current(sys_utils::HandleT handle = sys_utils::getCurrentModuleHandle());
};

} // namespace ll::mod
19 changes: 19 additions & 0 deletions src/ll/api/utils/SystemUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -145,4 +146,22 @@ bool isStdoutSupportAnsi() {
}
return false;
}

std::optional<std::system_error> 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<std::system_error> 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
49 changes: 39 additions & 10 deletions src/ll/api/utils/SystemUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::filesystem::path> 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<std::byte> getImageRange(std::string_view name = "");

LLNDAPI std::string getSystemLocaleName();

LLNDAPI std::string const& getSystemName();

LLNDAPI std::pair<std::tm, int> getLocalTime(); // tm & ms

LLNDAPI bool isStdoutSupportAnsi();

LLNDAPI bool isWine();

LLNDAPI std::span<std::byte> 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<std::filesystem::path> 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<std::system_error> load(std::filesystem::path const& path) noexcept;
LLAPI std::optional<std::system_error> free() noexcept;

LLNDAPI std::pair<std::tm, int> getLocalTime(); // tm & ms
LLNDAPI void* getAddress(char const* symbol) noexcept;

LLNDAPI bool isStdoutSupportAnsi();
template <class T>
T getAddress(char const* symbol) noexcept {
return reinterpret_cast<T>(getAddress(symbol));
}

[[nodiscard]] inline std::string getCallerModuleFileName(void* addr = _ReturnAddress()) {
return getModuleFileName(getModuleHandle(addr));
}
[[nodiscard]] constexpr HandleT handle() const noexcept { return lib; }
};

template <std::invocable<wchar_t*, size_t, size_t&> Fn>
[[nodiscard]] inline std::optional<std::wstring> adaptFixedSizeToAllocatedResult(Fn&& callback) noexcept {
Expand Down
60 changes: 39 additions & 21 deletions src/ll/core/mod/ModRegistrar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,22 @@ void ModRegistrar::loadAllMods() {
}
}

std::unordered_set<std::string> needLoad;
std::unordered_set<std::string> loadingQueueHash;
std::vector<std::string> pendingRemoved;
std::queue<std::string> 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) {
Expand All @@ -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<std::string> 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
Expand All @@ -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) {
Expand All @@ -197,24 +211,28 @@ 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);
}
}
}
}
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<std::string> loadErrored;
for (auto& name : sort.sorted) {
auto& manifest = manifests.at(name);
Expand All @@ -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;
}
Expand Down
43 changes: 22 additions & 21 deletions src/ll/core/mod/NativeModManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
#include "ll/core/LeviLamina.h"

#include "errhandlingapi.h"
#include "libloaderapi.h"
#include "minwindef.h"
#include "processenv.h"
#include "winerror.h"
Expand Down Expand Up @@ -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<Mod::callback_t*>(addr));
currentLoadingMod->onUnload(reinterpret_cast<Mod::callback_t*>(GetProcAddress(lib, "ll_plugin_unload")));
currentLoadingMod->onEnable(reinterpret_cast<Mod::callback_t*>(GetProcAddress(lib, "ll_plugin_enable")));
currentLoadingMod->onDisable(reinterpret_cast<Mod::callback_t*>(GetProcAddress(lib, "ll_plugin_disable")));
// TODO: remove in release
if (auto addr = lib.getAddress<Mod::callback_t*>("ll_plugin_load"); addr) {
currentLoadingMod->onLoad(addr);
currentLoadingMod->onUnload(lib.getAddress<Mod::callback_t*>("ll_plugin_unload"));
currentLoadingMod->onEnable(lib.getAddress<Mod::callback_t*>("ll_plugin_enable"));
currentLoadingMod->onDisable(lib.getAddress<Mod::callback_t*>("ll_plugin_disable"));
} else {
currentLoadingMod->onLoad(reinterpret_cast<Mod::callback_t*>(GetProcAddress(lib, "ll_mod_load")));
currentLoadingMod->onUnload(reinterpret_cast<Mod::callback_t*>(GetProcAddress(lib, "ll_mod_unload")));
currentLoadingMod->onEnable(reinterpret_cast<Mod::callback_t*>(GetProcAddress(lib, "ll_mod_enable")));
currentLoadingMod->onDisable(reinterpret_cast<Mod::callback_t*>(GetProcAddress(lib, "ll_mod_disable")));
currentLoadingMod->onLoad(lib.getAddress<Mod::callback_t*>("ll_mod_load"));
currentLoadingMod->onUnload(lib.getAddress<Mod::callback_t*>("ll_mod_unload"));
currentLoadingMod->onEnable(lib.getAddress<Mod::callback_t*>("ll_mod_enable"));
currentLoadingMod->onDisable(lib.getAddress<Mod::callback_t*>("ll_mod_disable"));
}
return currentLoadingMod->onLoad().transform([&, this] {
addMod(currentLoadingMod->getManifest().name, currentLoadingMod);
handleMap[lib] = currentLoadingMod;
handleMap[lib.handle()] = currentLoadingMod;
});
}

Expand All @@ -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);
Expand Down

0 comments on commit c55d750

Please sign in to comment.