From ef7b2237d9c2980fc8e1968ad68baafa6a41516e Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 23 Oct 2018 18:35:20 -0400 Subject: [PATCH 1/4] nro: Make LoadNro method accessible outside of apploader code. --- src/core/loader/nro.cpp | 21 +++++++++++++++------ src/core/loader/nro.h | 3 +++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 243b499f2c..bc8e402a80 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -127,18 +127,23 @@ static constexpr u32 PageAlignSize(u32 size) { return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; } -bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { - // Read NSO header - NroHeader nro_header{}; - if (sizeof(NroHeader) != file.ReadObject(&nro_header)) { +/*static*/ bool AppLoader_NRO::LoadNro(const std::vector& data, const std::string& name, + VAddr load_base) { + + if (data.size() < sizeof(NroHeader)) { return {}; } + + // Read NSO header + NroHeader nro_header{}; + std::memcpy(&nro_header, data.data(), sizeof(NroHeader)); if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { return {}; } // Build program image - std::vector program_image = file.ReadBytes(PageAlignSize(nro_header.file_size)); + std::vector program_image(PageAlignSize(nro_header.file_size)); + std::memcpy(program_image.data(), data.data(), program_image.size()); if (program_image.size() != PageAlignSize(nro_header.file_size)) { return {}; } @@ -182,11 +187,15 @@ bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); // Register module with GDBStub - GDBStub::RegisterModule(file.GetName(), load_base, load_base); + GDBStub::RegisterModule(name, load_base, load_base); return true; } +bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { + return AppLoader_NRO::LoadNro(file.ReadAllBytes(), file.GetName(), load_base); +} + ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { if (is_loaded) { return ResultStatus::ErrorAlreadyLoaded; diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 50ee5a78a8..3e69593024 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "common/common_types.h" #include "core/loader/linker.h" #include "core/loader/loader.h" @@ -40,6 +41,8 @@ public: ResultStatus ReadTitle(std::string& title) override; bool IsRomFSUpdatable() const override; + static bool LoadNro(const std::vector& data, const std::string& name, VAddr load_base); + private: bool LoadNro(const FileSys::VfsFile& file, VAddr load_base); From a609b6907a67e927d1dd0ade0135c004c9507fad Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 23 Oct 2018 18:39:10 -0400 Subject: [PATCH 2/4] Kernel/Memory: Added a function to first a suitable guest address at which to allocate a region of a given size. --- src/core/hle/kernel/vm_manager.cpp | 20 ++++++++++++++++++++ src/core/hle/kernel/vm_manager.h | 8 ++++++++ 2 files changed, 28 insertions(+) diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index e1a34eef1f..1a92c8f70a 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -143,6 +143,26 @@ ResultVal VMManager::MapBackingMemory(VAddr target, u8* me return MakeResult(MergeAdjacent(vma_handle)); } +ResultVal VMManager::FindFreeRegion(u64 size) const { + // Find the first Free VMA. + const VAddr base = GetASLRRegionBaseAddress(); + const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) { + if (vma.second.type != VMAType::Free) + return false; + + const VAddr vma_end = vma.second.base + vma.second.size; + return vma_end > base && vma_end >= base + size; + }); + + if (vma_handle == vma_map.end()) { + // TODO(Subv): Find the correct error code here. + return ResultCode(-1); + } + + const VAddr target = std::max(base, vma_handle->second.base); + return MakeResult(target); +} + ResultVal VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state, Memory::MemoryHookPointer mmio_handler) { diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 84c890224c..2447cbb8fc 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -157,6 +157,14 @@ public: */ ResultVal MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); + /** + * Finds the first free address that can hold a region of the desired size. + * + * @param size Size of the desired region. + * @return The found free address. + */ + ResultVal FindFreeRegion(u64 size) const; + /** * Maps a memory-mapped IO region at a given address. * From c2049aa4e5e7a77b0f1988e7082921b2e5083fa6 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 23 Oct 2018 18:42:15 -0400 Subject: [PATCH 3/4] process: LoadModule should clear JIT instruction cache. --- src/core/hle/kernel/process.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 073dd5a7d6..420218d59b 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -232,6 +232,12 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) { MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); + + // Clear instruction cache in CPU JIT + Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); } ResultVal Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { From cebce2a93adb1c24c3ff14c62f9c052d93886707 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 23 Oct 2018 18:52:01 -0400 Subject: [PATCH 4/4] ldr: Partially implement LoadNro. - This is an incomplete implementation. It was tested with Super Mario Party. --- src/core/hle/service/ldr/ldr.cpp | 52 ++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index ec32faf15b..d607d985ee 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -3,9 +3,13 @@ // Refer to the license.txt file included. #include +#include +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/ldr/ldr.h" #include "core/hle/service/service.h" +#include "core/loader/nro.h" namespace Service::LDR { @@ -59,16 +63,58 @@ public: explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "LoadNro"}, + {0, &RelocatableObject::LoadNro, "LoadNro"}, {1, nullptr, "UnloadNro"}, - {2, nullptr, "LoadNrr"}, + {2, &RelocatableObject::LoadNrr, "LoadNrr"}, {3, nullptr, "UnloadNrr"}, - {4, nullptr, "Initialize"}, + {4, &RelocatableObject::Initialize, "Initialize"}, }; // clang-format on RegisterHandlers(functions); } + + void LoadNrr(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_LDR, "(STUBBED) called"); + } + + void LoadNro(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + rp.Skip(2, false); + const VAddr nro_addr{rp.Pop()}; + const u64 nro_size{rp.Pop()}; + const VAddr bss_addr{rp.Pop()}; + const u64 bss_size{rp.Pop()}; + + // Read NRO data from memory + std::vector nro_data(nro_size); + Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); + + // Load NRO as new executable module + const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)}; + Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr); + + // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party. + // It is currently missing: + // - Signature checks with LoadNRR + // - Checking if a module has already been loaded + // - Using/validating BSS, etc. params (these are used from NRO header instead) + // - Error checking + // - ...Probably other things + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(addr); + LOG_WARNING(Service_LDR, "(STUBBED) called"); + } + + void Initialize(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_LDR, "(STUBBED) called"); + } }; void InstallInterfaces(SM::ServiceManager& sm) {