From 2a84d3e9886c54b2805a32ddfffd74fd69c0849b Mon Sep 17 00:00:00 2001 From: Le Philousophe Date: Mon, 18 Jul 2022 19:25:57 +0200 Subject: [PATCH] VITA: Add plugins support --- backends/module.mk | 1 + backends/platform/sdl/psp2/psp2-main.cpp | 4 +- backends/platform/sdl/psp2/psp2.mk | 22 +++- backends/plugins/psp2/plugin.cpp | 113 ++++++++++++++++ backends/plugins/psp2/plugin.yml | 9 ++ backends/plugins/psp2/psp2-plugin.h | 41 ++++++ backends/plugins/psp2/psp2-provider.cpp | 160 +++++++++++++++++++++++ backends/plugins/psp2/psp2-provider.h | 37 ++++++ base/plugins.cpp | 2 + configure | 30 ++++- 10 files changed, 410 insertions(+), 9 deletions(-) create mode 100644 backends/plugins/psp2/plugin.cpp create mode 100644 backends/plugins/psp2/plugin.yml create mode 100644 backends/plugins/psp2/psp2-plugin.h create mode 100644 backends/plugins/psp2/psp2-provider.cpp create mode 100644 backends/plugins/psp2/psp2-provider.h diff --git a/backends/module.mk b/backends/module.mk index 91e500e01f6..009038af3d8 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -378,6 +378,7 @@ MODULE_OBJS += \ fs/posix/posix-iostream.o \ fs/posix-drives/posix-drives-fs.o \ fs/posix-drives/posix-drives-fs-factory.o \ + plugins/psp2/psp2-provider.o \ events/psp2sdl/psp2sdl-events.o endif diff --git a/backends/platform/sdl/psp2/psp2-main.cpp b/backends/platform/sdl/psp2/psp2-main.cpp index 3b94a40559c..b17f416ed45 100644 --- a/backends/platform/sdl/psp2/psp2-main.cpp +++ b/backends/platform/sdl/psp2/psp2-main.cpp @@ -25,7 +25,7 @@ #include "common/scummsys.h" #include "backends/platform/sdl/psp2/psp2.h" -#include "backends/plugins/sdl/sdl-provider.h" +#include "backends/plugins/psp2/psp2-provider.h" #include "base/main.h" int _newlib_heap_size_user = 192 * 1024 * 1024; @@ -50,7 +50,7 @@ int main(int argc, char *argv[]) { g_system->init(); #ifdef DYNAMIC_MODULES - PluginManager::instance().addPluginProvider(new SDLPluginProvider()); + PluginManager::instance().addPluginProvider(new PSP2PluginProvider()); #endif sceAppMgrGetAppParam(boot_params); diff --git a/backends/platform/sdl/psp2/psp2.mk b/backends/platform/sdl/psp2/psp2.mk index 6417b934ce8..f9ecbe0fea6 100644 --- a/backends/platform/sdl/psp2/psp2.mk +++ b/backends/platform/sdl/psp2/psp2.mk @@ -3,7 +3,7 @@ PSP2_EXE_STRIPPED := scummvm_stripped$(EXEEXT) $(PSP2_EXE_STRIPPED): $(EXECUTABLE) $(STRIP) --strip-debug $< -o $@ -psp2vpk: $(PSP2_EXE_STRIPPED) +psp2vpk: $(PSP2_EXE_STRIPPED) $(PLUGINS) rm -rf psp2pkg rm -f $(EXECUTABLE).vpk mkdir -p psp2pkg/sce_sys/livearea/contents @@ -11,6 +11,14 @@ psp2vpk: $(PSP2_EXE_STRIPPED) mkdir -p psp2pkg/doc/ vita-elf-create $(PSP2_EXE_STRIPPED) $(EXECUTABLE).velf vita-make-fself -s -c $(EXECUTABLE).velf psp2pkg/eboot.bin +ifdef DYNAMIC_MODULES + # Use psp2rela to convert the main binary to static, this allows plugins to use functions from it without any relocation + # TODO: Use psp2rela -fetch_base flag instead of using objdump when the change is widespread + set -e ;\ + textaddr=$$($(OBJDUMP) -p $(EXECUTABLE) | awk '/ LOAD / { vaddr=$$5; getline; if ($$6 == "r-x") print vaddr; }') ;\ + dataaddr=$$($(OBJDUMP) -p $(EXECUTABLE) | awk '/ LOAD / { vaddr=$$5; getline; if ($$6 == "rw-") print vaddr; }') ;\ + psp2rela -src=psp2pkg/eboot.bin -dst=psp2pkg/eboot.bin -static_mode -text_addr=$$textaddr -data_addr=$$dataaddr +endif vita-mksfoex -s TITLE_ID=VSCU00001 -d ATTRIBUTE2=12 "$(EXECUTABLE)" psp2pkg/sce_sys/param.sfo cp $(srcdir)/dists/psp2/icon0.png psp2pkg/sce_sys/ cp $(srcdir)/dists/psp2/template.xml psp2pkg/sce_sys/livearea/contents/ @@ -25,6 +33,18 @@ ifdef DIST_FILES_NETWORKING endif ifdef DIST_FILES_VKEYBD cp $(DIST_FILES_VKEYBD) psp2pkg/data/ +endif +ifdef DYNAMIC_MODULES + mkdir -p psp2pkg/plugins + # Each plugin is built as ELF with .suprx extension in main directory because of PLUGIN_SUFFIX variable + # Then it's stripped and converted here to Vita ELF and SELF inside the package directory + set -e ;\ + for p in $(PLUGINS); do \ + p=$${p%.suprx} ;\ + $(STRIP) --strip-debug $$p.suprx -o $$p.stripped.elf ;\ + vita-elf-create -n -e $(srcdir)/backends/plugins/psp2/plugin.yml $$p.stripped.elf $$p.velf ;\ + vita-make-fself -s $$p.velf psp2pkg/plugins/$$(basename "$$p").suprx ;\ + done endif cp $(DIST_FILES_DOCS) psp2pkg/doc/ cp $(srcdir)/dists/psp2/readme-psp2.md psp2pkg/doc/ diff --git a/backends/plugins/psp2/plugin.cpp b/backends/plugins/psp2/plugin.cpp new file mode 100644 index 00000000000..ec80e4d371c --- /dev/null +++ b/backends/plugins/psp2/plugin.cpp @@ -0,0 +1,113 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +// Disable symbol overrides so that we can use system headers. +#define FORBIDDEN_SYMBOL_ALLOW_ALL + +#include "common/scummsys.h" + +#if defined(DYNAMIC_MODULES) && defined(PSP2) + +#include +#include + +#include "base/plugins.h" +#include "backends/plugins/psp2/psp2-plugin.h" + +extern "C" { + +int32 PLUGIN_getVersion(); +int32 PLUGIN_getType(); +int32 PLUGIN_getTypeVersion(); +PluginObject *PLUGIN_getObject(); + +static PSP2FunctionPointers PSP2Functions = { + PSP2FunctionPointers_VERSION, + PLUGIN_getVersion, + PLUGIN_getType, + PLUGIN_getTypeVersion, + PLUGIN_getObject, +}; + +// hacks to make libc work +//void* __dso_handle = (void*) &__dso_handle; +extern void *__dso_handle __attribute__((weak)); + +/* These magic symbols are provided by the linker. */ +extern void (*__preinit_array_start []) (void) __attribute__((weak)); +extern void (*__preinit_array_end []) (void) __attribute__((weak)); +extern void (*__init_array_start []) (void) __attribute__((weak)); +extern void (*__init_array_end []) (void) __attribute__((weak)); +extern void (*__fini_array_start [])(void) __attribute__((weak)); +extern void (*__fini_array_end [])(void) __attribute__((weak)); + +static void __libc_init_array(void) { + size_t count, i; + + count = __preinit_array_end - __preinit_array_start; + for (i = 0; i < count; i++) { + __preinit_array_start[i](); + } + + count = __init_array_end - __init_array_start; + for (i = 0; i < count; i++) { + __init_array_start[i](); + } +} + +static void __libc_fini_array(void) { + size_t count, i; + + count = __fini_array_end - __fini_array_start; + for (i = count; i > 0; i--) { + __fini_array_start[i-1](); + } +} + +int module_stop(SceSize argc, const void *args) { + if (&__dso_handle != nullptr) { + __cxxabiv1::__cxa_finalize(&__dso_handle); + } + __libc_fini_array(); + return SCE_KERNEL_STOP_SUCCESS; +} + +int module_exit() { + if (&__dso_handle != nullptr) { + __cxxabiv1::__cxa_finalize(&__dso_handle); + } + __libc_fini_array(); + return SCE_KERNEL_STOP_SUCCESS; +} + +int _start(SceSize argc, void *args) __attribute__ ((weak, alias ("module_start"))); +int module_start(SceSize argc, void *args) { + PSP2FunctionPointers **arg = *(PSP2FunctionPointers ***)args; + + __libc_init_array(); + + *arg = &PSP2Functions; + + return SCE_KERNEL_START_SUCCESS; +} + +} +#endif // defined(DYNAMIC_MODULES) && defined(PSP2) diff --git a/backends/plugins/psp2/plugin.yml b/backends/plugins/psp2/plugin.yml new file mode 100644 index 00000000000..0fd1384946a --- /dev/null +++ b/backends/plugins/psp2/plugin.yml @@ -0,0 +1,9 @@ +plugin: + attributes: 0 + version: + major: 1 + minor: 1 + main: + start: module_start + stop: module_stop + exit: module_exit diff --git a/backends/plugins/psp2/psp2-plugin.h b/backends/plugins/psp2/psp2-plugin.h new file mode 100644 index 00000000000..d5223468f08 --- /dev/null +++ b/backends/plugins/psp2/psp2-plugin.h @@ -0,0 +1,41 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef BACKENDS_PLUGINS_PSP2_PLUGIN_H +#define BACKENDS_PLUGINS_PSP2_PLUGIN_H + +#if defined(DYNAMIC_MODULES) && defined(PSP2) + +struct PSP2FunctionPointers { + uint32 version; + int32 (*PLUGIN_getVersion)(); + int32 (*PLUGIN_getType)(); + int32 (*PLUGIN_getTypeVersion)(); + PluginObject *(*PLUGIN_getObject)(); +}; + +// Increment this when modifying the structure above +#define PSP2FunctionPointers_VERSION 1 + +#endif // defined(DYNAMIC_MODULES) && defined(PSP2) + +#endif + diff --git a/backends/plugins/psp2/psp2-provider.cpp b/backends/plugins/psp2/psp2-provider.cpp new file mode 100644 index 00000000000..72b0f7570bb --- /dev/null +++ b/backends/plugins/psp2/psp2-provider.cpp @@ -0,0 +1,160 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +// Disable symbol overrides so that we can use system headers. +#define FORBIDDEN_SYMBOL_ALLOW_ALL + +#include "common/scummsys.h" + +#if defined(DYNAMIC_MODULES) && defined(PSP2) + +#include + +#include "backends/plugins/psp2/psp2-provider.h" +#include "backends/plugins/psp2/psp2-plugin.h" +#include "common/debug.h" +#include "common/fs.h" + +// HACK: This is needed so that standard library functions that are only +// used in plugins can be found in the main executable. +#include +void *forceLinkFunctions[] = { + // Select the nothrow variant + (void *)(void *(*)(std::size_t, std::nothrow_t const&))operator new [], + (void *)coshf, + (void *)fgetc, + (void *)fmaxf, + (void *)fminf, + (void *)frexpf, + (void *)getc, + (void *)mbstowcs, + // Select the double version + (void *)(double (*)(double))nearbyint, + (void *)rename, + (void *)sinhf, + (void *)strcoll, + (void *)strspn, + (void *)tanhf, + (void *)vsprintf, + (void *)wcstombs, + (void *)__cxxabiv1::__cxa_finalize +}; + +class PSP2Plugin final : public Plugin { +protected: + SceUID _modId; + const Common::String _filename; + +public: + PSP2Plugin(const Common::String &filename) + : _filename(filename), _modId(0) {} + + bool loadPlugin() override { + assert(!_modId); + PSP2FunctionPointers *functions = nullptr; + PSP2FunctionPointers **arg = &functions; + + int status = 0; + _modId = sceKernelLoadStartModule(_filename.c_str(), sizeof( arg ), &arg, 0, NULL, &status ); + + if (!_modId) { + debug("Failed loading plugin '%s' (error code %d)", _filename.c_str(), status); + return false; + } else { + debug(1, "Success loading plugin '%s', handle %08x", _filename.c_str(), _modId); + } + + // Validate the Vita version + if (!functions) { + debug("Failed loading plugin '%s': no pointer", _filename.c_str()); + unloadPlugin(); + return false; + } + if (functions->version != PSP2FunctionPointers_VERSION) { + debug("Failed loading plugin '%s': unexpected version %d", _filename.c_str(), functions->version); + unloadPlugin(); + return false; + } + + // Validate the plugin API version + int32 version = functions->PLUGIN_getVersion(); + if (version != PLUGIN_VERSION) { + warning("Plugin uses a different API version (you have: '%d', needed is: '%d')", version, PLUGIN_VERSION); + unloadPlugin(); + return false; + } + + // Get the type of the plugin + _type = (PluginType)functions->PLUGIN_getType(); + if (_type >= PLUGIN_TYPE_MAX) { + warning("Plugin type unknown: %d", _type); + unloadPlugin(); + return false; + } + + // Validate the plugin type API version + int32 typeVersion = functions->PLUGIN_getTypeVersion(); + if (typeVersion != pluginTypeVersions[_type]) { + warning("Plugin uses a different type API version (you have: '%d', needed is: '%d')", typeVersion, pluginTypeVersions[_type]); + unloadPlugin(); + return false; + } + + // Get the plugin object + _pluginObject = functions->PLUGIN_getObject(); + if (!_pluginObject) { + warning("Couldn't get the plugin object"); + unloadPlugin(); + return false; + } + + debug(1, "Successfully loaded plugin '%s'", _filename.c_str()); + return true; + } + + void unloadPlugin() override { + delete _pluginObject; + + if (_modId) { + int status = 0; + int ret = sceKernelStopUnloadModule(_modId, 0, NULL, 0, NULL, &status); + if (ret != SCE_OK) { + debug("Failed unloading plugin '%s': %d/%d", _filename.c_str(), ret, status); + } + _modId = 0; + } + } + + virtual const char *getFileName() const { + return _filename.c_str(); + } +}; + + +Plugin* PSP2PluginProvider::createPlugin(const Common::FSNode &node) const { + return new PSP2Plugin(node.getPath()); +} + +void PSP2PluginProvider::addCustomDirectories(Common::FSList &dirs) const { + dirs.push_back(Common::FSNode("app0:/plugins")); +} + +#endif // defined(DYNAMIC_MODULES) && defined(PSP2) diff --git a/backends/plugins/psp2/psp2-provider.h b/backends/plugins/psp2/psp2-provider.h new file mode 100644 index 00000000000..f5a6e766a0d --- /dev/null +++ b/backends/plugins/psp2/psp2-provider.h @@ -0,0 +1,37 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef BACKENDS_PLUGINS_PSP2_PROVIDER_H +#define BACKENDS_PLUGINS_PSP2_PROVIDER_H + +#include "base/plugins.h" + +#if defined(DYNAMIC_MODULES) && defined(PSP2) + +class PSP2PluginProvider final : public FilePluginProvider { +protected: + Plugin* createPlugin(const Common::FSNode &node) const override; + void addCustomDirectories(Common::FSList &dirs) const; +}; + +#endif // defined(DYNAMIC_MODULES) && defined(PSP2) + +#endif diff --git a/base/plugins.cpp b/base/plugins.cpp index 04f9a6d74b2..a30d3511a91 100644 --- a/base/plugins.cpp +++ b/base/plugins.cpp @@ -188,7 +188,9 @@ PluginList FilePluginProvider::getPlugins() { #ifndef WIN32 pluginDirs.push_back(Common::FSNode(".")); #endif + #ifndef PSP2 pluginDirs.push_back(Common::FSNode("plugins")); + #endif // Add the provider's custom directories addCustomDirectories(pluginDirs); diff --git a/configure b/configure index 7370740ded9..58f1e1b1900 100755 --- a/configure +++ b/configure @@ -3059,16 +3059,21 @@ EOF append_var CXXFLAGS "-mlong-calls" append_var CXXFLAGS "-mword-relocations" append_var CXXFLAGS "-fomit-frame-pointer" - #use link time optimization to further reduce exe size - append_var CXXFLAGS "-flto" - append_var LDFLAGS "-flto=jobserver" #ensure verbose output during linking to prevent buildbot kills after 1200 seconds if test "$_verbose_build" = yes ; then append_var LDFLAGS "-Wl,-v --verbose" fi - #use linker dead code elimination to further reduce exe size - append_var CXXFLAGS "-ffunction-sections -fdata-sections" - append_var LDFLAGS "-Wl,--gc-sections" + if test "$_dynamic_modules" = yes ; then + _detection_features_static=no + _plugins_default=dynamic + else + #use link time optimization to further reduce exe size + append_var CXXFLAGS "-flto" + append_var LDFLAGS "-flto=jobserver" + #use linker dead code elimination to further reduce exe size + append_var CXXFLAGS "-ffunction-sections -fdata-sections" + append_var LDFLAGS "-Wl,--gc-sections" + fi if test "$_debug_build" = no; then #optimize for smallest file size. This is necessary to prevent a crash on startup #due to the large executable file size when many engines are enabled @@ -4254,6 +4259,19 @@ POST_OBJS_FLAGS := -Wl,-no-whole-archive _mak_plugins=' LDFLAGS += -Wl,-T$(srcdir)/backends/plugins/psp/main_prog.ld -Wl,-zmax-page-size=128 PLUGIN_LDFLAGS += -Wl,-T$(srcdir)/backends/plugins/psp/plugin.ld -Wl,-zmax-page-size=128 -lstdc++ +' + ;; + psp2) + _plugin_prefix="" + # This will create an ELF with a suprx extension which we will have to mangle when packaging + _plugin_suffix=".suprx" + append_var CXXFLAGS "-fuse-cxa-atexit" + append_var DEFINES "-DUNCACHED_PLUGINS" +_mak_plugins=' +PLUGIN_EXTRA_DEPS = $(EXECUTABLE) backends/plugins/psp2/plugin.o +PLUGIN_LDFLAGS += -nostartfiles -nodefaultlibs backends/plugins/psp2/plugin.o -Wl,-q -Xlinker --just-symbols -Xlinker $(EXECUTABLE) -lgcc +PRE_OBJS_FLAGS := -Wl,--whole-archive +POST_OBJS_FLAGS := -Wl,--no-whole-archive ' ;; riscos)