Support Linux and OSX on MemoryAlloc and CompareExchange128, some cleanup

This commit is contained in:
Gabriel 2019-02-17 00:01:49 -03:00
commit 9986eb0fa9
6 changed files with 163 additions and 39 deletions

View file

@ -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>

View file

@ -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);
}
} }
} }

View file

@ -23,22 +23,53 @@ 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)
{ {
0x53, // push rbx throw new PlatformNotSupportedException();
0x49, 0x8B, 0x00, // mov rax, [r8] }
0x49, 0x8B, 0x19, // mov rbx, [r9]
0x49, 0x89, 0xCA, // mov r10, rcx byte[] interlockedCompareExchange128Code;
0x49, 0x89, 0xD3, // mov r11, rdx
0x49, 0x8B, 0x49, 0x08, // mov rcx, [r9+8] if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
0x49, 0x8B, 0x50, 0x08, // mov rdx, [r8+8] {
0xF0, 0x49, 0x0F, 0xC7, 0x0B, // lock cmpxchg16b [r11] interlockedCompareExchange128Code = new byte[]
0x49, 0x89, 0x02, // mov [r10], rax {
0x4C, 0x89, 0xD0, // mov rax, r10 0x53, // push rbx
0x49, 0x89, 0x52, 0x08, // mov [r10+8], rdx 0x49, 0x8B, 0x00, // mov rax, [r8]
0x5B, // pop rbx 0x49, 0x8B, 0x19, // mov rbx, [r9]
0xC3 // ret 0x49, 0x89, 0xCA, // mov r10, rcx
}; 0x49, 0x89, 0xD3, // mov r11, rdx
0x49, 0x8B, 0x49, 0x08, // mov rcx, [r9+8]
0x49, 0x8B, 0x50, 0x08, // mov rdx, [r8+8]
0xF0, 0x49, 0x0F, 0xC7, 0x0B, // lock cmpxchg16b [r11]
0x49, 0x89, 0x02, // mov [r10], rax
0x4C, 0x89, 0xD0, // mov rax, r10
0x49, 0x89, 0x52, 0x08, // mov [r10+8], rdx
0x5B, // pop rbx
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;

View file

@ -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);

View 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;
}
}
}

View 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}\".") { }
}
}