From 9749a43cc4f7b1374619035f057c383d292af5ed Mon Sep 17 00:00:00 2001
From: wwylele <wwylele@gmail.com>
Date: Tue, 5 Dec 2017 21:05:40 +0200
Subject: [PATCH] ldr_ro: create per-client storage; pass process where needed

---
 src/core/hle/service/ldr_ro/ldr_ro.cpp        | 114 ++++++++++--------
 src/core/hle/service/ldr_ro/ldr_ro.h          |   8 +-
 .../service/ldr_ro/memory_synchronizer.cpp    |   5 +-
 .../hle/service/ldr_ro/memory_synchronizer.h  |   6 +-
 4 files changed, 77 insertions(+), 56 deletions(-)

diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp
index bedbf2f54..5c8fedd1e 100644
--- a/src/core/hle/service/ldr_ro/ldr_ro.cpp
+++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp
@@ -40,9 +40,9 @@ static const ResultCode ERROR_NOT_LOADED = // 0xD8A12C0D
     ResultCode(static_cast<ErrorDescription>(13), ErrorModule::RO, ErrorSummary::InvalidState,
                ErrorLevel::Permanent);
 
-static bool VerifyBufferState(VAddr buffer_ptr, u32 size) {
-    auto vma = Kernel::g_current_process->vm_manager.FindVMA(buffer_ptr);
-    return vma != Kernel::g_current_process->vm_manager.vma_map.end() &&
+static bool VerifyBufferState(Kernel::Process& process, VAddr buffer_ptr, u32 size) {
+    auto vma = process.vm_manager.FindVMA(buffer_ptr);
+    return vma != process.vm_manager.vma_map.end() &&
            vma->second.base + vma->second.size >= buffer_ptr + size &&
            vma->second.permissions == Kernel::VMAPermission::ReadWrite &&
            vma->second.meminfo_state == Kernel::MemoryState::Private;
@@ -63,12 +63,15 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) {
 
     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 
-    if (loaded_crs != 0) {
+    auto insert_result = slots.insert({process->process_id, {}});
+    if (!insert_result.second) {
         LOG_ERROR(Service_LDR, "Already initialized");
         rb.Push(ERROR_ALREADY_INITIALIZED);
         return;
     }
 
+    auto& slot = insert_result.first->second;
+
     if (crs_size < CRO_HEADER_SIZE) {
         LOG_ERROR(Service_LDR, "CRS is too small");
         rb.Push(ERROR_BUFFER_TOO_SMALL);
@@ -93,7 +96,7 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) {
         return;
     }
 
-    if (!VerifyBufferState(crs_buffer_ptr, crs_size)) {
+    if (!VerifyBufferState(*process, crs_buffer_ptr, crs_size)) {
         LOG_ERROR(Service_LDR, "CRS original buffer is in invalid state");
         rb.Push(ERROR_INVALID_MEMORY_STATE);
         return;
@@ -112,7 +115,7 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) {
         // TODO(wwylele): should be memory aliasing
         std::shared_ptr<std::vector<u8>> crs_mem = std::make_shared<std::vector<u8>>(crs_size);
         Memory::ReadBlock(crs_buffer_ptr, crs_mem->data(), crs_size);
-        result = Kernel::g_current_process->vm_manager
+        result = process->vm_manager
                      .MapMemoryBlock(crs_address, crs_mem, 0, crs_size, Kernel::MemoryState::Code)
                      .Code();
         if (result.IsError()) {
@@ -121,15 +124,15 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) {
             return;
         }
 
-        result = Kernel::g_current_process->vm_manager.ReprotectRange(crs_address, crs_size,
-                                                                      Kernel::VMAPermission::Read);
+        result =
+            process->vm_manager.ReprotectRange(crs_address, crs_size, Kernel::VMAPermission::Read);
         if (result.IsError()) {
             LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
             rb.Push(result);
             return;
         }
 
-        memory_synchronizer.AddMemoryBlock(crs_address, crs_buffer_ptr, crs_size);
+        slot.memory_synchronizer.AddMemoryBlock(crs_address, crs_buffer_ptr, crs_size);
     } else {
         // Do nothing if buffer_ptr == address
         // TODO(wwylele): verify this behaviour. This is only seen in the web browser app,
@@ -148,9 +151,9 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) {
         return;
     }
 
-    memory_synchronizer.SynchronizeOriginalMemory();
+    slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
 
-    loaded_crs = crs_address;
+    slot.loaded_crs = crs_address;
 
     rb.Push(RESULT_SUCCESS);
 }
@@ -204,12 +207,14 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
 
     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
 
-    if (loaded_crs == 0) {
+    auto find_result = slots.find(process->process_id);
+    if (find_result == slots.end()) {
         LOG_ERROR(Service_LDR, "Not initialized");
         rb.Push(ERROR_NOT_INITIALIZED);
         rb.Push<u32>(0);
         return;
     }
+    auto& slot = find_result->second;
 
     if (cro_size < CRO_HEADER_SIZE) {
         LOG_ERROR(Service_LDR, "CRO too small");
@@ -239,7 +244,7 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
         return;
     }
 
-    if (!VerifyBufferState(cro_buffer_ptr, cro_size)) {
+    if (!VerifyBufferState(*process, cro_buffer_ptr, cro_size)) {
         LOG_ERROR(Service_LDR, "CRO original buffer is in invalid state");
         rb.Push(ERROR_INVALID_MEMORY_STATE);
         rb.Push<u32>(0);
@@ -268,7 +273,7 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
         // TODO(wwylele): should be memory aliasing
         std::shared_ptr<std::vector<u8>> cro_mem = std::make_shared<std::vector<u8>>(cro_size);
         Memory::ReadBlock(cro_buffer_ptr, cro_mem->data(), cro_size);
-        result = Kernel::g_current_process->vm_manager
+        result = process->vm_manager
                      .MapMemoryBlock(cro_address, cro_mem, 0, cro_size, Kernel::MemoryState::Code)
                      .Code();
         if (result.IsError()) {
@@ -278,17 +283,17 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
             return;
         }
 
-        result = Kernel::g_current_process->vm_manager.ReprotectRange(cro_address, cro_size,
-                                                                      Kernel::VMAPermission::Read);
+        result =
+            process->vm_manager.ReprotectRange(cro_address, cro_size, Kernel::VMAPermission::Read);
         if (result.IsError()) {
             LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
-            Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
+            process->vm_manager.UnmapRange(cro_address, cro_size);
             rb.Push(result);
             rb.Push<u32>(0);
             return;
         }
 
-        memory_synchronizer.AddMemoryBlock(cro_address, cro_buffer_ptr, cro_size);
+        slot.memory_synchronizer.AddMemoryBlock(cro_address, cro_buffer_ptr, cro_size);
     } else {
         // Do nothing if buffer_ptr == address
         // TODO(wwylele): verify this behaviour.
@@ -303,45 +308,44 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
     result = cro.VerifyHash(cro_size, crr_address);
     if (result.IsError()) {
         LOG_ERROR(Service_LDR, "Error verifying CRO in CRR %08X", result.raw);
-        Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
+        process->vm_manager.UnmapRange(cro_address, cro_size);
         rb.Push(result);
         rb.Push<u32>(0);
         return;
     }
 
-    result = cro.Rebase(loaded_crs, cro_size, data_segment_address, data_segment_size,
+    result = cro.Rebase(slot.loaded_crs, cro_size, data_segment_address, data_segment_size,
                         bss_segment_address, bss_segment_size, false);
     if (result.IsError()) {
         LOG_ERROR(Service_LDR, "Error rebasing CRO %08X", result.raw);
-        Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
+        process->vm_manager.UnmapRange(cro_address, cro_size);
         rb.Push(result);
         rb.Push<u32>(0);
         return;
     }
 
-    result = cro.Link(loaded_crs, link_on_load_bug_fix);
+    result = cro.Link(slot.loaded_crs, link_on_load_bug_fix);
     if (result.IsError()) {
         LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw);
-        Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
+        process->vm_manager.UnmapRange(cro_address, cro_size);
         rb.Push(result);
         rb.Push<u32>(0);
         return;
     }
 
-    cro.Register(loaded_crs, auto_link);
+    cro.Register(slot.loaded_crs, auto_link);
 
     u32 fix_size = cro.Fix(fix_level);
 
-    memory_synchronizer.SynchronizeOriginalMemory();
+    slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
 
     // TODO(wwylele): verify the behaviour when buffer_ptr == address
     if (cro_buffer_ptr != cro_address) {
         if (fix_size != cro_size) {
-            result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address + fix_size,
-                                                                      cro_size - fix_size);
+            result = process->vm_manager.UnmapRange(cro_address + fix_size, cro_size - fix_size);
             if (result.IsError()) {
                 LOG_ERROR(Service_LDR, "Error unmapping memory block %08X", result.raw);
-                Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
+                process->vm_manager.UnmapRange(cro_address, cro_size);
                 rb.Push(result);
                 rb.Push<u32>(0);
                 return;
@@ -349,18 +353,18 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
         }
 
         // Changes the block size
-        memory_synchronizer.ResizeMemoryBlock(cro_address, cro_buffer_ptr, fix_size);
+        slot.memory_synchronizer.ResizeMemoryBlock(cro_address, cro_buffer_ptr, fix_size);
     }
 
     VAddr exe_begin;
     u32 exe_size;
     std::tie(exe_begin, exe_size) = cro.GetExecutablePages();
     if (exe_begin) {
-        result = Kernel::g_current_process->vm_manager.ReprotectRange(
-            exe_begin, exe_size, Kernel::VMAPermission::ReadExecute);
+        result = process->vm_manager.ReprotectRange(exe_begin, exe_size,
+                                                    Kernel::VMAPermission::ReadExecute);
         if (result.IsError()) {
             LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
-            Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fix_size);
+            process->vm_manager.UnmapRange(cro_address, fix_size);
             rb.Push(result);
             rb.Push<u32>(0);
             return;
@@ -389,11 +393,13 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) {
 
     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 
-    if (loaded_crs == 0) {
+    auto find_result = slots.find(process->process_id);
+    if (find_result == slots.end()) {
         LOG_ERROR(Service_LDR, "Not initialized");
         rb.Push(ERROR_NOT_INITIALIZED);
         return;
     }
+    auto& slot = find_result->second;
 
     if (cro_address & Memory::PAGE_MASK) {
         LOG_ERROR(Service_LDR, "CRO address is not aligned");
@@ -411,9 +417,9 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) {
 
     u32 fixed_size = cro.GetFixedSize();
 
-    cro.Unregister(loaded_crs);
+    cro.Unregister(slot.loaded_crs);
 
-    ResultCode result = cro.Unlink(loaded_crs);
+    ResultCode result = cro.Unlink(slot.loaded_crs);
     if (result.IsError()) {
         LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw);
         rb.Push(result);
@@ -433,15 +439,15 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) {
 
     cro.Unrebase(false);
 
-    memory_synchronizer.SynchronizeOriginalMemory();
+    slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
 
     // TODO(wwylele): verify the behaviour when buffer_ptr == address
     if (cro_address != cro_buffer_ptr) {
-        result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fixed_size);
+        result = process->vm_manager.UnmapRange(cro_address, fixed_size);
         if (result.IsError()) {
             LOG_ERROR(Service_LDR, "Error unmapping CRO %08X", result.raw);
         }
-        memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr);
+        slot.memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr);
     }
 
     Core::CPU().InvalidateCacheRange(cro_address, fixed_size);
@@ -460,11 +466,13 @@ void RO::LinkCRO(Kernel::HLERequestContext& ctx) {
 
     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 
-    if (loaded_crs == 0) {
+    auto find_result = slots.find(process->process_id);
+    if (find_result == slots.end()) {
         LOG_ERROR(Service_LDR, "Not initialized");
         rb.Push(ERROR_NOT_INITIALIZED);
         return;
     }
+    auto& slot = find_result->second;
 
     if (cro_address & Memory::PAGE_MASK) {
         LOG_ERROR(Service_LDR, "CRO address is not aligned");
@@ -480,12 +488,12 @@ void RO::LinkCRO(Kernel::HLERequestContext& ctx) {
 
     LOG_INFO(Service_LDR, "Linking CRO \"%s\"", cro.ModuleName().data());
 
-    ResultCode result = cro.Link(loaded_crs, false);
+    ResultCode result = cro.Link(slot.loaded_crs, false);
     if (result.IsError()) {
         LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw);
     }
 
-    memory_synchronizer.SynchronizeOriginalMemory();
+    slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
 
     rb.Push(result);
 }
@@ -501,11 +509,13 @@ void RO::UnlinkCRO(Kernel::HLERequestContext& ctx) {
 
     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 
-    if (loaded_crs == 0) {
+    auto find_result = slots.find(process->process_id);
+    if (find_result == slots.end()) {
         LOG_ERROR(Service_LDR, "Not initialized");
         rb.Push(ERROR_NOT_INITIALIZED);
         return;
     }
+    auto& slot = find_result->second;
 
     if (cro_address & Memory::PAGE_MASK) {
         LOG_ERROR(Service_LDR, "CRO address is not aligned");
@@ -521,12 +531,12 @@ void RO::UnlinkCRO(Kernel::HLERequestContext& ctx) {
 
     LOG_INFO(Service_LDR, "Unlinking CRO \"%s\"", cro.ModuleName().data());
 
-    ResultCode result = cro.Unlink(loaded_crs);
+    ResultCode result = cro.Unlink(slot.loaded_crs);
     if (result.IsError()) {
         LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw);
     }
 
-    memory_synchronizer.SynchronizeOriginalMemory();
+    slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
 
     rb.Push(result);
 }
@@ -540,29 +550,31 @@ void RO::Shutdown(Kernel::HLERequestContext& ctx) {
 
     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 
-    if (loaded_crs == 0) {
+    auto find_result = slots.find(process->process_id);
+    if (find_result == slots.end()) {
         LOG_ERROR(Service_LDR, "Not initialized");
         rb.Push(ERROR_NOT_INITIALIZED);
         return;
     }
+    auto& slot = find_result->second;
 
-    CROHelper crs(loaded_crs);
+    CROHelper crs(slot.loaded_crs);
     crs.Unrebase(true);
 
-    memory_synchronizer.SynchronizeOriginalMemory();
+    slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
 
     ResultCode result = RESULT_SUCCESS;
 
     // TODO(wwylele): verify the behaviour when buffer_ptr == address
-    if (loaded_crs != crs_buffer_ptr) {
-        result = Kernel::g_current_process->vm_manager.UnmapRange(loaded_crs, crs.GetFileSize());
+    if (slot.loaded_crs != crs_buffer_ptr) {
+        result = process->vm_manager.UnmapRange(slot.loaded_crs, crs.GetFileSize());
         if (result.IsError()) {
             LOG_ERROR(Service_LDR, "Error unmapping CRS %08X", result.raw);
         }
-        memory_synchronizer.RemoveMemoryBlock(loaded_crs, crs_buffer_ptr);
+        slot.memory_synchronizer.RemoveMemoryBlock(slot.loaded_crs, crs_buffer_ptr);
     }
 
-    loaded_crs = 0;
+    slots.erase(find_result);
     rb.Push(result);
 }
 
diff --git a/src/core/hle/service/ldr_ro/ldr_ro.h b/src/core/hle/service/ldr_ro/ldr_ro.h
index f0aa53af5..b2be17dba 100644
--- a/src/core/hle/service/ldr_ro/ldr_ro.h
+++ b/src/core/hle/service/ldr_ro/ldr_ro.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <unordered_map>
 #include "core/hle/service/ldr_ro/memory_synchronizer.h"
 #include "core/hle/service/service.h"
 
@@ -144,8 +145,11 @@ private:
      */
     void Shutdown(Kernel::HLERequestContext& self);
 
-    MemorySynchronizer memory_synchronizer;
-    VAddr loaded_crs = 0; ///< the virtual address of the static module
+    struct ClientSlot {
+        MemorySynchronizer memory_synchronizer;
+        VAddr loaded_crs = 0; ///< the virtual address of the static module
+    };
+    std::unordered_map<u32, ClientSlot> slots;
 };
 
 void InstallInterfaces(SM::ServiceManager& service_manager);
diff --git a/src/core/hle/service/ldr_ro/memory_synchronizer.cpp b/src/core/hle/service/ldr_ro/memory_synchronizer.cpp
index 0d44bf6bd..9fd44088a 100644
--- a/src/core/hle/service/ldr_ro/memory_synchronizer.cpp
+++ b/src/core/hle/service/ldr_ro/memory_synchronizer.cpp
@@ -4,6 +4,7 @@
 
 #include <algorithm>
 #include "common/assert.h"
+#include "core/hle/kernel/process.h"
 #include "core/hle/service/ldr_ro/memory_synchronizer.h"
 
 namespace Service {
@@ -32,9 +33,9 @@ void MemorySynchronizer::RemoveMemoryBlock(VAddr mapping, VAddr original) {
     memory_blocks.erase(FindMemoryBlock(mapping, original));
 }
 
-void MemorySynchronizer::SynchronizeOriginalMemory() {
+void MemorySynchronizer::SynchronizeOriginalMemory(Kernel::Process& process) {
     for (auto& block : memory_blocks) {
-        Memory::CopyBlock(block.original, block.mapping, block.size);
+        Memory::CopyBlock(process, block.original, block.mapping, block.size);
     }
 }
 
diff --git a/src/core/hle/service/ldr_ro/memory_synchronizer.h b/src/core/hle/service/ldr_ro/memory_synchronizer.h
index 438293a58..2634406ba 100644
--- a/src/core/hle/service/ldr_ro/memory_synchronizer.h
+++ b/src/core/hle/service/ldr_ro/memory_synchronizer.h
@@ -7,6 +7,10 @@
 #include <vector>
 #include "core/memory.h"
 
+namespace Kernel {
+class Process;
+}
+
 namespace Service {
 namespace LDR {
 
@@ -24,7 +28,7 @@ public:
     void ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size);
     void RemoveMemoryBlock(VAddr mapping, VAddr original);
 
-    void SynchronizeOriginalMemory();
+    void SynchronizeOriginalMemory(Kernel::Process& process);
 
 private:
     struct MemoryBlock {