diff --git a/lib/llvm-backend/cpp/object_loader.cpp b/lib/llvm-backend/cpp/object_loader.cpp index 532350578..bf034a308 100644 --- a/lib/llvm-backend/cpp/object_loader.cpp +++ b/lib/llvm-backend/cpp/object_loader.cpp @@ -7,201 +7,214 @@ extern "C" void __deregister_frame(uint8_t *); struct MemoryManager : llvm::RuntimeDyld::MemoryManager { public: - MemoryManager(callbacks_t callbacks) : callbacks(callbacks) {} + MemoryManager(callbacks_t callbacks) : callbacks(callbacks) {} - virtual ~MemoryManager() override { - deregisterEHFrames(); - // Deallocate all of the allocated memory. - callbacks.dealloc_memory(code_section.base, code_section.size); - callbacks.dealloc_memory(read_section.base, read_section.size); - callbacks.dealloc_memory(readwrite_section.base, readwrite_section.size); + virtual ~MemoryManager() override { + deregisterEHFrames(); + // Deallocate all of the allocated memory. + callbacks.dealloc_memory(code_section.base, code_section.size); + callbacks.dealloc_memory(read_section.base, read_section.size); + callbacks.dealloc_memory(readwrite_section.base, readwrite_section.size); + } + + virtual uint8_t *allocateCodeSection(uintptr_t size, unsigned alignment, + unsigned section_id, + llvm::StringRef section_name) override { + return allocate_bump(code_section, code_bump_ptr, size, alignment); + } + + virtual uint8_t *allocateDataSection(uintptr_t size, unsigned alignment, + unsigned section_id, + llvm::StringRef section_name, + bool read_only) override { + // Allocate from the read-only section or the read-write section, depending + // on if this allocation should be read-only or not. + if (read_only) { + return allocate_bump(read_section, read_bump_ptr, size, alignment); + } else { + return allocate_bump(readwrite_section, readwrite_bump_ptr, size, + alignment); } + } - virtual uint8_t* allocateCodeSection(uintptr_t size, unsigned alignment, unsigned section_id, llvm::StringRef section_name) override { - return allocate_bump(code_section, code_bump_ptr, size, alignment); - } - - virtual uint8_t* allocateDataSection(uintptr_t size, unsigned alignment, unsigned section_id, llvm::StringRef section_name, bool read_only) override { - // Allocate from the read-only section or the read-write section, depending on if this allocation - // should be read-only or not. - if (read_only) { - return allocate_bump(read_section, read_bump_ptr, size, alignment); - } else { - return allocate_bump(readwrite_section, readwrite_bump_ptr, size, alignment); - } - } - - virtual void reserveAllocationSpace( - uintptr_t code_size, - uint32_t code_align, - uintptr_t read_data_size, - uint32_t read_data_align, - uintptr_t read_write_data_size, - uint32_t read_write_data_align - ) override { - auto aligner = [](uintptr_t ptr, size_t align) { - if (ptr == 0) { - return align; - } - return (ptr + align - 1) & ~(align - 1); - }; - - - uint8_t *code_ptr_out = nullptr; - size_t code_size_out = 0; - auto code_result = callbacks.alloc_memory(aligner(code_size, 4096), PROTECT_READ_WRITE, &code_ptr_out, &code_size_out); - assert(code_result == RESULT_OK); - code_section = Section { code_ptr_out, code_size_out }; - code_bump_ptr = (uintptr_t)code_ptr_out; - - uint8_t *read_ptr_out = nullptr; - size_t read_size_out = 0; - auto read_result = callbacks.alloc_memory(aligner(read_data_size, 4096), PROTECT_READ_WRITE, &read_ptr_out, &read_size_out); - assert(read_result == RESULT_OK); - read_section = Section { read_ptr_out, read_size_out }; - read_bump_ptr = (uintptr_t)read_ptr_out; - - uint8_t *readwrite_ptr_out = nullptr; - size_t readwrite_size_out = 0; - auto readwrite_result = callbacks.alloc_memory(aligner(read_write_data_size, 4096), PROTECT_READ_WRITE, &readwrite_ptr_out, &readwrite_size_out); - assert(readwrite_result == RESULT_OK); - readwrite_section = Section { readwrite_ptr_out, readwrite_size_out }; - readwrite_bump_ptr = (uintptr_t)readwrite_ptr_out; - } - - /* Turn on the `reserveAllocationSpace` callback. */ - virtual bool needsToReserveAllocationSpace() override { - return true; - } - - virtual void registerEHFrames(uint8_t* addr, uint64_t LoadAddr, size_t size) override { - // We don't know yet how to do this on Windows, so we hide this on compilation - // so we can compile and pass spectests on unix systems - #ifndef _WIN32 - eh_frame_ptr = addr; - eh_frame_size = size; - eh_frames_registered = true; - callbacks.visit_fde(addr, size, __register_frame); - #endif - } - - virtual void deregisterEHFrames() override { - // We don't know yet how to do this on Windows, so we hide this on compilation - // so we can compile and pass spectests on unix systems - #ifndef _WIN32 - if (eh_frames_registered) { - callbacks.visit_fde(eh_frame_ptr, eh_frame_size, __deregister_frame); - } - #endif - } - - virtual bool finalizeMemory(std::string *ErrMsg = nullptr) override { - auto code_result = callbacks.protect_memory(code_section.base, code_section.size, mem_protect_t::PROTECT_READ_EXECUTE); - if (code_result != RESULT_OK) { - return false; - } - - auto read_result = callbacks.protect_memory(read_section.base, read_section.size, mem_protect_t::PROTECT_READ); - if (read_result != RESULT_OK) { - return false; - } - - // The readwrite section is already mapped as read-write. - - return false; - } - - virtual void notifyObjectLoaded(llvm::RuntimeDyld &RTDyld, const llvm::object::ObjectFile &Obj) override {} -private: - struct Section { - uint8_t* base; - size_t size; + virtual void reserveAllocationSpace(uintptr_t code_size, uint32_t code_align, + uintptr_t read_data_size, + uint32_t read_data_align, + uintptr_t read_write_data_size, + uint32_t read_write_data_align) override { + auto aligner = [](uintptr_t ptr, size_t align) { + if (ptr == 0) { + return align; + } + return (ptr + align - 1) & ~(align - 1); }; - uint8_t* allocate_bump(Section& section, uintptr_t& bump_ptr, size_t size, size_t align) { - auto aligner = [](uintptr_t& ptr, size_t align) { - ptr = (ptr + align - 1) & ~(align - 1); - }; + uint8_t *code_ptr_out = nullptr; + size_t code_size_out = 0; + auto code_result = + callbacks.alloc_memory(aligner(code_size, 4096), PROTECT_READ_WRITE, + &code_ptr_out, &code_size_out); + assert(code_result == RESULT_OK); + code_section = Section{code_ptr_out, code_size_out}; + code_bump_ptr = (uintptr_t)code_ptr_out; - // Align the bump pointer to the requires alignment. - aligner(bump_ptr, align); + uint8_t *read_ptr_out = nullptr; + size_t read_size_out = 0; + auto read_result = callbacks.alloc_memory(aligner(read_data_size, 4096), + PROTECT_READ_WRITE, &read_ptr_out, + &read_size_out); + assert(read_result == RESULT_OK); + read_section = Section{read_ptr_out, read_size_out}; + read_bump_ptr = (uintptr_t)read_ptr_out; - auto ret_ptr = bump_ptr; - bump_ptr += size; + uint8_t *readwrite_ptr_out = nullptr; + size_t readwrite_size_out = 0; + auto readwrite_result = callbacks.alloc_memory( + aligner(read_write_data_size, 4096), PROTECT_READ_WRITE, + &readwrite_ptr_out, &readwrite_size_out); + assert(readwrite_result == RESULT_OK); + readwrite_section = Section{readwrite_ptr_out, readwrite_size_out}; + readwrite_bump_ptr = (uintptr_t)readwrite_ptr_out; + } - assert(bump_ptr <= (uintptr_t)section.base + section.size); + /* Turn on the `reserveAllocationSpace` callback. */ + virtual bool needsToReserveAllocationSpace() override { return true; } - return (uint8_t*)ret_ptr; + virtual void registerEHFrames(uint8_t *addr, uint64_t LoadAddr, + size_t size) override { +// We don't know yet how to do this on Windows, so we hide this on compilation +// so we can compile and pass spectests on unix systems +#ifndef _WIN32 + eh_frame_ptr = addr; + eh_frame_size = size; + eh_frames_registered = true; + callbacks.visit_fde(addr, size, __register_frame); +#endif + } + + virtual void deregisterEHFrames() override { +// We don't know yet how to do this on Windows, so we hide this on compilation +// so we can compile and pass spectests on unix systems +#ifndef _WIN32 + if (eh_frames_registered) { + callbacks.visit_fde(eh_frame_ptr, eh_frame_size, __deregister_frame); + } +#endif + } + + virtual bool finalizeMemory(std::string *ErrMsg = nullptr) override { + auto code_result = + callbacks.protect_memory(code_section.base, code_section.size, + mem_protect_t::PROTECT_READ_EXECUTE); + if (code_result != RESULT_OK) { + return false; } - Section code_section, read_section, readwrite_section; - uintptr_t code_bump_ptr, read_bump_ptr, readwrite_bump_ptr; - uint8_t* eh_frame_ptr; - size_t eh_frame_size; - bool eh_frames_registered = false; + auto read_result = callbacks.protect_memory( + read_section.base, read_section.size, mem_protect_t::PROTECT_READ); + if (read_result != RESULT_OK) { + return false; + } - callbacks_t callbacks; + // The readwrite section is already mapped as read-write. + + return false; + } + + virtual void + notifyObjectLoaded(llvm::RuntimeDyld &RTDyld, + const llvm::object::ObjectFile &Obj) override {} + +private: + struct Section { + uint8_t *base; + size_t size; + }; + + uint8_t *allocate_bump(Section §ion, uintptr_t &bump_ptr, size_t size, + size_t align) { + auto aligner = [](uintptr_t &ptr, size_t align) { + ptr = (ptr + align - 1) & ~(align - 1); + }; + + // Align the bump pointer to the requires alignment. + aligner(bump_ptr, align); + + auto ret_ptr = bump_ptr; + bump_ptr += size; + + assert(bump_ptr <= (uintptr_t)section.base + section.size); + + return (uint8_t *)ret_ptr; + } + + Section code_section, read_section, readwrite_section; + uintptr_t code_bump_ptr, read_bump_ptr, readwrite_bump_ptr; + uint8_t *eh_frame_ptr; + size_t eh_frame_size; + bool eh_frames_registered = false; + + callbacks_t callbacks; }; struct SymbolLookup : llvm::JITSymbolResolver { public: - SymbolLookup(callbacks_t callbacks) : callbacks(callbacks) {} + SymbolLookup(callbacks_t callbacks) : callbacks(callbacks) {} - void lookup(const LookupSet& symbols, OnResolvedFunction OnResolved) { - LookupResult result; + void lookup(const LookupSet &symbols, OnResolvedFunction OnResolved) { + LookupResult result; - for (auto symbol : symbols) { - result.emplace(symbol, symbol_lookup(symbol)); - } - - OnResolved(result); + for (auto symbol : symbols) { + result.emplace(symbol, symbol_lookup(symbol)); } - llvm::Expected getResponsibilitySet(const LookupSet &Symbols) { - const std::set empty; - return empty; - } + OnResolved(result); + } + + llvm::Expected getResponsibilitySet(const LookupSet &Symbols) { + const std::set empty; + return empty; + } private: - llvm::JITEvaluatedSymbol symbol_lookup(llvm::StringRef name) { - uint64_t addr = callbacks.lookup_vm_symbol(name.data(), name.size()); + llvm::JITEvaluatedSymbol symbol_lookup(llvm::StringRef name) { + uint64_t addr = callbacks.lookup_vm_symbol(name.data(), name.size()); - return llvm::JITEvaluatedSymbol(addr, llvm::JITSymbolFlags::None); - } + return llvm::JITEvaluatedSymbol(addr, llvm::JITSymbolFlags::None); + } - callbacks_t callbacks; + callbacks_t callbacks; }; -WasmModule::WasmModule( - const uint8_t *object_start, - size_t object_size, - callbacks_t callbacks -) : memory_manager(std::unique_ptr(new MemoryManager(callbacks))) -{ - +WasmModule::WasmModule(const uint8_t *object_start, size_t object_size, + callbacks_t callbacks) + : memory_manager( + std::unique_ptr(new MemoryManager(callbacks))) { - if (auto created_object_file = llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef( - llvm::StringRef((const char *)object_start, object_size), "object" - ))) { - object_file = cantFail(std::move(created_object_file)); - SymbolLookup symbol_resolver(callbacks); - runtime_dyld = std::unique_ptr(new llvm::RuntimeDyld(*memory_manager, symbol_resolver)); + if (auto created_object_file = + llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef( + llvm::StringRef((const char *)object_start, object_size), + "object"))) { + object_file = cantFail(std::move(created_object_file)); + SymbolLookup symbol_resolver(callbacks); + runtime_dyld = std::unique_ptr( + new llvm::RuntimeDyld(*memory_manager, symbol_resolver)); - runtime_dyld->setProcessAllSections(true); + runtime_dyld->setProcessAllSections(true); - runtime_dyld->loadObject(*object_file); - runtime_dyld->finalizeWithMemoryManagerLocking(); + runtime_dyld->loadObject(*object_file); + runtime_dyld->finalizeWithMemoryManagerLocking(); - if (runtime_dyld->hasError()) { - _init_failed = true; - return; - } - } else { - _init_failed = true; + if (runtime_dyld->hasError()) { + _init_failed = true; + return; } + } else { + _init_failed = true; + } } -void* WasmModule::get_func(llvm::StringRef name) const { - auto symbol = runtime_dyld->getSymbol(name); - return (void*)symbol.getAddress(); +void *WasmModule::get_func(llvm::StringRef name) const { + auto symbol = runtime_dyld->getSymbol(name); + return (void *)symbol.getAddress(); } diff --git a/lib/llvm-backend/cpp/object_loader.hh b/lib/llvm-backend/cpp/object_loader.hh index 63630a479..7a410b2dc 100644 --- a/lib/llvm-backend/cpp/object_loader.hh +++ b/lib/llvm-backend/cpp/object_loader.hh @@ -1,236 +1,220 @@ #include #include -#include +#include #include #include -#include -typedef enum -{ - PROTECT_NONE, - PROTECT_READ, - PROTECT_READ_WRITE, - PROTECT_READ_EXECUTE, +#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, +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 (*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 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; +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; + lookup_vm_symbol_t lookup_vm_symbol; - visit_fde_t visit_fde; + visit_fde_t visit_fde; } callbacks_t; -typedef struct -{ - size_t data, vtable; +typedef struct { + size_t data, vtable; } box_any_t; -struct WasmException -{ - public: - virtual std::string description() const noexcept = 0; +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 UncatchableException : WasmException { +public: + virtual std::string description() const noexcept override { + return "Uncatchable exception"; + } }; -struct UserException : UncatchableException -{ - public: - UserException(size_t data, size_t vtable) : error_data({ data, vtable }) {} +struct UserException : UncatchableException { +public: + UserException(size_t data, size_t vtable) : error_data({data, vtable}) {} - virtual std::string description() const noexcept override - { - return "user exception"; - } + virtual std::string description() const noexcept override { + return "user exception"; + } - // The parts of a `Box`. - box_any_t error_data; + // The parts of a `Box`. + box_any_t error_data; }; -struct WasmTrap : UncatchableException -{ - public: - enum Type - { - Unreachable = 0, - IncorrectCallIndirectSignature = 1, - MemoryOutOfBounds = 2, - CallIndirectOOB = 3, - IllegalArithmetic = 4, - Unknown, - }; +struct BreakpointException : UncatchableException { +public: + BreakpointException(uintptr_t callback) : callback(callback) {} - WasmTrap(Type type) : type(type) {} + virtual std::string description() const noexcept override { + return "breakpoint exception"; + } - 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; - } + uintptr_t callback; }; -struct CatchableException : WasmException -{ - public: - CatchableException(uint32_t type_id, uint32_t value_num) : type_id(type_id), value_num(value_num) {} +struct WasmTrap : UncatchableException { +public: + enum Type { + Unreachable = 0, + IncorrectCallIndirectSignature = 1, + MemoryOutOfBounds = 2, + CallIndirectOOB = 3, + IllegalArithmetic = 4, + Unknown, + }; - virtual std::string description() const noexcept override - { - return "catchable exception"; + 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; } - - uint32_t type_id, value_num; - uint64_t values[1]; + return out; + } }; -struct WasmModule -{ - public: - WasmModule( - const uint8_t *object_start, - size_t object_size, - callbacks_t callbacks); +struct CatchableException : WasmException { +public: + CatchableException(uint32_t type_id, uint32_t value_num) + : type_id(type_id), value_num(value_num) {} - void *get_func(llvm::StringRef name) const; + virtual std::string description() const noexcept override { + return "catchable exception"; + } - bool _init_failed = false; - private: - std::unique_ptr memory_manager; - std::unique_ptr object_file; - std::unique_ptr runtime_dyld; + uint32_t type_id, value_num; + uint64_t values[1]; }; -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); +struct WasmModule { +public: + WasmModule(const uint8_t *object_start, size_t object_size, + callbacks_t callbacks); - if ((*module_out)->_init_failed) { - return RESULT_OBJECT_LOAD_FAILURE; - } + void *get_func(llvm::StringRef name) const; - return RESULT_OK; - } + bool _init_failed = false; - [[noreturn]] void throw_trap(WasmTrap::Type ty) { - throw WasmTrap(ty); - } +private: + std::unique_ptr memory_manager; + std::unique_ptr object_file; + std::unique_ptr runtime_dyld; +}; - void module_delete(WasmModule *module) - { - delete module; - } +extern "C" { +void callback_trampoline(void *, void *); - // Throw a fat pointer that's assumed to be `*mut dyn Any` on the rust - // side. - [[noreturn]] void throw_any(size_t data, size_t vtable) { - throw UserException(data, vtable); - } +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); - bool invoke_trampoline( - trampoline_t trampoline, - void *ctx, - void *func, - void *params, - void *results, - WasmTrap::Type *trap_out, - box_any_t *user_error, - void *invoke_env) throw() - { - try - { - trampoline(ctx, func, params, results); - return true; - } - catch (const WasmTrap &e) - { - *trap_out = e.type; - return false; - } - catch (const UserException &e) - { - *user_error = e.error_data; - return false; - } - catch (const WasmException &e) - { - *trap_out = WasmTrap::Type::Unknown; - return false; - } - catch (...) - { - *trap_out = WasmTrap::Type::Unknown; - return false; - } - } + if ((*module_out)->_init_failed) { + return RESULT_OBJECT_LOAD_FAILURE; + } - void *get_func_symbol(WasmModule *module, const char *name) - { - return module->get_func(llvm::StringRef(name)); - } -} \ No newline at end of file + return RESULT_OK; +} + +[[noreturn]] void throw_trap(WasmTrap::Type ty) { throw WasmTrap(ty); } + +void module_delete(WasmModule *module) { delete module; } + +// Throw a fat pointer that's assumed to be `*mut dyn Any` on the rust +// side. +[[noreturn]] void throw_any(size_t data, size_t vtable) { + throw UserException(data, vtable); +} + +// Throw a pointer that's assumed to be codegen::BreakpointHandler on the +// rust side. +[[noreturn]] void throw_breakpoint(uintptr_t callback) { + throw BreakpointException(callback); +} + +bool invoke_trampoline(trampoline_t trampoline, void *ctx, void *func, + void *params, void *results, WasmTrap::Type *trap_out, + box_any_t *user_error, void *invoke_env) noexcept { + try { + trampoline(ctx, func, params, results); + return true; + } catch (const WasmTrap &e) { + *trap_out = e.type; + return false; + } catch (const UserException &e) { + *user_error = e.error_data; + return false; + } catch (const BreakpointException &e) { + callback_trampoline(user_error, (void *)e.callback); + 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)); +} +} diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs index ff8c281df..94b86e34d 100644 --- a/lib/llvm-backend/src/backend.rs +++ b/lib/llvm-backend/src/backend.rs @@ -39,7 +39,8 @@ extern "C" { fn module_delete(module: *mut LLVMModule); fn get_func_symbol(module: *mut LLVMModule, name: *const c_char) -> *const vm::Func; - fn throw_trap(ty: i32); + fn throw_trap(ty: i32) -> !; + fn throw_breakpoint(ty: i64) -> !; /// This should be the same as spliting up the fat pointer into two arguments, /// but this is cleaner, I think? @@ -103,6 +104,7 @@ fn get_callbacks() -> Callbacks { fn_name!("vm.memory.size.static.local") => vmcalls::local_static_memory_size as _, fn_name!("vm.exception.trap") => throw_trap as _, + fn_name!("vm.breakpoint") => throw_breakpoint as _, _ => ptr::null(), } diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index 07dbbacc4..1e9a62182 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -496,6 +496,21 @@ pub struct CodegenError { pub message: String, } +// This is only called by C++ code, the 'pub' + '#[no_mangle]' combination +// prevents unused function elimination. +#[no_mangle] +pub unsafe extern "C" fn callback_trampoline( + b: *mut Option>, + callback: *mut BreakpointHandler, +) { + let callback = Box::from_raw(callback); + let result: Result<(), Box> = callback(BreakpointInfo { fault: None }); + match result { + Ok(()) => *b = None, + Err(e) => *b = Some(e), + } +} + pub struct LLVMModuleCodeGenerator { context: Option, builder: Option, @@ -612,7 +627,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { InternalEvent::FunctionBegin(_) | InternalEvent::FunctionEnd => { return Ok(()); } - InternalEvent::Breakpoint(_callback) => { + InternalEvent::Breakpoint(callback) => { + let raw = Box::into_raw(Box::new(callback)) as u64; + let callback = intrinsics.i64_ty.const_int(raw, false); + builder.build_call( + intrinsics.throw_breakpoint, + &[callback.as_basic_value_enum()], + "", + ); return Ok(()); } InternalEvent::GetInternal(idx) => { diff --git a/lib/llvm-backend/src/intrinsics.rs b/lib/llvm-backend/src/intrinsics.rs index 0d4e05087..461c54d5d 100644 --- a/lib/llvm-backend/src/intrinsics.rs +++ b/lib/llvm-backend/src/intrinsics.rs @@ -147,6 +147,7 @@ pub struct Intrinsics { pub memory_size_shared_import: FunctionValue, pub throw_trap: FunctionValue, + pub throw_breakpoint: FunctionValue, pub ctx_ptr_ty: PointerType, } @@ -309,7 +310,6 @@ impl Intrinsics { i32_ty.fn_type(&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic], false); let ret_i1_take_i1_i1 = i1_ty.fn_type(&[i1_ty_basic, i1_ty_basic], false); - Self { ctlz_i32: module.add_function("llvm.ctlz.i32", ret_i32_take_i32_i1, None), ctlz_i64: module.add_function("llvm.ctlz.i64", ret_i64_take_i64_i1, None), @@ -525,6 +525,11 @@ impl Intrinsics { void_ty.fn_type(&[i32_ty_basic], false), None, ), + throw_breakpoint: module.add_function( + "vm.breakpoint", + void_ty.fn_type(&[i64_ty_basic], false), + None, + ), ctx_ptr_ty, } } diff --git a/lib/middleware-common/src/metering.rs b/lib/middleware-common/src/metering.rs index 75e2f6ef0..e9f02d509 100644 --- a/lib/middleware-common/src/metering.rs +++ b/lib/middleware-common/src/metering.rs @@ -129,7 +129,7 @@ pub fn set_points_used_ctx(ctx: &mut Ctx, value: u64) { ctx.set_internal(&INTERNAL_FIELD, value); } -#[cfg(all(test, feature = "singlepass"))] +#[cfg(all(test, any(feature = "singlepass", feature = "llvm")))] mod tests { use super::*; use wabt::wat2wasm; @@ -247,7 +247,7 @@ mod tests { // verify it returns the correct value assert_eq!(value, 7); - // verify is uses the correct number of points + // verify it used the correct number of points assert_eq!(get_points_used(&instance), 74); } @@ -276,7 +276,7 @@ mod tests { _ => unreachable!(), } - // verify is uses the correct number of points + // verify it used the correct number of points assert_eq!(get_points_used(&instance), 109); // Used points will be slightly more than `limit` because of the way we do gas checking. } diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index c93115d14..f07620b23 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -134,7 +134,7 @@ impl std::fmt::Display for RuntimeError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { RuntimeError::Trap { ref msg } => { - write!(f, "WebAssembly trap occured during runtime: {}", msg) + write!(f, "WebAssembly trap occurred during runtime: {}", msg) } RuntimeError::Error { data } => { if let Some(s) = data.downcast_ref::() {