mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-18 06:10:35 +00:00
[loader] Preserve nxlink argv markers
This commit is contained in:
@@ -1139,6 +1139,8 @@ add_library(core STATIC
|
||||
launch_timestamp_cache.h
|
||||
loader/deconstructed_rom_directory.cpp
|
||||
loader/deconstructed_rom_directory.h
|
||||
loader/homebrew_nxlink.cpp
|
||||
loader/homebrew_nxlink.h
|
||||
loader/kip.cpp
|
||||
loader/kip.h
|
||||
loader/loader.cpp
|
||||
|
||||
@@ -214,6 +214,7 @@ Result KProcess::Initialize(KernelCore& kernel, const Svc::CreateProcessParamete
|
||||
m_homebrew_next_load_path_addr = 0;
|
||||
m_homebrew_next_load_argv_addr = 0;
|
||||
m_is_homebrew_in_place_next_load = false;
|
||||
m_has_homebrew_nxlink_argv_marker = false;
|
||||
m_code_size = params.code_num_pages * PageSize;
|
||||
m_is_application = True(params.flags & Svc::CreateProcessFlag::IsApplication);
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <string_view>
|
||||
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
@@ -90,6 +92,7 @@ private:
|
||||
KProcessAddress m_process_handle_addr{};
|
||||
KProcessAddress m_homebrew_next_load_path_addr{};
|
||||
KProcessAddress m_homebrew_next_load_argv_addr{};
|
||||
std::array<char, 16> m_homebrew_nxlink_argv_marker{};
|
||||
KHandleTable m_handle_table;
|
||||
KProcessAddress m_plr_address{};
|
||||
ThreadList m_thread_list{};
|
||||
@@ -144,6 +147,7 @@ private:
|
||||
bool m_is_immortal : 1 = false;
|
||||
bool m_is_handle_table_initialized : 1 = false;
|
||||
bool m_is_homebrew_in_place_next_load : 1 = false;
|
||||
bool m_has_homebrew_nxlink_argv_marker : 1 = false;
|
||||
|
||||
private:
|
||||
Result StartTermination(KernelCore& kernel);
|
||||
@@ -246,6 +250,24 @@ public:
|
||||
void SetHomebrewInPlaceNextLoad(bool enabled) {
|
||||
m_is_homebrew_in_place_next_load = enabled;
|
||||
}
|
||||
void SetHomebrewNxlinkArgvMarker(std::string_view marker) {
|
||||
if (marker.size() != m_homebrew_nxlink_argv_marker.size()) {
|
||||
m_has_homebrew_nxlink_argv_marker = false;
|
||||
return;
|
||||
}
|
||||
|
||||
marker.copy(m_homebrew_nxlink_argv_marker.data(), m_homebrew_nxlink_argv_marker.size());
|
||||
m_has_homebrew_nxlink_argv_marker = true;
|
||||
}
|
||||
void ClearHomebrewNxlinkArgvMarker() {
|
||||
m_has_homebrew_nxlink_argv_marker = false;
|
||||
}
|
||||
std::string_view GetHomebrewNxlinkArgvMarker() const {
|
||||
if (!m_has_homebrew_nxlink_argv_marker) {
|
||||
return {};
|
||||
}
|
||||
return {m_homebrew_nxlink_argv_marker.data(), m_homebrew_nxlink_argv_marker.size()};
|
||||
}
|
||||
bool IsHomebrewInPlaceNextLoad() const {
|
||||
return m_is_homebrew_in_place_next_load;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/loader/homebrew_nxlink.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
namespace Loader::HomebrewNxlink {
|
||||
namespace {
|
||||
|
||||
std::optional<std::string> GetLastArgvToken(std::string_view argv_string) {
|
||||
while (!argv_string.empty() && argv_string.back() == '\0') {
|
||||
argv_string.remove_suffix(1);
|
||||
}
|
||||
|
||||
std::optional<std::string_view> last_token;
|
||||
bool in_token = false;
|
||||
bool quoted = false;
|
||||
size_t token_begin = 0;
|
||||
size_t token_size = 0;
|
||||
|
||||
for (size_t i = 0; i <= argv_string.size(); i++) {
|
||||
const char c = i < argv_string.size() ? argv_string[i] : '\0';
|
||||
|
||||
if (!in_token) {
|
||||
if (c == '\0' || std::isspace(static_cast<unsigned char>(c))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
in_token = true;
|
||||
token_size = 0;
|
||||
if (c == '"') {
|
||||
quoted = true;
|
||||
token_begin = i + 1;
|
||||
} else {
|
||||
quoted = false;
|
||||
token_begin = i;
|
||||
token_size = 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool token_end =
|
||||
quoted ? c == '"' || c == '\0'
|
||||
: c == '\0' || std::isspace(static_cast<unsigned char>(c));
|
||||
if (token_end) {
|
||||
if (token_size != 0) {
|
||||
last_token = argv_string.substr(token_begin, token_size);
|
||||
}
|
||||
in_token = false;
|
||||
quoted = false;
|
||||
token_size = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
token_size++;
|
||||
}
|
||||
|
||||
if (!last_token) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::string{*last_token};
|
||||
}
|
||||
|
||||
void AppendArgvToken(std::string& argv_string, std::string_view token) {
|
||||
while (!argv_string.empty() && argv_string.back() == '\0') {
|
||||
argv_string.pop_back();
|
||||
}
|
||||
if (!argv_string.empty()) {
|
||||
argv_string.push_back(' ');
|
||||
}
|
||||
argv_string.append(token);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool IsArgvMarker(std::string_view token) {
|
||||
if (token.size() != ArgvMarkerSize || token.substr(8) != ArgvMarkerSuffix) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
if (!std::isxdigit(static_cast<unsigned char>(token[i]))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<std::string> GetArgvMarker(std::string_view argv_string) {
|
||||
auto last_token = GetLastArgvToken(argv_string);
|
||||
if (!last_token || !IsArgvMarker(*last_token)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return last_token;
|
||||
}
|
||||
|
||||
std::optional<std::string> PrepareArgv(std::string& argv_string,
|
||||
std::string_view inherited_marker) {
|
||||
auto marker = GetArgvMarker(argv_string);
|
||||
if (!marker && IsArgvMarker(inherited_marker)) {
|
||||
AppendArgvToken(argv_string, inherited_marker);
|
||||
marker = std::string{inherited_marker};
|
||||
}
|
||||
return marker;
|
||||
}
|
||||
|
||||
} // namespace Loader::HomebrewNxlink
|
||||
@@ -0,0 +1,23 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace Loader::HomebrewNxlink {
|
||||
|
||||
constexpr size_t ArgvMarkerSize = 16;
|
||||
constexpr std::string_view ArgvMarkerSuffix = "_NXLINK_";
|
||||
|
||||
// This module handles only the libnx argv marker. Client stream handling should be an
|
||||
// explicit byte-sink API; the marker alone must not imply logging.
|
||||
bool IsArgvMarker(std::string_view token);
|
||||
std::optional<std::string> GetArgvMarker(std::string_view argv_string);
|
||||
std::optional<std::string> PrepareArgv(std::string& argv_string,
|
||||
std::string_view inherited_marker);
|
||||
|
||||
} // namespace Loader::HomebrewNxlink
|
||||
+18
-3
@@ -33,6 +33,7 @@
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/homebrew_nxlink.h"
|
||||
#include "core/loader/nro.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@@ -319,6 +320,7 @@ struct HomebrewNroImage {
|
||||
size_t image_size{};
|
||||
size_t args_offset{};
|
||||
std::optional<size_t> exit_process_offset;
|
||||
std::optional<std::string> nxlink_argv_marker;
|
||||
std::string argv_string;
|
||||
};
|
||||
|
||||
@@ -340,7 +342,8 @@ static void SetHomebrewConfigPointers(Kernel::KProcess& process, u64 config_addr
|
||||
static std::optional<HomebrewNroImage> BuildHomebrewNroImage(const std::vector<u8>& data,
|
||||
std::string nro_path,
|
||||
std::string file_name,
|
||||
std::string launch_argv) {
|
||||
std::string launch_argv,
|
||||
std::string_view inherited_marker) {
|
||||
if (data.size() < sizeof(NroHeader)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -385,6 +388,7 @@ static std::optional<HomebrewNroImage> BuildHomebrewNroImage(const std::vector<u
|
||||
} else {
|
||||
image.argv_string = QuoteHomebrewArgvComponent(argv0);
|
||||
}
|
||||
image.nxlink_argv_marker = HomebrewNxlink::PrepareArgv(image.argv_string, inherited_marker);
|
||||
if (image.argv_string.empty() || image.argv_string.back() != '\0') {
|
||||
image.argv_string.push_back('\0');
|
||||
}
|
||||
@@ -447,8 +451,8 @@ bool LoadNroInPlace(Core::System& system, Kernel::KProcess& process, Kernel::KTh
|
||||
return false;
|
||||
}
|
||||
|
||||
auto image =
|
||||
BuildHomebrewNroImage(nro_file->ReadAllBytes(), nro_path, nro_file->GetName(), launch_argv);
|
||||
auto image = BuildHomebrewNroImage(nro_file->ReadAllBytes(), nro_path, nro_file->GetName(),
|
||||
launch_argv, process.GetHomebrewNxlinkArgvMarker());
|
||||
if (!image) {
|
||||
LOG_WARNING(Loader, "NextLoad: in-place handoff failed because '{}' is invalid",
|
||||
nro_path);
|
||||
@@ -538,6 +542,11 @@ bool LoadNroInPlace(Core::System& system, Kernel::KProcess& process, Kernel::KTh
|
||||
process.SetArgReturnAddress(Kernel::KProcessAddress{
|
||||
image->exit_process_offset ? base + *image->exit_process_offset : 0});
|
||||
SetHomebrewConfigPointers(process, config_addr, next_load_path_addr, next_load_argv_addr);
|
||||
if (image->nxlink_argv_marker) {
|
||||
process.SetHomebrewNxlinkArgvMarker(*image->nxlink_argv_marker);
|
||||
} else {
|
||||
process.ClearHomebrewNxlinkArgvMarker();
|
||||
}
|
||||
system.GetFileSystemController().RegisterProcess(
|
||||
process.GetProcessId(), program_id,
|
||||
std::make_unique<FileSys::RomFSFactory>(loader, system.GetContentProvider(),
|
||||
@@ -619,6 +628,7 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
|
||||
argv_string.push_back(' ');
|
||||
argv_string += program_args;
|
||||
}
|
||||
const auto nxlink_argv_marker = HomebrewNxlink::PrepareArgv(argv_string, {});
|
||||
if (argv_string.empty() || argv_string.back() != '\0') {
|
||||
argv_string.push_back('\0');
|
||||
}
|
||||
@@ -712,6 +722,11 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
|
||||
codeset.memory = std::move(program_image);
|
||||
process.LoadModule(system.Kernel(), std::move(codeset), process.GetEntryPoint());
|
||||
process.SetHomebrewInPlaceNextLoad(false);
|
||||
if (nxlink_argv_marker) {
|
||||
process.SetHomebrewNxlinkArgvMarker(*nxlink_argv_marker);
|
||||
} else {
|
||||
process.ClearHomebrewNxlinkArgvMarker();
|
||||
}
|
||||
{
|
||||
const u64 base = GetInteger(process.GetEntryPoint());
|
||||
const u64 config_addr = base + args_offset_in_image;
|
||||
|
||||
Reference in New Issue
Block a user