diff --git a/src/common/emulatorConfig.cpp b/src/common/emulatorConfig.cpp index f97400965..58ec3e913 100644 --- a/src/common/emulatorConfig.cpp +++ b/src/common/emulatorConfig.cpp @@ -182,4 +182,8 @@ const Keymap& GetKeymap() { return g_config->keymap; } +const std::vector& GetGuestArguments() { + return g_config->guest_arguments; +} + } // namespace Config diff --git a/src/common/emulatorConfig.h b/src/common/emulatorConfig.h index 7deb31959..97e6a5c26 100644 --- a/src/common/emulatorConfig.h +++ b/src/common/emulatorConfig.h @@ -83,6 +83,7 @@ struct ConfigOptions { bool red_zone_protection_enabled = false; #endif Keymap keymap; + std::vector guest_arguments; }; void Load(const ConfigOptions& cfg); @@ -136,6 +137,7 @@ bool RedZoneProtectionEnabled(); #endif const Keymap& GetKeymap(); +const std::vector& GetGuestArguments(); } // namespace Config diff --git a/src/emulator.h b/src/emulator.h index cf8f345f2..3d9965e0c 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -16,6 +16,7 @@ struct RunOptions { }; void Run(const RunOptions& options); +int LoadExec(const char* path, const char* const argv[]); } // namespace Emulator diff --git a/src/libs/libC.cpp b/src/libs/libC.cpp index 451fc2280..7ed98856b 100644 --- a/src/libs/libC.cpp +++ b/src/libs/libC.cpp @@ -54,11 +54,7 @@ struct CContext { std::list atexit; }; -struct InitEnvParams { - int argc; - uint32_t pad; - const char* argv[3]; -}; +using InitEnvParams = Loader::EntryParams; static int g_argc = 0; static const char* const* g_argv = nullptr; @@ -271,9 +267,7 @@ static KYTY_SYSV_ABI void init_env(const InitEnvParams* params) { return; } - constexpr int argv_capacity = static_cast(sizeof(params->argv) / sizeof(params->argv[0])); - - EXIT_NOT_IMPLEMENTED(params->argc < 0 || params->argc >= argv_capacity); + EXIT_NOT_IMPLEMENTED(params->argc < 0); g_argc = params->argc; g_argv = params->argv; diff --git a/src/libs/libSystemService.cpp b/src/libs/libSystemService.cpp index 0cffc9bc2..e0dbf7665 100644 --- a/src/libs/libSystemService.cpp +++ b/src/libs/libSystemService.cpp @@ -4,6 +4,7 @@ #include "common/emulatorConfig.h" #include "common/logging/log.h" #include "common/stringUtils.h" +#include "emulator.h" #include "graphics/presentation/systemOverlay.h" #include "libs/dialog.h" #include "libs/errno.h" @@ -99,6 +100,11 @@ static int KYTY_SYSV_ABI SystemServiceHideSplashScreen() { return OK; } +int KYTY_SYSV_ABI SystemServiceLoadExec(const char* path, const char* const argv[]) { + PRINT_NAME(); + return Emulator::LoadExec(path, argv); +} + static int KYTY_SYSV_ABI SystemServiceParamGetInt(int param_id, int* value) { PRINT_NAME(); @@ -559,6 +565,7 @@ LIB_DEFINE(InitSystemGesture_1) { } // namespace SystemGesture LIB_DEFINE(InitSystemService_1) { + LIB_FUNC("JoBqSQt1yyA", SystemService::SystemServiceLoadExec); LIB_FUNC("Vo5V8KAwCmk", SystemService::SystemServiceHideSplashScreen); LIB_FUNC("fZo48un7LK4", SystemService::SystemServiceParamGetInt); LIB_FUNC("SsC-m-S9JTA", SystemService::SystemServiceParamGetString); diff --git a/src/loader/loadExec.cpp b/src/loader/loadExec.cpp new file mode 100644 index 000000000..e5ed0ea33 --- /dev/null +++ b/src/loader/loadExec.cpp @@ -0,0 +1,63 @@ +#include "emulator.h" + +#include "common/file.h" +#include "common/logging/log.h" +#include "kernel/fileSystem.h" +#include "libs/errno.h" + +#include +#include +#include +#include +#include + +#if KYTY_PLATFORM == KYTY_PLATFORM_LINUX +#include +#include +#endif + +namespace Emulator { + +int LoadExec(const char* path, const char* const argv[]) { + using namespace Libs::SystemService; + if (path == nullptr || !std::string_view(path).starts_with("/app0/") || + !Common::File::IsFileExisting(Libs::LibKernel::FileSystem::GetRealFilename(path))) { + return SYSTEM_SERVICE_ERROR_PARAMETER; + } +#if KYTY_PLATFORM == KYTY_PLATFORM_LINUX + std::ifstream command("/proc/self/cmdline", std::ios::binary); + std::vector arguments; + for (std::string argument; std::getline(command, argument, '\0') && argument != "--guest-exec";) { + arguments.push_back(std::move(argument)); + } + if (arguments.empty()) { + return SYSTEM_SERVICE_ERROR_INTERNAL; + } + arguments.insert(arguments.end(), {"--guest-exec", path}); + size_t bytes = 0; + for (size_t i = 0; argv != nullptr && argv[i] != nullptr; ++i) { + const auto size = strnlen(argv[i], 4096 - bytes) + 1; + if (size > 4096 - bytes) { + return SYSTEM_SERVICE_ERROR_PARAMETER; + } + bytes += size; + arguments.emplace_back(argv[i]); + } + std::vector host_argv; + for (auto& argument: arguments) { + host_argv.push_back(argument.data()); + } + host_argv.push_back(nullptr); + LOGF("LoadExec: %s\n", path); + Log::Flush(); + if (close_range(3, ~0u, CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC) == 0) { + execvp(host_argv[0], host_argv.data()); + } + LOGF("LoadExec failed: %s\n", std::strerror(errno)); + return SYSTEM_SERVICE_ERROR_INTERNAL; +#else + return SYSTEM_SERVICE_ERROR_UNAVAILABLE; +#endif +} + +} // namespace Emulator diff --git a/src/loader/runtimeLinker.cpp b/src/loader/runtimeLinker.cpp index f69487df7..e03768983 100644 --- a/src/loader/runtimeLinker.cpp +++ b/src/loader/runtimeLinker.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -88,16 +89,6 @@ ThreadLocalStorage::~ThreadLocalStorage() { } } -#pragma pack(1) - -struct EntryParams { - int argc; - uint32_t pad; - const char* argv[3]; -}; - -#pragma pack() - using atexit_func_t = KYTY_SYSV_ABI void (*)(); using entry_func_t = KYTY_SYSV_ABI void (*)(EntryParams* params, atexit_func_t atexit_func); using module_ini_fini_func_t = KYTY_SYSV_ABI int (*)(size_t args, const void* argp, @@ -1283,11 +1274,16 @@ void RuntimeLinker::Execute(const std::filesystem::path& game_patch) { LOGF_COLOR(Log::Color::BrightYellow, "---\n--- Execute: %s\n---\n", "Main"); if (auto entry = GetEntry(); entry != 0) { + const auto& arguments = Config::GetGuestArguments(); + const auto params_size = offsetof(EntryParams, argv) + + (arguments.size() + 2) * sizeof(const char*); auto* params = reinterpret_cast( - (reinterpret_cast(main_stack_top) - 0x100u) & ~static_cast(0x0f)); - std::memset(params, 0, sizeof(EntryParams)); - params->argc = 1; - params->argv[0] = "KytyEmu"; + (reinterpret_cast(main_stack_top) - params_size - 0x100u) & + ~static_cast(0x0f)); + std::memset(params, 0, params_size); + for (const auto& argument: arguments) { + params->argv[params->argc++] = argument.c_str(); + } LOGF("stack_addr = %" PRIx64 "\n", reinterpret_cast(params)); diff --git a/src/loader/runtimeLinker.h b/src/loader/runtimeLinker.h index 151f9b44d..f79cb50f1 100644 --- a/src/loader/runtimeLinker.h +++ b/src/loader/runtimeLinker.h @@ -14,6 +14,12 @@ namespace Loader { +struct EntryParams { + int argc; + uint32_t pad; + const char* argv[1]; +}; + class Elf64; struct Elf64_Sym; struct Elf64_Rela; diff --git a/src/main.cpp b/src/main.cpp index 1ac7ffb7a..6cfdcba70 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -300,6 +300,10 @@ static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_he value.c_str()); return false; } + } else if (arg == "--guest-exec") { + options.elf = Common::PathFromUtf8(Common::FixFilenameSlash(value)); + options.config.guest_arguments.assign(argv + i + 1, argv + argc); + break; } else if (arg == "--game-patch") { if (!options.game_patch.empty()) { ::printf("--game-patch can only be specified once\n");