loader: use os apis for interacting with process memory

This commit is contained in:
Michael Scire 2022-04-17 18:51:36 -07:00
parent 9056e0b05f
commit 70d67bb115
12 changed files with 240 additions and 186 deletions

View file

@ -25,6 +25,7 @@
#include <stratosphere/os/os_virtual_address_memory.hpp> #include <stratosphere/os/os_virtual_address_memory.hpp>
#include <stratosphere/os/os_native_handle.hpp> #include <stratosphere/os/os_native_handle.hpp>
#include <stratosphere/os/os_process_handle_api.hpp> #include <stratosphere/os/os_process_handle_api.hpp>
#include <stratosphere/os/os_process_memory_api.hpp>
#include <stratosphere/os/os_random.hpp> #include <stratosphere/os/os_random.hpp>
#include <stratosphere/os/os_mutex.hpp> #include <stratosphere/os/os_mutex.hpp>
#include <stratosphere/os/os_condition_variable.hpp> #include <stratosphere/os/os_condition_variable.hpp>

View file

@ -23,11 +23,13 @@ namespace ams::os {
constexpr inline size_t MemoryBlockUnitSize = 0x200000; constexpr inline size_t MemoryBlockUnitSize = 0x200000;
enum MemoryPermission { enum MemoryPermission {
MemoryPermission_None = (0 << 0), MemoryPermission_None = (0 << 0),
MemoryPermission_ReadOnly = (1 << 0), MemoryPermission_ReadOnly = (1 << 0),
MemoryPermission_WriteOnly = (1 << 1), 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) #if defined(ATMOSPHERE_OS_HORIZON)

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/os/os_native_handle.hpp>
#include <stratosphere/os/os_memory_common.hpp>
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);
}

View file

@ -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. */ /* Try to allocate a large-aligned space, if we can. */
if (size >= AslrSpaceLargeAlign) { if (align_offset || size >= AslrSpaceLargeAlign) {
if (auto large_align = m_allocator.AllocateSpace(size, AslrSpaceLargeAlign, 0); large_align != 0) { if (auto large_align = m_allocator.AllocateSpace(size, AslrSpaceLargeAlign, align_offset & (AslrSpaceLargeAlign - 1)); large_align != 0) {
return large_align; return large_align;
} }
} }
@ -63,11 +63,11 @@ namespace ams::os::impl {
} }
template<typename MapFunction, typename UnmapFunction> template<typename MapFunction, typename UnmapFunction>
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. */ /* Try to map up to 64 times. */
for (int i = 0; i < 64; ++i) { for (int i = 0; i < 64; ++i) {
/* Reserve space to map the memory. */ /* 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) { if (map_address == 0) {
break; break;
} }

View file

@ -59,7 +59,7 @@ namespace ams::os::impl {
/* Map at a random address. */ /* Map at a random address. */
uintptr_t mapped_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 { [handle, svc_perm](uintptr_t map_address, size_t map_size) -> Result {
R_TRY_CATCH(svc::MapIoRegion(handle, map_address, map_size, svc_perm)) { R_TRY_CATCH(svc::MapIoRegion(handle, map_address, map_size, svc_perm)) {
/* TODO: What's the correct result for these? */ /* 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 { [handle](uintptr_t map_address, size_t map_size) -> void {
return IoRegionImpl::UnmapIoRegion(handle, reinterpret_cast<void *>(map_address), map_size); return IoRegionImpl::UnmapIoRegion(handle, reinterpret_cast<void *>(map_address), map_size);
} },
size,
0
)); ));
/* Return the address we mapped at. */ /* Return the address we mapped at. */

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
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);
};
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<void *>(map_address), handle, process_address, map_size);
},
size,
process_address
));
/* Return the address we mapped at. */
*out = reinterpret_cast<void *>(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<uintptr_t>(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();
}
}

View file

@ -59,7 +59,7 @@ namespace ams::os::impl {
/* Map at a random address. */ /* Map at a random address. */
uintptr_t mapped_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 { [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_TRY_CATCH(svc::MapSharedMemory(handle, map_address, map_size, svc_perm)) {
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState()) R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
@ -69,7 +69,9 @@ namespace ams::os::impl {
}, },
[handle](uintptr_t map_address, size_t map_size) -> void { [handle](uintptr_t map_address, size_t map_size) -> void {
return SharedMemoryImpl::Unmap(handle, reinterpret_cast<void *>(map_address), map_size); return SharedMemoryImpl::Unmap(handle, reinterpret_cast<void *>(map_address), map_size);
} },
size,
0
)); ));
/* Return the address we mapped at. */ /* Return the address we mapped at. */

View file

@ -58,7 +58,7 @@ namespace ams::os::impl {
/* Map at a random address. */ /* Map at a random address. */
uintptr_t mapped_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 { [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_TRY_CATCH(svc::MapTransferMemory(handle, map_address, map_size, svc_owner_perm)) {
R_CONVERT(svc::ResultInvalidHandle, os::ResultInvalidHandle()) R_CONVERT(svc::ResultInvalidHandle, os::ResultInvalidHandle())
@ -71,7 +71,9 @@ namespace ams::os::impl {
}, },
[handle](uintptr_t map_address, size_t map_size) -> void { [handle](uintptr_t map_address, size_t map_size) -> void {
return TransferMemoryImpl::Unmap(handle, reinterpret_cast<void *>(map_address), map_size); return TransferMemoryImpl::Unmap(handle, reinterpret_cast<void *>(map_address), map_size);
} },
size,
0
)); ));
/* Return the address we mapped at. */ /* Return the address we mapped at. */

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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));
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
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;
}
};
}

View file

@ -14,7 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <stratosphere.hpp> #include <stratosphere.hpp>
#include "ldr_auto_close.hpp"
#include "ldr_capabilities.hpp" #include "ldr_capabilities.hpp"
#include "ldr_content_management.hpp" #include "ldr_content_management.hpp"
#include "ldr_development_manager.hpp" #include "ldr_development_manager.hpp"
@ -176,7 +175,7 @@ namespace ams::ldr {
return static_cast<Acid::PoolPartition>((meta->acid->flags & Acid::AcidFlag_PoolPartitionMask) >> Acid::AcidFlag_PoolPartitionShift); return static_cast<Acid::PoolPartition>((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. */ /* Clear NSOs. */
std::memset(nso_headers, 0, sizeof(*nso_headers) * Nso_Count); std::memset(nso_headers, 0, sizeof(*nso_headers) * Nso_Count);
std::memset(has_nso, 0, sizeof(*has_nso) * Nso_Count); std::memset(has_nso, 0, sizeof(*has_nso) * Nso_Count);
@ -198,7 +197,7 @@ namespace ams::ldr {
R_SUCCEED(); 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. */ /* We must always have a main. */
R_UNLESS(has_nso[Nso_Main], ldr::ResultInvalidNso()); R_UNLESS(has_nso[Nso_Main], ldr::ResultInvalidNso());
@ -370,63 +369,6 @@ namespace ams::ldr {
R_SUCCEED(); 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) { Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument) {
/* Clear output. */ /* Clear output. */
out->args_address = 0; out->args_address = 0;
@ -517,25 +459,7 @@ namespace ams::ldr {
R_SUCCEED(); 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) { 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) {
/* 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<const u32 *>(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) {
/* Select read size based on compression. */ /* Select read size based on compression. */
if (!is_compressed) { if (!is_compressed) {
file_size = segment->size; file_size = segment->size;
@ -568,25 +492,29 @@ namespace ams::ldr {
R_SUCCEED(); 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. */ /* Map and read data from file. */
{ {
AutoCloseMap map(map_address, process_handle, nso_address, nso_size); /* Map the process memory. */
R_TRY(map.GetResult()); 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<uintptr_t>(mapped_memory);
/* Load NSO segments. */ /* 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, 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)); (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, 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)); (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, 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)); (nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size));
/* Clear unused space to zero. */ /* Clear unused space to zero. */
const size_t text_end = nso_header->text_dst_offset + nso_header->text_size; 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 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; const size_t rw_end = nso_header->rw_dst_offset + nso_header->rw_size;
std::memset(reinterpret_cast<void *>(map_address), 0, nso_header->text_dst_offset); std::memset(reinterpret_cast<void *>(map_address + 0), 0, nso_header->text_dst_offset);
std::memset(reinterpret_cast<void *>(map_address + text_end), 0, nso_header->ro_dst_offset - text_end); std::memset(reinterpret_cast<void *>(map_address + text_end), 0, nso_header->ro_dst_offset - text_end);
std::memset(reinterpret_cast<void *>(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end); std::memset(reinterpret_cast<void *>(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end);
std::memset(reinterpret_cast<void *>(map_address + rw_end), 0, nso_header->bss_size); std::memset(reinterpret_cast<void *>(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 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); const size_t rw_size = util::AlignUp(nso_header->rw_size + nso_header->bss_size, os::MemoryPageSize);
if (text_size) { 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) { 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) { 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(); R_SUCCEED();
@ -623,10 +551,7 @@ namespace ams::ldr {
R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read)); R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read));
ON_SCOPE_EXIT { fs::CloseFile(file); }; ON_SCOPE_EXIT { fs::CloseFile(file); };
uintptr_t map_address; R_TRY(LoadAutoLoadModule(process_info->process_handle, file, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i]));
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]));
} }
} }
@ -634,13 +559,11 @@ namespace ams::ldr {
if (argument != nullptr) { if (argument != nullptr) {
/* Write argument data into memory. */ /* Write argument data into memory. */
{ {
uintptr_t map_address; void *map_address = nullptr;
R_TRY(SearchFreeRegion(std::addressof(map_address), process_info->args_size)); 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); ProgramArguments *args = static_cast<ProgramArguments *>(map_address);
R_TRY(map.GetResult());
ProgramArguments *args = reinterpret_cast<ProgramArguments *>(map_address);
std::memset(args, 0, sizeof(*args)); std::memset(args, 0, sizeof(*args));
args->allocated_size = process_info->args_size; args->allocated_size = process_info->args_size;
args->arguments_size = argument->argument_size; args->arguments_size = argument->argument_size;
@ -648,12 +571,33 @@ namespace ams::ldr {
} }
/* Set argument region permissions. */ /* 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(); 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<const u32 *>(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. */ /* Process Creation API. */
@ -670,22 +614,13 @@ namespace ams::ldr {
/* Validate meta. */ /* Validate meta. */
R_TRY(ValidateMeta(std::addressof(meta), loc, mount.GetCodeVerificationData())); R_TRY(ValidateMeta(std::addressof(meta), loc, mount.GetCodeVerificationData()));
/* Load, validate NSOs. */ /* Load, validate NSO headers. */
R_TRY(LoadNsoHeaders(g_nso_headers, g_has_nso)); R_TRY(LoadAutoLoadHeaders(g_nso_headers, g_has_nso));
R_TRY(ValidateNsoHeaders(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; ProcessInfo info;
R_TRY(CreateProcessImpl(std::addressof(info), std::addressof(meta), g_nso_headers, g_has_nso, argument, flags, resource_limit)); R_TRY(CreateProcessAndLoadAutoLoadModules(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));
}
/* Register NSOs with the RoManager. */ /* Register NSOs with the RoManager. */
{ {