Skip to content

Instantly share code, notes, and snippets.

@SteveSandersonMS
Created April 25, 2023 12:41
Show Gist options
  • Save SteveSandersonMS/f1a54f033d7fb78fd9c409d406398fa2 to your computer and use it in GitHub Desktop.
Save SteveSandersonMS/f1a54f033d7fb78fd9c409d406398fa2 to your computer and use it in GitHub Desktop.
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