From 70d67bb1152c2a9543f5c5b3d9037137081facfb Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 17 Apr 2022 18:51:36 -0700 Subject: [PATCH] loader: use os apis for interacting with process memory --- .../include/stratosphere/os.hpp | 1 + .../stratosphere/os/os_memory_common.hpp | 10 +- .../stratosphere/os/os_process_memory_api.hpp | 28 +++ .../os/impl/os_aslr_space_manager_types.hpp | 10 +- .../os/impl/os_io_region_impl.os.horizon.cpp | 6 +- .../source/os/impl/os_process_memory_impl.hpp | 29 +++ .../os_process_memory_impl.os.horizon.cpp | 74 ++++++++ .../impl/os_shared_memory_impl.os.horizon.cpp | 6 +- .../os_transfer_memory_impl.os.horizon.cpp | 6 +- .../source/os/os_process_memory.cpp | 33 ++++ stratosphere/loader/source/ldr_auto_close.hpp | 54 ------ .../loader/source/ldr_process_creation.cpp | 169 ++++++------------ 12 files changed, 240 insertions(+), 186 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/os/os_process_memory_api.hpp create mode 100644 libraries/libstratosphere/source/os/impl/os_process_memory_impl.hpp create mode 100644 libraries/libstratosphere/source/os/impl/os_process_memory_impl.os.horizon.cpp create mode 100644 libraries/libstratosphere/source/os/os_process_memory.cpp delete mode 100644 stratosphere/loader/source/ldr_auto_close.hpp diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp index 7133512cb..e43a4884d 100644 --- a/libraries/libstratosphere/include/stratosphere/os.hpp +++ b/libraries/libstratosphere/include/stratosphere/os.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp b/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp index 3668a2ab3..523d2d127 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp @@ -23,11 +23,13 @@ namespace ams::os { constexpr inline size_t MemoryBlockUnitSize = 0x200000; enum MemoryPermission { - MemoryPermission_None = (0 << 0), - MemoryPermission_ReadOnly = (1 << 0), - MemoryPermission_WriteOnly = (1 << 1), + MemoryPermission_None = (0 << 0), + MemoryPermission_ReadOnly = (1 << 0), + MemoryPermission_WriteOnly = (1 << 1), + MemoryPermission_ExecuteOnly = (1 << 2), - MemoryPermission_ReadWrite = MemoryPermission_ReadOnly | MemoryPermission_WriteOnly, + MemoryPermission_ReadWrite = MemoryPermission_ReadOnly | MemoryPermission_WriteOnly, + MemoryPermission_ReadExecute = MemoryPermission_ReadOnly | MemoryPermission_ExecuteOnly, }; #if defined(ATMOSPHERE_OS_HORIZON) diff --git a/libraries/libstratosphere/include/stratosphere/os/os_process_memory_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_process_memory_api.hpp new file mode 100644 index 000000000..aea5f1a0c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_process_memory_api.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::os { + + Result MapProcessMemory(void **out, NativeHandle handle, u64 process_address, size_t process_size); + void UnmapProcessMemory(void *mapped_memory, NativeHandle handle, u64 process_address, size_t process_size); + + Result SetProcessMemoryPermission(NativeHandle handle, u64 process_address, u64 process_size, MemoryPermission perm); + +} diff --git a/libraries/libstratosphere/source/os/impl/os_aslr_space_manager_types.hpp b/libraries/libstratosphere/source/os/impl/os_aslr_space_manager_types.hpp index f4d6b0006..dc0517f6c 100644 --- a/libraries/libstratosphere/source/os/impl/os_aslr_space_manager_types.hpp +++ b/libraries/libstratosphere/source/os/impl/os_aslr_space_manager_types.hpp @@ -46,10 +46,10 @@ namespace ams::os::impl { /* ... */ } - uintptr_t AllocateSpace(size_t size) { + uintptr_t AllocateSpace(size_t size, size_t align_offset) { /* Try to allocate a large-aligned space, if we can. */ - if (size >= AslrSpaceLargeAlign) { - if (auto large_align = m_allocator.AllocateSpace(size, AslrSpaceLargeAlign, 0); large_align != 0) { + if (align_offset || size >= AslrSpaceLargeAlign) { + if (auto large_align = m_allocator.AllocateSpace(size, AslrSpaceLargeAlign, align_offset & (AslrSpaceLargeAlign - 1)); large_align != 0) { return large_align; } } @@ -63,11 +63,11 @@ namespace ams::os::impl { } template - Result MapAtRandomAddress(uintptr_t *out, size_t size, MapFunction map_function, UnmapFunction unmap_function) { + Result MapAtRandomAddress(uintptr_t *out, MapFunction map_function, UnmapFunction unmap_function, size_t size, size_t align_offset) { /* Try to map up to 64 times. */ for (int i = 0; i < 64; ++i) { /* Reserve space to map the memory. */ - const uintptr_t map_address = this->AllocateSpace(size); + const uintptr_t map_address = this->AllocateSpace(size, align_offset); if (map_address == 0) { break; } diff --git a/libraries/libstratosphere/source/os/impl/os_io_region_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_io_region_impl.os.horizon.cpp index ed5112d50..a798f8888 100644 --- a/libraries/libstratosphere/source/os/impl/os_io_region_impl.os.horizon.cpp +++ b/libraries/libstratosphere/source/os/impl/os_io_region_impl.os.horizon.cpp @@ -59,7 +59,7 @@ namespace ams::os::impl { /* Map at a random address. */ uintptr_t mapped_address; - R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address), size, + R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address), [handle, svc_perm](uintptr_t map_address, size_t map_size) -> Result { R_TRY_CATCH(svc::MapIoRegion(handle, map_address, map_size, svc_perm)) { /* TODO: What's the correct result for these? */ @@ -73,7 +73,9 @@ namespace ams::os::impl { }, [handle](uintptr_t map_address, size_t map_size) -> void { return IoRegionImpl::UnmapIoRegion(handle, reinterpret_cast(map_address), map_size); - } + }, + size, + 0 )); /* Return the address we mapped at. */ diff --git a/libraries/libstratosphere/source/os/impl/os_process_memory_impl.hpp b/libraries/libstratosphere/source/os/impl/os_process_memory_impl.hpp new file mode 100644 index 000000000..faf035fe8 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_process_memory_impl.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::os::impl { + + class ProcessMemoryImpl { + public: + static Result Map(void **out, NativeHandle handle, u64 process_address, size_t size); + static void Unmap(void *mapped_memory, NativeHandle handle, u64 process_address, size_t size); + + static Result SetMemoryPermission(NativeHandle handle, u64 process_address, u64 size, MemoryPermission perm); + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/os/impl/os_process_memory_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_process_memory_impl.os.horizon.cpp new file mode 100644 index 000000000..fc836391b --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_process_memory_impl.os.horizon.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_process_memory_impl.hpp" +#include "os_aslr_space_manager.hpp" + +namespace ams::os::impl { + + namespace { + + svc::MemoryPermission ConvertToSvcMemoryPermission(os::MemoryPermission perm) { + switch (perm) { + case os::MemoryPermission_None: return svc::MemoryPermission_None; + case os::MemoryPermission_ReadOnly: return svc::MemoryPermission_Read; + case os::MemoryPermission_ReadWrite: return svc::MemoryPermission_ReadWrite; + case os::MemoryPermission_ReadExecute: return svc::MemoryPermission_ReadExecute; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + } + + Result ProcessMemoryImpl::Map(void **out, NativeHandle handle, u64 process_address, size_t size) { + /* Map at a random address. */ + uintptr_t mapped_address; + R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address), + [handle, process_address](uintptr_t map_address, size_t map_size) -> Result { + R_TRY_CATCH(svc::MapProcessMemory(map_address, handle, process_address, map_size)) { + R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource()) + R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState()) + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + R_SUCCEED(); + }, + [handle, process_address](uintptr_t map_address, size_t map_size) -> void { + return ProcessMemoryImpl::Unmap(reinterpret_cast(map_address), handle, process_address, map_size); + }, + size, + process_address + )); + + /* Return the address we mapped at. */ + *out = reinterpret_cast(mapped_address); + R_SUCCEED(); + } + + void ProcessMemoryImpl::Unmap(void *mapped_memory, NativeHandle handle, u64 process_address, size_t size) { + R_ABORT_UNLESS(svc::UnmapProcessMemory(reinterpret_cast(mapped_memory), handle, process_address, size)); + } + + Result ProcessMemoryImpl::SetMemoryPermission(NativeHandle handle, u64 process_address, u64 size, MemoryPermission perm) { + /* Set the process memory permission. */ + R_TRY_CATCH(svc::SetProcessMemoryPermission(handle, process_address, size, ConvertToSvcMemoryPermission(perm))) { + R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource()) + R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState()) + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + R_SUCCEED(); + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_shared_memory_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_shared_memory_impl.os.horizon.cpp index 799ef001a..b336576f1 100644 --- a/libraries/libstratosphere/source/os/impl/os_shared_memory_impl.os.horizon.cpp +++ b/libraries/libstratosphere/source/os/impl/os_shared_memory_impl.os.horizon.cpp @@ -59,7 +59,7 @@ namespace ams::os::impl { /* Map at a random address. */ uintptr_t mapped_address; - R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address), size, + R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address), [handle, svc_perm](uintptr_t map_address, size_t map_size) -> Result { R_TRY_CATCH(svc::MapSharedMemory(handle, map_address, map_size, svc_perm)) { R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState()) @@ -69,7 +69,9 @@ namespace ams::os::impl { }, [handle](uintptr_t map_address, size_t map_size) -> void { return SharedMemoryImpl::Unmap(handle, reinterpret_cast(map_address), map_size); - } + }, + size, + 0 )); /* Return the address we mapped at. */ diff --git a/libraries/libstratosphere/source/os/impl/os_transfer_memory_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_transfer_memory_impl.os.horizon.cpp index 025d46d22..459c6942a 100644 --- a/libraries/libstratosphere/source/os/impl/os_transfer_memory_impl.os.horizon.cpp +++ b/libraries/libstratosphere/source/os/impl/os_transfer_memory_impl.os.horizon.cpp @@ -58,7 +58,7 @@ namespace ams::os::impl { /* Map at a random address. */ uintptr_t mapped_address; - R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address), size, + R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address), [handle, svc_owner_perm](uintptr_t map_address, size_t map_size) -> Result { R_TRY_CATCH(svc::MapTransferMemory(handle, map_address, map_size, svc_owner_perm)) { R_CONVERT(svc::ResultInvalidHandle, os::ResultInvalidHandle()) @@ -71,7 +71,9 @@ namespace ams::os::impl { }, [handle](uintptr_t map_address, size_t map_size) -> void { return TransferMemoryImpl::Unmap(handle, reinterpret_cast(map_address), map_size); - } + }, + size, + 0 )); /* Return the address we mapped at. */ diff --git a/libraries/libstratosphere/source/os/os_process_memory.cpp b/libraries/libstratosphere/source/os/os_process_memory.cpp new file mode 100644 index 000000000..65d023db6 --- /dev/null +++ b/libraries/libstratosphere/source/os/os_process_memory.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/os_process_memory_impl.hpp" + +namespace ams::os { + + Result MapProcessMemory(void **out, NativeHandle handle, u64 process_address, size_t process_size) { + R_RETURN(::ams::os::impl::ProcessMemoryImpl::Map(out, handle, process_address, process_size)); + } + + void UnmapProcessMemory(void *mapped_memory, NativeHandle handle, u64 process_address, size_t process_size) { + return ::ams::os::impl::ProcessMemoryImpl::Unmap(mapped_memory, handle, process_address, process_size); + } + + Result SetProcessMemoryPermission(NativeHandle handle, u64 process_address, u64 process_size, MemoryPermission perm) { + R_RETURN(::ams::os::impl::ProcessMemoryImpl::SetMemoryPermission(handle, process_address, process_size, perm)); + } + +} diff --git a/stratosphere/loader/source/ldr_auto_close.hpp b/stratosphere/loader/source/ldr_auto_close.hpp deleted file mode 100644 index 4c67004fc..000000000 --- a/stratosphere/loader/source/ldr_auto_close.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include - -namespace ams::ldr { - - class AutoCloseMap { - NON_COPYABLE(AutoCloseMap); - NON_MOVEABLE(AutoCloseMap); - private: - Result m_result; - uintptr_t m_map_address; - os::NativeHandle m_handle; - u64 m_address; - u64 m_size; - public: - AutoCloseMap(uintptr_t map, os::NativeHandle handle, u64 addr, u64 size) : m_map_address(map), m_handle(handle), m_address(addr), m_size(size) { - m_result = svc::MapProcessMemory(m_map_address, m_handle, m_address, m_size); - } - - ~AutoCloseMap() { - if (m_handle != os::InvalidNativeHandle && R_SUCCEEDED(m_result)) { - R_ABORT_UNLESS(svc::UnmapProcessMemory(m_map_address, m_handle, m_address, m_size)); - } - } - - Result GetResult() const { - return m_result; - } - - bool IsSuccess() const { - return R_SUCCEEDED(m_result); - } - - void Cancel() { - m_handle = os::InvalidNativeHandle; - } - }; - -} diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index 59eb46647..bf0f3fe79 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -14,7 +14,6 @@ * along with this program. If not, see . */ #include -#include "ldr_auto_close.hpp" #include "ldr_capabilities.hpp" #include "ldr_content_management.hpp" #include "ldr_development_manager.hpp" @@ -176,7 +175,7 @@ namespace ams::ldr { return static_cast((meta->acid->flags & Acid::AcidFlag_PoolPartitionMask) >> Acid::AcidFlag_PoolPartitionShift); } - Result LoadNsoHeaders(NsoHeader *nso_headers, bool *has_nso) { + Result LoadAutoLoadHeaders(NsoHeader *nso_headers, bool *has_nso) { /* Clear NSOs. */ std::memset(nso_headers, 0, sizeof(*nso_headers) * Nso_Count); std::memset(has_nso, 0, sizeof(*has_nso) * Nso_Count); @@ -198,7 +197,7 @@ namespace ams::ldr { R_SUCCEED(); } - Result ValidateNsoHeaders(const NsoHeader *nso_headers, const bool *has_nso) { + Result CheckAutoLoad(const NsoHeader *nso_headers, const bool *has_nso) { /* We must always have a main. */ R_UNLESS(has_nso[Nso_Main], ldr::ResultInvalidNso()); @@ -370,63 +369,6 @@ namespace ams::ldr { R_SUCCEED(); } - ALWAYS_INLINE u64 GetCurrentProcessInfo(svc::InfoType info_type) { - u64 value; - R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), info_type, svc::PseudoHandle::CurrentProcess, 0)); - return value; - } - - Result SearchFreeRegion(uintptr_t *out, size_t mapping_size) { - /* Get address space extents. */ - const uintptr_t heap_start = GetCurrentProcessInfo(svc::InfoType_HeapRegionAddress); - const size_t heap_size = GetCurrentProcessInfo(svc::InfoType_HeapRegionSize); - const uintptr_t alias_start = GetCurrentProcessInfo(svc::InfoType_AliasRegionAddress); - const size_t alias_size = GetCurrentProcessInfo(svc::InfoType_AliasRegionSize); - const uintptr_t aslr_start = GetCurrentProcessInfo(svc::InfoType_AslrRegionAddress); - const size_t aslr_size = GetCurrentProcessInfo(svc::InfoType_AslrRegionSize); - - /* Iterate upwards to find a free region. */ - uintptr_t address = aslr_start; - while (true) { - /* Declare variables for memory querying. */ - svc::MemoryInfo mem_info; - svc::PageInfo page_info; - - /* Check that we're still within bounds. */ - R_UNLESS(address < address + mapping_size, svc::ResultOutOfMemory()); - - /* If we're within the heap region, skip to the end of the heap region. */ - if (heap_size != 0 && !(address + mapping_size - 1 < heap_start || heap_start + heap_size - 1 < address)) { - R_UNLESS(address < heap_start + heap_size, svc::ResultOutOfMemory()); - address = heap_start + heap_size; - continue; - } - - /* If we're within the alias region, skip to the end of the alias region. */ - if (alias_size != 0 && !(address + mapping_size - 1 < alias_start || alias_start + alias_size - 1 < address)) { - R_UNLESS(address < alias_start + alias_size, svc::ResultOutOfMemory()); - address = alias_start + alias_size; - continue; - } - - /* Get the current memory range. */ - R_ABORT_UNLESS(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address)); - - /* If the memory range is free and big enough, use it. */ - if (mem_info.state == svc::MemoryState_Free && mapping_size <= ((mem_info.base_address + mem_info.size) - address)) { - *out = address; - R_SUCCEED(); - } - - /* Check that we can advance. */ - R_UNLESS(address < mem_info.base_address + mem_info.size, svc::ResultOutOfMemory()); - R_UNLESS(mem_info.base_address + mem_info.size - 1 < aslr_start + aslr_size - 1, svc::ResultOutOfMemory()); - - /* Advance. */ - address = mem_info.base_address + mem_info.size; - } - } - Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument) { /* Clear output. */ out->args_address = 0; @@ -517,25 +459,7 @@ namespace ams::ldr { R_SUCCEED(); } - Result CreateProcessImpl(ProcessInfo *out, const Meta *meta, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) { - /* Get CreateProcessParameter. */ - svc::CreateProcessParameter param; - R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit)); - - /* Decide on an NSO layout. */ - R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), nso_headers, has_nso, argument)); - - /* Actually create process. */ - svc::Handle process_handle; - R_TRY(svc::CreateProcess(std::addressof(process_handle), std::addressof(param), static_cast(meta->aci_kac), meta->aci->kac_size / sizeof(u32))); - - /* Set the output handle. */ - out->process_handle = process_handle; - - R_SUCCEED(); - } - - Result LoadNsoSegment(fs::FileHandle file, const NsoHeader::SegmentInfo *segment, size_t file_size, const u8 *file_hash, bool is_compressed, bool check_hash, uintptr_t map_base, uintptr_t map_end) { + Result LoadAutoLoadModuleSegment(fs::FileHandle file, const NsoHeader::SegmentInfo *segment, size_t file_size, const u8 *file_hash, bool is_compressed, bool check_hash, uintptr_t map_base, uintptr_t map_end) { /* Select read size based on compression. */ if (!is_compressed) { file_size = segment->size; @@ -568,25 +492,29 @@ namespace ams::ldr { R_SUCCEED(); } - Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, uintptr_t map_address, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) { + Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) { /* Map and read data from file. */ { - AutoCloseMap map(map_address, process_handle, nso_address, nso_size); - R_TRY(map.GetResult()); + /* Map the process memory. */ + void *mapped_memory = nullptr; + R_TRY(os::MapProcessMemory(std::addressof(mapped_memory), process_handle, nso_address, nso_size)); + ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, process_handle, nso_address, nso_size); }; + + const uintptr_t map_address = reinterpret_cast(mapped_memory); /* Load NSO segments. */ - R_TRY(LoadNsoSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Text]), nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0, - (nso_header->flags & NsoHeader::Flag_CheckHashText) != 0, map_address + nso_header->text_dst_offset, map_address + nso_size)); - R_TRY(LoadNsoSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Ro]), nso_header->ro_compressed_size, nso_header->ro_hash, (nso_header->flags & NsoHeader::Flag_CompressedRo) != 0, - (nso_header->flags & NsoHeader::Flag_CheckHashRo) != 0, map_address + nso_header->ro_dst_offset, map_address + nso_size)); - R_TRY(LoadNsoSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Rw]), nso_header->rw_compressed_size, nso_header->rw_hash, (nso_header->flags & NsoHeader::Flag_CompressedRw) != 0, - (nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size)); + R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Text]), nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0, + (nso_header->flags & NsoHeader::Flag_CheckHashText) != 0, map_address + nso_header->text_dst_offset, map_address + nso_size)); + R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Ro]), nso_header->ro_compressed_size, nso_header->ro_hash, (nso_header->flags & NsoHeader::Flag_CompressedRo) != 0, + (nso_header->flags & NsoHeader::Flag_CheckHashRo) != 0, map_address + nso_header->ro_dst_offset, map_address + nso_size)); + R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Rw]), nso_header->rw_compressed_size, nso_header->rw_hash, (nso_header->flags & NsoHeader::Flag_CompressedRw) != 0, + (nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size)); /* Clear unused space to zero. */ const size_t text_end = nso_header->text_dst_offset + nso_header->text_size; const size_t ro_end = nso_header->ro_dst_offset + nso_header->ro_size; const size_t rw_end = nso_header->rw_dst_offset + nso_header->rw_size; - std::memset(reinterpret_cast(map_address), 0, nso_header->text_dst_offset); + std::memset(reinterpret_cast(map_address + 0), 0, nso_header->text_dst_offset); std::memset(reinterpret_cast(map_address + text_end), 0, nso_header->ro_dst_offset - text_end); std::memset(reinterpret_cast(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end); std::memset(reinterpret_cast(map_address + rw_end), 0, nso_header->bss_size); @@ -603,13 +531,13 @@ namespace ams::ldr { const size_t ro_size = util::AlignUp(nso_header->ro_size, os::MemoryPageSize); const size_t rw_size = util::AlignUp(nso_header->rw_size + nso_header->bss_size, os::MemoryPageSize); if (text_size) { - R_TRY(svc::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, svc::MemoryPermission_ReadExecute)); + R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, os::MemoryPermission_ReadExecute)); } if (ro_size) { - R_TRY(svc::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, svc::MemoryPermission_Read)); + R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, os::MemoryPermission_ReadOnly)); } if (rw_size) { - R_TRY(svc::SetProcessMemoryPermission(process_handle, nso_address + nso_header->rw_dst_offset, rw_size, svc::MemoryPermission_ReadWrite)); + R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->rw_dst_offset, rw_size, os::MemoryPermission_ReadWrite)); } R_SUCCEED(); @@ -623,10 +551,7 @@ namespace ams::ldr { R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read)); ON_SCOPE_EXIT { fs::CloseFile(file); }; - uintptr_t map_address; - R_TRY(SearchFreeRegion(std::addressof(map_address), process_info->nso_size[i])); - - R_TRY(LoadAutoLoadModule(process_info->process_handle, file, map_address, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i])); + R_TRY(LoadAutoLoadModule(process_info->process_handle, file, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i])); } } @@ -634,13 +559,11 @@ namespace ams::ldr { if (argument != nullptr) { /* Write argument data into memory. */ { - uintptr_t map_address; - R_TRY(SearchFreeRegion(std::addressof(map_address), process_info->args_size)); + void *map_address = nullptr; + R_TRY(os::MapProcessMemory(std::addressof(map_address), process_info->process_handle, process_info->args_address, process_info->args_size)); + ON_SCOPE_EXIT { os::UnmapProcessMemory(map_address, process_info->process_handle, process_info->args_address, process_info->args_size); }; - AutoCloseMap map(map_address, process_info->process_handle, process_info->args_address, process_info->args_size); - R_TRY(map.GetResult()); - - ProgramArguments *args = reinterpret_cast(map_address); + ProgramArguments *args = static_cast(map_address); std::memset(args, 0, sizeof(*args)); args->allocated_size = process_info->args_size; args->arguments_size = argument->argument_size; @@ -648,12 +571,33 @@ namespace ams::ldr { } /* Set argument region permissions. */ - R_TRY(svc::SetProcessMemoryPermission(process_info->process_handle, process_info->args_address, process_info->args_size, svc::MemoryPermission_ReadWrite)); + /* NOTE: Nintendo uses svc::SetProcessMemoryPermission directly here. */ + R_TRY(os::SetProcessMemoryPermission(process_info->process_handle, process_info->args_address, process_info->args_size, os::MemoryPermission_ReadWrite)); } R_SUCCEED(); } + Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) { + /* Get CreateProcessParameter. */ + svc::CreateProcessParameter param; + R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit)); + + /* Decide on an NSO layout. */ + R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), nso_headers, has_nso, argument)); + + /* Actually create process. */ + svc::Handle process_handle; + R_TRY(svc::CreateProcess(std::addressof(process_handle), std::addressof(param), static_cast(meta->aci_kac), meta->aci->kac_size / sizeof(u32))); + + /* Set the output handle, and ensure that if we fail after this point we clean it up. */ + out->process_handle = process_handle; + ON_RESULT_FAILURE { svc::CloseHandle(process_handle); }; + + /* Load all auto load modules. */ + R_RETURN(LoadAutoLoadModules(out, nso_headers, has_nso, argument)); + } + } /* Process Creation API. */ @@ -670,22 +614,13 @@ namespace ams::ldr { /* Validate meta. */ R_TRY(ValidateMeta(std::addressof(meta), loc, mount.GetCodeVerificationData())); - /* Load, validate NSOs. */ - R_TRY(LoadNsoHeaders(g_nso_headers, g_has_nso)); - R_TRY(ValidateNsoHeaders(g_nso_headers, g_has_nso)); + /* Load, validate NSO headers. */ + R_TRY(LoadAutoLoadHeaders(g_nso_headers, g_has_nso)); + R_TRY(CheckAutoLoad(g_nso_headers, g_has_nso)); - /* Actually create process. */ + /* Actually create the process and load NSOs into process memory. */ ProcessInfo info; - R_TRY(CreateProcessImpl(std::addressof(info), std::addressof(meta), g_nso_headers, g_has_nso, argument, flags, resource_limit)); - - /* Load NSOs into process memory. */ - { - /* Ensure we close the process handle, if we fail. */ - ON_RESULT_FAILURE { os::CloseNativeHandle(info.process_handle); }; - - /* Load all NSOs. */ - R_TRY(LoadAutoLoadModules(std::addressof(info), g_nso_headers, g_has_nso, argument)); - } + R_TRY(CreateProcessAndLoadAutoLoadModules(std::addressof(info), std::addressof(meta), g_nso_headers, g_has_nso, argument, flags, resource_limit)); /* Register NSOs with the RoManager. */ {