mirror of
https://github.com/wiiu-env/WiiUPluginLoaderBackend.git
synced 2026-05-06 13:06:09 -05:00
Add logic to handle the "reent" bug
This commit is contained in:
parent
66fd70e759
commit
a22da69f2d
|
|
@ -16,6 +16,7 @@ bool DisplayNotificationMessage(std::string_view text, NotificationModuleNotific
|
|||
}
|
||||
|
||||
if (type == NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO) {
|
||||
NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO, NOTIFICATION_MODULE_DEFAULT_OPTION_KEEP_UNTIL_SHOWN, true);
|
||||
NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO, NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT, duration);
|
||||
NotificationModule_AddInfoNotification(text.data());
|
||||
} else if (type == NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR) {
|
||||
|
|
@ -35,4 +36,13 @@ bool DisplayInfoNotificationMessage(std::string_view text, float duration) {
|
|||
|
||||
bool DisplayErrorNotificationMessage(std::string_view text, float duration) {
|
||||
return DisplayNotificationMessage(text, NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR, duration);
|
||||
}
|
||||
}
|
||||
|
||||
bool DisplayWarnNotificationMessage(std::string_view text, float duration) {
|
||||
if (!gNotificationModuleLoaded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NotificationModule_AddErrorNotificationEx(text.data(), duration, 0.5f, {0, 0, 0, 255}, {255, 242, 0, 255}, nullptr, nullptr, true);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
#include <string_view>
|
||||
|
||||
bool DisplayInfoNotificationMessage(std::string_view text, float duration);
|
||||
|
||||
bool DisplayErrorNotificationMessage(std::string_view text, float duration);
|
||||
bool DisplayErrorNotificationMessage(std::string_view text, float duration);
|
||||
bool DisplayWarnNotificationMessage(std::string_view text, float duration);
|
||||
|
|
@ -15,25 +15,125 @@
|
|||
#include "plugin/TrackingPluginHeapMemoryAllocator.h"
|
||||
#include "utils/ElfUtils.h"
|
||||
#include "utils/StringTools.h"
|
||||
#include "utils/WUPSBackendSettings.h"
|
||||
#include "utils/logger.h"
|
||||
|
||||
#include <wums/defines/relocation_defines.h>
|
||||
|
||||
#include <coreinit/cache.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <ranges>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
namespace {
|
||||
bool hasPossiblyReentBug(const PluginMetaInformation &meta) {
|
||||
if (meta.getWUPSVersion() < WUPSVersion(0, 7, 1)) {
|
||||
// get_reent only got implemented with 0.7.1 or higher
|
||||
return false;
|
||||
}
|
||||
if (meta.getWUPSVersion() >= WUPSVersion(0, 9, 1)) {
|
||||
// Any plugin compiled with 0.9.1 is fine!
|
||||
return false;
|
||||
}
|
||||
|
||||
const time_t buildTime = parseBuildDate(meta.getBuildTimestamp().c_str());
|
||||
|
||||
// Threshold: Jan 25 2026 00:00:00
|
||||
tm thresholdTm = {};
|
||||
thresholdTm.tm_mday = 25;
|
||||
thresholdTm.tm_mon = 0; // Jan
|
||||
thresholdTm.tm_year = 2026 - 1900;
|
||||
thresholdTm.tm_isdst = -1;
|
||||
const time_t threshold = mktime(&thresholdTm);
|
||||
|
||||
// Date-only comparison
|
||||
return buildTime >= threshold;
|
||||
}
|
||||
|
||||
void displayUseAnywayWarning(const PluginMetaInformation &info) {
|
||||
DEBUG_FUNCTION_LINE_INFO("But the user decided to use it anyway. Show them an info");
|
||||
// Has been explicitly re-enabled by the user.
|
||||
DEBUG_FUNCTION_LINE_WARN("Using plugin \"%s\" is unstable. It may cause crashes: Please recompile with WUPS 0.9.1", info.getName().c_str());
|
||||
const auto errMsg = string_format("Using plugin \"%s\" is unstable and may cause crashes. Consider updating or disabling it.", info.getName().c_str());
|
||||
DisplayInfoNotificationMessage(errMsg, 30.0f);
|
||||
}
|
||||
|
||||
bool HandlePossibleReentBug(const PluginLoadWrapper &pluginDataWrapper, const PluginMetaInformation &metaInfo, bool &reentBugWarningShown) {
|
||||
const auto &brokenReentFilenames = WUPSBackendSettings::GetBrokenReentPluginFilenames();
|
||||
if (pluginDataWrapper.isLoadAndLink()) { // Only check plugins are actually loaded
|
||||
const std::optional<std::string> pluginFilenameOpt = getPluginFilename(pluginDataWrapper.getPluginData()->getSource());
|
||||
if (hasPossiblyReentBug(metaInfo)) {
|
||||
DEBUG_FUNCTION_LINE_INFO("Plugin \"%s\" has been (possibly) compiled with the reent bug. Please recompile with WUPS 0.9.1", metaInfo.getName().c_str());
|
||||
|
||||
bool errorHasBeenShownFile = pluginFilenameOpt && brokenReentFilenames.contains(*pluginFilenameOpt);
|
||||
bool errorHasBeenShownWiiloaded = pluginDataWrapper.getPluginData()->isReentBugAllowed();
|
||||
|
||||
if (errorHasBeenShownFile || errorHasBeenShownWiiloaded) {
|
||||
displayUseAnywayWarning(metaInfo);
|
||||
} else {
|
||||
if (pluginFilenameOpt) { // loaded from sd card
|
||||
DEBUG_FUNCTION_LINE_INFO("Disable it and show an warning.");
|
||||
WUPSBackendSettings::AddBrokenReentPluginFilename(*pluginFilenameOpt);
|
||||
WUPSBackendSettings::AddInactivePluginFilename(*pluginFilenameOpt);
|
||||
|
||||
if (!reentBugWarningShown) {
|
||||
DisplayWarnNotificationMessage("Some plugins have been disabled to avoid potential crashes. Update them if possible.", 300.0f);
|
||||
reentBugWarningShown = true;
|
||||
}
|
||||
} else {
|
||||
constexpr float REENT_BUG_WARN_DURATION = 30.0f;
|
||||
// wiiloaded
|
||||
DEBUG_FUNCTION_LINE_INFO("Its wiiloaded -> we disable it.");
|
||||
DisplayWarnNotificationMessage("Enable it in the config menu if you want to use it anyway", REENT_BUG_WARN_DURATION);
|
||||
auto errMsg = string_format("Using plugin \"%s\" might causes crashes and has NOT been loaded.", metaInfo.getName().c_str());
|
||||
DisplayWarnNotificationMessage(errMsg, REENT_BUG_WARN_DURATION);
|
||||
pluginDataWrapper.getPluginData()->setAllowWithReentBug();
|
||||
}
|
||||
|
||||
// We don't want to load it.
|
||||
return false;
|
||||
}
|
||||
} else if (pluginFilenameOpt) {
|
||||
// If plugin is compatible, remove it from the reent broken list.
|
||||
DEBUG_FUNCTION_LINE_VERBOSE("Plugin \"%s\" has no reent bug, remove it from settings file", metaInfo.getName().c_str());
|
||||
WUPSBackendSettings::RemoveBrokenReentPluginFilename(*pluginFilenameOpt);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::vector<PluginContainer>
|
||||
PluginManagement::loadPlugins(const std::vector<PluginLoadWrapper> &pluginDataList) {
|
||||
std::vector<PluginContainer> plugins;
|
||||
|
||||
WUPSBackendSettings::LoadSettings();
|
||||
|
||||
bool reentBugWarningShown = false;
|
||||
|
||||
for (const auto &pluginDataWrapper : pluginDataList) {
|
||||
PluginParseErrors error = PLUGIN_PARSE_ERROR_UNKNOWN;
|
||||
auto metaInfo = PluginMetaInformationFactory::loadPlugin(*pluginDataWrapper.getPluginData(), error);
|
||||
if (metaInfo && error == PLUGIN_PARSE_ERROR_NONE) {
|
||||
if (pluginDataWrapper.isLoadAndLink()) {
|
||||
|
||||
// Early exit for failed parsing
|
||||
if (!metaInfo || error != PLUGIN_PARSE_ERROR_NONE) {
|
||||
auto errMsg = string_format("Failed to load plugin: %s", pluginDataWrapper.getPluginData()->getSource().c_str());
|
||||
if (error == PLUGIN_PARSE_ERROR_INCOMPATIBLE_VERSION) {
|
||||
errMsg += ". Incompatible version.";
|
||||
}
|
||||
DEBUG_FUNCTION_LINE_ERR("%s", errMsg.c_str());
|
||||
DisplayErrorNotificationMessage(errMsg, 15.0f);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto addPluginAsStub = [&]() {
|
||||
plugins.emplace_back(std::move(*metaInfo), PluginLinkInformation::CreateStub(), pluginDataWrapper.getPluginData(), std::nullopt);
|
||||
};
|
||||
|
||||
if (pluginDataWrapper.isLoadAndLink()) {
|
||||
const bool pluginShouldBeLoaded = HandlePossibleReentBug(pluginDataWrapper, *metaInfo, reentBugWarningShown);
|
||||
if (pluginShouldBeLoaded) {
|
||||
DEBUG_FUNCTION_LINE_INFO("LOAD (ACTIVE) %s", metaInfo->getName().c_str());
|
||||
|
||||
auto linkInfo = PluginLinkInformationFactory::load(*pluginDataWrapper.getPluginData());
|
||||
|
|
@ -45,16 +145,11 @@ PluginManagement::loadPlugins(const std::vector<PluginLoadWrapper> &pluginDataLi
|
|||
}
|
||||
plugins.emplace_back(std::move(*metaInfo), std::move(*linkInfo), pluginDataWrapper.getPluginData(), pluginDataWrapper.getHeapTrackingOptions());
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE_INFO("LOAD (INACTIVE) %s", metaInfo->getName().c_str());
|
||||
plugins.emplace_back(std::move(*metaInfo), PluginLinkInformation::CreateStub(), pluginDataWrapper.getPluginData(), std::nullopt);
|
||||
addPluginAsStub();
|
||||
}
|
||||
} else {
|
||||
auto errMsg = string_format("Failed to load plugin: %s", pluginDataWrapper.getPluginData()->getSource().c_str());
|
||||
if (error == PLUGIN_PARSE_ERROR_INCOMPATIBLE_VERSION) {
|
||||
errMsg += ". Incompatible version.";
|
||||
}
|
||||
DEBUG_FUNCTION_LINE_ERR("%s", errMsg.c_str());
|
||||
DisplayErrorNotificationMessage(errMsg, 15.0f);
|
||||
DEBUG_FUNCTION_LINE_INFO("LOAD (INACTIVE) %s", metaInfo->getName().c_str());
|
||||
addPluginAsStub();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -63,6 +158,7 @@ PluginManagement::loadPlugins(const std::vector<PluginLoadWrapper> &pluginDataLi
|
|||
OSFatal("WiiUPluginLoaderBackend: Failed to patch functions");
|
||||
}
|
||||
|
||||
WUPSBackendSettings::SaveSettings();
|
||||
return plugins;
|
||||
}
|
||||
|
||||
|
|
@ -94,8 +190,7 @@ bool PluginManagement::doRelocation(const std::vector<RelocationData> &relocData
|
|||
DEBUG_FUNCTION_LINE_ERR("Failed to acquire %s", rplName.c_str());
|
||||
return false;
|
||||
}
|
||||
// Keep track RPLs we are using.
|
||||
// They will be released on exit
|
||||
// Keep track RPLs we are using. They will be released on exit
|
||||
usedRPls[rplName] = rplHandle;
|
||||
} else {
|
||||
rplHandle = usedRPls[rplName];
|
||||
|
|
@ -134,8 +229,6 @@ bool PluginManagement::doRelocation(const std::vector<RelocationData> &relocData
|
|||
|
||||
bool PluginManagement::doRelocations(const std::vector<PluginContainer> &plugins,
|
||||
std::map<std::string, OSDynLoad_Module> &usedRPls) {
|
||||
|
||||
|
||||
OSDynLoadAllocFn prevDynLoadAlloc = nullptr;
|
||||
OSDynLoadFreeFn prevDynLoadFree = nullptr;
|
||||
|
||||
|
|
@ -162,7 +255,6 @@ bool PluginManagement::doRelocations(const std::vector<PluginContainer> &plugins
|
|||
}
|
||||
|
||||
OSDynLoad_SetAllocator(prevDynLoadAlloc, prevDynLoadFree);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ WUMS_APPLICATION_STARTS() {
|
|||
|
||||
if (!PluginManagement::doRelocations(gLoadedPlugins, gUsedRPLs)) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Relocations failed");
|
||||
OSFatal("WiiUPluginLoaderBackend: Relocations failed.\n See crash logs for more information.");
|
||||
OSFatal("WiiUPluginLoaderBackend: Relocations failed.\nSee crash logs for more information.");
|
||||
}
|
||||
// PluginManagement::memsetBSS(plugins);
|
||||
|
||||
|
|
|
|||
|
|
@ -47,3 +47,11 @@ size_t PluginData::getMemoryFootprint() const {
|
|||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
void PluginData::setAllowWithReentBug() {
|
||||
mIsReentBugAllowed = true;
|
||||
}
|
||||
|
||||
bool PluginData::isReentBugAllowed() const {
|
||||
return mIsReentBugAllowed;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,10 +45,15 @@ public:
|
|||
|
||||
size_t getMemoryFootprint() const;
|
||||
|
||||
void setAllowWithReentBug();
|
||||
|
||||
bool isReentBugAllowed() const;
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> mBuffer;
|
||||
std::string mSource;
|
||||
std::unique_ptr<uint32_t> mHandle = std::make_unique<uint32_t>();
|
||||
bool mIsReentBugAllowed = false;
|
||||
};
|
||||
|
||||
struct PluginDataSharedPtrComparator {
|
||||
|
|
|
|||
|
|
@ -5,15 +5,22 @@
|
|||
#include "utils/logger.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace WUPSBackendSettings {
|
||||
namespace {
|
||||
std::set<std::string> sInactivePlugins;
|
||||
}
|
||||
std::set<std::string> sBrokenReentPlugins;
|
||||
bool isDirty = false;
|
||||
} // namespace
|
||||
|
||||
#define INACTIVE_PLUGINS_KEY "inactive_plugins"
|
||||
#define INACTIVE_PLUGINS_KEY "inactive_plugins"
|
||||
#define BROKEN_REENT_PLUGINS_KEY "possibly_broken_reent_plugins"
|
||||
|
||||
bool IsDirty() {
|
||||
return isDirty;
|
||||
}
|
||||
|
||||
bool LoadSettings() {
|
||||
nlohmann::json j = nlohmann::json::object();
|
||||
|
|
@ -28,15 +35,29 @@ namespace WUPSBackendSettings {
|
|||
if (j.contains(INACTIVE_PLUGINS_KEY) && j[INACTIVE_PLUGINS_KEY].is_array()) {
|
||||
for (auto &cur : j[INACTIVE_PLUGINS_KEY]) {
|
||||
if (cur.is_string()) {
|
||||
sInactivePlugins.insert(cur);
|
||||
sInactivePlugins.insert(cur.get<std::string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sBrokenReentPlugins.clear();
|
||||
if (j.contains(BROKEN_REENT_PLUGINS_KEY) && j[BROKEN_REENT_PLUGINS_KEY].is_array()) {
|
||||
for (auto &cur : j[BROKEN_REENT_PLUGINS_KEY]) {
|
||||
if (cur.is_string()) {
|
||||
sBrokenReentPlugins.insert(cur.get<std::string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isDirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveSettings() {
|
||||
if (!isDirty) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string folderPath = getModulePath() + "/configs/";
|
||||
std::string filePath = folderPath + "wupsbackend.json";
|
||||
if (!FSUtils::CreateSubfolder(folderPath)) {
|
||||
|
|
@ -49,8 +70,9 @@ namespace WUPSBackendSettings {
|
|||
return false;
|
||||
}
|
||||
|
||||
nlohmann::json j = nlohmann::json::object();
|
||||
j[INACTIVE_PLUGINS_KEY] = sInactivePlugins;
|
||||
nlohmann::json j = nlohmann::json::object();
|
||||
j[INACTIVE_PLUGINS_KEY] = sInactivePlugins;
|
||||
j[BROKEN_REENT_PLUGINS_KEY] = sBrokenReentPlugins;
|
||||
|
||||
std::string jsonString = j.dump(4, ' ', false, nlohmann::json::error_handler_t::ignore);
|
||||
auto writeResult = file.write((const uint8_t *) jsonString.c_str(), jsonString.size());
|
||||
|
|
@ -61,19 +83,41 @@ namespace WUPSBackendSettings {
|
|||
return false;
|
||||
}
|
||||
|
||||
isDirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClearInactivePluginFilenames() {
|
||||
sInactivePlugins.clear();
|
||||
if (!sInactivePlugins.empty()) {
|
||||
sInactivePlugins.clear();
|
||||
isDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AddInactivePluginFilename(const std::string &filename) {
|
||||
sInactivePlugins.insert(filename);
|
||||
if (sInactivePlugins.insert(filename).second) {
|
||||
isDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AddBrokenReentPluginFilename(const std::string &filename) {
|
||||
if (sBrokenReentPlugins.insert(filename).second) {
|
||||
isDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveBrokenReentPluginFilename(const std::string &filename) {
|
||||
if (sBrokenReentPlugins.erase(filename) > 0) {
|
||||
isDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
const std::set<std::string> &GetInactivePluginFilenames() {
|
||||
return sInactivePlugins;
|
||||
}
|
||||
|
||||
const std::set<std::string> &GetBrokenReentPluginFilenames() {
|
||||
return sBrokenReentPlugins;
|
||||
}
|
||||
|
||||
} // namespace WUPSBackendSettings
|
||||
|
|
@ -21,4 +21,8 @@ namespace WUPSBackendSettings {
|
|||
}
|
||||
|
||||
const std::set<std::string> &GetInactivePluginFilenames();
|
||||
|
||||
const std::set<std::string> &GetBrokenReentPluginFilenames();
|
||||
void AddBrokenReentPluginFilename(const std::string &filename);
|
||||
void RemoveBrokenReentPluginFilename(const std::string &filename);
|
||||
}; // namespace WUPSBackendSettings
|
||||
|
|
|
|||
|
|
@ -235,11 +235,9 @@ void ConfigUtils::displayMenu() {
|
|||
std::vector<std::string> newInactivePluginsList;
|
||||
for (const auto &cur : newActivePluginsList) {
|
||||
if (!cur.isLoadAndLink()) {
|
||||
auto &source = cur.getPluginData()->getSource();
|
||||
if (source.starts_with(getPluginPath()) && source.ends_with(".wps")) {
|
||||
std::size_t found = source.find_last_of("/\\");
|
||||
std::string filename = source.substr(found + 1);
|
||||
newInactivePluginsList.push_back(filename);
|
||||
const auto &source = cur.getPluginData()->getSource();
|
||||
if (const auto filenameOpt = getPluginFilename(source); filenameOpt) {
|
||||
newInactivePluginsList.push_back(*filenameOpt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -343,4 +343,48 @@ const char *hookNameToString(const wups_loader_hook_type_t type) {
|
|||
return "WUPS_LOADER_HOOK_INIT_REENT_FUNCTIONS";
|
||||
}
|
||||
return "<UNKNOWN>";
|
||||
}
|
||||
|
||||
std::optional<std::string> getPluginFilename(const std::string &source) {
|
||||
if (source.starts_with(getPluginPath()) && source.ends_with(".wps")) {
|
||||
const std::size_t found = source.find_last_of("/\\");
|
||||
return source.substr(found + 1);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
time_t parseBuildDate(const char *s) {
|
||||
// Expected format: "Apr 16 2026" (ignoring any trailing time)
|
||||
if (!s || strlen(s) < 11) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||
|
||||
tm t = {};
|
||||
|
||||
// 1. Identify Month (0-2)
|
||||
t.tm_mon = -1;
|
||||
for (int i = 0; i < 12; ++i) {
|
||||
if (strncmp(s, months[i], 3) == 0) {
|
||||
t.tm_mon = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (t.tm_mon == -1) return 0;
|
||||
|
||||
// 2. Parse Day (starts at index 4)
|
||||
t.tm_mday = static_cast<int>(strtol(s + 4, nullptr, 10));
|
||||
|
||||
// 3. Parse Year (starts at index 7)
|
||||
// strtol will stop at the space before the time string automatically
|
||||
t.tm_year = static_cast<int>(strtol(s + 7, nullptr, 10)) - 1900;
|
||||
|
||||
// We only care about the date, so we zero out the time
|
||||
t.tm_hour = 0;
|
||||
t.tm_min = 0;
|
||||
t.tm_sec = 0;
|
||||
t.tm_isdst = -1;
|
||||
|
||||
return mktime(&t);
|
||||
}
|
||||
|
|
@ -171,4 +171,12 @@ std::string getModuleAndSymbolName(uint32_t addr);
|
|||
|
||||
void PrintCapturedStackTrace(std::span<const uint32_t> trace);
|
||||
|
||||
const char *hookNameToString(wups_loader_hook_type_t type);
|
||||
const char *hookNameToString(wups_loader_hook_type_t type);
|
||||
|
||||
/**
|
||||
* Helper to extract the filename from the source path.
|
||||
* Returns nullopt if the source isn't a standard plugin path.
|
||||
*/
|
||||
std::optional<std::string> getPluginFilename(const std::string &source);
|
||||
|
||||
time_t parseBuildDate(const char *s);
|
||||
Loading…
Reference in New Issue
Block a user