diff --git a/src/xenia/base/memory_posix.cc b/src/xenia/base/memory_posix.cc index 2ff36a603de..1f976b2d3d0 100644 --- a/src/xenia/base/memory_posix.cc +++ b/src/xenia/base/memory_posix.cc @@ -10,9 +10,11 @@ #include "xenia/base/memory.h" #include +#include #include #include #include +#include #include "xenia/base/math.h" #include "xenia/base/platform.h" @@ -81,12 +83,34 @@ uint32_t ToPosixProtectFlags(PageAccess access) { bool IsWritableExecutableMemorySupported() { return true; } +struct MappedFileRange { + size_t region_begin; + size_t region_end; +}; + +std::vector 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,7 +120,34 @@ 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 size_t region_begin = (size_t)base_address; + const size_t region_end = (size_t)base_address + length; + + std::lock_guard 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, @@ -178,12 +229,39 @@ 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 guard(g_mapped_file_ranges_mutex); + mapped_file_ranges.push_back({(size_t)result, (size_t)result + length}); + return result; + } } bool UnmapFileView(FileMappingHandle handle, void* base_address, size_t length) { + std::lock_guard 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 == (size_t)base_address && + mapped_range->region_end == (size_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; }