mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
loader: use os apis for interacting with process memory
This commit is contained in:
parent
9056e0b05f
commit
70d67bb115
12 changed files with 240 additions and 186 deletions
|
@ -25,6 +25,7 @@
|
|||
#include <stratosphere/os/os_virtual_address_memory.hpp>
|
||||
#include <stratosphere/os/os_native_handle.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_mutex.hpp>
|
||||
#include <stratosphere/os/os_condition_variable.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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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<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. */
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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<void *>(map_address), map_size);
|
||||
}
|
||||
},
|
||||
size,
|
||||
0
|
||||
));
|
||||
|
||||
/* Return the address we mapped at. */
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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<void *>(map_address), map_size);
|
||||
}
|
||||
},
|
||||
size,
|
||||
0
|
||||
));
|
||||
|
||||
/* Return the address we mapped at. */
|
||||
|
|
|
@ -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<void *>(map_address), map_size);
|
||||
}
|
||||
},
|
||||
size,
|
||||
0
|
||||
));
|
||||
|
||||
/* Return the address we mapped at. */
|
||||
|
|
33
libraries/libstratosphere/source/os/os_process_memory.cpp
Normal file
33
libraries/libstratosphere/source/os/os_process_memory.cpp
Normal 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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -14,7 +14,6 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#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<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. */
|
||||
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<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) {
|
||||
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<uintptr_t>(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<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 + ro_end), 0, nso_header->rw_dst_offset - ro_end);
|
||||
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 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<ProgramArguments *>(map_address);
|
||||
ProgramArguments *args = static_cast<ProgramArguments *>(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<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. */
|
||||
|
@ -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. */
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue