diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5f06e03d..aee2a841 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -161,6 +161,7 @@ jobs: - iOS arm64 - Android x86_64 - Android arm64-v8a + - Emscripten configuration: - Debug - Release @@ -201,6 +202,9 @@ jobs: abi: arm64-v8a rust-target: aarch64-linux-android cmake-options: "-DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/cmake/toolchain/android-ndk.cmake" + - platform: Emscripten + runner: ubuntu-24.04 + cmake-options: "-DMOMO_ENABLE_RUST_CODE=Off -DCMAKE_TOOLCHAIN_FILE=$(dirname $(which emcc))/cmake/Modules/Platform/Emscripten.cmake" steps: - name: Checkout Source uses: actions/checkout@v4 @@ -214,6 +218,10 @@ jobs: if: "${{ matrix.rust-target }}" run: rustup target add ${{ matrix.rust-target }} + - name: Install Emscripten + if: "${{ matrix.platform == 'Emscripten' }}" + uses: mymindstorm/setup-emsdk@v14 + - name: Install Clang if: "${{ matrix.platform == 'Linux x86_64 Clang' }}" run: | diff --git a/.gitmodules b/.gitmodules index 9c5a000b..ce0fdeaf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,7 +2,7 @@ path = deps/unicorn url = ../unicorn.git shallow = true - branch = dev + branch = wasm [submodule "deps/reflect"] path = deps/reflect url = https://github.com/qlibs/reflect.git diff --git a/cmake/compiler-env.cmake b/cmake/compiler-env.cmake index a6602ecd..5f610950 100644 --- a/cmake/compiler-env.cmake +++ b/cmake/compiler-env.cmake @@ -23,6 +23,8 @@ set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) set(CMAKE_POSITION_INDEPENDENT_CODE ON) +########################################## + if(NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten") set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) endif() @@ -49,6 +51,9 @@ if(UNIX) momo_add_c_and_cxx_compile_options( -fvisibility=hidden -ftrivial-auto-var-init=zero + #-Wbad-function-cast + #-Wcast-function-type + -Wno-int-conversion ) endif() @@ -86,6 +91,22 @@ endif() ########################################## +if(CMAKE_SYSTEM_NAME MATCHES "Emscripten") + add_link_options( + -sALLOW_MEMORY_GROWTH=1 + -sASSERTIONS + -sWASM_BIGINT + -sENVIRONMENT=web + -sUSE_OFFSET_CONVERTER + -sEXCEPTION_CATCHING_ALLOWED=[..] + -sEXIT_RUNTIME + #-lnodefs.js -sNODERAWFS=1 + #-sASYNCIFY +) +endif() + +########################################## + if(MSVC) string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") string(REPLACE "/EHs" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index eaca04f9..5ed0c395 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -1,7 +1,5 @@ -if (NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") - set(UNICORN_ARCH "x86" CACHE STRING "") - add_subdirectory(unicorn) -endif() +set(UNICORN_ARCH "x86" CACHE STRING "") +add_subdirectory(unicorn) ########################################## diff --git a/deps/unicorn b/deps/unicorn index 73be28b6..b29e3445 160000 --- a/deps/unicorn +++ b/deps/unicorn @@ -1 +1 @@ -Subproject commit 73be28b6509d0cbf3333071aec4efbb9be1f1e59 +Subproject commit b29e3445a4694e3a1061c910906b57d7d2a05ed9 diff --git a/src/backends/CMakeLists.txt b/src/backends/CMakeLists.txt index 49734038..12d192df 100644 --- a/src/backends/CMakeLists.txt +++ b/src/backends/CMakeLists.txt @@ -1,6 +1,4 @@ -if (NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") - add_subdirectory(unicorn-emulator) -endif() +add_subdirectory(unicorn-emulator) if (MOMO_ENABLE_RUST_CODE) add_subdirectory(icicle-emulator) diff --git a/src/backends/unicorn-emulator/function_wrapper2.hpp b/src/backends/unicorn-emulator/function_wrapper2.hpp new file mode 100644 index 00000000..f9628bda --- /dev/null +++ b/src/backends/unicorn-emulator/function_wrapper2.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include +#include + +#include + +uint32_t resolve_indexed_argument_part(uint32_t* args, size_t& index) +{ + return args[index++]; +} + +template +T resolve_indexed_argument_internal(uint32_t* args, size_t& index) +{ + const auto a1 = resolve_indexed_argument_part(args, index); + + if(sizeof(T) <= sizeof(a1)) { + return (T)a1; + } + + const auto a2 = resolve_indexed_argument_part(args, index); + + const auto arg = (a1 | ((uint64_t)a2 << 32)); + return (T)arg; +} + +template +T resolve_indexed_argument(uint32_t* args, size_t& index) +{ + auto arg = resolve_indexed_argument_internal(args, index); + return arg; +} + +template +class function_wrapper2 : public utils::object +{ + public: + using user_data_pointer = void*; + using c_function_type = ReturnType(Args..., user_data_pointer); + using functor_type = std::function; + + function_wrapper2() = default; + + function_wrapper2(functor_type functor) + : functor_(std::make_unique(std::move(functor))) + { + } + + c_function_type* get_c_function() const + { + return (c_function_type*)(void*)+[](uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4,uint32_t a5,uint32_t a6,uint32_t a7,uint32_t a8,uint32_t a9,uint32_t a10,uint32_t a11,uint32_t a12)-> uint64_t { + + uint32_t real_args[] { + a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12 + }; + + const auto lambda = +[](Args... args, user_data_pointer user_data) -> ReturnType { + return (*static_cast(user_data))(std::forward(args)...); + }; + + size_t index = 0; + std::tuple func_args{resolve_indexed_argument>>(real_args, index)..., resolve_indexed_argument(real_args, index)}; + + (void)index; + + if constexpr(!std::is_void_v){ + return (uint64_t)std::apply(lambda, std::move(func_args)); + } + + std::apply(lambda, std::move(func_args)); + return 0; + }; + } + + void* get_function() const + { + return reinterpret_cast(this->get_c_function()); + } + + user_data_pointer get_user_data() const + { + return this->functor_.get(); + } + + private: + std::unique_ptr functor_{}; +}; diff --git a/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp b/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp index 6c449b13..97cd07c3 100644 --- a/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp +++ b/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp @@ -7,6 +7,7 @@ #include "unicorn_hook.hpp" #include "function_wrapper.hpp" +#include "function_wrapper2.hpp" #include namespace unicorn @@ -385,10 +386,7 @@ namespace unicorn emulator_hook* hook_instruction(const int instruction_type, instruction_hook_callback callback) override { - function_wrapper wrapper([c = std::move(callback)](uc_engine*) { - return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0; - }); - + unicorn_hook hook{*this}; auto container = std::make_unique(); @@ -396,18 +394,39 @@ namespace unicorn if (inst_type == x64_hookable_instructions::invalid) { + function_wrapper wrapper([c = std::move(callback)](uc_engine*) { + return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0; + }); + uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN_INVALID, wrapper.get_function(), wrapper.get_user_data(), 0, std::numeric_limits::max())); - } - else - { + container->add(std::move(wrapper), std::move(hook)); + } + else if(inst_type == x64_hookable_instructions::syscall){ + function_wrapper wrapper([c = std::move(callback)](uc_engine*) { + c(); + }); + const auto uc_instruction = map_hookable_instruction(inst_type); uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(), wrapper.get_user_data(), 0, std::numeric_limits::max(), uc_instruction)); - } - container->add(std::move(wrapper), std::move(hook)); + container->add(std::move(wrapper), std::move(hook)); + } + else + { + function_wrapper wrapper([c = std::move(callback)](uc_engine*) { + return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0; + }); + + const auto uc_instruction = map_hookable_instruction(inst_type); + uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(), + wrapper.get_user_data(), 0, std::numeric_limits::max(), + uc_instruction)); + + container->add(std::move(wrapper), std::move(hook)); + } auto* result = container->as_opaque_hook(); @@ -533,7 +552,7 @@ namespace unicorn c(address); // }; - function_wrapper wrapper(std::move(exec_wrapper)); + function_wrapper2 wrapper(std::move(exec_wrapper)); unicorn_hook hook{*this}; diff --git a/src/windows-emulator/CMakeLists.txt b/src/windows-emulator/CMakeLists.txt index 94ec8aa2..16167e84 100644 --- a/src/windows-emulator/CMakeLists.txt +++ b/src/windows-emulator/CMakeLists.txt @@ -14,11 +14,9 @@ if(NOT MOMO_ENABLE_CLANG_TIDY) target_precompile_headers(windows-emulator PRIVATE std_include.hpp) endif() -if (NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") target_link_libraries(windows-emulator PRIVATE unicorn-emulator ) -endif() if (MOMO_ENABLE_RUST_CODE) target_link_libraries(windows-emulator PRIVATE diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 372d1d19..4cb8b45f 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -3,9 +3,7 @@ #include "cpu_context.hpp" -#ifndef OS_EMSCRIPTEN #include -#endif #if MOMO_ENABLE_RUST_CODE #include @@ -265,10 +263,6 @@ namespace std::unique_ptr create_default_x64_emulator() { -#ifdef OS_EMSCRIPTEN - return icicle::create_x64_emulator(); -#else - #if MOMO_ENABLE_RUST_CODE const auto* env = getenv("EMULATOR_ICICLE"); if (env && (env == "1"sv || env == "true"sv)) @@ -276,8 +270,8 @@ std::unique_ptr create_default_x64_emulator() return icicle::create_x64_emulator(); } #endif + return unicorn::create_x64_emulator(); -#endif } windows_emulator::windows_emulator(application_settings app_settings, const emulator_settings& settings,