File xenia_PR2230.patch of Package xenia

From 7f0a8b0085976b8956f754a99b31584ef3ef2beb Mon Sep 17 00:00:00 2001
From: guccigang420 <>
Date: Sat, 30 Sep 2023 21:56:15 +0200
Subject: [PATCH] Fixed mmap usage and (re)-implemented most parts of
 memory_posix.cc

---
 src/xenia/base/memory_posix.cc | 159 +++++++++++++++++++++++++++++++--
 1 file changed, 153 insertions(+), 6 deletions(-)

diff --git a/src/xenia/base/memory_posix.cc b/src/xenia/base/memory_posix.cc
index 2ff36a603d..a71890901d 100644
--- a/src/xenia/base/memory_posix.cc
+++ b/src/xenia/base/memory_posix.cc
@@ -13,6 +13,9 @@
 #include <sys/mman.h>
 #include <unistd.h>
 #include <cstddef>
+#include <fstream>
+#include <mutex>
+#include <sstream>
 
 #include "xenia/base/math.h"
 #include "xenia/base/platform.h"
@@ -79,14 +82,53 @@ uint32_t ToPosixProtectFlags(PageAccess access) {
   }
 }
 
+PageAccess ToXeniaProtectFlags(char* protection) {
+  if (protection[0] == 'r' && protection[1] == 'w' && protection[2] == 'x') {
+    return PageAccess::kExecuteReadWrite;
+  } else if (protection[0] == 'r' && protection[1] == '-' &&
+             protection[2] == 'x') {
+    return PageAccess::kExecuteReadOnly;
+  } else if (protection[0] == 'r' && protection[1] == 'w' &&
+             protection[2] == '-') {
+    return PageAccess::kReadWrite;
+  } else if (protection[0] == 'r' && protection[1] == '-' &&
+             protection[2] == '-') {
+    return PageAccess::kReadOnly;
+  } else {
+    return PageAccess::kNoAccess;
+  }
+}
+
 bool IsWritableExecutableMemorySupported() { return true; }
 
+struct MappedFileRange {
+  uintptr_t region_begin;
+  uintptr_t region_end;
+};
+
+std::vector<struct MappedFileRange> mapped_file_ranges;
+std::mutex g_mapped_file_ranges_mutex;
+
 void* AllocFixed(void* base_address, size_t length,
                  AllocationType allocation_type, PageAccess access) {
   // mmap does not support reserve / commit, so ignore allocation_type.
   uint32_t prot = ToPosixProtectFlags(access);
-  void* result = mmap(base_address, length, prot,
-                      MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
+  int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+
+  if (base_address != nullptr) {
+    bool should_protect = allocation_type == AllocationType::kCommit;
+    if (should_protect) {
+      if (Protect(base_address, length, access)){
+        return base_address;
+      } else {
+        return nullptr;
+      }
+    }
+    flags |= MAP_FIXED_NOREPLACE;
+  }
+
+  void* result = mmap(base_address, length, prot, flags, -1, 0);
+
   if (result == MAP_FAILED) {
     return nullptr;
   } else {
@@ -96,20 +138,97 @@ void* AllocFixed(void* base_address, size_t length,
 
 bool DeallocFixed(void* base_address, size_t length,
                   DeallocationType deallocation_type) {
-  return munmap(base_address, length) == 0;
+  const uintptr_t region_begin = (uintptr_t)base_address;
+  const uintptr_t region_end = (uintptr_t)base_address + length;
+
+  std::lock_guard<std::mutex> guard(g_mapped_file_ranges_mutex);
+  for (const auto& mapped_range : mapped_file_ranges) {
+    if (region_begin >= mapped_range.region_begin &&
+        region_end <= mapped_range.region_end) {
+          
+      switch(deallocation_type) {
+        case DeallocationType::kDecommit:
+          return Protect(base_address, length, PageAccess::kNoAccess);
+        case DeallocationType::kRelease:
+          assert_always("Error: Tried to release mapped memory!");
+        default:
+          assert_unhandled_case(deallocation_type);
+      }
+      
+    }
+  }
+
+  switch(deallocation_type) {
+    case DeallocationType::kDecommit:
+      return Protect(base_address, length, PageAccess::kNoAccess);
+    case DeallocationType::kRelease:
+      return munmap(base_address, length) == 0;
+    default:
+      assert_unhandled_case(deallocation_type);
+  }
 }
 
 bool Protect(void* base_address, size_t length, PageAccess access,
              PageAccess* out_old_access) {
-  // Linux does not have a syscall to query memory permissions.
-  assert_null(out_old_access);
+  if (out_old_access) {
+    size_t length_copy = length;
+    QueryProtect(base_address, length_copy, *out_old_access);
+  }
 
   uint32_t prot = ToPosixProtectFlags(access);
   return mprotect(base_address, length, prot) == 0;
 }
 
 bool QueryProtect(void* base_address, size_t& length, PageAccess& access_out) {
+// No generic POSIX solution exists. The Linux solution should work on all Linux
+// kernel based OS, including Android.
+#if XE_PLATFORM_LINUX
+  std::ifstream memory_maps;
+  memory_maps.open("/proc/self/maps", std::ios_base::in);
+  std::string maps_entry_string;
+
+  while (std::getline(memory_maps, maps_entry_string)) {
+    std::stringstream entry_stream(maps_entry_string);
+    uintptr_t map_region_begin, map_region_end;
+    char separator, protection[4];
+
+    entry_stream >> std::hex >> map_region_begin >> separator >>
+        map_region_end >> protection;
+
+    if (map_region_begin <= (uintptr_t)base_address &&
+        map_region_end > (uintptr_t)base_address) {
+      length = map_region_end - reinterpret_cast<uintptr_t>(base_address);
+
+      access_out = ToXeniaProtectFlags(protection);
+
+      // Look at the next consecutive mappings
+      while (std::getline(memory_maps, maps_entry_string)) {
+        std::stringstream next_entry_stream(maps_entry_string);
+        uintptr_t next_map_region_begin, next_map_region_end;
+        char next_protection[4];
+
+        next_entry_stream >> std::hex >> next_map_region_begin >> separator >>
+            next_map_region_end >> next_protection;
+        if (map_region_end == next_map_region_begin &&
+            access_out == ToXeniaProtectFlags(next_protection)) {
+          length =
+              next_map_region_end - reinterpret_cast<uintptr_t>(base_address);
+          continue;
+        } else {
+          break;
+        }
+      }
+
+      memory_maps.close();
+      return true;
+    }
+  }
+
+  memory_maps.close();
+  return false;
+#else
   return false;
+#endif
 }
 
 FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
@@ -178,12 +297,40 @@ void CloseFileMappingHandle(FileMappingHandle handle,
 void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
                   PageAccess access, size_t file_offset) {
   uint32_t prot = ToPosixProtectFlags(access);
-  return mmap64(base_address, length, prot, MAP_PRIVATE | MAP_ANONYMOUS, handle,
+
+  int flags = MAP_SHARED;
+  if (base_address != nullptr) {
+    flags |= MAP_FIXED_NOREPLACE;
+  }
+
+  void* result = mmap(base_address, length, prot, flags, handle,
                 file_offset);
+
+  if (result == MAP_FAILED) {
+    return nullptr;
+  } else {
+    std::lock_guard<std::mutex> guard(g_mapped_file_ranges_mutex);
+    mapped_file_ranges.push_back(
+        {(uintptr_t)result, (uintptr_t)result + length});
+    return result;
+  }
 }
 
 bool UnmapFileView(FileMappingHandle handle, void* base_address,
                    size_t length) {
+  std::lock_guard<std::mutex> guard(g_mapped_file_ranges_mutex);
+  for (auto mapped_range = mapped_file_ranges.begin();
+       mapped_range != mapped_file_ranges.end();) {
+    if (mapped_range->region_begin == (uintptr_t)base_address &&
+        mapped_range->region_end == (uintptr_t)base_address + length) {
+      mapped_file_ranges.erase(mapped_range);
+      return munmap(base_address, length) == 0;
+    } else {
+      mapped_range++;
+    }
+  }
+  // TODO: Implement partial file unmapping.
+  assert_always("Error: Partial unmapping of files not yet supported.");
   return munmap(base_address, length) == 0;
 }
 
openSUSE Build Service is sponsored by