From 9bacb883f111f9823277da085a65d2d1079b5a09 Mon Sep 17 00:00:00 2001 From: Spooks <62370103+Spooks4576@users.noreply.github.com> Date: Wed, 15 Jul 2026 19:34:35 -0600 Subject: [PATCH] Fix Linux aligned mapping retention (#247) * Fix Linux aligned mapping retention * Cover Linux aligned mapping retention --- .../Memory/PhysicalVirtualMemory.cs | 6 +- .../Memory/GuestMemoryAllocatorTests.cs | 83 +++++++++++++++++++ 2 files changed, 85 insertions(+), 4 deletions(-) diff --git a/src/SharpEmu.Core/Memory/PhysicalVirtualMemory.cs b/src/SharpEmu.Core/Memory/PhysicalVirtualMemory.cs index f069d8f1..4e88262e 100644 --- a/src/SharpEmu.Core/Memory/PhysicalVirtualMemory.cs +++ b/src/SharpEmu.Core/Memory/PhysicalVirtualMemory.cs @@ -349,10 +349,8 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA var requestedCursor = AlignUp(desiredAddress, effectiveAlignment); var cursor = GetAllocationSearchCursor(desiredAddress, requestedCursor, effectiveAlignment, executable); - // POSIX treats the requested address as a hint, and Rosetta may - // relocate whole guest windows. Over-allocate once so the returned - // host range always contains an aligned guest-visible start. - if (!OperatingSystem.IsWindows()) + // macOS needs alignment over-allocation; Linux uses exact-address search. + if (OperatingSystem.IsMacOS()) { var reserveSize = effectiveAlignment > PageSize ? alignedSize + effectiveAlignment diff --git a/tests/SharpEmu.Libs.Tests/Memory/GuestMemoryAllocatorTests.cs b/tests/SharpEmu.Libs.Tests/Memory/GuestMemoryAllocatorTests.cs index 23cd11d2..435fd207 100644 --- a/tests/SharpEmu.Libs.Tests/Memory/GuestMemoryAllocatorTests.cs +++ b/tests/SharpEmu.Libs.Tests/Memory/GuestMemoryAllocatorTests.cs @@ -95,6 +95,32 @@ public sealed class GuestMemoryAllocatorTests Assert.Equal(0UL, (ulong)memory.GetPointer(address)); } + [Fact] + public void AlignedAllocationDoesNotRetainOverallocatedMappingsOutsideMacOS() + { + if (OperatingSystem.IsMacOS()) + { + return; + } + + const ulong desiredAddress = 0x00005000_0000_0123; + const ulong alignment = 0x10000; + const ulong alignedAddress = 0x00005000_0001_0000; + const ulong allocationSize = 0x2000; + using var host = new RelocatingHostMemory(alignedAddress); + using var memory = new PhysicalVirtualMemory(host); + + Assert.True(memory.TryAllocateAtOrAbove(desiredAddress, 0x1234, false, alignment, out var actualAddress)); + Assert.Equal(alignedAddress + alignment, actualAddress); + Assert.Equal( + [ + (alignedAddress, allocationSize), + (alignedAddress + alignment, allocationSize), + ], + host.AllocationCalls); + Assert.Equal([alignedAddress + 0x1000], host.FreedAddresses); + } + private sealed class FakeHostMemory : IHostMemory { public ulong Allocate(ulong desiredAddress, ulong size, HostPageProtection protection) => @@ -197,6 +223,63 @@ public sealed class GuestMemoryAllocatorTests } } + private sealed class RelocatingHostMemory(ulong firstAddress) : IHostMemory, IDisposable + { + private bool _relocatedFirstAllocation; + + public List<(ulong Address, ulong Size)> AllocationCalls { get; } = []; + + public List FreedAddresses { get; } = []; + + public ulong Allocate(ulong desiredAddress, ulong size, HostPageProtection protection) + { + AllocationCalls.Add((desiredAddress, size)); + if (!_relocatedFirstAllocation) + { + _relocatedFirstAllocation = true; + return firstAddress + 0x1000; + } + + return desiredAddress; + } + + public ulong Reserve(ulong desiredAddress, ulong size, HostPageProtection protection) => 0; + + public bool Commit(ulong address, ulong size, HostPageProtection protection) => true; + + public bool Free(ulong address) + { + FreedAddresses.Add(address); + return true; + } + + public bool Protect(ulong address, ulong size, HostPageProtection protection, out uint rawOldProtection) + { + rawOldProtection = 0; + return true; + } + + public bool ProtectRaw(ulong address, ulong size, uint rawProtection, out uint rawOldProtection) + { + rawOldProtection = 0; + return true; + } + + public bool Query(ulong address, out HostRegionInfo info) + { + info = default; + return false; + } + + public void FlushInstructionCache(ulong address, ulong size) + { + } + + public void Dispose() + { + } + } + private sealed class LazyHostMemory(ulong address) : IHostMemory, IDisposable { public bool CommitSucceeds { get; set; } = true;