loader: refactor ro manager/improve accuracy

This commit is contained in:
Michael Scire 2021-10-10 18:22:32 -07:00
parent d8a36e39f2
commit d9dc04318d
24 changed files with 328 additions and 218 deletions

View file

@ -33,7 +33,7 @@ namespace ams::dmnt::cheat {
MemoryRegionExtents heap_extents;
MemoryRegionExtents alias_extents;
MemoryRegionExtents aslr_extents;
u8 main_nso_build_id[0x20];
u8 main_nso_module_id[0x20];
};
static_assert(util::is_pod<CheatProcessMetadata>::value && sizeof(CheatProcessMetadata) == 0x70, "CheatProcessMetadata definition!");

View file

@ -73,8 +73,12 @@ namespace ams::ldr {
}
static_assert(sizeof(PinId) == sizeof(u64) && util::is_pod<PinId>::value, "PinId definition!");
/* Import ModuleInfo from libnx. */
using ModuleInfo = ::LoaderModuleInfo;
struct ModuleInfo {
u8 module_id[0x20];
u64 address;
u64 size;
};
static_assert(sizeof(ModuleInfo) == 0x30);
/* NSO types. */
struct NsoHeader {
@ -123,7 +127,7 @@ namespace ams::ldr {
};
SegmentInfo segments[Segment_Count];
};
u8 build_id[sizeof(ModuleInfo::build_id)];
u8 module_id[sizeof(ModuleInfo::module_id)];
union {
u32 compressed_sizes[Segment_Count];
struct {

View file

@ -17,9 +17,10 @@
#pragma once
#include <vapours.hpp>
#include <stratosphere/ro/ro_types.hpp>
#include <stratosphere/ldr/ldr_types.hpp>
#include <stratosphere/sf.hpp>
#define AMS_RO_I_DEBUG_MONITOR_INTERFACE_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, GetProcessModuleInfo, (sf::Out<u32> out_count, const sf::OutArray<LoaderModuleInfo> &out_infos, os::ProcessId process_id), (out_count, out_infos, process_id))
AMS_SF_METHOD_INFO(C, H, 0, Result, GetProcessModuleInfo, (sf::Out<u32> out_count, const sf::OutArray<ldr::ModuleInfo> &out_infos, os::ProcessId process_id), (out_count, out_infos, process_id))
AMS_SF_DEFINE_INTERFACE(ams::ro::impl, IDebugMonitorInterface, AMS_RO_I_DEBUG_MONITOR_INTERFACE_INTERFACE_INFO)

View file

@ -27,10 +27,11 @@ namespace ams::ro {
NrrKind_Count,
};
static constexpr size_t ModuleIdSize = 0x20;
struct ModuleId {
u8 build_id[0x20];
u8 data[ModuleIdSize];
};
static_assert(sizeof(ModuleId) == sizeof(LoaderModuleInfo::build_id), "ModuleId definition!");
static_assert(sizeof(ModuleId) == ModuleIdSize);
struct NrrCertification {
static constexpr size_t RsaKeySize = 0x100;

View file

@ -43,3 +43,16 @@
\
return ::ams::util::GetReference(s_singleton_storage); \
}
#define AMS_CONSTINIT_SINGLETON_TRAITS(_CLASSNAME_) \
private: \
NON_COPYABLE(_CLASSNAME_); \
NON_MOVEABLE(_CLASSNAME_); \
private: \
constexpr _CLASSNAME_ () = default; \
public: \
static _CLASSNAME_ &GetInstance() { \
/* Declare singleton instance variables. */ \
static constinit _CLASSNAME_ s_singleton_instance; \
return s_singleton_instance; \
}

View file

@ -56,8 +56,8 @@ namespace ams::patcher {
/* Read module id from name. */
std::memset(out_module_id, 0, sizeof(*out_module_id));
for (unsigned int name_ofs = 0, id_ofs = 0; name_ofs < name_len - extension_len && id_ofs < sizeof(*out_module_id); id_ofs++) {
out_module_id->build_id[id_ofs] |= ConvertHexNybble(name[name_ofs++]) << 4;
out_module_id->build_id[id_ofs] |= ConvertHexNybble(name[name_ofs++]);
out_module_id->data[id_ofs] |= ConvertHexNybble(name[name_ofs++]) << 4;
out_module_id->data[id_ofs] |= ConvertHexNybble(name[name_ofs++]);
}
return true;
@ -76,7 +76,7 @@ namespace ams::patcher {
bool IsIpsFileForModule(const char *name, const ro::ModuleId *module_id) {
const size_t name_len = std::strlen(name);
/* The path must be correct size for a build id (with trailing zeroes optionally trimmed) + ".ips". */
/* The path must be correct size for a module id (with trailing zeroes optionally trimmed) + ".ips". */
if (!(IpsFileExtensionLength < name_len && name_len <= ModuleIpsPatchLength)) {
return false;
}

View file

@ -330,7 +330,7 @@ namespace ams::scs {
Result PrepareToLaunchProgram(ncm::ProgramId program_id, const void *args, size_t args_size) {
/* Set the arguments. */
R_TRY_CATCH(ldr::SetProgramArgument(program_id, args, args_size)) {
R_CATCH(ldr::ResultTooManyArguments) {
R_CATCH(ldr::ResultArgumentCountOverflow) {
/* There are too many arguments already registered. Flush the arguments queue. */
R_TRY(ldr::FlushArguments());

View file

@ -21,29 +21,30 @@ namespace ams::ldr {
R_DEFINE_NAMESPACE_RESULT_MODULE(9);
R_DEFINE_ERROR_RESULT(TooLongArgument, 1);
R_DEFINE_ERROR_RESULT(TooManyArguments, 2);
R_DEFINE_ERROR_RESULT(TooLargeMeta, 3);
R_DEFINE_ERROR_RESULT(InvalidMeta, 4);
R_DEFINE_ERROR_RESULT(InvalidNso, 5);
R_DEFINE_ERROR_RESULT(InvalidPath, 6);
R_DEFINE_ERROR_RESULT(TooManyProcesses, 7);
R_DEFINE_ERROR_RESULT(NotPinned, 8);
R_DEFINE_ERROR_RESULT(InvalidProgramId, 9);
R_DEFINE_ERROR_RESULT(ArgumentOverflow, 1);
R_DEFINE_ERROR_RESULT(ArgumentCountOverflow, 2);
R_DEFINE_ERROR_RESULT(MetaOverflow, 3);
R_DEFINE_ERROR_RESULT(InvalidMeta, 4);
R_DEFINE_ERROR_RESULT(InvalidNso, 5);
R_DEFINE_ERROR_RESULT(InvalidPath, 6);
R_DEFINE_ERROR_RESULT(MaxProcess, 7);
R_DEFINE_ERROR_RESULT(NotPinned, 8);
R_DEFINE_ERROR_RESULT(InvalidProgramId, 9);
R_DEFINE_ERROR_RESULT(InvalidVersion, 10);
R_DEFINE_ERROR_RESULT(InvalidAcidSignature, 11);
R_DEFINE_ERROR_RESULT(InvalidNcaSignature, 12);
R_DEFINE_ERROR_RESULT(InsufficientAddressSpace, 51);
R_DEFINE_ERROR_RESULT(InvalidNro, 52);
R_DEFINE_ERROR_RESULT(InvalidNrr, 53);
R_DEFINE_ERROR_RESULT(InvalidSignature, 54);
R_DEFINE_ERROR_RESULT(InsufficientNroRegistrations, 55);
R_DEFINE_ERROR_RESULT(InsufficientNrrRegistrations, 56);
R_DEFINE_ERROR_RESULT(OutOfAddressSpace, 51);
R_DEFINE_ERROR_RESULT(InvalidNroImage, 52);
R_DEFINE_ERROR_RESULT(InvalidNrrImage, 53);
R_DEFINE_ERROR_RESULT(NotAuthorized, 54);
R_DEFINE_ERROR_RESULT(MaxModule, 55);
R_DEFINE_ERROR_RESULT(MaxRegistration, 56);
R_DEFINE_ERROR_RESULT(NroAlreadyLoaded, 57);
R_DEFINE_ERROR_RESULT(InvalidAddress, 81);
R_DEFINE_ERROR_RESULT(InvalidSize, 82);
R_DEFINE_ERROR_RESULT(InvalidCurrentMemory, 83);
R_DEFINE_ERROR_RESULT(NotLoaded, 84);
R_DEFINE_ERROR_RESULT(NotRegistered, 85);
R_DEFINE_ERROR_RESULT(InvalidSession, 86);

View file

@ -54,7 +54,7 @@ namespace ams::creport {
if (std::strcmp(m_modules[i].name, "") != 0) {
file.WriteFormat(" Name: %s\n", module.name);
}
file.DumpMemory(" Build Id: ", module.build_id, sizeof(module.build_id));
file.DumpMemory(" Module Id: ", module.module_id, sizeof(module.module_id));
}
}
@ -104,10 +104,10 @@ namespace ams::creport {
module.start_address = mi.base_address;
module.end_address = mi.base_address + mi.size;
GetModuleName(module.name, module.start_address, module.end_address);
GetModuleBuildId(module.build_id, module.end_address);
GetModuleId(module.module_id, module.end_address);
/* Some homebrew won't have a name. Add a fake one for readability. */
if (std::strcmp(module.name, "") == 0) {
util::SNPrintf(module.name, sizeof(module.name), "[%02x%02x%02x%02x]", module.build_id[0], module.build_id[1], module.build_id[2], module.build_id[3]);
util::SNPrintf(module.name, sizeof(module.name), "[%02x%02x%02x%02x]", module.module_id[0], module.module_id[1], module.module_id[2], module.module_id[3]);
}
}
@ -221,9 +221,9 @@ namespace ams::creport {
out_name[ModuleNameLengthMax - 1] = '\x00';
}
void ModuleList::GetModuleBuildId(u8 *out_build_id, uintptr_t ro_start_address) {
void ModuleList::GetModuleId(u8 *out, uintptr_t ro_start_address) {
/* Clear output. */
std::memset(out_build_id, 0, ModuleBuildIdLength);
std::memset(out, 0, ModuleIdSize);
/* Verify .rodata is read-only. */
svc::MemoryInfo mi;
@ -238,10 +238,10 @@ namespace ams::creport {
return;
}
/* Find GNU\x00 to locate start of build id. */
for (int ofs = read_size - sizeof(GnuSignature) - ModuleBuildIdLength; ofs >= 0; ofs--) {
/* Find GNU\x00 to locate start of module id (GNU build id). */
for (int ofs = read_size - sizeof(GnuSignature) - ModuleIdSize; ofs >= 0; ofs--) {
if (std::memcmp(g_last_rodata_pages + ofs, GnuSignature, sizeof(GnuSignature)) == 0) {
std::memcpy(out_build_id, g_last_rodata_pages + ofs + sizeof(GnuSignature), ModuleBuildIdLength);
std::memcpy(out, g_last_rodata_pages + ofs + sizeof(GnuSignature), ModuleIdSize);
break;
}
}

View file

@ -23,11 +23,11 @@ namespace ams::creport {
private:
static constexpr size_t ModuleCountMax = 0x60;
static constexpr size_t ModuleNameLengthMax = 0x20;
static constexpr size_t ModuleBuildIdLength = 0x20;
static constexpr size_t ModuleIdSize = 0x20;
struct ModuleInfo {
char name[ModuleNameLengthMax];
u8 build_id[ModuleBuildIdLength];
u8 module_id[ModuleIdSize];
u64 start_address;
u64 end_address;
};
@ -58,7 +58,7 @@ namespace ams::creport {
bool TryFindModule(uintptr_t *out_address, uintptr_t guess);
void TryAddModule(uintptr_t guess);
void GetModuleName(char *out_name, uintptr_t text_start, uintptr_t ro_start);
void GetModuleBuildId(u8 *out_build_id, uintptr_t ro_start);
void GetModuleId(u8 *out, uintptr_t ro_start);
};
}

View file

@ -113,7 +113,7 @@ namespace ams::dmnt::cheat::impl {
Result AttachToApplicationProcess(bool on_process_launch);
bool ParseCheats(const char *s, size_t len);
bool LoadCheats(const ncm::ProgramId program_id, const u8 *build_id);
bool LoadCheats(const ncm::ProgramId program_id, const u8 *module_id);
bool ParseCheatToggles(const char *s, size_t len);
bool LoadCheatToggles(const ncm::ProgramId program_id);
void SaveCheatToggles(const ncm::ProgramId program_id);
@ -826,16 +826,16 @@ namespace ams::dmnt::cheat::impl {
/* Get module information from loader. */
{
LoaderModuleInfo proc_modules[2];
ldr::ModuleInfo proc_modules[2];
s32 num_modules;
/* TODO: ldr::dmnt:: */
R_ABORT_UNLESS_IF_NEW_PROCESS(ldrDmntGetProcessModuleInfo(static_cast<u64>(m_cheat_process_metadata.process_id), proc_modules, util::size(proc_modules), std::addressof(num_modules)));
R_ABORT_UNLESS_IF_NEW_PROCESS(ldrDmntGetProcessModuleInfo(static_cast<u64>(m_cheat_process_metadata.process_id), reinterpret_cast<LoaderModuleInfo *>(proc_modules), util::size(proc_modules), std::addressof(num_modules)));
/* All applications must have two modules. */
/* Only accept one (which means we're attaching to HBL) */
/* if we aren't auto-attaching. */
const LoaderModuleInfo *proc_module = nullptr;
const ldr::ModuleInfo *proc_module = nullptr;
if (num_modules == 2) {
proc_module = std::addressof(proc_modules[1]);
} else if (num_modules == 1 && !on_process_launch) {
@ -844,13 +844,13 @@ namespace ams::dmnt::cheat::impl {
return dmnt::cheat::ResultCheatNotAttached();
}
m_cheat_process_metadata.main_nso_extents.base = proc_module->base_address;
m_cheat_process_metadata.main_nso_extents.base = proc_module->address;
m_cheat_process_metadata.main_nso_extents.size = proc_module->size;
std::memcpy(m_cheat_process_metadata.main_nso_build_id, proc_module->build_id, sizeof(m_cheat_process_metadata.main_nso_build_id));
std::memcpy(m_cheat_process_metadata.main_nso_module_id, proc_module->module_id, sizeof(m_cheat_process_metadata.main_nso_module_id));
}
/* Read cheats off the SD. */
if (!this->LoadCheats(m_cheat_process_metadata.program_id, m_cheat_process_metadata.main_nso_build_id) ||
if (!this->LoadCheats(m_cheat_process_metadata.program_id, m_cheat_process_metadata.main_nso_module_id) ||
!this->LoadCheatToggles(m_cheat_process_metadata.program_id)) {
/* If new process launch, require success. */
R_UNLESS(!on_process_launch, dmnt::cheat::ResultCheatNotAttached());
@ -1059,16 +1059,16 @@ namespace ams::dmnt::cheat::impl {
return true;
}
bool CheatProcessManager::LoadCheats(const ncm::ProgramId program_id, const u8 *build_id) {
bool CheatProcessManager::LoadCheats(const ncm::ProgramId program_id, const u8 *module_id) {
/* Reset existing entries. */
this->ResetAllCheatEntries();
/* Open the file for program/build_id. */
/* Open the file for program/module_id. */
fs::FileHandle file;
{
char path[fs::EntryNameLengthMax + 1];
util::SNPrintf(path, sizeof(path), "sdmc:/atmosphere/contents/%016lx/cheats/%02x%02x%02x%02x%02x%02x%02x%02x.txt", program_id.value,
build_id[0], build_id[1], build_id[2], build_id[3], build_id[4], build_id[5], build_id[6], build_id[7]);
module_id[0], module_id[1], module_id[2], module_id[3], module_id[4], module_id[5], module_id[6], module_id[7]);
if (R_FAILED(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Read))) {
return false;
}

View file

@ -48,13 +48,13 @@ namespace ams::ldr::args {
}
Result Set(ncm::ProgramId program_id, const void *args, size_t args_size) {
R_UNLESS(args_size < ArgumentSizeMax, ldr::ResultTooLongArgument());
R_UNLESS(args_size < ArgumentSizeMax, ldr::ResultArgumentOverflow());
ArgumentInfo *arg_info = FindArgumentInfo(program_id);
if (arg_info == nullptr) {
arg_info = FindFreeArgumentInfo();
}
R_UNLESS(arg_info != nullptr, ldr::ResultTooManyArguments());
R_UNLESS(arg_info != nullptr, ldr::ResultArgumentCountOverflow());
arg_info->program_id = program_id;
arg_info->args_size = args_size;

View file

@ -20,7 +20,6 @@
#include "ldr_process_creation.hpp"
#include "ldr_launch_record.hpp"
#include "ldr_loader_service.hpp"
#include "ldr_ro_manager.hpp"
namespace ams::ldr {
@ -64,7 +63,7 @@ namespace ams::ldr {
char path[fs::EntryNameLengthMax];
/* Get location and override status. */
R_TRY(ldr::ro::GetProgramLocationAndStatus(std::addressof(loc), std::addressof(override_status), id));
R_TRY(ldr::GetProgramLocationAndOverrideStatusFromPinId(std::addressof(loc), std::addressof(override_status), id));
if (loc.storage_id != static_cast<u8>(ncm::StorageId::None)) {
R_TRY(ResolveContentPath(path, loc));
@ -87,11 +86,11 @@ namespace ams::ldr {
}
Result LoaderService::PinProgram(sf::Out<PinId> out_id, const ncm::ProgramLocation &loc) {
return ldr::ro::PinProgram(out_id.GetPointer(), loc, cfg::OverrideStatus{});
return ldr::PinProgram(out_id.GetPointer(), loc, cfg::OverrideStatus{});
}
Result LoaderService::UnpinProgram(PinId id) {
return ldr::ro::UnpinProgram(id);
return ldr::UnpinProgram(id);
}
Result LoaderService::SetProgramArgumentsDeprecated(ncm::ProgramId program_id, const sf::InPointerBuffer &args, u32 args_size) {
@ -107,8 +106,9 @@ namespace ams::ldr {
}
Result LoaderService::GetProcessModuleInfo(sf::Out<u32> count, const sf::OutPointerArray<ModuleInfo> &out, os::ProcessId process_id) {
R_UNLESS(out.GetSize() <= std::numeric_limits<s32>::max(), ldr::ResultInvalidSize());
return ldr::ro::GetProcessModuleInfo(count.GetPointer(), out.GetPointer(), out.GetSize(), process_id);
*count = 0;
std::memset(out.GetPointer(), 0, out.GetSize() * sizeof(ldr::ModuleInfo));
return ldr::GetProcessModuleInfo(count.GetPointer(), out.GetPointer(), out.GetSize(), process_id);
}
Result LoaderService::SetEnabledProgramVerification(bool enabled) {
@ -138,7 +138,7 @@ namespace ams::ldr {
}
Result LoaderService::AtmospherePinProgram(sf::Out<PinId> out_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status) {
return ldr::ro::PinProgram(out_id.GetPointer(), loc, override_status);
return ldr::PinProgram(out_id.GetPointer(), loc, override_status);
}
}

View file

@ -147,7 +147,7 @@ namespace ams::ldr {
R_TRY(fs::GetFileSize(std::addressof(npdm_size), file));
/* Read data into cache buffer. */
R_UNLESS(npdm_size <= static_cast<s64>(MetaCacheBufferSize), ldr::ResultTooLargeMeta());
R_UNLESS(npdm_size <= static_cast<s64>(MetaCacheBufferSize), ldr::ResultMetaOverflow());
R_TRY(fs::ReadFile(file, 0, cache->buffer, npdm_size));
}

View file

@ -86,7 +86,7 @@ namespace ams::ldr {
AMS_ASSUME(ofs < sizeof(module_id));
AMS_ASSUME(str[1] != 0);
module_id.build_id[ofs] = (ParseNybble(str[0]) << 4) | (ParseNybble(str[1]) << 0);
module_id.data[ofs] = (ParseNybble(str[0]) << 4) | (ParseNybble(str[1]) << 0);
str += 2;
ofs++;
@ -112,21 +112,21 @@ namespace ams::ldr {
}
/* Apply IPS patches. */
void LocateAndApplyIpsPatchesToModule(const u8 *build_id, uintptr_t mapped_nso, size_t mapped_size) {
void LocateAndApplyIpsPatchesToModule(const u8 *module_id_data, uintptr_t mapped_nso, size_t mapped_size) {
if (!EnsureSdCardMounted()) {
return;
}
ro::ModuleId module_id;
std::memcpy(std::addressof(module_id.build_id), build_id, sizeof(module_id.build_id));
std::memcpy(std::addressof(module_id.data), module_id_data, sizeof(module_id.data));
ams::patcher::LocateAndApplyIpsPatchesToModule(LoaderSdMountName, NsoPatchesDirectory, NsoPatchesProtectedSize, NsoPatchesProtectedOffset, std::addressof(module_id), reinterpret_cast<u8 *>(mapped_nso), mapped_size);
}
/* Apply embedded patches. */
void ApplyEmbeddedPatchesToModule(const u8 *build_id, uintptr_t mapped_nso, size_t mapped_size) {
void ApplyEmbeddedPatchesToModule(const u8 *module_id_data, uintptr_t mapped_nso, size_t mapped_size) {
/* Make module id. */
ro::ModuleId module_id;
std::memcpy(std::addressof(module_id.build_id), build_id, sizeof(module_id.build_id));
std::memcpy(std::addressof(module_id.data), module_id_data, sizeof(module_id.data));
if (IsUsb30ForceEnabled()) {
for (const auto &patch : Usb30ForceEnablePatches) {

View file

@ -19,9 +19,9 @@
namespace ams::ldr {
/* Apply IPS patches. */
void LocateAndApplyIpsPatchesToModule(const u8 *build_id, uintptr_t mapped_nso, size_t mapped_size);
void LocateAndApplyIpsPatchesToModule(const u8 *module_id_data, uintptr_t mapped_nso, size_t mapped_size);
/* Apply embedded patches. */
void ApplyEmbeddedPatchesToModule(const u8 *build_id, uintptr_t mapped_nso, size_t mapped_size);
void ApplyEmbeddedPatchesToModule(const u8 *module_id_data, uintptr_t mapped_nso, size_t mapped_size);
}

View file

@ -591,10 +591,10 @@ namespace ams::ldr {
std::memset(reinterpret_cast<void *>(map_address + rw_end), 0, nso_header->bss_size);
/* Apply embedded patches. */
ApplyEmbeddedPatchesToModule(nso_header->build_id, map_address, nso_size);
ApplyEmbeddedPatchesToModule(nso_header->module_id, map_address, nso_size);
/* Apply IPS patches. */
LocateAndApplyIpsPatchesToModule(nso_header->build_id, map_address, nso_size);
LocateAndApplyIpsPatchesToModule(nso_header->module_id, map_address, nso_size);
}
/* Set permissions. */
@ -683,24 +683,32 @@ namespace ams::ldr {
ProcessInfo info;
R_TRY(CreateProcessImpl(std::addressof(info), std::addressof(meta), nso_headers, has_nso, arg_info, flags, reslimit_h));
/* Ensure we close the process handle, if we fail. */
ON_SCOPE_EXIT { os::CloseNativeHandle(info.process_handle); };
/* Load NSOs into process memory. */
R_TRY(LoadNsosIntoProcessMemory(std::addressof(info), nso_headers, has_nso, arg_info));
{
/* Ensure we close the process handle, if we fail. */
auto process_guard = SCOPE_GUARD { os::CloseNativeHandle(info.process_handle); };
/* Register NSOs with ro manager. */
/* Load all NSOs. */
R_TRY(LoadNsosIntoProcessMemory(std::addressof(info), nso_headers, has_nso, arg_info));
/* We don't need to close the process handle, since we succeeded. */
process_guard.Cancel();
}
/* Register NSOs with the RoManager. */
{
/* Nintendo doesn't validate this get, but we do. */
os::ProcessId process_id = os::GetProcessId(info.process_handle);
/* Register new process. */
ldr::ro::RegisterProcess(pin_id, process_id, loc.program_id);
/* NOTE: Nintendo uses meta->aci->program_id, not loc.program_id. Should we? */
const auto as_type = GetAddressSpaceType(std::addressof(meta));
RoManager::GetInstance().RegisterProcess(pin_id, process_id, loc.program_id, as_type == Npdm::AddressSpaceType_64Bit || as_type == Npdm::AddressSpaceType_64BitDeprecated);
/* Register all NSOs. */
for (size_t i = 0; i < Nso_Count; i++) {
if (has_nso[i]) {
ldr::ro::RegisterModule(pin_id, nso_headers[i].build_id, info.nso_address[i], info.nso_size[i]);
RoManager::GetInstance().AddNso(pin_id, nso_headers[i].module_id, info.nso_address[i], info.nso_size[i]);
}
}
}
@ -719,7 +727,6 @@ namespace ams::ldr {
/* Move the process handle to output. */
*out = info.process_handle;
info.process_handle = os::InvalidNativeHandle;
}
return ResultSuccess();
@ -741,4 +748,24 @@ namespace ams::ldr {
return GetProgramInfoFromMeta(out, std::addressof(meta));
}
Result PinProgram(PinId *out_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status) {
R_UNLESS(RoManager::GetInstance().Allocate(out_id, loc, override_status), ldr::ResultMaxProcess());
return ResultSuccess();
}
Result UnpinProgram(PinId id) {
R_UNLESS(RoManager::GetInstance().Free(id), ldr::ResultNotPinned());
return ResultSuccess();
}
Result GetProcessModuleInfo(u32 *out_count, ldr::ModuleInfo *out, size_t max_out_count, os::ProcessId process_id) {
R_UNLESS(RoManager::GetInstance().GetProcessModuleInfo(out_count, out, max_out_count, process_id), ldr::ResultNotPinned());
return ResultSuccess();
}
Result GetProgramLocationAndOverrideStatusFromPinId(ncm::ProgramLocation *out, cfg::OverrideStatus *out_status, PinId pin_id) {
R_UNLESS(RoManager::GetInstance().GetProgramLocationAndStatus(out, out_status, pin_id), ldr::ResultNotPinned());
return ResultSuccess();
}
}

View file

@ -22,4 +22,11 @@ namespace ams::ldr {
Result CreateProcess(os::NativeHandle *out, PinId pin_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status, const char *path, u32 flags, os::NativeHandle reslimit_h);
Result GetProgramInfo(ProgramInfo *out, cfg::OverrideStatus *out_status, const ncm::ProgramLocation &loc);
Result PinProgram(PinId *out_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status);
Result UnpinProgram(PinId id);
Result GetProcessModuleInfo(u32 *out_count, ldr::ModuleInfo *out, size_t max_out_count, os::ProcessId process_id);
Result GetProgramLocationAndOverrideStatusFromPinId(ncm::ProgramLocation *out, cfg::OverrideStatus *out_status, PinId pin_id);
}

View file

@ -16,147 +16,166 @@
#include <stratosphere.hpp>
#include "ldr_ro_manager.hpp"
namespace ams::ldr::ro {
namespace ams::ldr {
namespace {
/* Convenience definitions. */
constexpr PinId InvalidPinId = {};
constexpr size_t ProcessCountMax = 0x40;
constexpr size_t ModuleCountMax = 0x20;
/* Types. */
struct ModuleInfo {
ldr::ModuleInfo info;
bool in_use;
};
struct ProcessInfo {
PinId pin_id;
os::ProcessId process_id;
ncm::ProgramId program_id;
cfg::OverrideStatus override_status;
ncm::ProgramLocation loc;
ModuleInfo modules[ModuleCountMax];
bool in_use;
};
/* Globals. */
ProcessInfo g_process_infos[ProcessCountMax];
u64 g_cur_pin_id = 1;
/* Helpers. */
ProcessInfo *GetProcessInfo(PinId pin_id) {
for (size_t i = 0; i < ProcessCountMax; i++) {
ProcessInfo *info = g_process_infos + i;
if (info->in_use && info->pin_id == pin_id) {
return info;
}
}
return nullptr;
}
ProcessInfo *GetProcessInfo(os::ProcessId process_id) {
for (size_t i = 0; i < ProcessCountMax; i++) {
ProcessInfo *info = g_process_infos + i;
if (info->in_use && info->process_id == process_id) {
return info;
}
}
return nullptr;
}
ProcessInfo *GetFreeProcessInfo() {
for (size_t i = 0; i < ProcessCountMax; i++) {
ProcessInfo *info = g_process_infos + i;
if (!info->in_use) {
return info;
}
}
return nullptr;
}
}
/* RO Manager API. */
Result PinProgram(PinId *out, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &status) {
bool RoManager::Allocate(PinId *out, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &status) {
/* Ensure that output pin id is set. */
*out = InvalidPinId;
ProcessInfo *info = GetFreeProcessInfo();
R_UNLESS(info != nullptr, ldr::ResultTooManyProcesses());
std::memset(info, 0, sizeof(*info));
info->pin_id = { g_cur_pin_id++ };
info->loc = loc;
info->override_status = status;
info->in_use = true;
*out = info->pin_id;
return ResultSuccess();
}
Result UnpinProgram(PinId id) {
ProcessInfo *info = GetProcessInfo(id);
R_UNLESS(info != nullptr, ldr::ResultNotPinned());
info->in_use = false;
return ResultSuccess();
}
Result GetProgramLocationAndStatus(ncm::ProgramLocation *out, cfg::OverrideStatus *out_status, PinId id) {
ProcessInfo *info = GetProcessInfo(id);
R_UNLESS(info != nullptr, ldr::ResultNotPinned());
*out = info->loc;
*out_status = info->override_status;
return ResultSuccess();
}
Result RegisterProcess(PinId id, os::ProcessId process_id, ncm::ProgramId program_id) {
ProcessInfo *info = GetProcessInfo(id);
R_UNLESS(info != nullptr, ldr::ResultNotPinned());
info->program_id = program_id;
info->process_id = process_id;
return ResultSuccess();
}
Result RegisterModule(PinId id, const u8 *build_id, uintptr_t address, size_t size) {
ProcessInfo *info = GetProcessInfo(id);
R_UNLESS(info != nullptr, ldr::ResultNotPinned());
/* Nintendo doesn't actually care about successful allocation. */
for (size_t i = 0; i < ModuleCountMax; i++) {
ModuleInfo *module = info->modules + i;
if (module->in_use) {
continue;
}
std::memcpy(module->info.build_id, build_id, sizeof(module->info.build_id));
module->info.base_address = address;
module->info.size = size;
module->in_use = true;
break;
/* Allocate a process info. */
auto *found = this->AllocateProcessInfo();
if (found == nullptr) {
return false;
}
return ResultSuccess();
/* Setup the process info. */
std::memset(found, 0, sizeof(*found));
found->pin_id = { ++m_pin_id };
found->program_location = loc;
found->override_status = status;
found->in_use = true;
/* Set the output pin id. */
*out = found->pin_id;
return true;
}
Result GetProcessModuleInfo(u32 *out_count, ldr::ModuleInfo *out, size_t max_out_count, os::ProcessId process_id) {
const ProcessInfo *info = GetProcessInfo(process_id);
R_UNLESS(info != nullptr, ldr::ResultNotPinned());
bool RoManager::Free(PinId pin_id) {
/* Find the process. */
auto *found = this->FindProcessInfo(pin_id);
if (found == nullptr) {
return false;
}
/* Set the process as not in use. */
found->in_use = false;
/* Set all the process's nsos as not in use. */
for (auto i = 0; i < NsoCount; ++i) {
found->nso_infos[i].in_use = false;
}
return true;
}
void RoManager::RegisterProcess(PinId pin_id, os::ProcessId process_id, ncm::ProgramId program_id, bool is_64_bit_address_space) {
/* Find the process. */
auto *found = this->FindProcessInfo(pin_id);
if (found == nullptr) {
return;
}
/* Set the process id and program id. */
found->process_id = process_id;
found->program_id = program_id;
AMS_UNUSED(is_64_bit_address_space);
}
bool RoManager::GetProgramLocationAndStatus(ncm::ProgramLocation *out, cfg::OverrideStatus *out_status, PinId pin_id) {
/* Find the process. */
auto *found = this->FindProcessInfo(pin_id);
if (found == nullptr) {
return false;
}
/* Set the output location/status. */
*out = found->program_location;
*out_status = found->override_status;
return true;
}
void RoManager::AddNso(PinId pin_id, const u8 *module_id, u64 address, u64 size) {
/* Find the process. */
auto *found = this->FindProcessInfo(pin_id);
if (found == nullptr) {
return;
}
/* Allocate an nso. */
auto *info = this->AllocateNsoInfo(found);
if (info == nullptr) {
return;
}
/* Copy the information into the nso info. */
std::memcpy(info->module_info.module_id, module_id, sizeof(info->module_info.module_id));
info->module_info.address = address;
info->module_info.size = size;
info->in_use = true;
}
bool RoManager::GetProcessModuleInfo(u32 *out_count, ModuleInfo *out, size_t max_out_count, os::ProcessId process_id) {
/* Find the process. */
auto *found = this->FindProcessInfo(process_id);
if (found == nullptr) {
return false;
}
/* Copy allocated nso module infos. */
size_t count = 0;
for (size_t i = 0; i < ModuleCountMax && count < max_out_count; i++) {
const ModuleInfo *module = info->modules + i;
if (!module->in_use) {
for (auto i = 0; i < NsoCount && count < max_out_count; ++i) {
/* Skip unallocated nsos. */
if (!found->nso_infos[i].in_use) {
continue;
}
out[count++] = module->info;
/* Copy out the module info. */
out[count++] = found->nso_infos[i].module_info;
}
*out_count = static_cast<u32>(count);
return ResultSuccess();
/* Set the output count. */
*out_count = count;
return true;
}
RoManager::ProcessInfo *RoManager::AllocateProcessInfo() {
for (auto i = 0; i < ProcessCount; ++i) {
if (!m_processes[i].in_use) {
return m_processes + i;
}
}
return nullptr;
}
RoManager::ProcessInfo *RoManager::FindProcessInfo(PinId pin_id) {
for (auto i = 0; i < ProcessCount; ++i) {
if (m_processes[i].in_use && m_processes[i].pin_id == pin_id) {
return m_processes + i;
}
}
return nullptr;
}
RoManager::ProcessInfo *RoManager::FindProcessInfo(os::ProcessId process_id) {
for (auto i = 0; i < ProcessCount; ++i) {
if (m_processes[i].in_use && m_processes[i].process_id == process_id) {
return m_processes + i;
}
}
return nullptr;
}
RoManager::ProcessInfo *RoManager::FindProcessInfo(ncm::ProgramId program_id) {
for (auto i = 0; i < ProcessCount; ++i) {
if (m_processes[i].in_use && m_processes[i].program_id == program_id) {
return m_processes + i;
}
}
return nullptr;
}
RoManager::NsoInfo *RoManager::AllocateNsoInfo(ProcessInfo *info) {
for (auto i = 0; i < NsoCount; ++i) {
if (!info->nso_infos[i].in_use) {
return info->nso_infos + i;
}
}
return nullptr;
}
}

View file

@ -16,14 +16,50 @@
#pragma once
#include <stratosphere.hpp>
namespace ams::ldr::ro {
namespace ams::ldr {
/* RO Manager API. */
Result PinProgram(PinId *out, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &status);
Result UnpinProgram(PinId id);
Result GetProgramLocationAndStatus(ncm::ProgramLocation *out, cfg::OverrideStatus *out_status, PinId id);
Result RegisterProcess(PinId id, os::ProcessId process_id, ncm::ProgramId program_id);
Result RegisterModule(PinId id, const u8 *build_id, uintptr_t address, size_t size);
Result GetProcessModuleInfo(u32 *out_count, ModuleInfo *out, size_t max_out_count, os::ProcessId process_id);
class RoManager {
AMS_CONSTINIT_SINGLETON_TRAITS(RoManager);
public:
static constexpr PinId InvalidPinId = {};
static constexpr int ProcessCount = 0x40;
static constexpr int NsoCount = 0x20;
private:
struct NsoInfo {
bool in_use;
ldr::ModuleInfo module_info;
};
struct ProcessInfo {
bool in_use;
PinId pin_id;
os::ProcessId process_id;
ncm::ProgramId program_id;
cfg::OverrideStatus override_status;
ncm::ProgramLocation program_location;
NsoInfo nso_infos[NsoCount];
};
private:
ProcessInfo m_processes[ProcessCount];
u64 m_pin_id;
public:
bool Allocate(PinId *out, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &status);
bool Free(PinId pin_id);
void RegisterProcess(PinId pin_id, os::ProcessId process_id, ncm::ProgramId program_id, bool is_64_bit_address_space);
bool GetProgramLocationAndStatus(ncm::ProgramLocation *out, cfg::OverrideStatus *out_status, PinId pin_id);
void AddNso(PinId pin_id, const u8 *module_id, u64 address, u64 size);
bool GetProcessModuleInfo(u32 *out_count, ModuleInfo *out, size_t max_out_count, os::ProcessId process_id);
private:
ProcessInfo *AllocateProcessInfo();
ProcessInfo *FindProcessInfo(PinId pin_id);
ProcessInfo *FindProcessInfo(os::ProcessId process_id);
ProcessInfo *FindProcessInfo(ncm::ProgramId program_id);
NsoInfo *AllocateNsoInfo(ProcessInfo *info);
};
}

View file

@ -320,7 +320,7 @@ namespace ams::ro::impl {
m_nro_in_use[index] = in_use;
}
void GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count) const {
void GetProcessModuleInfo(u32 *out_count, ldr::ModuleInfo *out_infos, size_t max_out_count) const {
size_t count = 0;
for (size_t i = 0; i < MaxNroInfos && count < max_out_count; i++) {
@ -331,10 +331,11 @@ namespace ams::ro::impl {
const NroInfo *nro_info = m_nro_infos + i;
/* Just copy out the info. */
LoaderModuleInfo *out_info = std::addressof(out_infos[count++]);
memcpy(out_info->build_id, std::addressof(nro_info->module_id), sizeof(nro_info->module_id));
out_info->base_address = nro_info->base_address;
out_info->size = nro_info->nro_heap_size + nro_info->bss_heap_size;
auto &out_info = out_infos[count++];
std::memcpy(out_info.module_id, nro_info->module_id.data, sizeof(out_info.module_id));
out_info.address = nro_info->base_address;
out_info.size = nro_info->nro_heap_size + nro_info->bss_heap_size;
}
*out_count = static_cast<u32>(count);
@ -597,7 +598,7 @@ namespace ams::ro::impl {
}
/* Debug service implementations. */
Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, os::ProcessId process_id) {
Result GetProcessModuleInfo(u32 *out_count, ldr::ModuleInfo *out_infos, size_t max_out_count, os::ProcessId process_id) {
if (const ProcessContext *context = GetContextByProcessId(process_id); context != nullptr) {
context->GetProcessModuleInfo(out_count, out_infos, max_out_count);
}

View file

@ -41,6 +41,6 @@ namespace ams::ro::impl {
Result UnmapManualLoadModuleMemory(size_t context_id, u64 nro_address);
/* Debug service implementations. */
Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, os::ProcessId process_id);
Result GetProcessModuleInfo(u32 *out_count, ldr::ModuleInfo *out_infos, size_t max_out_count, os::ProcessId process_id);
}

View file

@ -19,7 +19,7 @@
namespace ams::ro {
Result DebugMonitorService::GetProcessModuleInfo(sf::Out<u32> out_count, const sf::OutArray<LoaderModuleInfo> &out_infos, os::ProcessId process_id) {
Result DebugMonitorService::GetProcessModuleInfo(sf::Out<u32> out_count, const sf::OutArray<ldr::ModuleInfo> &out_infos, os::ProcessId process_id) {
R_UNLESS(out_infos.GetSize() <= std::numeric_limits<s32>::max(), ro::ResultInvalidSize());
return impl::GetProcessModuleInfo(out_count.GetPointer(), out_infos.GetPointer(), out_infos.GetSize(), process_id);
}

View file

@ -20,7 +20,7 @@ namespace ams::ro {
class DebugMonitorService {
public:
Result GetProcessModuleInfo(sf::Out<u32> out_count, const sf::OutArray<LoaderModuleInfo> &out_infos, os::ProcessId process_id);
Result GetProcessModuleInfo(sf::Out<u32> out_count, const sf::OutArray<ldr::ModuleInfo> &out_infos, os::ProcessId process_id);
};
static_assert(ro::impl::IsIDebugMonitorInterface<DebugMonitorService>);