#include #include #include #include #include #include typedef enum { PROTECT_NONE, PROTECT_READ, PROTECT_READ_WRITE, PROTECT_READ_EXECUTE, } mem_protect_t; typedef enum { RESULT_OK, RESULT_ALLOCATE_FAILURE, RESULT_PROTECT_FAILURE, RESULT_DEALLOC_FAILURE, RESULT_OBJECT_LOAD_FAILURE, } result_t; typedef result_t (*alloc_memory_t)(size_t size, mem_protect_t protect, uint8_t** ptr_out, size_t* size_out); typedef result_t (*protect_memory_t)(uint8_t* ptr, size_t size, mem_protect_t protect); typedef result_t (*dealloc_memory_t)(uint8_t* ptr, size_t size); typedef uintptr_t (*lookup_vm_symbol_t)(const char* name_ptr, size_t length); typedef void (*fde_visitor_t)(uint8_t *fde); typedef result_t (*visit_fde_t)(uint8_t *fde, size_t size, fde_visitor_t visitor); typedef void (*trampoline_t)(void*, void*, void*, void*); typedef struct { /* Memory management. */ alloc_memory_t alloc_memory; protect_memory_t protect_memory; dealloc_memory_t dealloc_memory; lookup_vm_symbol_t lookup_vm_symbol; visit_fde_t visit_fde; } callbacks_t; struct WasmException { public: virtual std::string description() const noexcept = 0; }; struct UncatchableException : WasmException { public: virtual std::string description() const noexcept override { return "Uncatchable exception"; } }; struct UserException : UncatchableException { public: UserException(std::string msg) : msg(msg) {} virtual std::string description() const noexcept override { return std::string("user exception: ") + msg; } private: std::string msg; }; struct WasmTrap : UncatchableException { public: enum Type { Unreachable = 0, IncorrectCallIndirectSignature = 1, MemoryOutOfBounds = 2, CallIndirectOOB = 3, IllegalArithmetic = 4, Unknown, }; WasmTrap(Type type) : type(type) {} virtual std::string description() const noexcept override { std::ostringstream ss; ss << "WebAssembly trap:" << '\n' << " - type: " << type << '\n'; return ss.str(); } Type type; private: friend std::ostream& operator<<(std::ostream& out, const Type& ty) { switch (ty) { case Type::Unreachable: out << "unreachable"; break; case Type::IncorrectCallIndirectSignature: out << "incorrect call_indirect signature"; break; case Type::MemoryOutOfBounds: out << "memory access out-of-bounds"; break; case Type::CallIndirectOOB: out << "call_indirect out-of-bounds"; break; case Type::IllegalArithmetic: out << "illegal arithmetic operation"; break; case Type::Unknown: default: out << "unknown"; break; } return out; } }; struct CatchableException : WasmException { public: CatchableException(uint32_t type_id, uint32_t value_num) : type_id(type_id), value_num(value_num) {} virtual std::string description() const noexcept override { return "catchable exception"; } uint32_t type_id, value_num; uint64_t values[1]; }; struct WasmModule { public: WasmModule( const uint8_t *object_start, size_t object_size, callbacks_t callbacks ); void *get_func(llvm::StringRef name) const; private: std::unique_ptr memory_manager; std::unique_ptr object_file; std::unique_ptr runtime_dyld; }; extern "C" { result_t module_load(const uint8_t* mem_ptr, size_t mem_size, callbacks_t callbacks, WasmModule** module_out) { *module_out = new WasmModule(mem_ptr, mem_size, callbacks); return RESULT_OK; } [[noreturn]] void throw_trap(WasmTrap::Type ty) { throw WasmTrap(ty); } void module_delete(WasmModule* module) { delete module; } bool invoke_trampoline( trampoline_t trampoline, void* ctx, void* func, void* params, void* results, WasmTrap::Type* trap_out ) throw() { try { trampoline(ctx, func, params, results); return true; } catch(const WasmTrap& e) { *trap_out = e.type; return false; } catch(const WasmException& e) { *trap_out = WasmTrap::Type::Unknown; return false; } catch (...) { *trap_out = WasmTrap::Type::Unknown; return false; } } void* get_func_symbol(WasmModule* module, const char* name) { return module->get_func(llvm::StringRef(name)); } }