Support Linux and OSX on MemoryAlloc and CompareExchange128, some cleanup
This commit is contained in:
parent
23e4a00fc4
commit
9986eb0fa9
6 changed files with 163 additions and 39 deletions
|
@ -14,6 +14,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||||
<PackageReference Include="System.Runtime.Intrinsics.Experimental" Version="4.5.0-rc1" />
|
<PackageReference Include="System.Runtime.Intrinsics.Experimental" Version="4.5.0-rc1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -93,9 +93,9 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
if (pair)
|
if (pair)
|
||||||
{
|
{
|
||||||
//Exclusive loads should be atomic, for pairwise loads, the need to
|
//Exclusive loads should be atomic. For pairwise loads, we need to
|
||||||
//read all the data at once. for a 32-bits pairwise load, we do a
|
//read all the data at once. For a 32-bits pairwise load, we do a
|
||||||
//simple 64-bits load, for 128-bits load, we need to call a special
|
//simple 64-bits load, for a 128-bits load, we need to call a special
|
||||||
//method to read 128-bits atomically.
|
//method to read 128-bits atomically.
|
||||||
if (op.Size == 2)
|
if (op.Size == 2)
|
||||||
{
|
{
|
||||||
|
@ -169,7 +169,7 @@ namespace ChocolArm64.Instructions
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//8, 16, 32 or 64-bits load, or 64-bits pair load.
|
//8, 16, 32 or 64-bits (non-pairwise) load.
|
||||||
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
|
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
|
||||||
context.EmitLdtmp();
|
context.EmitLdtmp();
|
||||||
|
|
||||||
|
@ -181,24 +181,6 @@ namespace ChocolArm64.Instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
context.EmitStintzr(op.Rt);
|
context.EmitStintzr(op.Rt);
|
||||||
|
|
||||||
if (pair)
|
|
||||||
{
|
|
||||||
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
|
|
||||||
context.EmitLdtmp();
|
|
||||||
context.EmitLdc_I8(1 << op.Size);
|
|
||||||
|
|
||||||
context.Emit(OpCodes.Add);
|
|
||||||
|
|
||||||
EmitReadZxCall(context, op.Size);
|
|
||||||
|
|
||||||
if (exclusive)
|
|
||||||
{
|
|
||||||
WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueHigh));
|
|
||||||
}
|
|
||||||
|
|
||||||
context.EmitStintzr(op.Rt2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,17 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
static CompareExchange128()
|
static CompareExchange128()
|
||||||
{
|
{
|
||||||
byte[] interlockedCompareExchange128Code = new byte[]
|
//TODO: Also check if cmpxchg16b is supported on cpu flags.
|
||||||
|
if (RuntimeInformation.OSArchitecture != Architecture.X64)
|
||||||
|
{
|
||||||
|
throw new PlatformNotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] interlockedCompareExchange128Code;
|
||||||
|
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
interlockedCompareExchange128Code = new byte[]
|
||||||
{
|
{
|
||||||
0x53, // push rbx
|
0x53, // push rbx
|
||||||
0x49, 0x8B, 0x00, // mov rax, [r8]
|
0x49, 0x8B, 0x00, // mov rax, [r8]
|
||||||
|
@ -39,6 +49,27 @@ namespace ChocolArm64.Memory
|
||||||
0x5B, // pop rbx
|
0x5B, // pop rbx
|
||||||
0xC3 // ret
|
0xC3 // ret
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
|
||||||
|
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
{
|
||||||
|
interlockedCompareExchange128Code = new byte[]
|
||||||
|
{
|
||||||
|
0x53, // push %rbx
|
||||||
|
0x49, 0x89, 0xd1, // mov %rdx,%r9
|
||||||
|
0x48, 0x89, 0xcb, // mov %rcx,%rbx
|
||||||
|
0x48, 0x89, 0xf0, // mov %rsi,%rax
|
||||||
|
0x4c, 0x89, 0xca, // mov %r9,%rdx
|
||||||
|
0x4c, 0x89, 0xc1, // mov %r8,%rcx
|
||||||
|
0xf0, 0x48, 0x0f, 0xc7, 0x0f, // lock cmpxchg16b (%rdi)
|
||||||
|
0x5b, // pop %rbx
|
||||||
|
0xc3 // retq
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new PlatformNotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
ulong codeLength = (ulong)interlockedCompareExchange128Code.Length;
|
ulong codeLength = (ulong)interlockedCompareExchange128Code.Length;
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,11 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
return MemoryAllocWindows.Allocate(sizeNint);
|
return MemoryAllocWindows.Allocate(sizeNint);
|
||||||
}
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
|
||||||
|
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
{
|
||||||
|
return MemoryAllocUnix.Allocate(size);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new PlatformNotSupportedException();
|
throw new PlatformNotSupportedException();
|
||||||
|
@ -30,24 +35,41 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
return MemoryAllocWindows.AllocateWriteTracked(sizeNint);
|
return MemoryAllocWindows.AllocateWriteTracked(sizeNint);
|
||||||
}
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
|
||||||
|
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
{
|
||||||
|
return MemoryAllocUnix.Allocate(size);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new PlatformNotSupportedException();
|
throw new PlatformNotSupportedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool Reprotect(IntPtr address, ulong size, MemoryProtection permission)
|
public static void Reprotect(IntPtr address, ulong size, MemoryProtection permission)
|
||||||
{
|
{
|
||||||
|
bool result;
|
||||||
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
IntPtr sizeNint = new IntPtr((long)size);
|
IntPtr sizeNint = new IntPtr((long)size);
|
||||||
|
|
||||||
return MemoryAllocWindows.Reprotect(address, sizeNint, permission);
|
result = MemoryAllocWindows.Reprotect(address, sizeNint, permission);
|
||||||
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
|
||||||
|
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
{
|
||||||
|
result = MemoryAllocUnix.Reprotect(address, size, permission);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new PlatformNotSupportedException();
|
throw new PlatformNotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
throw new MemoryProtectionException(permission);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool Free(IntPtr address)
|
public static bool Free(IntPtr address)
|
||||||
|
@ -56,6 +78,11 @@ namespace ChocolArm64.Memory
|
||||||
{
|
{
|
||||||
return MemoryAllocWindows.Free(address);
|
return MemoryAllocWindows.Free(address);
|
||||||
}
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
|
||||||
|
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
{
|
||||||
|
return MemoryAllocUnix.Free(address);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new PlatformNotSupportedException();
|
throw new PlatformNotSupportedException();
|
||||||
|
@ -69,6 +96,9 @@ namespace ChocolArm64.Memory
|
||||||
IntPtr[] addresses,
|
IntPtr[] addresses,
|
||||||
out ulong count)
|
out ulong count)
|
||||||
{
|
{
|
||||||
|
//This is only supported on windows, but returning
|
||||||
|
//false (failed) is also valid for platforms without
|
||||||
|
//write tracking support on the OS.
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
return MemoryAllocWindows.GetModifiedPages(address, size, addresses, out count);
|
return MemoryAllocWindows.GetModifiedPages(address, size, addresses, out count);
|
||||||
|
|
70
ChocolArm64/Memory/MemoryAllocUnix.cs
Normal file
70
ChocolArm64/Memory/MemoryAllocUnix.cs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
using Mono.Unix.Native;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ChocolArm64.Memory
|
||||||
|
{
|
||||||
|
static class MemoryAllocUnix
|
||||||
|
{
|
||||||
|
public static IntPtr Allocate(ulong size)
|
||||||
|
{
|
||||||
|
ulong pageSize = (ulong)Syscall.sysconf(SysconfName._SC_PAGESIZE);
|
||||||
|
|
||||||
|
const MmapProts prot = MmapProts.PROT_READ | MmapProts.PROT_WRITE;
|
||||||
|
|
||||||
|
const MmapFlags flags = MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANONYMOUS;
|
||||||
|
|
||||||
|
IntPtr ptr = Syscall.mmap(IntPtr.Zero, size + pageSize, prot, flags, -1, 0);
|
||||||
|
|
||||||
|
if (ptr == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
throw new OutOfMemoryException();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
ptr = new IntPtr(ptr.ToInt64() + (long)pageSize);
|
||||||
|
|
||||||
|
*((ulong*)ptr - 1) = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Reprotect(IntPtr address, ulong size, Memory.MemoryProtection protection)
|
||||||
|
{
|
||||||
|
MmapProts prot = GetProtection(protection);
|
||||||
|
|
||||||
|
return Syscall.mprotect(address, size, prot) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MmapProts GetProtection(Memory.MemoryProtection protection)
|
||||||
|
{
|
||||||
|
switch (protection)
|
||||||
|
{
|
||||||
|
case Memory.MemoryProtection.None: return MmapProts.PROT_NONE;
|
||||||
|
case Memory.MemoryProtection.Read: return MmapProts.PROT_READ;
|
||||||
|
case Memory.MemoryProtection.ReadAndWrite: return MmapProts.PROT_READ | MmapProts.PROT_WRITE;
|
||||||
|
case Memory.MemoryProtection.ReadAndExecute: return MmapProts.PROT_READ | MmapProts.PROT_EXEC;
|
||||||
|
case Memory.MemoryProtection.Execute: return MmapProts.PROT_EXEC;
|
||||||
|
|
||||||
|
default: throw new ArgumentException($"Invalid permission \"{protection}\".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Free(IntPtr address)
|
||||||
|
{
|
||||||
|
ulong pageSize = (ulong)Syscall.sysconf(SysconfName._SC_PAGESIZE);
|
||||||
|
|
||||||
|
ulong size;
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
size = *((ulong*)address - 1);
|
||||||
|
|
||||||
|
address = new IntPtr(address.ToInt64() - (long)pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Syscall.munmap(address, size + pageSize) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
ChocolArm64/Memory/MemoryProtectionException.cs
Normal file
10
ChocolArm64/Memory/MemoryProtectionException.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ChocolArm64.Memory
|
||||||
|
{
|
||||||
|
class MemoryProtectionException : Exception
|
||||||
|
{
|
||||||
|
public MemoryProtectionException(MemoryProtection protection) :
|
||||||
|
base($"Failed to set memory protection to \"{protection}\".") { }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue