diff options
| author | Merry <MerryMage@users.noreply.github.com> | 2018-09-01 15:24:05 +0100 |
|---|---|---|
| committer | gdkchan <gab.dark.100@gmail.com> | 2018-09-01 11:24:05 -0300 |
| commit | 326777ca4a68b38c7a5e44c76291f09f07ddcf2e (patch) | |
| tree | 4049b9229ed326c9bb809a93778e6377d51ce209 /Ryujinx.Tests.Unicorn | |
| parent | 42dc925c3da59bf8801b14779482ee5bd9c25dc0 (diff) | |
Ryujinx.Tests: Add unicorn to test framework (#389)
* Ryujinx.Tests: Add unicorn to test framework
* CpuTestSimdArithmetic: Comment out inaccurate results
Diffstat (limited to 'Ryujinx.Tests.Unicorn')
| -rw-r--r-- | Ryujinx.Tests.Unicorn/IndexedProperty.cs | 28 | ||||
| -rw-r--r-- | Ryujinx.Tests.Unicorn/MemoryPermission.cs | 13 | ||||
| -rw-r--r-- | Ryujinx.Tests.Unicorn/Native/ArmRegister.cs | 296 | ||||
| -rw-r--r-- | Ryujinx.Tests.Unicorn/Native/Interface.cs | 68 | ||||
| -rw-r--r-- | Ryujinx.Tests.Unicorn/Native/UnicornArch.cs | 16 | ||||
| -rw-r--r-- | Ryujinx.Tests.Unicorn/Native/UnicornMemoryRegion.cs | 13 | ||||
| -rw-r--r-- | Ryujinx.Tests.Unicorn/Native/UnicornMode.cs | 34 | ||||
| -rw-r--r-- | Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj | 18 | ||||
| -rw-r--r-- | Ryujinx.Tests.Unicorn/UnicornAArch64.cs | 315 | ||||
| -rw-r--r-- | Ryujinx.Tests.Unicorn/UnicornError.cs | 30 | ||||
| -rw-r--r-- | Ryujinx.Tests.Unicorn/UnicornException.cs | 23 | ||||
| -rw-r--r-- | Ryujinx.Tests.Unicorn/libs/README.md | 3 | ||||
| -rw-r--r-- | Ryujinx.Tests.Unicorn/libs/windows/unicorn.dll | bin | 0 -> 4588032 bytes |
13 files changed, 857 insertions, 0 deletions
diff --git a/Ryujinx.Tests.Unicorn/IndexedProperty.cs b/Ryujinx.Tests.Unicorn/IndexedProperty.cs new file mode 100644 index 00000000..946620c6 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/IndexedProperty.cs @@ -0,0 +1,28 @@ +using System; + +namespace Ryujinx.Tests.Unicorn +{ + public class IndexedProperty<TIndex, TValue> + { + readonly Action<TIndex, TValue> SetAction; + readonly Func<TIndex, TValue> GetFunc; + + public IndexedProperty(Func<TIndex, TValue> getFunc, Action<TIndex, TValue> setAction) + { + this.GetFunc = getFunc; + this.SetAction = setAction; + } + + public TValue this[TIndex i] + { + get + { + return GetFunc(i); + } + set + { + SetAction(i, value); + } + } + } +} diff --git a/Ryujinx.Tests.Unicorn/MemoryPermission.cs b/Ryujinx.Tests.Unicorn/MemoryPermission.cs new file mode 100644 index 00000000..d79b2add --- /dev/null +++ b/Ryujinx.Tests.Unicorn/MemoryPermission.cs @@ -0,0 +1,13 @@ +using System; + +namespace Ryujinx.Tests.Unicorn +{ + public enum MemoryPermission + { + NONE = 0, + READ = 1, + WRITE = 2, + EXEC = 4, + ALL = 7, + } +} diff --git a/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs b/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs new file mode 100644 index 00000000..3554480c --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs @@ -0,0 +1,296 @@ +using System; + +namespace Ryujinx.Tests.Unicorn.Native +{ + public enum ArmRegister + { + INVALID = 0, + + X29, + X30, + NZCV, + SP, + WSP, + WZR, + XZR, + B0, + B1, + B2, + B3, + B4, + B5, + B6, + B7, + B8, + B9, + B10, + B11, + B12, + B13, + B14, + B15, + B16, + B17, + B18, + B19, + B20, + B21, + B22, + B23, + B24, + B25, + B26, + B27, + B28, + B29, + B30, + B31, + D0, + D1, + D2, + D3, + D4, + D5, + D6, + D7, + D8, + D9, + D10, + D11, + D12, + D13, + D14, + D15, + D16, + D17, + D18, + D19, + D20, + D21, + D22, + D23, + D24, + D25, + D26, + D27, + D28, + D29, + D30, + D31, + H0, + H1, + H2, + H3, + H4, + H5, + H6, + H7, + H8, + H9, + H10, + H11, + H12, + H13, + H14, + H15, + H16, + H17, + H18, + H19, + H20, + H21, + H22, + H23, + H24, + H25, + H26, + H27, + H28, + H29, + H30, + H31, + Q0, + Q1, + Q2, + Q3, + Q4, + Q5, + Q6, + Q7, + Q8, + Q9, + Q10, + Q11, + Q12, + Q13, + Q14, + Q15, + Q16, + Q17, + Q18, + Q19, + Q20, + Q21, + Q22, + Q23, + Q24, + Q25, + Q26, + Q27, + Q28, + Q29, + Q30, + Q31, + S0, + S1, + S2, + S3, + S4, + S5, + S6, + S7, + S8, + S9, + S10, + S11, + S12, + S13, + S14, + S15, + S16, + S17, + S18, + S19, + S20, + S21, + S22, + S23, + S24, + S25, + S26, + S27, + S28, + S29, + S30, + S31, + W0, + W1, + W2, + W3, + W4, + W5, + W6, + W7, + W8, + W9, + W10, + W11, + W12, + W13, + W14, + W15, + W16, + W17, + W18, + W19, + W20, + W21, + W22, + W23, + W24, + W25, + W26, + W27, + W28, + W29, + W30, + X0, + X1, + X2, + X3, + X4, + X5, + X6, + X7, + X8, + X9, + X10, + X11, + X12, + X13, + X14, + X15, + X16, + X17, + X18, + X19, + X20, + X21, + X22, + X23, + X24, + X25, + X26, + X27, + X28, + + V0, + V1, + V2, + V3, + V4, + V5, + V6, + V7, + V8, + V9, + V10, + V11, + V12, + V13, + V14, + V15, + V16, + V17, + V18, + V19, + V20, + V21, + V22, + V23, + V24, + V25, + V26, + V27, + V28, + V29, + V30, + V31, + + //> pseudo registers + PC, // program counter register + + CPACR_EL1, + ESR, + + //> thread registers + TPIDR_EL0, + TPIDRRO_EL0, + TPIDR_EL1, + + PSTATE, // PSTATE pseudoregister + + //> floating point control and status registers + FPCR, + FPSR, + + ENDING, // <-- mark the end of the list of registers + + //> alias registers + + IP0 = X16, + IP1 = X17, + FP = X29, + LR = X30, + } +} diff --git a/Ryujinx.Tests.Unicorn/Native/Interface.cs b/Ryujinx.Tests.Unicorn/Native/Interface.cs new file mode 100644 index 00000000..a6563220 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/Interface.cs @@ -0,0 +1,68 @@ +using System; +using System.Runtime.InteropServices; +using Ryujinx.Tests.Unicorn; + +namespace Ryujinx.Tests.Unicorn.Native +{ + public class Interface + { + public static void Checked(UnicornError error) + { + if (error != UnicornError.UC_ERR_OK) + { + throw new UnicornException(error); + } + } + + public static void MarshalArrayOf<T>(IntPtr input, int length, out T[] output) + { + var size = Marshal.SizeOf(typeof(T)); + output = new T[length]; + + for (int i = 0; i < length; i++) + { + IntPtr item = new IntPtr(input.ToInt64() + i * size); + output[i] = Marshal.PtrToStructure<T>(item); + } + } + + [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] + public static extern uint uc_version(out uint major, out uint minor); + + [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] + public static extern UnicornError uc_open(uint arch, uint mode, out IntPtr uc); + + [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] + public static extern UnicornError uc_close(IntPtr uc); + + [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr uc_strerror(UnicornError err); + + [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] + public static extern UnicornError uc_reg_write(IntPtr uc, int regid, byte[] value); + + [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] + public static extern UnicornError uc_reg_read(IntPtr uc, int regid, byte[] value); + + [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] + public static extern UnicornError uc_mem_write(IntPtr uc, ulong address, byte[] bytes, ulong size); + + [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] + public static extern UnicornError uc_mem_read(IntPtr uc, ulong address, byte[] bytes, ulong size); + + [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] + public static extern UnicornError uc_emu_start(IntPtr uc, ulong begin, ulong until, ulong timeout, ulong count); + + [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] + public static extern UnicornError uc_mem_map(IntPtr uc, ulong address, ulong size, uint perms); + + [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] + public static extern UnicornError uc_mem_unmap(IntPtr uc, ulong address, ulong size); + + [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] + public static extern UnicornError uc_mem_protect(IntPtr uc, ulong address, ulong size, uint perms); + + [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] + public static extern UnicornError uc_mem_regions(IntPtr uc, out IntPtr regions, out uint count); + } +} diff --git a/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs b/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs new file mode 100644 index 00000000..be088366 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs @@ -0,0 +1,16 @@ +using System; + +namespace Ryujinx.Tests.Unicorn.Native +{ + public enum UnicornArch + { + UC_ARCH_ARM = 1, // ARM architecture (including Thumb, Thumb-2) + UC_ARCH_ARM64, // ARM-64, also called AArch64 + UC_ARCH_MIPS, // Mips architecture + UC_ARCH_X86, // X86 architecture (including x86 & x86-64) + UC_ARCH_PPC, // PowerPC architecture (currently unsupported) + UC_ARCH_SPARC, // Sparc architecture + UC_ARCH_M68K, // M68K architecture + UC_ARCH_MAX, + } +} diff --git a/Ryujinx.Tests.Unicorn/Native/UnicornMemoryRegion.cs b/Ryujinx.Tests.Unicorn/Native/UnicornMemoryRegion.cs new file mode 100644 index 00000000..7ee34a74 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/UnicornMemoryRegion.cs @@ -0,0 +1,13 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Tests.Unicorn.Native +{ + [StructLayout(LayoutKind.Sequential)] + public struct UnicornMemoryRegion + { + public UInt64 begin; // begin address of the region (inclusive) + public UInt64 end; // end address of the region (inclusive) + public UInt32 perms; // memory permissions of the region + } +} diff --git a/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs b/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs new file mode 100644 index 00000000..950583bd --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs @@ -0,0 +1,34 @@ +using System; + +namespace Ryujinx.Tests.Unicorn.Native +{ + public enum UnicornMode + { + UC_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode) + UC_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode + // arm / arm64 + UC_MODE_ARM = 0, // ARM mode + UC_MODE_THUMB = 1 << 4, // THUMB mode (including Thumb-2) + UC_MODE_MCLASS = 1 << 5, // ARM's Cortex-M series (currently unsupported) + UC_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM (currently unsupported) + // mips + UC_MODE_MICRO = 1 << 4, // MicroMips mode (currently unsupported) + UC_MODE_MIPS3 = 1 << 5, // Mips III ISA (currently unsupported) + UC_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA (currently unsupported) + UC_MODE_MIPS32 = 1 << 2, // Mips32 ISA + UC_MODE_MIPS64 = 1 << 3, // Mips64 ISA + // x86 / x64 + UC_MODE_16 = 1 << 1, // 16-bit mode + UC_MODE_32 = 1 << 2, // 32-bit mode + UC_MODE_64 = 1 << 3, // 64-bit mode + // ppc + UC_MODE_PPC32 = 1 << 2, // 32-bit mode (currently unsupported) + UC_MODE_PPC64 = 1 << 3, // 64-bit mode (currently unsupported) + UC_MODE_QPX = 1 << 4, // Quad Processing eXtensions mode (currently unsupported) + // sparc + UC_MODE_SPARC32 = 1 << 2, // 32-bit mode + UC_MODE_SPARC64 = 1 << 3, // 64-bit mode + UC_MODE_V9 = 1 << 4, // SparcV9 mode (currently unsupported) + // m68k + } +} diff --git a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj new file mode 100644 index 00000000..ee7c103d --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj @@ -0,0 +1,18 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netcoreapp2.1</TargetFramework> + <RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> + + <PropertyGroup> + <GenerateAssemblyInfo>false</GenerateAssemblyInfo> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" /> + <PackageReference Include="System.Runtime.Intrinsics.Experimental" Version="4.5.0-rc1" /> + </ItemGroup> + +</Project> diff --git a/Ryujinx.Tests.Unicorn/UnicornAArch64.cs b/Ryujinx.Tests.Unicorn/UnicornAArch64.cs new file mode 100644 index 00000000..1cd3671f --- /dev/null +++ b/Ryujinx.Tests.Unicorn/UnicornAArch64.cs @@ -0,0 +1,315 @@ +using System; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace Ryujinx.Tests.Unicorn +{ + public class UnicornAArch64 + { + internal readonly IntPtr uc; + + public IndexedProperty<int, ulong> X + { + get + { + return new IndexedProperty<int, ulong>( + (int i) => GetX(i), + (int i, ulong value) => SetX(i, value)); + } + } + + public IndexedProperty<int, Vector128<float>> Q + { + get + { + return new IndexedProperty<int, Vector128<float>>( + (int i) => GetQ(i), + (int i, Vector128<float> value) => SetQ(i, value)); + } + } + + public ulong LR + { + get { return GetRegister(Native.ArmRegister.LR); } + set { SetRegister(Native.ArmRegister.LR, value); } + } + + public ulong SP + { + get { return GetRegister(Native.ArmRegister.SP); } + set { SetRegister(Native.ArmRegister.SP, value); } + } + + public ulong PC + { + get { return GetRegister(Native.ArmRegister.PC); } + set { SetRegister(Native.ArmRegister.PC, value); } + } + + public uint Pstate + { + get { return (uint)GetRegister(Native.ArmRegister.PSTATE); } + set { SetRegister(Native.ArmRegister.PSTATE, (uint)value); } + } + + public int Fpcr + { + get { return (int)GetRegister(Native.ArmRegister.FPCR); } + set { SetRegister(Native.ArmRegister.FPCR, (uint)value); } + } + + public int Fpsr + { + get { return (int)GetRegister(Native.ArmRegister.FPSR); } + set { SetRegister(Native.ArmRegister.FPSR, (uint)value); } + } + + public bool OverflowFlag + { + get { return (Pstate & 0x10000000u) != 0; } + set { Pstate = (Pstate & ~0x10000000u) | (value ? 0x10000000u : 0u); } + } + + public bool CarryFlag + { + get { return (Pstate & 0x20000000u) != 0; } + set { Pstate = (Pstate & ~0x20000000u) | (value ? 0x20000000u : 0u); } + } + + public bool ZeroFlag + { + get { return (Pstate & 0x40000000u) != 0; } + set { Pstate = (Pstate & ~0x40000000u) | (value ? 0x40000000u : 0u); } + } + + public bool NegativeFlag + { + get { return (Pstate & 0x80000000u) != 0; } + set { Pstate = (Pstate & ~0x80000000u) | (value ? 0x80000000u : 0u); } + } + + public UnicornAArch64() + { + Native.Interface.Checked(Native.Interface.uc_open((uint)Native.UnicornArch.UC_ARCH_ARM64, (uint)Native.UnicornMode.UC_MODE_LITTLE_ENDIAN, out uc)); + SetRegister(Native.ArmRegister.CPACR_EL1, 0x00300000); + } + + ~UnicornAArch64() + { + Native.Interface.Checked(Native.Interface.uc_close(uc)); + } + + public void RunForCount(ulong count) + { + Native.Interface.Checked(Native.Interface.uc_emu_start(uc, this.PC, 0xFFFFFFFFFFFFFFFFu, 0, count)); + } + + public void Step() + { + RunForCount(1); + } + + internal static Native.ArmRegister[] X_registers = new Native.ArmRegister[31] + { + Native.ArmRegister.X0, + Native.ArmRegister.X1, + Native.ArmRegister.X2, + Native.ArmRegister.X3, + Native.ArmRegister.X4, + Native.ArmRegister.X5, + Native.ArmRegister.X6, + Native.ArmRegister.X7, + Native.ArmRegister.X8, + Native.ArmRegister.X9, + Native.ArmRegister.X10, + Native.ArmRegister.X11, + Native.ArmRegister.X12, + Native.ArmRegister.X13, + Native.ArmRegister.X14, + Native.ArmRegister.X15, + Native.ArmRegister.X16, + Native.ArmRegister.X17, + Native.ArmRegister.X18, + Native.ArmRegister.X19, + Native.ArmRegister.X20, + Native.ArmRegister.X21, + Native.ArmRegister.X22, + Native.ArmRegister.X23, + Native.ArmRegister.X24, + Native.ArmRegister.X25, + Native.ArmRegister.X26, + Native.ArmRegister.X27, + Native.ArmRegister.X28, + Native.ArmRegister.X29, + Native.ArmRegister.X30, + }; + + internal static Native.ArmRegister[] Q_registers = new Native.ArmRegister[32] + { + Native.ArmRegister.Q0, + Native.ArmRegister.Q1, + Native.ArmRegister.Q2, + Native.ArmRegister.Q3, + Native.ArmRegister.Q4, + Native.ArmRegister.Q5, + Native.ArmRegister.Q6, + Native.ArmRegister.Q7, + Native.ArmRegister.Q8, + Native.ArmRegister.Q9, + Native.ArmRegister.Q10, + Native.ArmRegister.Q11, + Native.ArmRegister.Q12, + Native.ArmRegister.Q13, + Native.ArmRegister.Q14, + Native.ArmRegister.Q15, + Native.ArmRegister.Q16, + Native.ArmRegister.Q17, + Native.ArmRegister.Q18, + Native.ArmRegister.Q19, + Native.ArmRegister.Q20, + Native.ArmRegister.Q21, + Native.ArmRegister.Q22, + Native.ArmRegister.Q23, + Native.ArmRegister.Q24, + Native.ArmRegister.Q25, + Native.ArmRegister.Q26, + Native.ArmRegister.Q27, + Native.ArmRegister.Q28, + Native.ArmRegister.Q29, + Native.ArmRegister.Q30, + Native.ArmRegister.Q31, + }; + + internal ulong GetRegister(Native.ArmRegister register) + { + byte[] value_bytes = new byte[8]; + Native.Interface.Checked(Native.Interface.uc_reg_read(uc, (int)register, value_bytes)); + return (ulong)BitConverter.ToInt64(value_bytes, 0); + } + + internal void SetRegister(Native.ArmRegister register, ulong value) + { + byte[] value_bytes = BitConverter.GetBytes(value); + Native.Interface.Checked(Native.Interface.uc_reg_write(uc, (int)register, value_bytes)); + } + + internal Vector128<float> GetVector(Native.ArmRegister register) + { + byte[] value_bytes = new byte[16]; + Native.Interface.Checked(Native.Interface.uc_reg_read(uc, (int)register, value_bytes)); + unsafe + { + fixed (byte* p = &value_bytes[0]) + { + return Sse.LoadVector128((float*)p); + } + } + } + + internal void SetVector(Native.ArmRegister register, Vector128<float> value) + { + byte[] value_bytes = new byte[16]; + unsafe + { + fixed (byte* p = &value_bytes[0]) + { + Sse.Store((float*)p, value); + } + } + Native.Interface.Checked(Native.Interface.uc_reg_write(uc, (int)register, value_bytes)); + } + + public ulong GetX(int index) + { + Contract.Requires(index <= 30, "invalid register"); + + return GetRegister(X_registers[index]); + } + + public void SetX(int index, ulong value) + { + Contract.Requires(index <= 30, "invalid register"); + + SetRegister(X_registers[index], value); + } + + public Vector128<float> GetQ(int index) + { + Contract.Requires(index <= 31, "invalid vector"); + + return GetVector(Q_registers[index]); + } + + public void SetQ(int index, Vector128<float> value) + { + Contract.Requires(index <= 31, "invalid vector"); + + SetVector(Q_registers[index], value); + } + + public byte[] MemoryRead(ulong address, ulong size) + { + byte[] value = new byte[size]; + Native.Interface.Checked(Native.Interface.uc_mem_read(uc, address, value, size)); + return value; + } + + public byte MemoryRead8 (ulong address) { return MemoryRead(address, 1)[0]; } + public UInt16 MemoryRead16(ulong address) { return (UInt16)BitConverter.ToInt16(MemoryRead(address, 2), 0); } + public UInt32 MemoryRead32(ulong address) { return (UInt32)BitConverter.ToInt32(MemoryRead(address, 4), 0); } + public UInt64 MemoryRead64(ulong address) { return (UInt64)BitConverter.ToInt64(MemoryRead(address, 8), 0); } + + public void MemoryWrite(ulong address, byte[] value) + { + Native.Interface.Checked(Native.Interface.uc_mem_write(uc, address, value, (ulong)value.Length)); + } + + public void MemoryWrite8 (ulong address, byte value) { MemoryWrite(address, new byte[]{value}); } + public void MemoryWrite16(ulong address, Int16 value) { MemoryWrite(address, BitConverter.GetBytes(value)); } + public void MemoryWrite16(ulong address, UInt16 value) { MemoryWrite(address, BitConverter.GetBytes(value)); } + public void MemoryWrite32(ulong address, Int32 value) { MemoryWrite(address, BitConverter.GetBytes(value)); } + public void MemoryWrite32(ulong address, UInt32 value) { MemoryWrite(address, BitConverter.GetBytes(value)); } + public void MemoryWrite64(ulong address, Int64 value) { MemoryWrite(address, BitConverter.GetBytes(value)); } + public void MemoryWrite64(ulong address, UInt64 value) { MemoryWrite(address, BitConverter.GetBytes(value)); } + + public void MemoryMap(ulong address, ulong size, MemoryPermission permissions) + { + Native.Interface.Checked(Native.Interface.uc_mem_map(uc, address, size, (uint)permissions)); + } + + public void MemoryUnmap(ulong address, ulong size) + { + Native.Interface.Checked(Native.Interface.uc_mem_unmap(uc, address, size)); + } + + public void MemoryProtect(ulong address, ulong size, MemoryPermission permissions) + { + Native.Interface.Checked(Native.Interface.uc_mem_protect(uc, address, size, (uint)permissions)); + } + + public void DumpMemoryInformation() + { + Native.Interface.Checked(Native.Interface.uc_mem_regions(uc, out IntPtr regions_raw, out uint length)); + Native.Interface.MarshalArrayOf<Native.UnicornMemoryRegion>(regions_raw, (int)length, out var regions); + foreach (var region in regions) + { + Console.WriteLine("region: begin {0:X16} end {1:X16} perms {2:X8}", region.begin, region.end, region.perms); + } + } + + public static bool IsAvailable() + { + try + { + Native.Interface.uc_version(out uint major, out uint minor); + return true; + } + catch (DllNotFoundException) + { + return false; + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Tests.Unicorn/UnicornError.cs b/Ryujinx.Tests.Unicorn/UnicornError.cs new file mode 100644 index 00000000..db56c615 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/UnicornError.cs @@ -0,0 +1,30 @@ +using System; + +namespace Ryujinx.Tests.Unicorn +{ + public enum UnicornError + { + UC_ERR_OK = 0, // No error: everything was fine + UC_ERR_NOMEM, // Out-Of-Memory error: uc_open(), uc_emulate() + UC_ERR_ARCH, // Unsupported architecture: uc_open() + UC_ERR_HANDLE, // Invalid handle + UC_ERR_MODE, // Invalid/unsupported mode: uc_open() + UC_ERR_VERSION, // Unsupported version (bindings) + UC_ERR_READ_UNMAPPED, // Quit emulation due to READ on unmapped memory: uc_emu_start() + UC_ERR_WRITE_UNMAPPED, // Quit emulation due to WRITE on unmapped memory: uc_emu_start() + UC_ERR_FETCH_UNMAPPED, // Quit emulation due to FETCH on unmapped memory: uc_emu_start() + UC_ERR_HOOK, // Invalid hook type: uc_hook_add() + UC_ERR_INSN_INVALID, // Quit emulation due to invalid instruction: uc_emu_start() + UC_ERR_MAP, // Invalid memory mapping: uc_mem_map() + UC_ERR_WRITE_PROT, // Quit emulation due to UC_MEM_WRITE_PROT violation: uc_emu_start() + UC_ERR_READ_PROT, // Quit emulation due to UC_MEM_READ_PROT violation: uc_emu_start() + UC_ERR_FETCH_PROT, // Quit emulation due to UC_MEM_FETCH_PROT violation: uc_emu_start() + UC_ERR_ARG, // Inavalid argument provided to uc_xxx function (See specific function API) + UC_ERR_READ_UNALIGNED, // Unaligned read + UC_ERR_WRITE_UNALIGNED, // Unaligned write + UC_ERR_FETCH_UNALIGNED, // Unaligned fetch + UC_ERR_HOOK_EXIST, // hook for this event already existed + UC_ERR_RESOURCE, // Insufficient resource: uc_emu_start() + UC_ERR_EXCEPTION // Unhandled CPU exception + } +} diff --git a/Ryujinx.Tests.Unicorn/UnicornException.cs b/Ryujinx.Tests.Unicorn/UnicornException.cs new file mode 100644 index 00000000..3cb69370 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/UnicornException.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Tests.Unicorn +{ + public class UnicornException : Exception + { + public readonly UnicornError Error; + + internal UnicornException(UnicornError error) + { + Error = error; + } + + public override string Message + { + get + { + return Marshal.PtrToStringAnsi(Native.Interface.uc_strerror(Error)); + } + } + } +} diff --git a/Ryujinx.Tests.Unicorn/libs/README.md b/Ryujinx.Tests.Unicorn/libs/README.md new file mode 100644 index 00000000..bb370164 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/libs/README.md @@ -0,0 +1,3 @@ +The pre-compiled dynamic libraries in this directory are licenced under the GPLv2. + +The source code for windows/unicorn.dll is available at: https://github.com/MerryMage/UnicornDotNet/tree/299451c02d9c810d2feca51f5e9cb6d8b2f38960 diff --git a/Ryujinx.Tests.Unicorn/libs/windows/unicorn.dll b/Ryujinx.Tests.Unicorn/libs/windows/unicorn.dll Binary files differnew file mode 100644 index 00000000..0e3cea58 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/libs/windows/unicorn.dll |
