mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-24 05:22: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
|
launch_timestamp_cache.h
|
||||||
loader/deconstructed_rom_directory.cpp
|
loader/deconstructed_rom_directory.cpp
|
||||||
loader/deconstructed_rom_directory.h
|
loader/deconstructed_rom_directory.h
|
||||||
|
loader/homebrew_nxlink.cpp
|
||||||
|
loader/homebrew_nxlink.h
|
||||||
loader/kip.cpp
|
loader/kip.cpp
|
||||||
loader/kip.h
|
loader/kip.h
|
||||||
loader/loader.cpp
|
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_path_addr = 0;
|
||||||
m_homebrew_next_load_argv_addr = 0;
|
m_homebrew_next_load_argv_addr = 0;
|
||||||
m_is_homebrew_in_place_next_load = false;
|
m_is_homebrew_in_place_next_load = false;
|
||||||
|
m_has_homebrew_nxlink_argv_marker = false;
|
||||||
m_code_size = params.code_num_pages * PageSize;
|
m_code_size = params.code_num_pages * PageSize;
|
||||||
m_is_application = True(params.flags & Svc::CreateProcessFlag::IsApplication);
|
m_is_application = True(params.flags & Svc::CreateProcessFlag::IsApplication);
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
#include "core/arm/arm_interface.h"
|
#include "core/arm/arm_interface.h"
|
||||||
#include "core/file_sys/program_metadata.h"
|
#include "core/file_sys/program_metadata.h"
|
||||||
@@ -90,6 +92,7 @@ private:
|
|||||||
KProcessAddress m_process_handle_addr{};
|
KProcessAddress m_process_handle_addr{};
|
||||||
KProcessAddress m_homebrew_next_load_path_addr{};
|
KProcessAddress m_homebrew_next_load_path_addr{};
|
||||||
KProcessAddress m_homebrew_next_load_argv_addr{};
|
KProcessAddress m_homebrew_next_load_argv_addr{};
|
||||||
|
std::array<char, 16> m_homebrew_nxlink_argv_marker{};
|
||||||
KHandleTable m_handle_table;
|
KHandleTable m_handle_table;
|
||||||
KProcessAddress m_plr_address{};
|
KProcessAddress m_plr_address{};
|
||||||
ThreadList m_thread_list{};
|
ThreadList m_thread_list{};
|
||||||
@@ -144,6 +147,7 @@ private:
|
|||||||
bool m_is_immortal : 1 = false;
|
bool m_is_immortal : 1 = false;
|
||||||
bool m_is_handle_table_initialized : 1 = false;
|
bool m_is_handle_table_initialized : 1 = false;
|
||||||
bool m_is_homebrew_in_place_next_load : 1 = false;
|
bool m_is_homebrew_in_place_next_load : 1 = false;
|
||||||
|
bool m_has_homebrew_nxlink_argv_marker : 1 = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Result StartTermination(KernelCore& kernel);
|
Result StartTermination(KernelCore& kernel);
|
||||||
@@ -246,6 +250,24 @@ public:
|
|||||||
void SetHomebrewInPlaceNextLoad(bool enabled) {
|
void SetHomebrewInPlaceNextLoad(bool enabled) {
|
||||||
m_is_homebrew_in_place_next_load = 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 {
|
bool IsHomebrewInPlaceNextLoad() const {
|
||||||
return m_is_homebrew_in_place_next_load;
|
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_process.h"
|
||||||
#include "core/hle/kernel/k_thread.h"
|
#include "core/hle/kernel/k_thread.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
|
#include "core/loader/homebrew_nxlink.h"
|
||||||
#include "core/loader/nro.h"
|
#include "core/loader/nro.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
|
||||||
@@ -319,6 +320,7 @@ struct HomebrewNroImage {
|
|||||||
size_t image_size{};
|
size_t image_size{};
|
||||||
size_t args_offset{};
|
size_t args_offset{};
|
||||||
std::optional<size_t> exit_process_offset;
|
std::optional<size_t> exit_process_offset;
|
||||||
|
std::optional<std::string> nxlink_argv_marker;
|
||||||
std::string argv_string;
|
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,
|
static std::optional<HomebrewNroImage> BuildHomebrewNroImage(const std::vector<u8>& data,
|
||||||
std::string nro_path,
|
std::string nro_path,
|
||||||
std::string file_name,
|
std::string file_name,
|
||||||
std::string launch_argv) {
|
std::string launch_argv,
|
||||||
|
std::string_view inherited_marker) {
|
||||||
if (data.size() < sizeof(NroHeader)) {
|
if (data.size() < sizeof(NroHeader)) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -385,6 +388,7 @@ static std::optional<HomebrewNroImage> BuildHomebrewNroImage(const std::vector<u
|
|||||||
} else {
|
} else {
|
||||||
image.argv_string = QuoteHomebrewArgvComponent(argv0);
|
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') {
|
if (image.argv_string.empty() || image.argv_string.back() != '\0') {
|
||||||
image.argv_string.push_back('\0');
|
image.argv_string.push_back('\0');
|
||||||
}
|
}
|
||||||
@@ -447,8 +451,8 @@ bool LoadNroInPlace(Core::System& system, Kernel::KProcess& process, Kernel::KTh
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto image =
|
auto image = BuildHomebrewNroImage(nro_file->ReadAllBytes(), nro_path, nro_file->GetName(),
|
||||||
BuildHomebrewNroImage(nro_file->ReadAllBytes(), nro_path, nro_file->GetName(), launch_argv);
|
launch_argv, process.GetHomebrewNxlinkArgvMarker());
|
||||||
if (!image) {
|
if (!image) {
|
||||||
LOG_WARNING(Loader, "NextLoad: in-place handoff failed because '{}' is invalid",
|
LOG_WARNING(Loader, "NextLoad: in-place handoff failed because '{}' is invalid",
|
||||||
nro_path);
|
nro_path);
|
||||||
@@ -538,6 +542,11 @@ bool LoadNroInPlace(Core::System& system, Kernel::KProcess& process, Kernel::KTh
|
|||||||
process.SetArgReturnAddress(Kernel::KProcessAddress{
|
process.SetArgReturnAddress(Kernel::KProcessAddress{
|
||||||
image->exit_process_offset ? base + *image->exit_process_offset : 0});
|
image->exit_process_offset ? base + *image->exit_process_offset : 0});
|
||||||
SetHomebrewConfigPointers(process, config_addr, next_load_path_addr, next_load_argv_addr);
|
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(
|
system.GetFileSystemController().RegisterProcess(
|
||||||
process.GetProcessId(), program_id,
|
process.GetProcessId(), program_id,
|
||||||
std::make_unique<FileSys::RomFSFactory>(loader, system.GetContentProvider(),
|
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.push_back(' ');
|
||||||
argv_string += program_args;
|
argv_string += program_args;
|
||||||
}
|
}
|
||||||
|
const auto nxlink_argv_marker = HomebrewNxlink::PrepareArgv(argv_string, {});
|
||||||
if (argv_string.empty() || argv_string.back() != '\0') {
|
if (argv_string.empty() || argv_string.back() != '\0') {
|
||||||
argv_string.push_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);
|
codeset.memory = std::move(program_image);
|
||||||
process.LoadModule(system.Kernel(), std::move(codeset), process.GetEntryPoint());
|
process.LoadModule(system.Kernel(), std::move(codeset), process.GetEntryPoint());
|
||||||
process.SetHomebrewInPlaceNextLoad(false);
|
process.SetHomebrewInPlaceNextLoad(false);
|
||||||
|
if (nxlink_argv_marker) {
|
||||||
|
process.SetHomebrewNxlinkArgvMarker(*nxlink_argv_marker);
|
||||||
|
} else {
|
||||||
|
process.ClearHomebrewNxlinkArgvMarker();
|
||||||
|
}
|
||||||
{
|
{
|
||||||
const u64 base = GetInteger(process.GetEntryPoint());
|
const u64 base = GetInteger(process.GetEntryPoint());
|
||||||
const u64 config_addr = base + args_offset_in_image;
|
const u64 config_addr = base + args_offset_in_image;
|
||||||
|
|||||||
Reference in New Issue
Block a user