2022-05-14 14:00:20 +02:00
|
|
|
#include "PluginManagement.h"
|
2023-08-16 10:18:02 +02:00
|
|
|
#include "NotificationsUtils.h"
|
2022-05-14 14:00:20 +02:00
|
|
|
#include "hooks.h"
|
2020-12-26 14:17:50 +01:00
|
|
|
#include "plugin/PluginContainer.h"
|
2024-08-03 17:23:27 +02:00
|
|
|
#include "plugin/PluginLinkInformationFactory.h"
|
2024-08-04 16:42:24 +02:00
|
|
|
#include "plugin/PluginLoadWrapper.h"
|
2022-02-04 16:25:44 +01:00
|
|
|
#include "plugin/PluginMetaInformationFactory.h"
|
|
|
|
#include "utils/ElfUtils.h"
|
2023-08-16 10:18:02 +02:00
|
|
|
#include "utils/StringTools.h"
|
2022-05-14 14:00:20 +02:00
|
|
|
#include "utils/utils.h"
|
|
|
|
#include <coreinit/cache.h>
|
|
|
|
#include <coreinit/dynload.h>
|
|
|
|
#include <memory.h>
|
2024-11-27 20:44:36 +01:00
|
|
|
#include <ranges>
|
2023-11-04 15:32:45 +01:00
|
|
|
|
2024-11-27 20:44:20 +01:00
|
|
|
static uint32_t sTrampolineID = 0;
|
|
|
|
|
2024-03-24 07:40:58 +01:00
|
|
|
std::vector<PluginContainer>
|
2024-08-04 16:42:24 +02:00
|
|
|
PluginManagement::loadPlugins(const std::vector<PluginLoadWrapper> &pluginDataList, std::vector<relocation_trampoline_entry_t> &trampolineData) {
|
2024-03-24 07:40:58 +01:00
|
|
|
std::vector<PluginContainer> plugins;
|
2023-11-04 15:32:45 +01:00
|
|
|
|
2024-08-04 16:42:24 +02:00
|
|
|
for (const auto &pluginDataWrapper : pluginDataList) {
|
2023-11-04 15:32:45 +01:00
|
|
|
PluginParseErrors error = PLUGIN_PARSE_ERROR_UNKNOWN;
|
2024-08-04 16:42:24 +02:00
|
|
|
auto metaInfo = PluginMetaInformationFactory::loadPlugin(*pluginDataWrapper.getPluginData(), error);
|
2023-11-04 15:32:45 +01:00
|
|
|
if (metaInfo && error == PLUGIN_PARSE_ERROR_NONE) {
|
2024-08-04 16:42:24 +02:00
|
|
|
if (pluginDataWrapper.isLoadAndLink()) {
|
2024-08-04 13:54:56 +02:00
|
|
|
DEBUG_FUNCTION_LINE_INFO("We want to link %s by %s", metaInfo->getName().c_str(), metaInfo->getAuthor().c_str());
|
2024-08-04 16:42:24 +02:00
|
|
|
|
|
|
|
auto linkInfo = PluginLinkInformationFactory::load(*pluginDataWrapper.getPluginData(), trampolineData, sTrampolineID++);
|
2024-08-04 13:54:56 +02:00
|
|
|
if (!linkInfo) {
|
2024-08-04 16:42:24 +02:00
|
|
|
auto errMsg = string_format("Failed to load plugin: %s", pluginDataWrapper.getPluginData()->getSource().c_str());
|
2024-08-04 13:54:56 +02:00
|
|
|
DEBUG_FUNCTION_LINE_ERR("%s", errMsg.c_str());
|
|
|
|
DisplayErrorNotificationMessage(errMsg, 15.0f);
|
|
|
|
continue;
|
|
|
|
}
|
2024-08-04 16:42:24 +02:00
|
|
|
plugins.emplace_back(std::move(*metaInfo), std::move(*linkInfo), pluginDataWrapper.getPluginData());
|
2024-08-04 13:54:56 +02:00
|
|
|
} else {
|
|
|
|
DEBUG_FUNCTION_LINE_INFO("We want to skip %s by %s", metaInfo->getName().c_str(), metaInfo->getAuthor().c_str());
|
2024-08-04 16:42:24 +02:00
|
|
|
plugins.emplace_back(std::move(*metaInfo), PluginLinkInformation::CreateStub(), pluginDataWrapper.getPluginData());
|
2023-11-04 15:32:45 +01:00
|
|
|
}
|
|
|
|
} else {
|
2024-08-04 16:42:24 +02:00
|
|
|
auto errMsg = string_format("Failed to load plugin: %s", *pluginDataWrapper.getPluginData()->getSource().c_str());
|
2023-11-04 15:32:45 +01:00
|
|
|
if (error == PLUGIN_PARSE_ERROR_INCOMPATIBLE_VERSION) {
|
|
|
|
errMsg += ". Incompatible version.";
|
|
|
|
}
|
|
|
|
DEBUG_FUNCTION_LINE_ERR("%s", errMsg.c_str());
|
|
|
|
DisplayErrorNotificationMessage(errMsg, 15.0f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!PluginManagement::DoFunctionPatches(plugins)) {
|
|
|
|
DEBUG_FUNCTION_LINE_ERR("Failed to patch functions");
|
2024-05-08 17:49:25 +02:00
|
|
|
OSFatal("WiiUPluginLoaderBackend: Failed to patch functions");
|
2023-11-04 15:32:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return plugins;
|
|
|
|
}
|
2020-05-03 10:21:05 +02:00
|
|
|
|
2024-03-24 07:40:58 +01:00
|
|
|
bool PluginManagement::doRelocation(const std::vector<RelocationData> &relocData,
|
2023-11-04 15:32:45 +01:00
|
|
|
std::vector<relocation_trampoline_entry_t> &trampData,
|
2024-11-27 20:44:36 +01:00
|
|
|
const uint32_t trampolineID,
|
2022-10-05 20:08:49 +02:00
|
|
|
std::map<std::string, OSDynLoad_Module> &usedRPls) {
|
2022-02-04 16:25:44 +01:00
|
|
|
for (auto const &cur : relocData) {
|
2023-11-04 15:32:45 +01:00
|
|
|
uint32_t functionAddress = 0;
|
2024-03-24 07:40:58 +01:00
|
|
|
auto &functionName = cur.getName();
|
2020-06-03 18:22:44 +02:00
|
|
|
|
2020-12-26 14:17:50 +01:00
|
|
|
if (functionName == "MEMAllocFromDefaultHeap") {
|
2020-06-03 18:22:44 +02:00
|
|
|
OSDynLoad_Module rplHandle;
|
|
|
|
OSDynLoad_Acquire("homebrew_memorymapping", &rplHandle);
|
2023-07-15 13:10:23 +02:00
|
|
|
OSDynLoad_FindExport(rplHandle, OS_DYNLOAD_EXPORT_DATA, "MEMAllocFromMappedMemory", (void **) &functionAddress);
|
2020-12-26 14:17:50 +01:00
|
|
|
} else if (functionName == "MEMAllocFromDefaultHeapEx") {
|
2020-06-03 18:22:44 +02:00
|
|
|
OSDynLoad_Module rplHandle;
|
|
|
|
OSDynLoad_Acquire("homebrew_memorymapping", &rplHandle);
|
2023-07-15 13:10:23 +02:00
|
|
|
OSDynLoad_FindExport(rplHandle, OS_DYNLOAD_EXPORT_DATA, "MEMAllocFromMappedMemoryEx", (void **) &functionAddress);
|
2020-12-26 14:17:50 +01:00
|
|
|
} else if (functionName == "MEMFreeToDefaultHeap") {
|
2020-06-03 18:22:44 +02:00
|
|
|
OSDynLoad_Module rplHandle;
|
|
|
|
OSDynLoad_Acquire("homebrew_memorymapping", &rplHandle);
|
2023-07-15 13:10:23 +02:00
|
|
|
OSDynLoad_FindExport(rplHandle, OS_DYNLOAD_EXPORT_DATA, "MEMFreeToMappedMemory", (void **) &functionAddress);
|
2020-06-03 18:22:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (functionAddress == 0) {
|
2024-03-24 07:40:58 +01:00
|
|
|
auto rplName = cur.getImportRPLInformation().getRPLName();
|
|
|
|
int32_t isData = cur.getImportRPLInformation().isData();
|
2020-12-26 14:17:50 +01:00
|
|
|
OSDynLoad_Module rplHandle = nullptr;
|
2022-10-05 20:08:49 +02:00
|
|
|
|
|
|
|
if (!usedRPls.contains(rplName)) {
|
|
|
|
DEBUG_FUNCTION_LINE_VERBOSE("Acquire %s", rplName.c_str());
|
|
|
|
// Always acquire to increase refcount and make sure it won't get unloaded while we're using it.
|
2024-11-27 20:44:20 +01:00
|
|
|
if (const OSDynLoad_Error err = OSDynLoad_Acquire(rplName.c_str(), &rplHandle); err != OS_DYNLOAD_OK) {
|
2022-10-05 20:08:49 +02:00
|
|
|
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
|
|
|
|
usedRPls[rplName] = rplHandle;
|
2020-06-03 18:22:44 +02:00
|
|
|
} else {
|
2022-10-05 20:08:49 +02:00
|
|
|
rplHandle = usedRPls[rplName];
|
2020-06-03 18:22:44 +02:00
|
|
|
}
|
2022-10-05 20:08:49 +02:00
|
|
|
|
2024-11-27 20:44:20 +01:00
|
|
|
OSDynLoad_FindExport(rplHandle, static_cast<OSDynLoad_ExportType>(isData), functionName.c_str(), reinterpret_cast<void **>(&functionAddress));
|
2020-05-03 10:21:05 +02:00
|
|
|
}
|
|
|
|
|
2020-05-03 12:25:38 +02:00
|
|
|
if (functionAddress == 0) {
|
2022-04-22 22:55:53 +02:00
|
|
|
DEBUG_FUNCTION_LINE_ERR("Failed to find export for %s", functionName.c_str());
|
2020-05-03 10:21:05 +02:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
//DEBUG_FUNCTION_LINE("Found export for %s %s", rplName.c_str(), functionName.c_str());
|
|
|
|
}
|
2022-10-03 21:51:12 +02:00
|
|
|
|
2024-11-27 20:44:20 +01:00
|
|
|
if (!ElfUtils::elfLinkOne(cur.getType(), cur.getOffset(), cur.getAddend(), reinterpret_cast<uint32_t>(cur.getDestination()), functionAddress, trampData, RELOC_TYPE_IMPORT, trampolineID)) {
|
2022-05-14 14:00:20 +02:00
|
|
|
DEBUG_FUNCTION_LINE_ERR("elfLinkOne failed");
|
2020-05-03 10:21:05 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-14 14:00:20 +02:00
|
|
|
// Unloading RPLs which you want to use is stupid.
|
|
|
|
// Never uncomment this again.
|
|
|
|
/* for (auto &cur : moduleHandleCache) {
|
|
|
|
// Release handle if they are not from DynLoadPatchModule
|
|
|
|
if (((uint32_t) cur.second & 0xFFFF0000) != 0x13370000) {
|
|
|
|
OSDynLoad_Release(cur.second);
|
|
|
|
}
|
|
|
|
} */
|
|
|
|
|
2023-11-04 15:32:45 +01:00
|
|
|
DCFlushRange((void *) trampData.data(), trampData.size() * sizeof(relocation_trampoline_entry_t));
|
|
|
|
ICInvalidateRange((void *) trampData.data(), trampData.size() * sizeof(relocation_trampoline_entry_t));
|
2022-05-14 14:00:20 +02:00
|
|
|
OSMemoryBarrier();
|
2020-05-03 10:21:05 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-03-24 07:40:58 +01:00
|
|
|
bool PluginManagement::doRelocations(const std::vector<PluginContainer> &plugins,
|
2023-11-04 15:32:45 +01:00
|
|
|
std::vector<relocation_trampoline_entry_t> &trampData,
|
2022-10-05 20:08:49 +02:00
|
|
|
std::map<std::string, OSDynLoad_Module> &usedRPls) {
|
2023-11-04 15:32:45 +01:00
|
|
|
for (auto &cur : trampData) {
|
|
|
|
if (cur.status == RELOC_TRAMP_IMPORT_DONE) {
|
|
|
|
cur.status = RELOC_TRAMP_FREE;
|
2022-10-03 21:58:11 +02:00
|
|
|
}
|
|
|
|
}
|
2022-10-03 21:51:12 +02:00
|
|
|
|
|
|
|
OSDynLoadAllocFn prevDynLoadAlloc = nullptr;
|
|
|
|
OSDynLoadFreeFn prevDynLoadFree = nullptr;
|
|
|
|
|
|
|
|
OSDynLoad_GetAllocator(&prevDynLoadAlloc, &prevDynLoadFree);
|
|
|
|
OSDynLoad_SetAllocator(CustomDynLoadAlloc, CustomDynLoadFree);
|
|
|
|
|
2023-11-04 15:32:45 +01:00
|
|
|
for (const auto &pluginContainer : plugins) {
|
2024-08-04 15:15:01 +02:00
|
|
|
if (!pluginContainer.isLinkedAndLoaded()) {
|
2024-08-03 17:23:27 +02:00
|
|
|
continue;
|
|
|
|
}
|
2024-03-24 07:40:58 +01:00
|
|
|
DEBUG_FUNCTION_LINE_VERBOSE("Doing relocations for plugin: %s", pluginContainer.getMetaInformation().getName().c_str());
|
2024-08-04 14:12:52 +02:00
|
|
|
if (!PluginManagement::doRelocation(pluginContainer.getPluginLinkInformation().getRelocationDataList(),
|
2022-10-05 20:08:49 +02:00
|
|
|
trampData,
|
2024-08-04 14:12:52 +02:00
|
|
|
pluginContainer.getPluginLinkInformation().getTrampolineId(),
|
2022-10-05 20:08:49 +02:00
|
|
|
usedRPls)) {
|
2022-05-14 14:00:20 +02:00
|
|
|
return false;
|
2020-05-03 10:21:05 +02:00
|
|
|
}
|
|
|
|
}
|
2022-10-03 21:51:12 +02:00
|
|
|
|
|
|
|
OSDynLoad_SetAllocator(prevDynLoadAlloc, prevDynLoadFree);
|
|
|
|
|
2022-05-14 14:00:20 +02:00
|
|
|
return true;
|
2020-05-03 10:21:05 +02:00
|
|
|
}
|
|
|
|
|
2024-03-24 07:40:58 +01:00
|
|
|
bool PluginManagement::RestoreFunctionPatches(std::vector<PluginContainer> &plugins) {
|
|
|
|
for (auto &cur : std::ranges::reverse_view(plugins)) {
|
2024-08-04 14:12:52 +02:00
|
|
|
for (auto &curFunction : std::ranges::reverse_view(cur.getPluginLinkInformation().getFunctionDataList())) {
|
2024-03-24 07:40:58 +01:00
|
|
|
if (!curFunction.RemovePatch()) {
|
2024-11-27 20:44:20 +01:00
|
|
|
DEBUG_FUNCTION_LINE_ERR("Failed to remove function patch for: plugin %s", cur.getMetaInformation().getName().c_str());
|
2022-05-14 14:00:20 +02:00
|
|
|
return false;
|
|
|
|
}
|
2020-05-03 10:21:05 +02:00
|
|
|
}
|
|
|
|
}
|
2022-05-14 14:00:20 +02:00
|
|
|
return true;
|
2020-05-03 10:21:05 +02:00
|
|
|
}
|
|
|
|
|
2024-03-24 07:40:58 +01:00
|
|
|
bool PluginManagement::DoFunctionPatches(std::vector<PluginContainer> &plugins) {
|
|
|
|
for (auto &cur : plugins) {
|
2024-08-04 14:12:52 +02:00
|
|
|
for (auto &curFunction : cur.getPluginLinkInformation().getFunctionDataList()) {
|
2024-03-24 07:40:58 +01:00
|
|
|
if (!curFunction.AddPatch()) {
|
|
|
|
DEBUG_FUNCTION_LINE_ERR("Failed to add function patch for: plugin %s", cur.getMetaInformation().getName().c_str());
|
2022-05-14 14:00:20 +02:00
|
|
|
return false;
|
2020-05-17 20:41:11 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-03 14:16:11 +02:00
|
|
|
}
|
2022-05-14 14:00:20 +02:00
|
|
|
return true;
|
2020-05-03 14:16:11 +02:00
|
|
|
}
|
|
|
|
|
2024-11-27 20:44:20 +01:00
|
|
|
void PluginManagement::callInitHooks(const std::vector<PluginContainer> &plugins, const std::function<bool(const PluginContainer &)> &pred) {
|
|
|
|
CallHook(plugins, WUPS_LOADER_HOOK_INIT_CONFIG, pred);
|
|
|
|
CallHook(plugins, WUPS_LOADER_HOOK_INIT_STORAGE_DEPRECATED, pred);
|
|
|
|
CallHook(plugins, WUPS_LOADER_HOOK_INIT_STORAGE, pred);
|
|
|
|
CallHook(plugins, WUPS_LOADER_HOOK_INIT_PLUGIN, pred);
|
2021-02-19 19:41:04 +01:00
|
|
|
DEBUG_FUNCTION_LINE_VERBOSE("Done calling init hooks");
|
2022-05-14 14:00:20 +02:00
|
|
|
}
|