diff --git a/runtime/ishtar.vm/runtime/jit/ABI.cs b/runtime/ishtar.vm/runtime/jit/ABI.cs new file mode 100644 index 00000000..5d1be90b --- /dev/null +++ b/runtime/ishtar.vm/runtime/jit/ABI.cs @@ -0,0 +1,48 @@ +namespace ishtar; + +public class ABI(VirtualMachine vm) +{ + // ================== + // TODO, move to outside + // ================== + + private readonly Dictionary _cache = new (); + + public void LoadNativeLibrary(NativeImportEntity entity, CallFrame frame) + { + if (_cache.ContainsKey(entity.entry)) + return; + + var result = frame.vm.NativeStorage.TryLoad(new FileInfo(entity.entry), out var handle); + + if (!result) + { + frame.vm.FastFail(WNE.NATIVE_LIBRARY_COULD_NOT_LOAD, $"{entity.entry}", frame); + return; + } + + _cache[entity.entry] = new NativeImportCache(entity.entry, handle); + } + + public void LoadNativeSymbol(NativeImportEntity entity, CallFrame frame) + { + var cached = _cache[entity.entry]; + + if (cached.ImportedSymbols.ContainsKey(entity.fn)) + return; + + try + { + var symbol = frame.vm.NativeStorage.GetSymbol(cached.handle, entity.fn); + + cached.ImportedSymbols.Add(entity.fn, symbol); + entity.Handle = symbol; + } + catch + { + frame.vm.FastFail(WNE.NATIVE_LIBRARY_SYMBOL_COULD_NOT_FOUND, $"{entity.entry}::{entity.fn}", frame); + } + } + + +} diff --git a/runtime/ishtar.vm/runtime/jit/AssemblerArgumentConverter.cs b/runtime/ishtar.vm/runtime/jit/AssemblerArgumentConverter.cs new file mode 100644 index 00000000..b620e687 --- /dev/null +++ b/runtime/ishtar.vm/runtime/jit/AssemblerArgumentConverter.cs @@ -0,0 +1,131 @@ +namespace ishtar; + +using Iced.Intel; + +public class AssemblerArgumentConverter +{ + public static void GenerateAssemblerCode(List argumentInfos, + List argumentValues, nint nativeFunctionPtr, Assembler asm) + { + int numRegistersUsed = argumentInfos.Count(argInfo => argInfo.Instruction != x64_AssemblerStep.InstructionTarget.push); + + int stackSpaceNeeded = numRegistersUsed * 8; + + + asm.push(AssemblerRegisters.rbp); + asm.mov(AssemblerRegisters.rbp, AssemblerRegisters.rsp); + asm.sub(AssemblerRegisters.rsp, stackSpaceNeeded); + + int valueIndex = 0; + + foreach (var argInfo in argumentInfos) + { + if (argInfo.Instruction == x64_AssemblerStep.InstructionTarget.push) + asm.push(AssemblerRegisters.__[argInfo.StackOffset]); + else + { + if (argumentValues[valueIndex] is IntPtr ptr) + { + + asm.mov(new AssemblerRegister64(argInfo.Register), AssemblerRegisters.__[ptr]); + valueIndex++; + } + else + { + var val = argumentValues[valueIndex++]; + if (val is byte b) + asm.mov(new AssemblerRegister64(argInfo.Register), b); + if (val is short s) + asm.mov(new AssemblerRegister64(argInfo.Register), s); + if (val is int i) + asm.mov(new AssemblerRegister64(argInfo.Register), i); + if (val is long l) + asm.mov(new AssemblerRegister64(argInfo.Register), l); + //if (val is float f) + // asm.mov(xmm0, 12f); + if (val is char c) + asm.mov(new AssemblerRegister64(argInfo.Register), c); + if (val is bool bb) + asm.mov(new AssemblerRegister64(argInfo.Register), (byte)(bb ? 1 : 0)); + if (val is float or double) + throw new NotSupportedException(); + } + + asm.call(AssemblerRegisters.__[nativeFunctionPtr]); + asm.add(AssemblerRegisters.rsp, stackSpaceNeeded); + asm.pop(AssemblerRegisters.rbp); + asm.ret(); + } + } + + + } + + public static List ConvertArguments(List argumentTypes, List argumentValues) + { + Dictionary availableRegisters = new() + { + { typeof(byte), [AssemblerRegisters.dl, AssemblerRegisters.cl, AssemblerRegisters.r8, AssemblerRegisters.r9, AssemblerRegisters.r12, AssemblerRegisters.r13] }, + { typeof(short), [AssemblerRegisters.dx, AssemblerRegisters.cx, AssemblerRegisters.r8, AssemblerRegisters.r9, AssemblerRegisters.r12, AssemblerRegisters.r13] }, + { typeof(int), [AssemblerRegisters.r9, AssemblerRegisters.r8, AssemblerRegisters.ecx, AssemblerRegisters.edx, AssemblerRegisters.r12, AssemblerRegisters.r13] }, + { typeof(long), [AssemblerRegisters.r8, AssemblerRegisters.rcx, AssemblerRegisters.rdx, AssemblerRegisters.r9, AssemblerRegisters.r12, AssemblerRegisters.r13] }, + { typeof(IntPtr), [AssemblerRegisters.rcx, AssemblerRegisters.rdx, AssemblerRegisters.r8, AssemblerRegisters.r9, AssemblerRegisters.r10, AssemblerRegisters.r11, AssemblerRegisters.r12, AssemblerRegisters.r13] }, + { typeof(float), [AssemblerRegisters.xmm0, AssemblerRegisters.xmm1, AssemblerRegisters.xmm2, AssemblerRegisters.xmm3, AssemblerRegisters.xmm4, AssemblerRegisters.xmm5] }, + { typeof(double), [AssemblerRegisters.xmm0, AssemblerRegisters.xmm1, AssemblerRegisters.xmm2, AssemblerRegisters.xmm3, AssemblerRegisters.xmm4, AssemblerRegisters.xmm5] }, + { typeof(char), [AssemblerRegisters.dl, AssemblerRegisters.cl, AssemblerRegisters.r8, AssemblerRegisters.r9, AssemblerRegisters.r12, AssemblerRegisters.r13] }, + { typeof(bool), [AssemblerRegisters.al, AssemblerRegisters.dl, AssemblerRegisters.cl, AssemblerRegisters.r8, AssemblerRegisters.r9, AssemblerRegisters.r12, AssemblerRegisters.r13] } + }; + + List argumentInfos = []; + int stackOffset = 16; + + for (int i = 0; i < argumentTypes.Count; i++) + { + Type type = argumentTypes[i]; + object value = argumentValues[i]; + + if (!availableRegisters.ContainsKey(type)) + { + argumentInfos.Add(new x64_AssemblerStep { Instruction = x64_AssemblerStep.InstructionTarget.push, StackOffset = stackOffset }); + stackOffset += 8; + } + else if (type == typeof(float)) + { + var registers = availableRegisters[type]; + foreach (var reg in registers) + { + if (!argumentInfos.Exists(info => info.Register == reg)) + { + argumentInfos.Add(new x64_AssemblerStep + { + Instruction = x64_AssemblerStep.InstructionTarget.mov, + Register = reg, + StackOffset = -1 + }); + break; + } + } + } + else + { + var registers = availableRegisters[type]; + foreach (var reg in registers) + { + if (!argumentInfos.Exists(info => info.Register == reg)) + { + argumentInfos.Add(new x64_AssemblerStep { Instruction = x64_AssemblerStep.InstructionTarget.mov, Register = reg }); + break; + } + } + + if (!argumentInfos.Exists(info => info.Instruction == x64_AssemblerStep.InstructionTarget.mov)) + { + argumentInfos.Add(new x64_AssemblerStep { Instruction = x64_AssemblerStep.InstructionTarget.push, StackOffset = stackOffset }); + stackOffset += 8; + } + } + } + + return argumentInfos; + } +} diff --git a/runtime/ishtar.vm/runtime/jit/AssemblerFormatterOutputImpl.cs b/runtime/ishtar.vm/runtime/jit/AssemblerFormatterOutputImpl.cs new file mode 100644 index 00000000..ce06aad6 --- /dev/null +++ b/runtime/ishtar.vm/runtime/jit/AssemblerFormatterOutputImpl.cs @@ -0,0 +1,10 @@ +namespace ishtar; + +using Iced.Intel; + +internal sealed class AssemblerFormatterOutputImpl : FormatterOutput +{ + public readonly List<(string text, FormatterTextKind kind)> List = + new List<(string text, FormatterTextKind kind)>(); + public override void Write(string text, FormatterTextKind kind) => List.Add((text, kind)); +} \ No newline at end of file diff --git a/runtime/ishtar.vm/runtime/IshtarJIT.cs b/runtime/ishtar.vm/runtime/jit/IshtarJIT.cs similarity index 71% rename from runtime/ishtar.vm/runtime/IshtarJIT.cs rename to runtime/ishtar.vm/runtime/jit/IshtarJIT.cs index 9f0dc923..49e0b5ff 100644 --- a/runtime/ishtar.vm/runtime/IshtarJIT.cs +++ b/runtime/ishtar.vm/runtime/jit/IshtarJIT.cs @@ -13,6 +13,17 @@ public unsafe class IshtarJIT(VirtualMachine vm) public Assembler AllocEmitter() => new Assembler(64); + public delegate* GenerateHalt() + { + var asm = AllocEmitter(); + + asm.mov(rax, 60); + asm.xor(rdi, rdi); + asm.syscall(); + + return (delegate*)RecollectExecutableMemory(asm); + } + /// /// /// @@ -80,158 +91,6 @@ public IntPtr WrapNativeCall(IntPtr procedureHandle, IntPtr retMemory) return new IntPtr(RecollectExecutableMemory(c)); } - public struct NativeCallData - { - public nint procedure; - public long argCount; - public nint returnMemoryPointer; - public nint argsPointer; - } - - - - public struct x64_AssemblerStep - { - public InstructionTarget Instruction; - public Register Register; - public int StackOffset; - - public enum InstructionTarget - { - push, - mov - } - } - - public class ArgumentConverter - { - public static void GenerateAssemblerCode(List argumentInfos, - List argumentValues, nint nativeFunctionPtr, Assembler asm) - { - int numRegistersUsed = argumentInfos.Count(argInfo => argInfo.Instruction != x64_AssemblerStep.InstructionTarget.push); - - int stackSpaceNeeded = numRegistersUsed * 8; - - - asm.push(rbp); - asm.mov(rbp, rsp); - asm.sub(rsp, stackSpaceNeeded); - - int valueIndex = 0; - - foreach (var argInfo in argumentInfos) - { - if (argInfo.Instruction == x64_AssemblerStep.InstructionTarget.push) - asm.push(__[argInfo.StackOffset]); - else - { - if (argumentValues[valueIndex] is IntPtr ptr) - { - - asm.mov(new AssemblerRegister64(argInfo.Register), __[ptr]); - valueIndex++; - } - else - { - var val = argumentValues[valueIndex++]; - if (val is byte b) - asm.mov(new AssemblerRegister64(argInfo.Register), b); - if (val is short s) - asm.mov(new AssemblerRegister64(argInfo.Register), s); - if (val is int i) - asm.mov(new AssemblerRegister64(argInfo.Register), i); - if (val is long l) - asm.mov(new AssemblerRegister64(argInfo.Register), l); - //if (val is float f) - // asm.mov(xmm0, 12f); - if (val is char c) - asm.mov(new AssemblerRegister64(argInfo.Register), c); - if (val is bool bb) - asm.mov(new AssemblerRegister64(argInfo.Register), (byte)(bb ? 1 : 0)); - if (val is float or double) - throw new NotSupportedException(); - } - - asm.call(__[nativeFunctionPtr]); - asm.add(rsp, stackSpaceNeeded); - asm.pop(rbp); - asm.ret(); - } - } - - - } - - public static List ConvertArguments(List argumentTypes, List argumentValues) - { - Dictionary availableRegisters = new() - { - { typeof(byte), [dl, cl, r8, r9, r12, r13] }, - { typeof(short), [dx, cx, r8, r9, r12, r13] }, - { typeof(int), [r9, r8, ecx, edx, r12, r13] }, - { typeof(long), [r8, rcx, rdx, r9, r12, r13] }, - { typeof(IntPtr), [rcx, rdx, r8, r9, r10, r11, r12, r13] }, - { typeof(float), [xmm0, xmm1, xmm2, xmm3, xmm4, xmm5] }, - { typeof(double), [xmm0, xmm1, xmm2, xmm3, xmm4, xmm5] }, - { typeof(char), [dl, cl, r8, r9, r12, r13] }, - { typeof(bool), [al, dl, cl, r8, r9, r12, r13] } - }; - - List argumentInfos = []; - int stackOffset = 16; - - for (int i = 0; i < argumentTypes.Count; i++) - { - Type type = argumentTypes[i]; - object value = argumentValues[i]; - - if (!availableRegisters.ContainsKey(type)) - { - argumentInfos.Add(new x64_AssemblerStep { Instruction = x64_AssemblerStep.InstructionTarget.push, StackOffset = stackOffset }); - stackOffset += 8; - } - else if (type == typeof(float)) - { - var registers = availableRegisters[type]; - foreach (var reg in registers) - { - if (!argumentInfos.Exists(info => info.Register == reg)) - { - argumentInfos.Add(new x64_AssemblerStep - { - Instruction = x64_AssemblerStep.InstructionTarget.mov, - Register = reg, - StackOffset = -1 - }); - break; - } - } - } - else - { - var registers = availableRegisters[type]; - foreach (var reg in registers) - { - if (!argumentInfos.Exists(info => info.Register == reg)) - { - argumentInfos.Add(new x64_AssemblerStep { Instruction = x64_AssemblerStep.InstructionTarget.mov, Register = reg }); - break; - } - } - - if (!argumentInfos.Exists(info => info.Instruction == x64_AssemblerStep.InstructionTarget.mov)) - { - argumentInfos.Add(new x64_AssemblerStep { Instruction = x64_AssemblerStep.InstructionTarget.push, StackOffset = stackOffset }); - stackOffset += 8; - } - } - } - - return argumentInfos; - } - } - - public List ConvertArgumentToDesc(IReadOnlyList args) { Dictionary availableRegisters = new() @@ -337,7 +196,6 @@ public void GenerateHeader(Assembler asm, int stackSize, nint procedure) asm.mov(rax, (nint)returnValue); asm.mov(__qword_ptr[rsp], rax); } - var index = 0; var sbpOffset = 0; for (var i = 0; i < desc.Count; i++) @@ -602,7 +460,6 @@ private TypeMarshalBox RemapToNative(VeinArgumentRef arg) return new TypeMarshalBox((byte)sizeof(nint), arg.Type); } - public record TypeMarshalBox(byte size, VeinClass clazz); private TypeMarshalBox RemapValueTypeToNative(VeinArgumentRef arg) { @@ -611,12 +468,7 @@ private TypeMarshalBox RemapValueTypeToNative(VeinArgumentRef arg) return new TypeMarshalBox(size, type); } - - public class NativeCallInfo - { - public bool retIsPointer { get; set; } - } - + public void* WrapNativeCall_WithArg_Int32(void* procedure, long value) { var c = AllocEmitter(); @@ -688,14 +540,7 @@ public class NativeCallInfo return RecollectExecutableMemory(c); } - - sealed class FormatterOutputImpl : FormatterOutput - { - public readonly List<(string text, FormatterTextKind kind)> List = - new List<(string text, FormatterTextKind kind)>(); - public override void Write(string text, FormatterTextKind kind) => List.Add((text, kind)); - } - + static ConsoleColor GetColor(FormatterTextKind kind) { switch (kind) @@ -727,7 +572,7 @@ public static void DumpExecutableProcedure(byte[] body) decoder.IP = 0xDEAD; var formatter = new MasmFormatter(); - var output = new FormatterOutputImpl(); + var output = new AssemblerFormatterOutputImpl(); foreach (var instr in decoder) { @@ -755,7 +600,7 @@ public static void DumpExecutableProcedure(byte[] body) decoder.IP = 0xDEAD; var formatter = new MasmFormatter(); - var output = new FormatterOutputImpl(); + var output = new AssemblerFormatterOutputImpl(); foreach (var instr in decoder) { @@ -785,93 +630,3 @@ public static void DumpExecutableProcedure(byte[] body) public static void FlushInstructions(void* ipBaseAddr, uint size) => NativeApi.FlushInstructionCache((void*)Process.GetCurrentProcess().Handle, ipBaseAddr, size); } - - -/* - * 0x0000000000400526: push rbp - 0x0000000000400527: mov rbp,rsp # stack-frame boilerplate - 0x000000000040052a: mov edi,0x4005c4 # first arg - 0x000000000040052f: mov eax,0x0 # 0 FP args in vector registers - */ -/* void* - * - L000a: xor eax, eax - L000c: mov [rbp-8], rax - L0010: mov [rbp+0x10], rcx - L0014: mov [rbp+0x18], rdx - L0018: mov rcx, 0x7ffca892cb00 - L0022: xor edx, edx - */ -/* void* - * -L000a: xor eax, eax -L000c: mov [rbp-8], rax -L0010: mov [rbp+0x10], rcx -L0014: mov [rbp+0x18], rdx -L0018: mov [rbp+0x20], r8 -L001c: mov rcx, 0x7ffca89dcb00 -L0026: xor edx, edx - */ - -//c.push(rbp); -//c.sub(rsp, 0x20); -//c.lea(rbp, __[rsp+0x20]); -//c.mov(r11, procedureHandle.ToInt64()); -//c.jmp(r11); - -//c.nop(); -//c.mov(__[retMemory.ToInt64()], rax); -//c.pop(rbp); - -/*C.ret() - L000a: call 0x00007ffca8030460 - L000f: mov rdx, 0x432322245 - L0019: mov [rdx], rax - L001c: add rsp, 0x20 - L0020: pop rbp - L0021: ret -*/ - -/* -C.ret(Int32) - L000a: mov [rbp+0x10], ecx - L000d: mov ecx, [rbp+0x10] - - L0010: call 0x00007ffca8040460 - L0015: mov rdx, 0x432322245 - L001f: mov [rdx], rax - L0022: add rsp, 0x20 - L0026: pop rbp - L0027: ret*/ - -/*C.ret(Int32, Int32) - L000a: mov [rbp+0x10], ecx - L000d: mov [rbp+0x18], edx - L0010: mov ecx, [rbp+0x10] - L0013: mov edx, [rbp+0x18] - L0016: call 0x00007ffca8060460 - L001b: mov rdx, 0x432322245 - L0025: mov [rdx], rax - L0028: add rsp, 0x20 - L002c: pop rbp - L002d: ret -*/ -public enum RegisterKind -{ - GPR64, - GPR32, - GPR16, - GPR8 -} - -public static class RegisterEx -{ - public static RegisterKind GetKind(this Register reg) - { - if (reg.IsGPR64()) - return RegisterKind.GPR64; - if (reg.IsGPR32()) - return RegisterKind.GPR32; - return RegisterKind.GPR8; - } -} diff --git a/runtime/ishtar.vm/runtime/jit/NativeCallData.cs b/runtime/ishtar.vm/runtime/jit/NativeCallData.cs new file mode 100644 index 00000000..3f9786dc --- /dev/null +++ b/runtime/ishtar.vm/runtime/jit/NativeCallData.cs @@ -0,0 +1,9 @@ +namespace ishtar; + +public struct NativeCallData +{ + public nint procedure; + public long argCount; + public nint returnMemoryPointer; + public nint argsPointer; +} \ No newline at end of file diff --git a/runtime/ishtar.vm/runtime/jit/NativeCallInfo.cs b/runtime/ishtar.vm/runtime/jit/NativeCallInfo.cs new file mode 100644 index 00000000..249f5ce3 --- /dev/null +++ b/runtime/ishtar.vm/runtime/jit/NativeCallInfo.cs @@ -0,0 +1,6 @@ +namespace ishtar; + +public class NativeCallInfo +{ + public bool retIsPointer { get; set; } +} \ No newline at end of file diff --git a/runtime/ishtar.vm/runtime/jit/RegisterEx.cs b/runtime/ishtar.vm/runtime/jit/RegisterEx.cs new file mode 100644 index 00000000..604958aa --- /dev/null +++ b/runtime/ishtar.vm/runtime/jit/RegisterEx.cs @@ -0,0 +1,15 @@ +namespace ishtar; + +using Iced.Intel; + +public static class RegisterEx +{ + public static RegisterKind GetKind(this Register reg) + { + if (reg.IsGPR64()) + return RegisterKind.GPR64; + if (reg.IsGPR32()) + return RegisterKind.GPR32; + return RegisterKind.GPR8; + } +} \ No newline at end of file diff --git a/runtime/ishtar.vm/runtime/jit/RegisterKind.cs b/runtime/ishtar.vm/runtime/jit/RegisterKind.cs new file mode 100644 index 00000000..5daabf6e --- /dev/null +++ b/runtime/ishtar.vm/runtime/jit/RegisterKind.cs @@ -0,0 +1,9 @@ +namespace ishtar; + +public enum RegisterKind +{ + GPR64, + GPR32, + GPR16, + GPR8 +} \ No newline at end of file diff --git a/runtime/ishtar.vm/runtime/jit/TypeMarshalBox.cs b/runtime/ishtar.vm/runtime/jit/TypeMarshalBox.cs new file mode 100644 index 00000000..9ff5af6d --- /dev/null +++ b/runtime/ishtar.vm/runtime/jit/TypeMarshalBox.cs @@ -0,0 +1,5 @@ +namespace ishtar; + +using vein.runtime; + +public record TypeMarshalBox(byte size, VeinClass clazz); \ No newline at end of file diff --git a/runtime/ishtar.vm/runtime/jit/x64_AssemblerStep.cs b/runtime/ishtar.vm/runtime/jit/x64_AssemblerStep.cs new file mode 100644 index 00000000..3cb54c3d --- /dev/null +++ b/runtime/ishtar.vm/runtime/jit/x64_AssemblerStep.cs @@ -0,0 +1,16 @@ +namespace ishtar; + +using Iced.Intel; + +public struct x64_AssemblerStep +{ + public InstructionTarget Instruction; + public Register Register; + public int StackOffset; + + public enum InstructionTarget + { + push, + mov + } +} \ No newline at end of file diff --git a/runtime/ishtar.vm/runtime/jit/x86.cs b/runtime/ishtar.vm/runtime/jit/x86.cs deleted file mode 100644 index 083f00cf..00000000 --- a/runtime/ishtar.vm/runtime/jit/x86.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace ishtar -{ - [ExcludeFromCodeCoverage] - public class jit_x86 - { - public static int EAX = 0; - public static int EСX = 1; - public static int EDX = 2; - public static int EBX = 3; - public static int ESP = 4; - public static int EBP = 5; - public static int ESI = 6; - public static int EDI = 7; - } - - [ExcludeFromCodeCoverage] - public unsafe struct jit_buffer - { - public void* p; - public ushort* b; - public uint* w; - public int* c; - public int* i; - } - - [ExcludeFromCodeCoverage] - public unsafe struct _jlist - { - public int pos, target; - public _jlist* next; - } -}