From 6f0cddd4404ec5203e71fea77e75fc373299ba3f Mon Sep 17 00:00:00 2001 From: thenotsoquietninja44-collab Date: Sun, 11 Oct 2026 08:23:11 +0200 Subject: [PATCH] implement LoadExec (windows) --- src/common/emulatorConfig.h | 3 +- src/loader/loadExec.cpp | 103 +++++++++++++++++++++++++++++------- 2 files changed, 87 insertions(+), 19 deletions(-) diff --git a/src/common/emulatorConfig.h b/src/common/emulatorConfig.h index 97e6a5c26..8684a80b9 100644 --- a/src/common/emulatorConfig.h +++ b/src/common/emulatorConfig.h @@ -83,7 +83,8 @@ struct ConfigOptions { bool red_zone_protection_enabled = false; #endif Keymap keymap; - std::vector guest_arguments; + // Ordinary boots need a non-null argv[0]; --guest-exec supplies the caller's arguments. + std::vector guest_arguments {""}; }; void Load(const ConfigOptions& cfg); diff --git a/src/loader/loadExec.cpp b/src/loader/loadExec.cpp index e5ed0ea33..0e3e4174e 100644 --- a/src/loader/loadExec.cpp +++ b/src/loader/loadExec.cpp @@ -1,59 +1,126 @@ -#include "emulator.h" - #include "common/file.h" #include "common/logging/log.h" +#include "emulator.h" #include "kernel/fileSystem.h" #include "libs/errno.h" #include +#include #include #include #include #include -#if KYTY_PLATFORM == KYTY_PLATFORM_LINUX +#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS +#define WIN32_LEAN_AND_MEAN +#include +#elif KYTY_PLATFORM == KYTY_PLATFORM_LINUX #include #include #endif namespace Emulator { +#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS +static std::wstring QuoteArgument(std::wstring_view argument) { + std::wstring quoted = L"\""; + size_t slashes = 0; + for (wchar_t c: argument) { + if (c == L'\\') { + ++slashes; + continue; + } + quoted.append(c == L'"' ? slashes * 2 + 1 : slashes, L'\\'); + quoted += c; + slashes = 0; + } + quoted.append(slashes * 2, L'\\'); + return quoted + L'"'; +} +#endif + 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; +#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS || KYTY_PLATFORM == KYTY_PLATFORM_LINUX + std::vector arguments {"--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]); + arguments.emplace_back(argv[i], size - 1); } + LOGF("LoadExec: %s\n", path); + Log::Flush(); +#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS + wchar_t executable[32768] {}; + const auto length = GetModuleFileNameW(nullptr, executable, 32768); + if (length == 0 || length >= 32768) { + return SYSTEM_SERVICE_ERROR_INTERNAL; + } + if (__wargv == nullptr) { + return SYSTEM_SERVICE_ERROR_INTERNAL; + } + std::wstring command = QuoteArgument(executable); + for (int i = 1; i < __argc && __wargv[i] != nullptr; ++i) { + if (std::wstring_view(__wargv[i]) == L"--guest-exec") { + break; + } + command += L" " + QuoteArgument(__wargv[i]); + } + for (const auto& argument: arguments) { + const int size = + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, argument.c_str(), -1, nullptr, 0); + if (size == 0) { + return SYSTEM_SERVICE_ERROR_PARAMETER; + } + std::wstring wide(size, L'\0'); + if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, argument.c_str(), -1, wide.data(), + size) == 0) { + return SYSTEM_SERVICE_ERROR_PARAMETER; + } + wide.resize(size - 1); + command += L" " + QuoteArgument(wide); + } + STARTUPINFOW startup {}; + startup.cb = sizeof(startup); + PROCESS_INFORMATION process {}; + if (!CreateProcessW(executable, command.data(), nullptr, nullptr, FALSE, CREATE_NO_WINDOW, + nullptr, nullptr, &startup, &process)) { + LOGF("LoadExec: process creation failed: %lu\n", GetLastError()); + return SYSTEM_SERVICE_ERROR_INTERNAL; + } + CloseHandle(process.hThread); + CloseHandle(process.hProcess); + std::fflush(nullptr); + std::quick_exit(0); +#else + std::ifstream command("/proc/self/cmdline", std::ios::binary); + std::vector host_arguments; + for (std::string argument; + std::getline(command, argument, '\0') && argument != "--guest-exec";) { + host_arguments.push_back(std::move(argument)); + } + if (host_arguments.empty()) { + return SYSTEM_SERVICE_ERROR_INTERNAL; + } + host_arguments.insert(host_arguments.end(), arguments.begin(), arguments.end()); std::vector host_argv; - for (auto& argument: arguments) { + for (auto& argument: host_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)); +#endif return SYSTEM_SERVICE_ERROR_INTERNAL; #else return SYSTEM_SERVICE_ERROR_UNAVAILABLE;