-
-
Save SteveSandersonMS/f1a54f033d7fb78fd9c409d406398fa2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Security.Cryptography; | |
using Wasmtime; | |
#pragma warning disable CS0162 // Unreachable code detected | |
internal static class WasmtimeEmscriptenExtensions | |
{ | |
public static void DefineEmscriptenAbi(this Linker linker) | |
{ | |
int lastThrownType = 0; | |
int exceptionLast = 0; | |
linker.AllowShadowing = true; | |
linker.DefineFunction("wasi_snapshot_preview1", "fd_seek", (Caller caller, int fd, int offset_low, int offset_high, int whence, int newOffset) => | |
{ | |
throw new InvalidOperationException($"Not implemented: fd_seek"); | |
return 0; | |
}); | |
linker.DefineFunction("wasi_snapshot_preview1", "fd_pread", (Caller caller, int a, int b, int c, int d, int e, int f) => | |
{ | |
throw new InvalidOperationException($"Not implemented: fd_pread"); | |
return 0; | |
}); | |
linker.DefineFunction("env", "__cxa_allocate_exception", (Caller caller, int size) => | |
{ | |
return (int)caller.GetFunction("malloc")!.Invoke(size)!; | |
}); | |
linker.DefineFunction("env", "__cxa_throw", (Caller caller, int ptr, int type, int destructor) => | |
{ | |
exceptionLast = ptr; | |
throw new EmscriptenCplusplusException(ptr, type, destructor); | |
}); | |
linker.DefineFunction("env", "__cxa_find_matching_catch_3", (caller, args, results) => | |
{ | |
foreach (var a in args) | |
{ | |
var caughtType = a.AsInt32(); | |
if (caughtType == 0 || caughtType == lastThrownType) | |
{ | |
break; | |
} | |
throw new NotImplementedException("__cxa_find_matching_catch_3 needs more implementation"); | |
} | |
caller.GetFunction("setTempRet0")!.Invoke((ValueBox)lastThrownType); | |
results[0] = (ValueBox)exceptionLast; | |
}, new[] { ValueKind.Int32 }, new[] { ValueKind.Int32 }); | |
linker.DefineFunction("env", "__cxa_begin_catch", (Caller caller, int excPtr) => | |
{ | |
var ptr = excPtr - 24; | |
var memory = caller.GetMemory("memory")!; | |
memory.WriteInt32(ptr + 12, 1); // set_caught | |
return excPtr; | |
}); | |
linker.DefineFunction("env", "__cxa_end_catch", (Caller caller) => | |
{ | |
}); | |
linker.DefineFunction("env", "__resumeException", (Caller caller, int a) => | |
{ | |
throw new NotImplementedException("__resumeException"); | |
}); | |
linker.DefineFunction("env", "_dlopen_js", (Caller caller, int param) => | |
{ | |
throw new NotImplementedException("_dlopen_js"); | |
return 0; | |
}); | |
linker.DefineFunction("env", "_dlsym_js", (Caller caller, int a, int b) => | |
{ | |
throw new NotImplementedException("_dlsym_js"); | |
return 0; | |
}); | |
linker.DefineFunction("env", "_dlinit", (Caller caller, int a) => | |
{ | |
throw new NotImplementedException("_dlinit"); | |
}); | |
linker.DefineFunction("env", "emscripten_notify_memory_growth", (Caller caller) => | |
{ | |
}); | |
linker.DefineFunction("env", "emscripten_get_heap_max", (Caller caller) => | |
{ | |
var memory = caller.GetMemory("memory")!; | |
return (int)memory.GetLength(); | |
}); | |
linker.DefineFunction("env", "emscripten_memcpy_big", (Caller caller, int a, int b, int c) => | |
{ | |
throw new NotImplementedException("emscripten_memcpy_big"); | |
}); | |
linker.DefineFunction("env", "emscripten_get_callstack", (Caller caller, int flags, int str, int maxBytes) => | |
{ | |
var memory = caller.GetMemory("memory")!; | |
var bytesWritten = memory.WriteString(str, "[Fake callstack]"); | |
memory.WriteInt32(str + bytesWritten, 0); | |
return bytesWritten + 1; | |
}); | |
linker.DefineFunction("env", "emscripten_resize_heap", (Caller caller, int requestedSize) => | |
{ | |
if (requestedSize > 100 * 1024 * 1024) | |
{ | |
throw new NotImplementedException($"Guest is asking to resize heap to {requestedSize}, which is too big"); | |
} | |
var memory = caller.GetMemory("memory")!; | |
var oldSize = memory.GetSize(); | |
var numExtraPagesNeeded = (requestedSize - oldSize) / 65536; | |
memory.Grow(numExtraPagesNeeded); | |
return 1; | |
}); | |
linker.DefineFunction("env", "_emscripten_get_now_is_monotonic", (Caller caller) => | |
{ | |
return 1; | |
}); | |
linker.DefineFunction("env", "emscripten_get_now", (Caller caller) => | |
{ | |
return (double)0; // TODO | |
}); | |
linker.DefineFunction("env", "emscripten_date_now", (Caller caller) => | |
{ | |
return (double)0; // TODO | |
}); | |
linker.DefineFunction("env", "__syscall_openat", (Caller caller, int dirfd, int path, int flags, int varargs) => | |
{ | |
var memory = caller.GetMemory("memory")!; | |
var pathStr = memory.ReadNullTerminatedString(path); | |
//Console.WriteLine($"Guest is trying to open {pathStr}"); | |
return 0; | |
}); | |
linker.DefineFunction("env", "__syscall_fcntl64", (Caller caller, int fd, int cmd, int varargs) => | |
{ | |
return 1; // TODO | |
}); | |
linker.DefineFunction("env", "__syscall_ioctl", (Caller caller, int a, int b, int c) => | |
{ | |
throw new NotImplementedException("__syscall_ioctl"); | |
return 0; | |
}); | |
linker.DefineFunction("env", "_munmap_js", (Caller caller, int a, int b, int c, int d, int e, int f) => | |
{ | |
throw new NotImplementedException("_munmap_js"); | |
return 0; | |
}); | |
linker.DefineFunction("env", "dotnet_browser_entropy", (Caller caller, int bufferAddress, int bufferLength) => | |
{ | |
var memory = caller.GetMemory("memory")!; | |
var buffer = memory.GetSpan(bufferAddress, bufferLength); | |
RandomNumberGenerator.Fill(buffer); | |
return 0; | |
}); | |
linker.DefineFunction("env", "abort", (Caller caller) => | |
{ | |
throw new EmscriptenAbortedException(); | |
}); | |
for (var numArgs = 0; numArgs < 10; numArgs++) | |
{ | |
var sigArgs = new string('i', numArgs); | |
DefineIndirectCallFunction(true, numArgs, $"v{sigArgs}"); | |
DefineIndirectCallFunction(false, numArgs, $"i{sigArgs}"); | |
} | |
void DefineIndirectCallFunction(bool isVoid, int numArgs, string sig) | |
{ | |
var paramKinds = Enumerable.Repeat(ValueKind.Int32, numArgs + 1).ToArray(); | |
var resultKinds = isVoid ? Array.Empty<ValueKind>() : new[] { ValueKind.Int32 }; | |
linker.DefineFunction("env", $"invoke_{sig}", (caller, args, results) => | |
{ | |
var funcPtr = (uint)args[0].AsInt32(); | |
var indirectFunctionTable = ((Instance)caller.GetData()!).GetTable("__indirect_function_table")!; | |
var callback = (Function)indirectFunctionTable.GetElement(funcPtr)!; | |
try | |
{ | |
var result = callback.Invoke(args[1..]); | |
if (results.Length > 0) | |
{ | |
results[0] = (ValueBox)(int)result!; | |
} | |
} | |
catch (WasmtimeException e) when (e.InnerException is EmscriptenCplusplusException cppException) | |
{ | |
lastThrownType = cppException.type; | |
caller.GetFunction("setThrew")!.Invoke((ValueBox)1, (ValueBox)0); | |
} | |
}, paramKinds, resultKinds); | |
} | |
} | |
internal class EmscriptenCplusplusException : Exception | |
{ | |
public int ptr; | |
public int type; | |
public int destructor; | |
public EmscriptenCplusplusException(int ptr, int type, int destructor) | |
{ | |
this.ptr = ptr; | |
this.type = type; | |
this.destructor = destructor; | |
} | |
} | |
internal class EmscriptenAbortedException : Exception | |
{ | |
public EmscriptenAbortedException() | |
{ | |
} | |
} | |
} | |
#pragma warning restore CS0162 // Unreachable code detected |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment