diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a6860204..00455b6b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -161,7 +161,8 @@ jobs: - iOS arm64 - Android x86_64 - Android arm64-v8a - - Emscripten + - Emscripten Web + - Emscripten Node.js configuration: - Debug - Release @@ -202,9 +203,12 @@ jobs: abi: arm64-v8a rust-target: aarch64-linux-android cmake-options: "-DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/cmake/toolchain/android-ndk.cmake" - - platform: Emscripten + - platform: Emscripten Web runner: ubuntu-24.04 cmake-options: "-DMOMO_ENABLE_RUST_CODE=Off -DCMAKE_TOOLCHAIN_FILE=$(dirname $(which emcc))/cmake/Modules/Platform/Emscripten.cmake" + - platform: Emscripten Node.js + runner: ubuntu-24.04 + cmake-options: "-DMOMO_EMSCRIPTEN_SUPPORT_NODEJS=On -DMOMO_ENABLE_RUST_CODE=Off -DCMAKE_TOOLCHAIN_FILE=$(dirname $(which emcc))/cmake/Modules/Platform/Emscripten.cmake" steps: - name: Checkout Source uses: actions/checkout@v4 @@ -219,7 +223,7 @@ jobs: run: rustup target add ${{ matrix.rust-target }} - name: Install Emscripten - if: "${{ matrix.platform == 'Emscripten' }}" + if: "${{ startsWith(matrix.platform, 'Emscripten') }}" uses: mymindstorm/setup-emsdk@v14 - name: Install Clang @@ -248,6 +252,7 @@ jobs: add-to-path: false - name: Setup Environment Variables + shell: bash if: ${{ startsWith(matrix.platform, 'Android') }} run: | echo "ANDROID_NDK_ROOT=${{ steps.setup-ndk.outputs.ndk-path }}" >> $GITHUB_ENV @@ -320,6 +325,7 @@ jobs: submodules: recursive - name: Setup Environment Variables + shell: bash run: | echo "RUST_BACKTRACE=1" >> $GITHUB_ENV echo "ASAN_OPTIONS=detect_odr_violation=0" >> $GITHUB_ENV @@ -382,6 +388,7 @@ jobs: submodules: recursive - name: Setup Environment Variables + shell: bash run: | echo "RUST_BACKTRACE=1" >> $GITHUB_ENV echo "ASAN_OPTIONS=detect_odr_violation=0" >> $GITHUB_ENV @@ -416,6 +423,44 @@ jobs: ANALYSIS_SAMPLE: ${{github.workspace}}/build/release/artifacts/test-sample.exe + smoke-test-node: + name: Smoke Test Node.js + runs-on: ubuntu-24.04 + needs: [create-emulation-root, build] + steps: + - name: Checkout Source + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Download Artifacts + uses: pyTooling/download-artifact@v4 + with: + name: Emscripten Node.js Release Artifacts + path: build/release/artifacts + + - name: Download Windows Artifacts + uses: pyTooling/download-artifact@v4 + with: + name: Windows x86_64 Release Artifacts + path: build/release/artifacts + + - name: Download Emulation Root + uses: pyTooling/download-artifact@v4 + with: + name: Windows 2022 Emulation Root + path: build/release/artifacts/root + + - name: Copy Test Sample + run: cp build/release/artifacts/test-sample.exe build/release/artifacts/root/filesys/c/ + + - name: CMake Test + run: cd build/release/artifacts && node ./windows-emulator-test.js + env: + EMULATOR_ROOT: ${{github.workspace}}/build/release/artifacts/root + EMULATOR_VERBOSE: ${{ github.event.inputs.verbose }} + + smoke-test-android: name: Smoke Test Android runs-on: ${{ matrix.runner }} @@ -495,10 +540,10 @@ jobs: with: submodules: recursive - - name: Download Emscripten Artifacts + - name: Download Emscripten Web Artifacts uses: pyTooling/download-artifact@v4 with: - name: Emscripten Release Artifacts + name: Emscripten Web Release Artifacts path: build/release/artifacts - name: Download Windows Artifacts @@ -562,7 +607,7 @@ jobs: summary: name: Pipeline Summary runs-on: ubuntu-24.04 - needs: [build-page, clang-tidy, build-apiset-dumper, smoke-test-android, create-emulation-root, build, test, win-test, verify-formatting] + needs: [build-page, clang-tidy, build-apiset-dumper, smoke-test-node, smoke-test-android, create-emulation-root, build, test, win-test, verify-formatting] if: always() steps: - uses: geekyeggo/delete-artifact@v5 diff --git a/CMakeLists.txt b/CMakeLists.txt index f430a56c..ec3e355d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ option(MOMO_ENABLE_AVX2 "Enable AVX2 support" ON) option(MOMO_ENABLE_SANITIZER "Enable sanitizer" OFF) option(MOMO_ENABLE_CLANG_TIDY "Enable clang-tidy checks" OFF) option(MOMO_ENABLE_RUST_CODE "Enable code parts written in rust" ON) +option(MOMO_EMSCRIPTEN_SUPPORT_NODEJS "Enable Node.js filesystem for emscripten compilation" OFF) option(MOMO_BUILD_AS_LIBRARY "Configure and Build the emulator as a shared library (without the samples and tests)" OFF) set(MOMO_REFLECTION_LEVEL "0" CACHE STRING "Reflection level for the build") diff --git a/cmake/compiler-env.cmake b/cmake/compiler-env.cmake index 77951879..d9d09c5c 100644 --- a/cmake/compiler-env.cmake +++ b/cmake/compiler-env.cmake @@ -89,8 +89,9 @@ endif() ########################################## if(CMAKE_SYSTEM_NAME MATCHES "Emscripten") - add_compile_options( + momo_add_c_and_cxx_compile_options( -fexceptions + -ftrivial-auto-var-init=zero ) add_link_options( @@ -98,13 +99,24 @@ if(CMAKE_SYSTEM_NAME MATCHES "Emscripten") -sALLOW_MEMORY_GROWTH=1 -sASSERTIONS -sWASM_BIGINT - -sENVIRONMENT=web -sUSE_OFFSET_CONVERTER #-sEXCEPTION_CATCHING_ALLOWED=[..] -sEXIT_RUNTIME - #-lnodefs.js -sNODERAWFS=1 #-sASYNCIFY -) + ) + + if(MOMO_EMSCRIPTEN_SUPPORT_NODEJS) + add_link_options( + -lnodefs.js -sNODERAWFS=1 + -sENVIRONMENT=node + -sMAXIMUM_MEMORY=4gb + --pre-js ${CMAKE_CURRENT_LIST_DIR}/misc/node-pre-script.js + ) + else() + add_link_options( + -sENVIRONMENT=worker + ) + endif() endif() ########################################## diff --git a/cmake/misc/node-pre-script.js b/cmake/misc/node-pre-script.js new file mode 100644 index 00000000..ee8eb4ee --- /dev/null +++ b/cmake/misc/node-pre-script.js @@ -0,0 +1,3 @@ +Module['preRun'] = () => { + ENV = process.env; +}; diff --git a/deps/unicorn b/deps/unicorn index 6b329651..a19ae94c 160000 --- a/deps/unicorn +++ b/deps/unicorn @@ -1 +1 @@ -Subproject commit 6b32965144d4ff91eb303beed0ec938ad467f33b +Subproject commit a19ae94cbf42ddb6aa2bf0b60c7c0fa907af039b diff --git a/src/backends/unicorn-emulator/function_wrapper_tcg.hpp b/src/backends/unicorn-emulator/function_wrapper_tcg.hpp deleted file mode 100644 index 4cdaed8a..00000000 --- a/src/backends/unicorn-emulator/function_wrapper_tcg.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -template -T resolve_indexed_argument_internal(const std::array& args, size_t& index) -{ - const auto a1 = args[index++]; - - if constexpr (sizeof(T) <= sizeof(a1) || sizeof(size_t) > 4) - { - return T(a1); - } - else - { - const auto a2 = args[index++]; - - const auto arg = (a1 | (static_cast(a2) << 32)); - return T(arg); - } -} - -template -T resolve_indexed_argument(const std::array& args, size_t& index) -{ - auto arg = resolve_indexed_argument_internal(args, index); - return arg; -} - -template -class function_wrapper_tcg : public utils::object -{ - public: - using user_data_pointer = void*; - using c_function_type = ReturnType(Args..., user_data_pointer); - using functor_type = std::function; - - function_wrapper_tcg() = default; - - function_wrapper_tcg(functor_type functor) - : functor_(std::make_unique(std::move(functor))) - { - } - - c_function_type* get_c_function() const - { - auto* func = +[](const size_t a1, const size_t a2, const size_t a3, const size_t a4, const size_t a5, - const size_t a6, const size_t a7, const size_t a8, const size_t a9, const size_t a10, - const size_t a11, const size_t a12) -> uint64_t { - const std::array arguments = {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>>(arguments, index)..., - resolve_indexed_argument(arguments, 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; - }; - - return reinterpret_cast(reinterpret_cast(func)); - } - - 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 5be03b90..d542ed8f 100644 --- a/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp +++ b/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp @@ -7,7 +7,6 @@ #include "unicorn_hook.hpp" #include "function_wrapper.hpp" -#include "function_wrapper_tcg.hpp" #include namespace unicorn @@ -550,7 +549,7 @@ namespace unicorn c(address); // }; - function_wrapper_tcg wrapper(std::move(exec_wrapper)); + function_wrapper wrapper(std::move(exec_wrapper)); unicorn_hook hook{*this};