diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..0676cf741 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,35 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +version: 2 +updates: + +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + +- package-ecosystem: "docker" + directory: "/.devcontainer" + schedule: + interval: "weekly" + +- package-ecosystem: "devcontainers" + directory: "/" + schedule: + interval: "weekly" + +- package-ecosystem: "pip" + directory: "/build-scripts" + schedule: + interval: "weekly" + +- package-ecosystem: "pip" + directory: "/language-bindings/python/wasm-c-api" + schedule: + interval: "weekly" + +- package-ecosystem: "pip" + directory: "/language-bindings/python/wamr-api" + schedule: + interval: "weekly" diff --git a/.github/workflows/codeql_buildscript.sh b/.github/scripts/codeql_buildscript.sh similarity index 91% rename from .github/workflows/codeql_buildscript.sh rename to .github/scripts/codeql_buildscript.sh index 34e0ddd79..4bcabfe25 100755 --- a/.github/workflows/codeql_buildscript.sh +++ b/.github/scripts/codeql_buildscript.sh @@ -1,5 +1,10 @@ #!/usr/bin/env bash +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + sudo apt update sudo apt install -y build-essential cmake g++-multilib libgcc-11-dev lib32gcc-11-dev ccache ninja-build ccache @@ -101,6 +106,26 @@ if [[ $? != 0 ]]; then exit 1; fi +# build iwasm with exception handling enabled +cd ${WAMR_DIR}/product-mini/platforms/linux +rm -rf build && mkdir build && cd build +cmake .. -DCMAKE_BUILD_TYPE=Debug -DWAMR_BUILD_EXCE_HANDLING=1 +make -j +if [[ $? != 0 ]]; then + echo "Failed to build iwasm with exception handling enabled!" + exit 1; +fi + +# build iwasm with memory64 enabled +cd ${WAMR_DIR}/product-mini/platforms/linux +rm -rf build && mkdir build && cd build +cmake .. -DCMAKE_BUILD_TYPE=Debug -DWAMR_BUILD_MEMORY64=1 +make -j +if [[ $? != 0 ]]; then + echo "Failed to build iwasm with memory64 enabled!" + exit 1; +fi + # build iwasm with hardware boundary check disabled cd ${WAMR_DIR}/product-mini/platforms/linux rm -rf build && mkdir build && cd build diff --git a/.github/scripts/codeql_fail_on_error.py b/.github/scripts/codeql_fail_on_error.py new file mode 100755 index 000000000..f150c38a2 --- /dev/null +++ b/.github/scripts/codeql_fail_on_error.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import json +import sys +import os +import requests + + +def fetch_dismissed_alerts(repo_name, github_token): + headers = { + "Authorization": f"token {github_token}", + "Accept": "application/vnd.github.v3+json", + } + url = ( + f"https://api.github.com/repos/{repo_name}/code-scanning/alerts?state=dismissed" + ) + response = requests.get(url, headers=headers) + return response.json() # This assumes a successful API call + + +def parse_location(location): + path = location.get("physicalLocation", {}).get("artifactLocation", {}).get("uri") + start_line = location.get("physicalLocation", {}).get("region", {}).get("startLine") + column_range = ( + location.get("physicalLocation", {}).get("region", {}).get("startColumn"), + location.get("physicalLocation", {}).get("region", {}).get("endColumn"), + ) + return (path, start_line, column_range) + + +def is_dismissed(rule_id, path, start_line, column_range, dismissed_alerts): + for alert in dismissed_alerts: + alert_rule_id = alert.get("rule", {}).get("id") + alert_path = alert.get("location", {}).get("path") + alert_start_line = alert.get("location", {}).get("start_line") + alert_column_range = ( + alert.get("location", {}).get("start_column"), + alert.get("location", {}).get("end_column"), + ) + + if ( + rule_id == alert_rule_id + and path == alert_path + and start_line == alert_start_line + and column_range == alert_column_range + ): + return True + return False + + +# Return whether SARIF file contains error-level results +def codeql_sarif_contain_error(filename, dismissed_alerts): + has_error = False + + with open(filename, "r") as f: + s = json.load(f) + + for run in s.get("runs", []): + rules_metadata = run["tool"]["driver"]["rules"] + if not rules_metadata: + rules_metadata = run["tool"]["extensions"][0]["rules"] + + for res in run.get("results", []): + if "ruleIndex" in res: + rule_index = res["ruleIndex"] + elif "rule" in res and "index" in res["rule"]: + rule_index = res["rule"]["index"] + else: + continue + + # check whether it's dismissed before + rule_id = res["ruleId"] + path, start_line, column_range = parse_location(res["locations"][0]) + # the source code is from dependencies + if "_deps" in path: + continue + if is_dismissed(rule_id, path, start_line, column_range, dismissed_alerts): + print( + f"====== Finding a dismissed entry: {rule_id} at {path}:{start_line} is dismissed.======" + ) + print(res) + continue + + try: + rule_level = rules_metadata[rule_index]["defaultConfiguration"]["level"] + except IndexError as e: + print(e, rule_index, len(rules_metadata)) + else: + if rule_level == "error": + # very likely to be an actual error + if rules_metadata[rule_index]["properties"].get("precision") in [ + "high", + "very-high", + ]: + # the security severity is above medium(Common Vulnerability Scoring System (CVSS) >= 4.0) + if "security-severity" in rules_metadata[rule_index][ + "properties" + ] and ( + float( + rules_metadata[rule_index]["properties"][ + "security-severity" + ] + ) + > 4.0 + ): + print("====== Finding a likely error. ======") + print(res) + has_error = True + + return has_error + + +if __name__ == "__main__": + GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") + GITHUB_REPOSITORY = os.getenv("GITHUB_REPOSITORY") + dismissed_alerts = fetch_dismissed_alerts(GITHUB_REPOSITORY, GITHUB_TOKEN) + + if codeql_sarif_contain_error(sys.argv[1], dismissed_alerts): + sys.exit(1) diff --git a/.github/scripts/fetch_and_compare_version.py b/.github/scripts/fetch_and_compare_version.py index ac206cade..ad9e53a0a 100644 --- a/.github/scripts/fetch_and_compare_version.py +++ b/.github/scripts/fetch_and_compare_version.py @@ -42,9 +42,12 @@ def fetch_version_from_code(): def fetch_latest_git_tag(): - list_tag_cmd = ( - 'git tag --list WAMR-*.*.* --sort=committerdate --format="%(refname:short)"' - ) + """ + Get the most recent tag from the HEAD, + if it's main branch, it should be the latest release tag. + if it's release/x.x.x branch, it should be the latest release tag of the branch. + """ + list_tag_cmd = "git describe --tags --abbrev=0 HEAD" p = subprocess.run(shlex.split(list_tag_cmd), capture_output=True, check=True) all_tags = p.stdout.decode().strip() diff --git a/.github/workflows/build_wamr_lldb.yml b/.github/workflows/build_wamr_lldb.yml index 3e1e10ffd..03474c53e 100644 --- a/.github/workflows/build_wamr_lldb.yml +++ b/.github/workflows/build_wamr_lldb.yml @@ -82,9 +82,7 @@ jobs: - name: install utils macos if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'macos') run: | - brew remove swig - brew install swig@4.1 cmake ninja libedit - brew link --overwrite swig@4.1 + brew install swig cmake ninja libedit sudo rm -rf /Library/Developer/CommandLineTools - name: install utils ubuntu diff --git a/.github/workflows/build_wamr_sdk.yml b/.github/workflows/build_wamr_sdk.yml index 69dbd7232..519bf9636 100644 --- a/.github/workflows/build_wamr_sdk.yml +++ b/.github/workflows/build_wamr_sdk.yml @@ -58,6 +58,12 @@ jobs: sudo rm ${basename} sudo mv wasi-sdk-* wasi-sdk + - name: download dependencies + run: | + cd ./wamr-app-framework/deps + ./download.sh + working-directory: wamr-sdk + - name: generate wamr-sdk release run: | cd ./wamr-app-framework/wamr-sdk diff --git a/.github/workflows/build_wamr_vscode_ext.yml b/.github/workflows/build_wamr_vscode_ext.yml index b91f054cf..322ba1c06 100644 --- a/.github/workflows/build_wamr_vscode_ext.yml +++ b/.github/workflows/build_wamr_vscode_ext.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/checkout@v4 - name: Use Node.js 16.x - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 16.x diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 8656b326c..80a1a30b1 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -28,7 +28,7 @@ jobs: # - https://gh.io/supported-runners-and-hardware-resources # - https://gh.io/using-larger-runners # Consider using larger runners for possible analysis time improvements. - runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-20.04' }} + runs-on: ${{ (matrix.language == 'swift' && 'macos-13') || 'ubuntu-20.04' }} timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} permissions: actions: read @@ -49,7 +49,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} @@ -64,9 +64,9 @@ jobs: # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - run: | - ./.github/workflows/codeql_buildscript.sh + ./.github/scripts/codeql_buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" upload: false @@ -95,14 +95,14 @@ jobs: output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif - name: Upload CodeQL results to code scanning - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: ${{ steps.step1.outputs.sarif-output }} category: "/language:${{matrix.language}}" - name: Upload CodeQL results as an artifact if: success() || failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: codeql-results path: ${{ steps.step1.outputs.sarif-output }} @@ -110,5 +110,8 @@ jobs: - name: Fail if an error is found run: | - ./.github/workflows/codeql_fail_on_error.py \ + ./.github/scripts/codeql_fail_on_error.py \ ${{ steps.step1.outputs.sarif-output }}/cpp.sarif + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} diff --git a/.github/workflows/codeql_fail_on_error.py b/.github/workflows/codeql_fail_on_error.py deleted file mode 100755 index 29791742b..000000000 --- a/.github/workflows/codeql_fail_on_error.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python3 - -import json -import sys - -# Return whether SARIF file contains error-level results -def codeql_sarif_contain_error(filename): - with open(filename, 'r') as f: - s = json.load(f) - - for run in s.get('runs', []): - rules_metadata = run['tool']['driver']['rules'] - if not rules_metadata: - rules_metadata = run['tool']['extensions'][0]['rules'] - - for res in run.get('results', []): - if 'ruleIndex' in res: - rule_index = res['ruleIndex'] - elif 'rule' in res and 'index' in res['rule']: - rule_index = res['rule']['index'] - else: - continue - try: - rule_level = rules_metadata[rule_index]['defaultConfiguration']['level'] - except IndexError as e: - print(e, rule_index, len(rules_metadata)) - else: - if rule_level == 'error': - return True - return False - -if __name__ == "__main__": - if codeql_sarif_contain_error(sys.argv[1]): - sys.exit(1) diff --git a/.github/workflows/compilation_on_android_ubuntu.yml b/.github/workflows/compilation_on_android_ubuntu.yml index f1e437774..21437ffc0 100644 --- a/.github/workflows/compilation_on_android_ubuntu.yml +++ b/.github/workflows/compilation_on_android_ubuntu.yml @@ -389,14 +389,14 @@ jobs: cd /opt sudo wget ${{ matrix.wasi_sdk_release }} sudo tar -xzf wasi-sdk-*.tar.gz - sudo mv wasi-sdk-20.0 wasi-sdk + sudo ln -sf wasi-sdk-20.0 wasi-sdk - name: download and install wabt run: | cd /opt sudo wget ${{ matrix.wabt_release }} sudo tar -xzf wabt-1.0.31-*.tar.gz - sudo mv wabt-1.0.31 wabt + sudo ln -sf wabt-1.0.31 wabt - name: Get LLVM libraries id: retrieve_llvm_libs uses: actions/cache@v4 @@ -491,6 +491,13 @@ jobs: ./iwasm wasm-apps/trap.aot | grep "#" > call_stack_aot.txt bash -x ../symbolicate.sh + - name: Build Sample [native-stack-overflow] + run: | + cd samples/native-stack-overflow + ./build.sh + ./run.sh test1 + ./run.sh test2 + test: needs: [ diff --git a/.github/workflows/compilation_on_macos.yml b/.github/workflows/compilation_on_macos.yml index 4f59f2386..d9598a00b 100644 --- a/.github/workflows/compilation_on_macos.yml +++ b/.github/workflows/compilation_on_macos.yml @@ -56,7 +56,7 @@ jobs: build_llvm_libraries_on_intel_macos: uses: ./.github/workflows/build_llvm_libraries.yml with: - os: "macos-latest" + os: "macos-13" arch: "X86" build_llvm_libraries_on_arm_macos: uses: ./.github/workflows/build_llvm_libraries.yml @@ -70,7 +70,7 @@ jobs: strategy: matrix: include: - - os: macos-latest + - os: macos-13 llvm_cache_key: ${{ needs.build_llvm_libraries_on_intel_macos.outputs.cache_key }} steps: - name: checkout @@ -131,7 +131,7 @@ jobs: "-DWAMR_BUILD_TAIL_CALL=1", "-DWAMR_DISABLE_HW_BOUND_CHECK=1", ] - os: [macos-latest] + os: [macos-13] platform: [darwin] exclude: # uncompatiable feature and platform @@ -173,7 +173,7 @@ jobs: - make_options_run_mode: $LLVM_EAGER_JIT_BUILD_OPTIONS make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1" include: - - os: macos-latest + - os: macos-13 llvm_cache_key: ${{ needs.build_llvm_libraries_on_intel_macos.outputs.cache_key }} steps: - name: checkout @@ -218,7 +218,7 @@ jobs: #$LLVM_EAGER_JIT_BUILD_OPTIONS, #$AOT_BUILD_OPTIONS, ] - os: [macos-latest] + os: [macos-13] wasi_sdk_release: [ "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sdk-20.0-macos.tar.gz", @@ -250,7 +250,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-latest, macos-14] + os: [macos-13, macos-14] wasi_sdk_release: [ "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sdk-20.0-macos.tar.gz", @@ -260,7 +260,7 @@ jobs: "https://github.com/WebAssembly/wabt/releases/download/1.0.31/wabt-1.0.31-macos-12.tar.gz", ] include: - - os: macos-latest + - os: macos-13 llvm_cache_key: ${{ needs.build_llvm_libraries_on_intel_macos.outputs.cache_key }} - os: macos-14 llvm_cache_key: ${{ needs.build_llvm_libraries_on_arm_macos.outputs.cache_key }} @@ -273,14 +273,14 @@ jobs: cd /opt sudo wget ${{ matrix.wasi_sdk_release }} sudo tar -xzf wasi-sdk-*.tar.gz - sudo mv wasi-sdk-20.0 wasi-sdk + sudo ln -sf wasi-sdk-20.0 wasi-sdk - name: download and install wabt run: | cd /opt sudo wget ${{ matrix.wabt_release }} sudo tar -xzf wabt-1.0.31-*.tar.gz - sudo mv wabt-1.0.31 wabt + sudo ln -sf wabt-1.0.31 wabt - name: Build Sample [basic] run: | @@ -346,7 +346,7 @@ jobs: cmake .. cmake --build . --config Release --parallel 4 working-directory: wamr-compiler - + - name: Build Sample [wasi-threads] run: | cd samples/wasi-threads @@ -378,4 +378,11 @@ jobs: cmake --build . --config Debug --parallel 4 ./iwasm wasm-apps/trap.wasm | grep "#" > call_stack.txt ./iwasm wasm-apps/trap.aot | grep "#" > call_stack_aot.txt - bash -x ../symbolicate.sh + bash -x ../symbolicate.sh + + - name: Build Sample [native-stack-overflow] + run: | + cd samples/native-stack-overflow + ./build.sh + ./run.sh test1 + ./run.sh test2 diff --git a/.github/workflows/compilation_on_nuttx.yml b/.github/workflows/compilation_on_nuttx.yml index 98cbb24ff..5ff2d181c 100644 --- a/.github/workflows/compilation_on_nuttx.yml +++ b/.github/workflows/compilation_on_nuttx.yml @@ -61,8 +61,6 @@ jobs: "boards/arm/rp2040/raspberrypi-pico/configs/nsh", # cortex-m7 "boards/arm/stm32h7/nucleo-h743zi/configs/nsh", - # riscv32imc - "boards/risc-v/espressif/esp32c3-generic/configs/nsh", # riscv32gc "boards/risc-v/qemu-rv/rv-virt/configs/nsh", # riscv64gc @@ -114,3 +112,26 @@ jobs: cd nuttx tools/configure.sh ${{ matrix.nuttx_board_config }} make -j$(nproc) EXTRAFLAGS=-Werror + + - name: Checkout Bloaty + uses: actions/checkout@v3 + with: + repository: google/bloaty + submodules: recursive + path: bloaty + + - name: Build Bloaty + run: | + cmake -Bbuild -GNinja bloaty + cmake --build build + + - name: Size Report + run: | + echo "Build target: ${{ matrix.nuttx_board_config }}" + echo "WAMR build config: ${{ matrix.wamr_config_option }}" + echo "WAMR size:" + build/bloaty -d compileunits --source-filter wamr nuttx/nuttx + echo "libc-builtin size (if enabled):" + build/bloaty -d compileunits --source-filter libc-builtin nuttx/nuttx + echo "libc-wasi size (if enabled):" + build/bloaty -d compileunits --source-filter libc-wasi nuttx/nuttx diff --git a/.github/workflows/create_tag.yml b/.github/workflows/create_tag.yml index 27eee2acf..5480592a9 100644 --- a/.github/workflows/create_tag.yml +++ b/.github/workflows/create_tag.yml @@ -32,8 +32,22 @@ jobs: - name: prepare id: preparation run: | - # show latest 3 versions - git tag --list WAMR-*.*.* --sort=committerdate --format="%(refname:short)" | tail -n 3 + # show latest 3 versions on the branch that create release + # Set the initial commit to the head of the branch + commit="HEAD" + # + # Loop to get the three most recent tags + for i in {1..3} + do + # Get the most recent tag reachable from the current commit + tag=$(git describe --tags --abbrev=0 $commit) + + # Print the tag + echo "$tag" + + # Move to the commit before the found tag to find the next tag in the next iteration + commit=$(git rev-list -n 1 $tag^) + done # compare latest git tag and semantic version definition result=$(python3 ./.github/scripts/fetch_and_compare_version.py) echo "script result is ${result}" diff --git a/.github/workflows/nightly_run.yml b/.github/workflows/nightly_run.yml index 341194df8..3e4e89caf 100644 --- a/.github/workflows/nightly_run.yml +++ b/.github/workflows/nightly_run.yml @@ -8,7 +8,7 @@ on: types: - opened - synchronize - # running nightly pipeline if you're changing it + # running nightly pipeline if you're changing it # stress tests are run only in nightly at the moment, so running them in they are changed paths: - ".github/workflows/nightly_run.yml" @@ -54,7 +54,7 @@ jobs: with: os: "ubuntu-22.04" arch: "X86" - + build_wamrc: needs: [ @@ -65,7 +65,7 @@ jobs: matrix: include: - os: ubuntu-20.04 - llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2004.outputs.cache_key }} + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2004.outputs.cache_key }} steps: - name: checkout uses: actions/checkout@v4 @@ -459,13 +459,13 @@ jobs: cd /opt sudo wget ${{ matrix.wasi_sdk_release }} sudo tar -xzf wasi-sdk-*.tar.gz - sudo mv wasi-sdk-20.0 wasi-sdk + sudo ln -sf wasi-sdk-20.0 wasi-sdk - name: download and install wabt run: | cd /opt sudo wget ${{ matrix.wabt_release }} sudo tar -xzf wabt-1.0.31-*.tar.gz - sudo mv wabt-1.0.31 wabt + sudo ln -sf wabt-1.0.31 wabt - name: Get LLVM libraries id: retrieve_llvm_libs @@ -547,6 +547,34 @@ jobs: cd samples/terminate ./build.sh ./run.sh + + - name: Build Sample [native-stack-overflow] + run: | + cd samples/native-stack-overflow + ./build.sh + ./run.sh test1 + ./run.sh test2 + + - name: Build Sample [native-lib] + run: | + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + ./iwasm --native-lib=./libtest_add.so --native-lib=./libtest_sqrt.so --native-lib=./libtest_hello.so --native-lib=./libtest_hello2.so wasm-app/test.wasm + working-directory: ./samples/native-lib + + - name: checkout wamr-app-framework + run: git clone https://github.com/bytecodealliance/wamr-app-framework.git + - name: download wamr-app-framework dependencies + run: LVGL=0 LV_DRIVERS=0 ./download.sh + working-directory: ./wamr-app-framework/deps + - name: Build Sample [simple] + run: | + ./build.sh -p host-interp + python3 ./sample_test_run.py $(pwd)/out + exit $? + working-directory: ./wamr-app-framework/samples/simple + test: needs: [ @@ -643,7 +671,7 @@ jobs: sudo tar -xzf wasi-sdk-*.tar.gz sudo mv wasi-sdk-20.0 wasi-sdk - # It is a temporary solution until new wasi-sdk that includes bug fixes is released + # It is a temporary solution until new wasi-sdk that includes bug fixes is released - name: build wasi-libc from source if: matrix.test_option == '$WASI_TEST_OPTIONS' run: | diff --git a/.github/workflows/release_process.yml b/.github/workflows/release_process.yml index de62867a8..2dce1b55c 100644 --- a/.github/workflows/release_process.yml +++ b/.github/workflows/release_process.yml @@ -69,7 +69,7 @@ jobs: needs: [create_tag, create_release] uses: ./.github/workflows/build_llvm_libraries.yml with: - os: "macos-latest" + os: "macos-13" arch: "AArch64 ARM Mips RISCV X86" # @@ -100,7 +100,7 @@ jobs: with: llvm_cache_key: ${{ needs.build_llvm_libraries_on_macos.outputs.cache_key }} release: true - runner: macos-latest + runner: macos-13 upload_url: ${{ needs.create_release.outputs.upload_url }} ver_num: ${{ needs.create_tag.outputs.new_ver }} @@ -132,7 +132,7 @@ jobs: with: cwd: product-mini/platforms/darwin llvm_cache_key: ${{ needs.build_llvm_libraries_on_macos.outputs.cache_key }} - runner: macos-latest + runner: macos-13 upload_url: ${{ needs.create_release.outputs.upload_url }} ver_num: ${{ needs.create_tag.outputs.new_ver}} @@ -165,7 +165,7 @@ jobs: uses: ./.github/workflows/build_wamr_sdk.yml with: config_file: wamr_config_macos_release.cmake - runner: macos-latest + runner: macos-13 upload_url: ${{ needs.create_release.outputs.upload_url }} ver_num: ${{ needs.create_tag.outputs.new_ver}} wasi_sdk_url: https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-macos.tar.gz @@ -212,7 +212,7 @@ jobs: needs: [create_tag, create_release] uses: ./.github/workflows/build_wamr_lldb.yml with: - runner: macos-latest + runner: macos-13 arch: universal upload_url: ${{ needs.create_release.outputs.upload_url }} ver_num: ${{ needs.create_tag.outputs.new_ver}} diff --git a/.github/workflows/spec_test_on_nuttx.yml b/.github/workflows/spec_test_on_nuttx.yml index 1fa314010..1dbeb8348 100644 --- a/.github/workflows/spec_test_on_nuttx.yml +++ b/.github/workflows/spec_test_on_nuttx.yml @@ -22,11 +22,14 @@ on: workflow_dispatch: +# Note on INTERPRETERS_WAMR_STACK_GUARD_SIZE: +# https://github.com/apache/nuttx-apps/pull/2241 is not included in +# releases/12.4 branch as of writing this. env: LLVM_CACHE_SUFFIX: "build-llvm_libraries_ex" WASI_SDK_PATH: "/opt/wasi-sdk" WAMR_COMMON_OPTION: - "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_STACKSIZE=327680\\nCONFIG_INTERPRETERS_WAMR_LOG=y\\nCONFIG_INTERPRETERS_WAMR_LIBC_BUILTIN=y\\nCONFIG_INTERPRETERS_WAMR_REF_TYPES=y\\nCONFIG_INTERPRETERS_WAMR_ENABLE_SPEC_TEST=y\\nCONFIG_INTERPRETERS_WAMR_SHARED_MEMORY=y\\nCONFIG_INTERPRETERS_WAMR_BULK_MEMORY=y\\nCONFIG_EOL_IS_LF=y\\nCONFIG_ARM_SEMIHOSTING_HOSTFS=y\\nCONFIG_ARM_SEMIHOSTING_HOSTFS_CACHE_COHERENCE=y\\nCONFIG_RISCV_SEMIHOSTING_HOSTFS=y\\nCONFIG_FS_HOSTFS=y\\nCONFIG_LIBC_FLOATINGPOINT=y\\n" + "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_STACKSIZE=327680\\nCONFIG_INTERPRETERS_WAMR_LOG=y\\nCONFIG_INTERPRETERS_WAMR_LIBC_BUILTIN=y\\nCONFIG_INTERPRETERS_WAMR_REF_TYPES=y\\nCONFIG_INTERPRETERS_WAMR_ENABLE_SPEC_TEST=y\\nCONFIG_INTERPRETERS_WAMR_SHARED_MEMORY=y\\nCONFIG_INTERPRETERS_WAMR_BULK_MEMORY=y\\nCONFIG_EOL_IS_LF=y\\nCONFIG_ARM_SEMIHOSTING_HOSTFS=y\\nCONFIG_ARM_SEMIHOSTING_HOSTFS_CACHE_COHERENCE=y\\nCONFIG_RISCV_SEMIHOSTING_HOSTFS=y\\nCONFIG_FS_HOSTFS=y\\nCONFIG_LIBC_FLOATINGPOINT=y\\nCONFIG_INTERPRETERS_WAMR_STACK_GUARD_SIZE=1024\\n" jobs: build_llvm_libraries: diff --git a/ADOPTERS.md b/ADOPTERS.md new file mode 100644 index 000000000..ef112e9a1 --- /dev/null +++ b/ADOPTERS.md @@ -0,0 +1,36 @@ +# WAMR adopters + +_If you are using WAMR in production/pre-production at your organization, please add your company name to this list. +The list is in alphabetical order._ + +| Organization | Contact | Status | Description of Use | +| -------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| [Alibaba](https://www.alibaba.com) | [@johnlanni](https://github.com/johnlanni) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Higress is a next-generation cloud-native gateway built on the core of open-source Istio + Envoy based on Alibaba's internal Envoy Gateway practice. | +| [Amazon](https://www.amazon.com) | [@loganek](https://github.com/loganek) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Prime Video is a global streaming service by Amazon that provides on-demand access to a vast library of movies, TV shows, and original programming, as well as live content. | +| [Ant Group](https://www.antgroup.com) | [@wei-tang](https://github.com/wei-tang) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | AntChain is a blockchain technology platform owned by Ant Group, a Chinese tech conglomerate that also runs Alipay, China's largest digital payment system. | +| [Bosch](https://www.bosch.com) | emily.ruppel@us.bosch.com | ![Undisclosed](https://img.shields.io/badge/-Undisclosed-orange?style=flat) | Silverline Cloud-Edge platform | +| [Disney](https://www.disney.com) | | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Disney+ Streaming | +| [Intel](https://www.intel.com) | [@wenyongh](https://github.com/wenyongh) | ![Undisclosed](https://img.shields.io/badge/-Undisclosed-orange?style=flat) | Edge and the embedded environments | +| [Moonbit](https://www.moonbitlang.com) | [@peter-jerry-ye](https://github.com/peter-jerry-ye) | ![Undisclosed](https://img.shields.io/badge/-Undisclosed-orange?style=flat) | MoonBit is an end-to-end programming language toolchain for cloud and edge computing using WebAssembly. | +| [Microsoft](https://www.microsoft.com) | [@Mossaka](https://github.com/Mossaka) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Hyperlight runs Wasm workloads in VMs without OS and kernel, it is a solution for improving the management and security of Wasm workloads on Azure. | +| [Midokura](https://www.midokura.com) | [@yamt](https://github.com/yamt) | ![Undisclosed](https://img.shields.io/badge/-Undisclosed-orange?style=flat) | The next-generation Edge AI sensing platform | +| [Siemens](https://www.siemens.com) | [@ttrenner](https://github.com/ttrenner) | ![Undisclosed](https://img.shields.io/badge/-Undisclosed-orange?style=flat) | Industrial, IoT | +| [Sony Semiconductor Solutions](https://www.sony-semicon.com) | [@dongsheng28849455](https://github.com/dongsheng28849455) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | AI digital camera | +| [Xiaomi](https://www.mi.com) | [@no1wudi](https://github.com/no1wudi) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Xiaomi Vela is Xiaomi's IoT embedded software platform based on the open-source, real-time operating system NuttX. | +| [Xiaomi](https://www.mi.com) | [@no1wudi](https://github.com/no1wudi) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | TEE (trusted execution environment) app engine. | + +# Adopted in open-source projects + +_The list is in alphabetical order._ + +| Project | Reference | +| ------------------------------------------------------------ | ------------------------------------------------------------ | +| [Apache Teaclave](https://github.com/apache/incubator-teaclave) | https://github.com/apache/incubator-teaclave/blob/master/docs/executing-wasm.md | +| [Envoy](https://github.com/envoyproxy/envoy) | https://github.com/envoyproxy/envoy/blob/main/docs/root/configuration/other_features/wasm.rst | +| [faasm](https://github.com/faasm/faasm) | https://github.com/faasm/faasm/blob/main/docs/source/wamr.md | +| [fluent-bit](https://github.com/fluent/fluent-bit) | https://github.com/fluent/fluent-bit/tree/master/lib/wasm-micro-runtime-WAMR-1.3.0 | +| [harfbuzz](https://github.com/harfbuzz/harfbuzz) | https://github.com/harfbuzz/harfbuzz/blob/main/docs/wasm-shaper.md#enabling-the-wasm-shaper-when-building-harfbuzz | +| [inclave-containers](https://github.com/inclavare-containers/inclavare-containers) | https://github.com/inclavare-containers/inclavare-containers | +| [private-data-objects](https://github.com/hyperledger-labs/private-data-objects) | https://github.com/hyperledger-labs/private-data-objects/blob/main/common/interpreter/wawaka_wasm/README.md | +| [runwasi](https://github.com/containerd/runwasi) | https://github.com/containerd/runwasi/pull/508 (WIP) | +| [Wasmnizer-ts](https://github.com/web-devkits/Wasmnizer-ts) | https://github.com/web-devkits/Wasmnizer-ts | diff --git a/CMakeLists.txt b/CMakeLists.txt index 8df86ddd0..0ffba05a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,11 @@ cmake_minimum_required (VERSION 3.0) +if(ESP_PLATFORM) + include (${COMPONENT_DIR}/build-scripts/esp-idf/wamr/CMakeLists.txt) + return() +endif() + project (iwasm) set (CMAKE_VERBOSE_MAKEFILE OFF) diff --git a/README.md b/README.md index d38a3208e..d89d0cd17 100644 --- a/README.md +++ b/README.md @@ -10,18 +10,23 @@ [Build WAMR](./doc/build_wamr.md) | [Build AOT Compiler](./wamr-compiler/README.md) | [Embed WAMR](./doc/embed_wamr.md) | [Export Native API](./doc/export_native_api.md) | [Build Wasm Apps](./doc/build_wasm_app.md) | [Samples](./samples/README.md) WebAssembly Micro Runtime (WAMR) is a lightweight standalone WebAssembly (Wasm) runtime with small footprint, high performance and highly configurable features for applications cross from embedded, IoT, edge to Trusted Execution Environment (TEE), smart contract, cloud native and so on. It includes a few parts as below: -- [**VMcore**](./core/iwasm/): A set of runtime libraries for loading and running Wasm modules. It supports several execution modes including interpreter, Ahead-of-Time compilation(AoT) and Just-in-Time compilation (JIT). The WAMR supports two JIT tiers - Fast JIT, LLVM JIT, and dynamic tier-up from Fast JIT to LLVM JIT. -- [**iwasm**](./product-mini/): The executable binary built with WAMR VMcore supports WASI and command line interface. +- [**VMcore**](./core/iwasm/): A set of runtime libraries for loading and running Wasm modules. It supports rich running modes including interpreter, Ahead-of-Time compilation(AoT) and Just-in-Time compilation (JIT). WAMR supports two JIT tiers - Fast JIT, LLVM JIT, and dynamic tier-up from Fast JIT to LLVM JIT. +- [**iwasm**](./product-mini/): The executable binary built with WAMR VMcore which supports WASI and command line interface. - [**wamrc**](./wamr-compiler/): The AOT compiler to compile Wasm file into AOT file - Useful components and tools for building real solutions with WAMR vmcore: - [App-framework](https://github.com/bytecodealliance/wamr-app-framework/blob/main/app-framework/README.md): A framework for supporting APIs for the Wasm applications - - [App-manager](https://github.com/bytecodealliance/wamr-app-framework/blob/main/app-mgr/README.md): a framework for dynamical loading the Wasm module remotely + - [App-manager](https://github.com/bytecodealliance/wamr-app-framework/blob/main/app-mgr/README.md): A framework for dynamical loading the Wasm module remotely - [WAMR-IDE](./test-tools/wamr-ide): An experimental VSCode extension for developping WebAssembly applications with C/C++ ### Key features - Full compliant to the W3C Wasm MVP -- Small runtime binary size (~85K for interpreter and ~50K for AOT) and low memory usage +- Small runtime binary size (core vmlib on cortex-m4f with tail-call/bulk memroy/shared memroy support, text size from bloaty) + * ~58.9K for fast interpreter + * ~56.3K for classic interpreter + * ~29.4K for aot runtime + * ~21.4K for libc-wasi library + * ~3.7K for libc-builtin library - Near to native speed by AOT and JIT - Self-implemented AOT module loader to enable AOT working on Linux, Windows, MacOS, Android, SGX and MCU systems - Choices of Wasm application libc support: the built-in libc subset for the embedded environment or [WASI](https://github.com/WebAssembly/WASI) for the standard libc @@ -41,9 +46,8 @@ WebAssembly Micro Runtime (WAMR) is a lightweight standalone WebAssembly (Wasm) - [wasm-c-api](https://github.com/WebAssembly/wasm-c-api), ref to [document](doc/wasm_c_api.md) and [sample](samples/wasm-c-api) - [128-bit SIMD](https://github.com/WebAssembly/simd), ref to [samples/workload](samples/workload) - [Reference Types](https://github.com/WebAssembly/reference-types), ref to [document](doc/ref_types.md) and [sample](samples/ref-types) -- [Non-trapping float-to-int conversions](https://github.com/WebAssembly/nontrapping-float-to-int-conversions) -- [Sign-extension operators](https://github.com/WebAssembly/sign-extension-ops), [Bulk memory operations](https://github.com/WebAssembly/bulk-memory-operations) -- [Multi-value](https://github.com/WebAssembly/multi-value), [Tail-call](https://github.com/WebAssembly/tail-call), [Shared memory](https://github.com/WebAssembly/threads/blob/main/proposals/threads/Overview.md#shared-linear-memory) +- [Bulk memory operations](https://github.com/WebAssembly/bulk-memory-operations), [Shared memory](https://github.com/WebAssembly/threads/blob/main/proposals/threads/Overview.md#shared-linear-memory), [Memory64](https://github.com/WebAssembly/memory64) +- [Tail-call](https://github.com/WebAssembly/tail-call), [Garbage Collection](https://github.com/WebAssembly/gc), [Exception Handling](https://github.com/WebAssembly/exception-handling) ### Supported architectures and platforms The WAMR VMcore supports the following architectures: @@ -61,14 +65,14 @@ The following platforms are supported, click each link below for how to build iw ## Getting started - [Build VM core](./doc/build_wamr.md) and [Build wamrc AOT compiler](./wamr-compiler/README.md) - [Build iwasm (mini product)](./product-mini/README.md): [Linux](./product-mini/README.md#linux), [SGX](./doc/linux_sgx.md), [MacOS](./product-mini/README.md#macos) and [Windows](./product-mini/README.md#windows) -- [Embed into C/C++](./doc/embed_wamr.md), [Embed into Python](./language-bindings/python), [Embed into Go](./language-bindings/go) +- [Embed into C/C++](./doc/embed_wamr.md), [Embed into Python](./language-bindings/python), [Embed into Go](./language-bindings/go), [Embed in Rust](./language-bindings/rust) - [Register native APIs for Wasm applications](./doc/export_native_api.md) - [Build wamrc AOT compiler](./wamr-compiler/README.md) - [Build Wasm applications](./doc/build_wasm_app.md) - [Port WAMR to a new platform](./doc/port_wamr.md) - [VS Code development container](./doc/devcontainer.md) - [Samples](./samples) and [Benchmarks](./tests/benchmarks) - +- [End-user APIs documentation](https://bytecodealliance.github.io/wamr.dev/apis/) ### Performance and memory @@ -82,7 +86,6 @@ The following platforms are supported, click each link below for how to build iw - [Performance and footprint data](https://github.com/bytecodealliance/wasm-micro-runtime/wiki/Performance): the performance and footprint data - Project Technical Steering Committee ==================================== The [WAMR PTSC Charter](./TSC_Charter.md) governs the operations of the project TSC. diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 3fcfaf4bc..0721fc638 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,102 @@ +## WAMR-2.0.0 + +### Breaking Changes +- The AOT ABI was changed after GC and memory64 features were introduced: + - Implement GC feature for interpreter, AOT and LLVM-JIT (#3125) + - Implement memory64 for classic interpreter (#3266) + - Always allocate linear memory using mmap (#3052) + - Refactor APIs and data structures as preliminary work for Memory64 (#3209) +- Remove unused argument in wasm_runtime_lookup_function (#3218) +- Separate app-manager and app-framework from WAMR (#3129) + +### New Features +- Implement GC feature for interpreter, AOT and LLVM-JIT (#3125) +- Implement memory64 for classic interpreter (#3266) +- Add wasi_ephemeral_nn module support (#3241) + +### Bug Fixes +- EH: Fix broken stack usage calculation (#3121) +- Fix loader check_wasi_abi_compatibility (#3126) +- Fix possible integer overflow in loader target block check (#3133) +- Fix locel.set in polymorphic stack (#3135) +- Fix threads opcodes' boundary check in classic-interp and fast-interp (#3136) +- fast-interp: Fix copy_stack_top_i64 overlap issue (#3146) +- Fix a ubsan complaint "applying zero offset to null pointer" (#3160) +- fast-interp: Fix GC opcode ref.as_non_null (#3156) +- Fix llvm jit push funcref/externref result type issue (#3169) +- Fix wasm loader handling opcode br_table (#3176) +- Fix ref.func opcode check when GC is enabled (#3181) +- lldb_function_to_function_dbi: Fix a null dereference (#3189) +- Fix compilation errors on MinGW (#3217) +- Fix compilation errors on esp-idf platform (#3224) +- Fix aot relocation symbols not found on windows 32-bit (#3231) +- posix_file.c: Correct the dirfd argument that passes to fstatat (#3244) +- Fix compilation errors on zephyr platform (#3255) +- Fix dynamic offset not updated in op_br for block with ret type (#3269) +- aot debug: Fix a NULL dereference (#3274) +- thread mgr: Free aux stack only when it was allocated (#3282) +- interp: Restore context from prev_frame after tail calling a native function (#3283) +- Sync simd opcode definitions spec (#3290) +- Fix posix_fadvise error handling (#3323) +- Fix windows relocation string parsing issue (#3333) + +### Enhancements +- Zero the memory mapped from os_mmap in NuttX (#3132) +- Use logger for runtime error/debug prints (#3097) +- aot_compile_op_call: Stop setting calling convention explicitly (#3140) +- aot compiler: Place precheck wrapper before the corresponding wrapped function (#3141) +- Fix null pointer access in fast-interp when configurable soft bound check is enabled (#3150) +- Clarify how to verify SGX evidence without an Intel SGX-enabled platform (#3158) +- zephyr: Use zephyr sys_cache instead of CMSIS (#3162) +- VSCode IDE enhancement and readme update (#3172) +- Add vprintf override for android and esp-idf (#3174) +- zephyr: Include math only with minimal libc (#3177) +- zephyr: Implement Alloc_With_System_Allocator (#3179) +- Use indirect call in pre-checker function to avoid relocation in XIP mode (#3142) +- Implement the remaining Windows filesystem functions (#3166) +- Fix LLVM assertion failure and update CONTRIBUTING.md (#3197) +- Allow overriding max memory on module instantiation (#3198) +- Get location info from function indexes in addr2line script (#3206) +- Demangle function names in stack trace when using addr2line script (#3211) +- Refactor APIs and data structures as preliminary work for Memory64 (#3209) +- Allow converting the zero wasm address to native (#3215) +- Small refactor on WASMModuleInstance and fix Go/Python language bindings (#3227) +- Add esp32c6 support (#3234) +- Make android platform's cmake flags configurable (#3239) +- Go binding: Change C.long to C.int64_t when call wasm_runtime_set_wasi_args_ex (#3235) +- Implement apis to set and get the name of a wasm module (#3254) +- Append '\0' to every name string in aot name section (#3249) +- Add cmake flag to control aot intrinsics (#3261) +- Add lock and ref_count for runtime init (#3263) +- nuttx: Migrate NuttX CMake build for WAMR (#3256) +- LLVM 19: Switch to debug records (#3272) +- aot debug: Process lldb_function_to_function_dbi only for C (#3278) +- Fix warnings/issues reported in Windows and by CodeQL/Coverity (#3275) +- Enhance wasm loading with LoadArgs and support module names (#3265) +- Add wamr to esp-idf components registry (#3287) +- zephyr: Add missing pthread library functions (#3291) +- Add more checks in wasm loader (#3300) +- Log warning if growing table failed (#3310) +- Enhance GC subtyping checks (#3317) +- User defined memory allocator for different purposes (#3316) +- Add a comment on WASM_STACK_GUARD_SIZE (#3332) +- Allow executing malloc/free from native in memory64 mode (#3315) +- Add functions to expose module import/export info (#3330) + +### Others +- Add ARM MacOS to the CI (#3120) +- Download jetstream src from github instead of browserbench.org (#3196) +- Update document to add wamr-rust-sdk introduction (#3204) +- Fix nightly run tsan ASLR issue (#3233) +- Add CodeQL Workflow for Code Security Analysis (#2812) +- Add issue templates (#3248) +- Fix CI error when install packages for macos-14 (#3270) +- Update document for GC, exception handling and memory64 features (#3284) +- Update release CI (#3295) +- Add native-stack-overflow sample (#3321) + +--- + ## WAMR-1.3.2 ### Breaking Changes diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index 8422b060b..c3a957d33 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -134,7 +134,9 @@ endif () # Sanitizers -set(WAMR_BUILD_SANITIZER $ENV{WAMR_BUILD_SANITIZER}) +if (NOT DEFINED WAMR_BUILD_SANITIZER) + set(WAMR_BUILD_SANITIZER $ENV{WAMR_BUILD_SANITIZER}) +endif () if (NOT DEFINED WAMR_BUILD_SANITIZER) set(WAMR_BUILD_SANITIZER "") @@ -554,3 +556,9 @@ else () # Disable aot intrinsics for interp, fast-jit and llvm-jit add_definitions (-DWASM_ENABLE_AOT_INTRINSICS=0) endif () +if (WAMR_BUILD_ALLOC_WITH_USAGE EQUAL 1) + add_definitions(-DWASM_MEM_ALLOC_WITH_USAGE=1) +endif() +if (NOT WAMR_BUILD_SANITIZER STREQUAL "") + message (" Sanitizer ${WAMR_BUILD_SANITIZER} enabled") +endif () diff --git a/build-scripts/esp-idf/wamr/CMakeLists.txt b/build-scripts/esp-idf/wamr/CMakeLists.txt index 47ccb055f..af0d8efc9 100644 --- a/build-scripts/esp-idf/wamr/CMakeLists.txt +++ b/build-scripts/esp-idf/wamr/CMakeLists.txt @@ -2,56 +2,102 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # Set WAMR's build options -if("${IDF_TARGET}" STREQUAL "esp32c3" OR "${IDF_TARGET}" STREQUAL "esp32c6") - set(WAMR_BUILD_TARGET "RISCV32") -else() - set(WAMR_BUILD_TARGET "XTENSA") -endif() - -set(WAMR_BUILD_PLATFORM "esp-idf") - -if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release) -endif () - -if (NOT DEFINED WAMR_BUILD_INTERP) - set (WAMR_BUILD_INTERP 1) -endif () - -if (NOT DEFINED WAMR_BUILD_FAST_INTERP) - set (WAMR_BUILD_FAST_INTERP 1) -endif () - -if (NOT DEFINED WAMR_BUILD_AOT) - set (WAMR_BUILD_AOT 1) -endif () - -if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) - set (WAMR_BUILD_LIBC_BUILTIN 1) -endif () - -if (NOT DEFINED WAMR_BUILD_APP_FRAMEWORK) - set (WAMR_BUILD_APP_FRAMEWORK 0) -endif () - if (NOT CMAKE_BUILD_EARLY_EXPANSION) - if (WAMR_BUILD_TARGET STREQUAL "XTENSA") - idf_build_set_property(COMPILE_DEFINITIONS "-DBUILD_TARGET_XTENSA=1" APPEND) - endif () - if (WAMR_BUILD_INTERP) - idf_build_set_property(COMPILE_DEFINITIONS "-DWASM_ENABLE_INTERP=1" APPEND) - endif () - if (WAMR_BUILD_AOT) - idf_build_set_property(COMPILE_DEFINITIONS "-DWASM_ENABLE_AOT=1" APPEND) - endif () - set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..) - include(${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) -endif() + if (CONFIG_IDF_TARGET_ARCH_RISCV) + set (WAMR_BUILD_TARGET "RISCV32") + elseif (CONFIG_IDF_TARGET_ARCH_XTENSA) + set (WAMR_BUILD_TARGET "XTENSA") + else () + message (FATAL_ERROR "Arch ${CONFIG_IDF_TARGET_ARCH} is not supported") + endif () -idf_component_register(SRCS ${WAMR_RUNTIME_LIB_SOURCE} ${PLATFORM_SHARED_SOURCE} - INCLUDE_DIRS ${IWASM_DIR}/include ${UTILS_SHARED_DIR} ${PLATFORM_SHARED_DIR} ${PLATFORM_SHARED_DIR}/../include - REQUIRES pthread lwip esp_timer -) + set (WAMR_BUILD_PLATFORM "esp-idf") + if (CONFIG_WAMR_BUILD_DEBUG) + set (CMAKE_BUILD_TYPE Debug) + else () + set (CMAKE_BUILD_TYPE Release) + endif () + if (CONFIG_WAMR_ENABLE_INTERP) + set (WAMR_BUILD_INTERP 1) + endif () + + if (CONFIG_WAMR_INTERP_FAST) + set (WAMR_BUILD_FAST_INTERP 1) + endif () + + if (CONFIG_WAMR_ENABLE_AOT) + set (WAMR_BUILD_AOT 1) + endif () + + if (CONFIG_WAMR_ENABLE_LIBC_BUILTIN) + set (WAMR_BUILD_LIBC_BUILTIN 1) + endif () + + if (CONFIG_WAMR_INTERP_LOADER_MINI) + set (WAMR_BUILD_MINI_LOADER 1) + endif () + + if (CONFIG_WAMR_ENABLE_MULTI_MODULE) + set (WAMR_BUILD_MULTI_MODULE 1) + endif () + + if (CONFIG_WAMR_ENABLE_SHARED_MEMORY) + set (WAMR_BUILD_SHARED_MEMORY 1) + endif () + + if (CONFIG_WAMR_ENABLE_MEMORY_PROFILING) + set (WAMR_BUILD_MEMORY_PROFILING 1) + endif () + + if (CONFIG_WAMR_ENABLE_PERF_PROFILING) + set (WAMR_BUILD_PERF_PROFILING 1) + endif () + + if (CONFIG_WAMR_ENABLE_REF_TYPES) + set (WAMR_BUILD_REF_TYPES 1) + endif () + + if (CONFIG_WAMR_ENABLE_LIBC_WASI) + set (WAMR_BUILD_LIBC_WASI 1) + endif () + + if (CONFIG_WAMR_ENABLE_LIB_PTHREAD) + set (WAMR_BUILD_LIB_PTHREAD 1) + endif () + + set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..) + include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + + list (APPEND srcs "${WAMR_RUNTIME_LIB_SOURCE}" + "${PLATFORM_SHARED_SOURCE}") + + set (include_dirs "${IWASM_DIR}/include" + "${UTILS_SHARED_DIR}" + "${PLATFORM_SHARED_DIR}" + "${PLATFORM_SHARED_DIR}/../include" + "${IWASM_COMMON_DIR}") +endif () + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${include_dirs} + REQUIRES pthread lwip esp_timer + KCONFIG ${CMAKE_CURRENT_LIST_DIR}/Kconfig) + +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") + +if (CONFIG_IDF_TARGET_ARCH_RISCV) + target_compile_definitions(${COMPONENT_LIB} PUBLIC -DBUILD_TARGET_RISCV32_ILP32=1) +elseif (CONFIG_IDF_TARGET_ARCH_XTENSA) + target_compile_definitions(${COMPONENT_LIB} PUBLIC -DBUILD_TARGET_XTENSA=1) +endif () + +if (CONFIG_WAMR_ENABLE_AOT) + target_compile_definitions(${COMPONENT_LIB} PUBLIC -DWASM_ENABLE_AOT=1) +endif () + +if (CONFIG_WAMR_ENABLE_INTERP) + target_compile_definitions(${COMPONENT_LIB} PUBLIC -DWASM_ENABLE_INTERP=1) +endif () diff --git a/build-scripts/esp-idf/wamr/Kconfig b/build-scripts/esp-idf/wamr/Kconfig new file mode 100644 index 000000000..5ed496255 --- /dev/null +++ b/build-scripts/esp-idf/wamr/Kconfig @@ -0,0 +1,77 @@ +menu "WASM Micro Runtime" + choice WAMR_BUILD_TYPE + prompt "Build type" + default WAMR_BUILD_RELEASE + + config WAMR_BUILD_RELEASE + bool "Release" + + config WAMR_BUILD_DEBUG + bool "Debug" + endchoice + + config WAMR_ENABLE_AOT + bool "AOT" + default y + + menuconfig WAMR_ENABLE_INTERP + bool "Interpreter" + default y + + if WAMR_ENABLE_INTERP + + choice WAMR_INTERP_MODE + prompt "Interpreter mode" + default WAMR_INTERP_FAST + + config WAMR_INTERP_CLASSIC + bool "Classic" + + config WAMR_INTERP_FAST + bool "Fast" + endchoice + + choice WAMR_INTERP_LOADER_MODE + prompt "Loader mode" + default WAMR_INTERP_LOADER_NORMAL + + config WAMR_INTERP_LOADER_NORMAL + bool "Normal" + + config WAMR_INTERP_LOADER_MINI + bool "Mini" + endchoice + endif + + config WAMR_ENABLE_LIB_PTHREAD + bool "Lib pthread" + default y + + config WAMR_ENABLE_LIBC_BUILTIN + bool "Libc builtin" + default y + + config WAMR_ENABLE_LIBC_WASI + bool "Libc WASI" + default y + + config WAMR_ENABLE_MEMORY_PROFILING + bool "Memory profiling" + default n + + config WAMR_ENABLE_MULTI_MODULE + bool "Multi module" + default n + + config WAMR_ENABLE_PERF_PROFILING + bool "Performance profiling" + default n + + config WAMR_ENABLE_REF_TYPES + bool "Reference types" + default n + + config WAMR_ENABLE_SHARED_MEMORY + bool "Shared memory" + default n +endmenu diff --git a/core/config.h b/core/config.h index d84ed3f36..0a77ec949 100644 --- a/core/config.h +++ b/core/config.h @@ -445,20 +445,91 @@ #endif /* Reserved bytes to the native thread stack boundary, throw native - stack overflow exception if the guard boudary is reached */ + * stack overflow exception if the guard boudary is reached + * + * WASM_STACK_GUARD_SIZE needs to be large enough for: + * + * - native functions + * + * w/o hw bound check, the overhead (aot_call_function etc) + the native + * function itself. as of writing this, the former is about 1000 bytes + * on macOS amd64. + * + * with hw bound check, theoretically, only needs to cover the logic to + * set up the jmp_buf stack. + * + * - aot runtime functions + * eg. aot_enlarge_memory. + * + * - w/o hw bound check, the intepreter loop + * + * the stack consumption heavily depends on compiler settings, + * especially for huge functions like the classic interpreter's + * wasm_interp_call_func_bytecode: + * + * 200 bytes (release build, macOS/amd64) + * 2600 bytes (debug build, macOS/amd64) + * + * libc snprintf (used by eg. wasm_runtime_set_exception) consumes about + * 1600 bytes stack on macOS/amd64, about 2000 bytes on Ubuntu amd64 20.04. + * + * - stack check wrapper functions generated by the aot compiler + * (--stack-bounds-checks=1) + * + * wamrc issues a warning + * "precheck functions themselves consume relatively large amount of stack" + * when it detects wrapper functions requiring more than 1KB. + * + * Note: on platforms with lazy function binding, don't forget to consider + * the symbol resolution overhead on the first call. For example, + * on Ubuntu amd64 20.04, it seems to consume about 1500 bytes. + * For some reasons, macOS amd64 12.7.4 seems to resolve symbols eagerly. + * (Observed with a binary with traditional non-chained fixups.) + * The latest macOS seems to apply chained fixups in kernel on page-in time. + * (thus it wouldn't consume userland stack.) + */ #ifndef WASM_STACK_GUARD_SIZE #if WASM_ENABLE_UVWASI != 0 /* UVWASI requires larger native stack */ #define WASM_STACK_GUARD_SIZE (4096 * 6) #else -#define WASM_STACK_GUARD_SIZE (1024) +/* + * Use a larger default for platforms like macOS/Linux. + * + * For example, the classic intepreter loop which ended up with a trap + * (wasm_runtime_set_exception) would consume about 2KB stack on x86-64 + * macOS. On Ubuntu amd64 20.04, it seems to consume a bit more. + * + * Although product-mini/platforms/nuttx always overrides + * WASM_STACK_GUARD_SIZE, exclude NuttX here just in case. + */ +#if defined(__APPLE__) || (defined(__unix__) && !defined(__NuttX__)) +#if BH_DEBUG != 0 /* assumption: BH_DEBUG matches CMAKE_BUILD_TYPE=Debug */ +#define WASM_STACK_GUARD_SIZE (1024 * 5) +#else +#define WASM_STACK_GUARD_SIZE (1024 * 3) +#endif +#else +/* + * Otherwise, assume very small requirement for now. + * + * Embedders for very small devices likely fine-tune WASM_STACK_GUARD_SIZE + * for their specific applications anyway. + */ +#define WASM_STACK_GUARD_SIZE 1024 +#endif #endif #endif /* Guard page count for stack overflow check with hardware trap */ #ifndef STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT +#if defined(__APPLE__) && defined(__aarch64__) +/* Note: on macOS/iOS arm64, the user page size is 16KB */ +#define STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT 1 +#else #define STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT 3 #endif +#endif /* Default wasm block address cache size and conflict list size */ #ifndef BLOCK_ADDR_CACHE_SIZE @@ -587,4 +658,8 @@ #define WASM_TABLE_MAX_SIZE 1024 #endif +#ifndef WASM_MEM_ALLOC_WITH_USAGE +#define WASM_MEM_ALLOC_WITH_USAGE 0 +#endif + #endif /* end of _CONFIG_H_ */ diff --git a/core/iwasm/aot/aot_loader.c b/core/iwasm/aot/aot_loader.c index 1634a8977..7db0db7cc 100644 --- a/core/iwasm/aot/aot_loader.c +++ b/core/iwasm/aot/aot_loader.c @@ -16,6 +16,10 @@ #include "debug/jit_debug.h" #endif +#if WASM_ENABLE_LINUX_PERF != 0 +#include "aot_perf_map.h" +#endif + #define YMM_PLT_PREFIX "__ymm@" #define XMM_PLT_PREFIX "__xmm@" #define REAL_PLT_PREFIX "__real@" @@ -1430,9 +1434,20 @@ load_table_init_data_list(const uint8 **p_buf, const uint8 *buf_end, read_uint64(buf, buf_end, init_expr_value); #if WASM_ENABLE_GC != 0 if (wasm_is_type_multi_byte_type(elem_type)) { - /* TODO: check ref_type */ - read_uint16(buf, buf_end, reftype.ref_ht_common.ref_type); - read_uint16(buf, buf_end, reftype.ref_ht_common.nullable); + uint16 ref_type, nullable; + read_uint16(buf, buf_end, ref_type); + if (elem_type != ref_type) { + set_error_buf(error_buf, error_buf_size, "invalid elem type"); + return false; + } + reftype.ref_ht_common.ref_type = (uint8)ref_type; + read_uint16(buf, buf_end, nullable); + if (nullable != 0 && nullable != 1) { + set_error_buf(error_buf, error_buf_size, + "invalid nullable value"); + return false; + } + reftype.ref_ht_common.nullable = (uint8)nullable; read_uint32(buf, buf_end, reftype.ref_ht_common.heap_type); } else @@ -1510,30 +1525,42 @@ fail: return false; } +static void +destroy_type(AOTType *type) +{ +#if WASM_ENABLE_GC != 0 + if (type->ref_count > 1) { + /* The type is referenced by other types + of current aot module */ + type->ref_count--; + return; + } + + if (type->type_flag == WASM_TYPE_FUNC) { + AOTFuncType *func_type = (AOTFuncType *)type; + if (func_type->ref_type_maps != NULL) { + bh_assert(func_type->ref_type_map_count > 0); + wasm_runtime_free(func_type->ref_type_maps); + } + } + else if (type->type_flag == WASM_TYPE_STRUCT) { + AOTStructType *struct_type = (AOTStructType *)type; + if (struct_type->ref_type_maps != NULL) { + bh_assert(struct_type->ref_type_map_count > 0); + wasm_runtime_free(struct_type->ref_type_maps); + } + } +#endif + wasm_runtime_free(type); +} + static void destroy_types(AOTType **types, uint32 count) { uint32 i; for (i = 0; i < count; i++) { - if (types[i]) { -#if WASM_ENABLE_GC != 0 - if (types[i]->type_flag == WASM_TYPE_FUNC) { - AOTFuncType *func_type = (AOTFuncType *)types[i]; - if (func_type->ref_type_maps != NULL) { - bh_assert(func_type->ref_type_map_count > 0); - wasm_runtime_free(func_type->ref_type_maps); - } - } - else if (types[i]->type_flag == WASM_TYPE_STRUCT) { - AOTStructType *struct_type = (AOTStructType *)types[i]; - if (struct_type->ref_type_maps != NULL) { - bh_assert(struct_type->ref_type_map_count > 0); - wasm_runtime_free(struct_type->ref_type_maps); - } - } -#endif - wasm_runtime_free(types[i]); + destroy_type(types[i]); } } wasm_runtime_free(types); @@ -1541,14 +1568,17 @@ destroy_types(AOTType **types, uint32 count) #if WASM_ENABLE_GC != 0 static void -init_base_type(AOTType *base_type, uint16 type_flag, bool is_sub_final, - uint32 parent_type_idx, uint16 rec_count, uint16 rec_idx) +init_base_type(AOTType *base_type, uint32 type_idx, uint16 type_flag, + bool is_sub_final, uint32 parent_type_idx, uint16 rec_count, + uint16 rec_idx) { base_type->type_flag = type_flag; + base_type->ref_count = 1; base_type->is_sub_final = is_sub_final; base_type->parent_type_idx = parent_type_idx; base_type->rec_count = rec_count; base_type->rec_idx = rec_idx; + base_type->rec_begin_type_idx = type_idx - rec_idx; } static bool @@ -1561,7 +1591,7 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, uint32 i, j; uint32 type_flag, param_cell_num, ret_cell_num; uint16 param_count, result_count, ref_type_map_count, rec_count, rec_idx; - bool is_sub_final; + bool is_equivalence_type, is_sub_final; uint32 parent_type_idx; WASMRefType ref_type; @@ -1575,12 +1605,31 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, /* Create each type */ for (i = 0; i < module->type_count; i++) { - buf = align_ptr(buf, 4); /* Read base type info */ read_uint16(buf, buf_end, type_flag); - read_uint16(buf, buf_end, is_sub_final); + + read_uint8(buf, buf_end, is_equivalence_type); + /* If there is an equivalence type, re-use it */ + if (is_equivalence_type) { + uint8 u8; + /* padding */ + read_uint8(buf, buf_end, u8); + (void)u8; + + read_uint32(buf, buf_end, j); + if (module->types[j]->ref_count == UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "wasm type's ref count too large"); + goto fail; + } + module->types[j]->ref_count++; + module->types[i] = module->types[j]; + continue; + } + + read_uint8(buf, buf_end, is_sub_final); read_uint32(buf, buf_end, parent_type_idx); read_uint16(buf, buf_end, rec_count); read_uint16(buf, buf_end, rec_idx); @@ -1605,7 +1654,7 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, types[i] = (AOTType *)func_type; - init_base_type((AOTType *)func_type, type_flag, is_sub_final, + init_base_type((AOTType *)func_type, i, type_flag, is_sub_final, parent_type_idx, rec_count, rec_idx); func_type->param_count = param_count; func_type->result_count = result_count; @@ -1711,7 +1760,7 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, offset = (uint32)offsetof(WASMStructObject, field_data); types[i] = (AOTType *)struct_type; - init_base_type((AOTType *)struct_type, type_flag, is_sub_final, + init_base_type((AOTType *)struct_type, i, type_flag, is_sub_final, parent_type_idx, rec_count, rec_idx); struct_type->field_count = field_count; struct_type->ref_type_map_count = ref_type_map_count; @@ -1797,7 +1846,7 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, types[i] = (AOTType *)array_type; - init_base_type((AOTType *)array_type, type_flag, is_sub_final, + init_base_type((AOTType *)array_type, i, type_flag, is_sub_final, parent_type_idx, rec_count, rec_idx); read_uint16(buf, buf_end, array_type->elem_flags); read_uint8(buf, buf_end, array_type->elem_type); @@ -1826,7 +1875,6 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, if (rec_count == 0) { bh_assert(rec_idx == 0); } - for (j = i - rec_idx; j <= i; j++) { AOTType *cur_type = module->types[j]; parent_type_idx = cur_type->parent_type_idx; @@ -1835,6 +1883,11 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, module->types[j]->parent_type = parent_type; module->types[j]->root_type = parent_type->root_type; + if (parent_type->inherit_depth == UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "parent type's inherit depth too large"); + goto fail; + } module->types[j]->inherit_depth = parent_type->inherit_depth + 1; } @@ -1852,7 +1905,7 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, AOTType *parent_type = module->types[parent_type_idx]; /* subtyping has been checked during compilation */ bh_assert(wasm_type_is_subtype_of( - module->types[j], parent_type, module->types, i)); + module->types[j], parent_type, module->types, i + 1)); (void)parent_type; } } @@ -3242,37 +3295,26 @@ load_relocation_section(const uint8 *buf, const uint8 *buf_end, bh_memcpy_s(symbol_name_buf, (uint32)sizeof(symbol_name_buf), symbol_name, symbol_name_len); - if ((group_name_len == strlen(".text") - || (module->is_indirect_mode - && group_name_len == strlen(".text") + 1)) + /* aot compiler emits string with '\0' since 2.0.0 */ + if (group_name_len == strlen(".text") + 1 && !strncmp(group_name, ".text", strlen(".text"))) { - if ((symbol_name_len == strlen(YMM_PLT_PREFIX) + 64 - || (module->is_indirect_mode - && symbol_name_len == strlen(YMM_PLT_PREFIX) + 64 + 1)) + /* aot compiler emits string with '\0' since 2.0.0 */ + if (symbol_name_len == strlen(YMM_PLT_PREFIX) + 64 + 1 && !strncmp(symbol_name, YMM_PLT_PREFIX, strlen(YMM_PLT_PREFIX))) { module->ymm_plt_count++; } - else if ((symbol_name_len == strlen(XMM_PLT_PREFIX) + 32 - || (module->is_indirect_mode - && symbol_name_len - == strlen(XMM_PLT_PREFIX) + 32 + 1)) + else if (symbol_name_len == strlen(XMM_PLT_PREFIX) + 32 + 1 && !strncmp(symbol_name, XMM_PLT_PREFIX, strlen(XMM_PLT_PREFIX))) { module->xmm_plt_count++; } - else if ((symbol_name_len == strlen(REAL_PLT_PREFIX) + 16 - || (module->is_indirect_mode - && symbol_name_len - == strlen(REAL_PLT_PREFIX) + 16 + 1)) + else if (symbol_name_len == strlen(REAL_PLT_PREFIX) + 16 + 1 && !strncmp(symbol_name, REAL_PLT_PREFIX, strlen(REAL_PLT_PREFIX))) { module->real_plt_count++; } - else if ((symbol_name_len >= strlen(REAL_PLT_PREFIX) + 8 - || (module->is_indirect_mode - && symbol_name_len - == strlen(REAL_PLT_PREFIX) + 8 + 1)) + else if (symbol_name_len >= strlen(REAL_PLT_PREFIX) + 8 + 1 && !strncmp(symbol_name, REAL_PLT_PREFIX, strlen(REAL_PLT_PREFIX))) { module->float_plt_count++; @@ -3590,104 +3632,6 @@ fail: return ret; } -#if WASM_ENABLE_LINUX_PERF != 0 -struct func_info { - uint32 idx; - void *ptr; -}; - -static uint32 -get_func_size(const AOTModule *module, struct func_info *sorted_func_ptrs, - uint32 idx) -{ - uint32 func_sz; - - if (idx == module->func_count - 1) - func_sz = (uintptr_t)module->code + module->code_size - - (uintptr_t)(sorted_func_ptrs[idx].ptr); - else - func_sz = (uintptr_t)(sorted_func_ptrs[idx + 1].ptr) - - (uintptr_t)(sorted_func_ptrs[idx].ptr); - - return func_sz; -} - -static int -compare_func_ptrs(const void *f1, const void *f2) -{ - return (intptr_t)((struct func_info *)f1)->ptr - - (intptr_t)((struct func_info *)f2)->ptr; -} - -static struct func_info * -sort_func_ptrs(const AOTModule *module, char *error_buf, uint32 error_buf_size) -{ - uint64 content_len; - struct func_info *sorted_func_ptrs; - unsigned i; - - content_len = (uint64)sizeof(struct func_info) * module->func_count; - sorted_func_ptrs = loader_malloc(content_len, error_buf, error_buf_size); - if (!sorted_func_ptrs) - return NULL; - - for (i = 0; i < module->func_count; i++) { - sorted_func_ptrs[i].idx = i; - sorted_func_ptrs[i].ptr = module->func_ptrs[i]; - } - - qsort(sorted_func_ptrs, module->func_count, sizeof(struct func_info), - compare_func_ptrs); - - return sorted_func_ptrs; -} - -static bool -create_perf_map(const AOTModule *module, char *error_buf, uint32 error_buf_size) -{ - struct func_info *sorted_func_ptrs = NULL; - char perf_map_info[128] = { 0 }; - FILE *perf_map = NULL; - uint32 i; - pid_t pid = getpid(); - bool ret = false; - - sorted_func_ptrs = sort_func_ptrs(module, error_buf, error_buf_size); - if (!sorted_func_ptrs) - goto quit; - - snprintf(perf_map_info, 128, "/tmp/perf-%d.map", pid); - perf_map = fopen(perf_map_info, "w"); - if (!perf_map) { - LOG_WARNING("warning: can't create /tmp/perf-%d.map, because %s", pid, - strerror(errno)); - goto quit; - } - - for (i = 0; i < module->func_count; i++) { - memset(perf_map_info, 0, 128); - snprintf(perf_map_info, 128, "%lx %x aot_func#%u\n", - (uintptr_t)sorted_func_ptrs[i].ptr, - get_func_size(module, sorted_func_ptrs, i), - sorted_func_ptrs[i].idx); - - fwrite(perf_map_info, 1, strlen(perf_map_info), perf_map); - } - - LOG_VERBOSE("generate /tmp/perf-%d.map", pid); - ret = true; - -quit: - if (sorted_func_ptrs) - free(sorted_func_ptrs); - - if (perf_map) - fclose(perf_map); - - return ret; -} -#endif /* WASM_ENABLE_LINUX_PERF != 0*/ - static bool load_from_sections(AOTModule *module, AOTSection *sections, bool is_load_from_file_buf, char *error_buf, @@ -3878,7 +3822,7 @@ load_from_sections(AOTModule *module, AOTSection *sections, } static AOTModule * -create_module(char *error_buf, uint32 error_buf_size) +create_module(char *name, char *error_buf, uint32 error_buf_size) { AOTModule *module = loader_malloc(sizeof(AOTModule), error_buf, error_buf_size); @@ -3890,7 +3834,7 @@ create_module(char *error_buf, uint32 error_buf_size) module->module_type = Wasm_Module_AoT; - module->name = ""; + module->name = name; #if WASM_ENABLE_MULTI_MODULE != 0 module->import_module_list = &module->import_module_list_head; @@ -3926,7 +3870,7 @@ AOTModule * aot_load_from_sections(AOTSection *section_list, char *error_buf, uint32 error_buf_size) { - AOTModule *module = create_module(error_buf, error_buf_size); + AOTModule *module = create_module("", error_buf, error_buf_size); if (!module) return NULL; @@ -4172,7 +4116,7 @@ load(const uint8 *buf, uint32 size, AOTModule *module, char *error_buf, #if WASM_ENABLE_LINUX_PERF != 0 if (wasm_runtime_get_linux_perf()) - if (!create_perf_map(module, error_buf, error_buf_size)) + if (!aot_create_perf_map(module, error_buf, error_buf_size)) goto fail; #endif @@ -4182,10 +4126,10 @@ fail: } AOTModule * -aot_load_from_aot_file(const uint8 *buf, uint32 size, char *error_buf, - uint32 error_buf_size) +aot_load_from_aot_file(const uint8 *buf, uint32 size, const LoadArgs *args, + char *error_buf, uint32 error_buf_size) { - AOTModule *module = create_module(error_buf, error_buf_size); + AOTModule *module = create_module(args->name, error_buf, error_buf_size); if (!module) return NULL; @@ -4379,7 +4323,7 @@ aot_unload(AOTModule *module) } if (module->string_literal_ptrs) { - wasm_runtime_free(module->string_literal_ptrs); + wasm_runtime_free((void *)module->string_literal_ptrs); } } #endif diff --git a/core/iwasm/aot/aot_perf_map.c b/core/iwasm/aot/aot_perf_map.c new file mode 100644 index 000000000..22700dcdd --- /dev/null +++ b/core/iwasm/aot/aot_perf_map.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_perf_map.h" +#include "bh_log.h" +#include "bh_platform.h" + +#if WASM_ENABLE_LINUX_PERF != 0 +struct func_info { + uint32 idx; + void *ptr; +}; + +static uint32 +get_func_size(const AOTModule *module, struct func_info *sorted_func_ptrs, + uint32 idx) +{ + uint32 func_sz; + + if (idx == module->func_count - 1) + func_sz = (uintptr_t)module->code + module->code_size + - (uintptr_t)(sorted_func_ptrs[idx].ptr); + else + func_sz = (uintptr_t)(sorted_func_ptrs[idx + 1].ptr) + - (uintptr_t)(sorted_func_ptrs[idx].ptr); + + return func_sz; +} + +static int +compare_func_ptrs(const void *f1, const void *f2) +{ + return (intptr_t)((struct func_info *)f1)->ptr + - (intptr_t)((struct func_info *)f2)->ptr; +} + +static struct func_info * +sort_func_ptrs(const AOTModule *module, char *error_buf, uint32 error_buf_size) +{ + uint64 content_len; + struct func_info *sorted_func_ptrs; + unsigned i; + + content_len = (uint64)sizeof(struct func_info) * module->func_count; + sorted_func_ptrs = wasm_runtime_malloc(content_len); + if (!sorted_func_ptrs) { + snprintf(error_buf, error_buf_size, + "allocate memory failed when creating perf map"); + return NULL; + } + + for (i = 0; i < module->func_count; i++) { + sorted_func_ptrs[i].idx = i; + sorted_func_ptrs[i].ptr = module->func_ptrs[i]; + } + + qsort(sorted_func_ptrs, module->func_count, sizeof(struct func_info), + compare_func_ptrs); + + return sorted_func_ptrs; +} + +bool +aot_create_perf_map(const AOTModule *module, char *error_buf, + uint32 error_buf_size) +{ + struct func_info *sorted_func_ptrs = NULL; + char perf_map_path[64] = { 0 }; + char perf_map_info[128] = { 0 }; + FILE *perf_map = NULL; + uint32 i; + pid_t pid = getpid(); + bool ret = false; + + sorted_func_ptrs = sort_func_ptrs(module, error_buf, error_buf_size); + if (!sorted_func_ptrs) + goto quit; + + snprintf(perf_map_path, sizeof(perf_map_path) - 1, "/tmp/perf-%d.map", pid); + perf_map = fopen(perf_map_path, "a"); + if (!perf_map) { + LOG_WARNING("warning: can't create /tmp/perf-%d.map, because %s", pid, + strerror(errno)); + goto quit; + } + + const char *module_name = aot_get_module_name((AOTModule *)module); + for (i = 0; i < module->func_count; i++) { + memset(perf_map_info, 0, 128); + if (strlen(module_name) > 0) + snprintf(perf_map_info, 128, "%lx %x [%s]#aot_func#%u\n", + (uintptr_t)sorted_func_ptrs[i].ptr, + get_func_size(module, sorted_func_ptrs, i), module_name, + sorted_func_ptrs[i].idx); + else + snprintf(perf_map_info, 128, "%lx %x aot_func#%u\n", + (uintptr_t)sorted_func_ptrs[i].ptr, + get_func_size(module, sorted_func_ptrs, i), + sorted_func_ptrs[i].idx); + + /* fwrite() is thread safe */ + fwrite(perf_map_info, 1, strlen(perf_map_info), perf_map); + } + + LOG_VERBOSE("write map information from %s into /tmp/perf-%d.map", + module_name, pid); + ret = true; + +quit: + if (sorted_func_ptrs) + wasm_runtime_free(sorted_func_ptrs); + + if (perf_map) + fclose(perf_map); + + return ret; +} +#endif /* WASM_ENABLE_LINUX_PERF != 0 */ \ No newline at end of file diff --git a/core/iwasm/aot/aot_perf_map.h b/core/iwasm/aot/aot_perf_map.h new file mode 100644 index 000000000..3e6583c5c --- /dev/null +++ b/core/iwasm/aot/aot_perf_map.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_PERF_MAP_H_ +#define _AOT_PERF_MAP_H_ + +#include "aot_runtime.h" + +bool +aot_create_perf_map(const AOTModule *module, char *error_buf, + uint32 error_buf_size); + +#endif /* _AOT_PERF_MAP_H_ */ \ No newline at end of file diff --git a/core/iwasm/aot/aot_reloc.h b/core/iwasm/aot/aot_reloc.h index 293e2fc79..8ead3cd93 100644 --- a/core/iwasm/aot/aot_reloc.h +++ b/core/iwasm/aot/aot_reloc.h @@ -143,6 +143,7 @@ typedef struct { REG_SYM(aot_array_init_with_data), \ REG_SYM(aot_create_func_obj), \ REG_SYM(aot_obj_is_instance_of), \ + REG_SYM(aot_func_type_is_super_of), \ REG_SYM(aot_rtt_type_new), \ REG_SYM(wasm_array_obj_copy), \ REG_SYM(wasm_array_obj_new), \ diff --git a/core/iwasm/aot/aot_runtime.c b/core/iwasm/aot/aot_runtime.c index d10db89af..f34decb5e 100644 --- a/core/iwasm/aot/aot_runtime.c +++ b/core/iwasm/aot/aot_runtime.c @@ -880,7 +880,7 @@ memory_instantiate(AOTModuleInstance *module_inst, AOTModuleInstance *parent, global_addr = module_inst->global_data + module->globals[global_idx].data_offset; *(uint32 *)global_addr = (uint32)aux_heap_base; - LOG_VERBOSE("Reset __heap_base global to %u", aux_heap_base); + LOG_VERBOSE("Reset __heap_base global to %" PRIu64, aux_heap_base); } else { /* Insert app heap before new page */ @@ -906,9 +906,10 @@ memory_instantiate(AOTModuleInstance *module_inst, AOTModuleInstance *parent, LOG_VERBOSE("Memory instantiate:"); LOG_VERBOSE(" page bytes: %u, init pages: %u, max pages: %u", num_bytes_per_page, init_page_count, max_page_count); - LOG_VERBOSE(" data offset: %u, stack size: %d", module->aux_data_end, - module->aux_stack_size); - LOG_VERBOSE(" heap offset: %u, heap size: %d\n", heap_offset, heap_size); + LOG_VERBOSE(" data offset: %" PRIu64 ", stack size: %d", + module->aux_data_end, module->aux_stack_size); + LOG_VERBOSE(" heap offset: %" PRIu64 ", heap size: %d\n", heap_offset, + heap_size); max_memory_data_size = (uint64)num_bytes_per_page * max_page_count; bh_assert(max_memory_data_size <= MAX_LINEAR_MEMORY_SIZE); @@ -1070,8 +1071,8 @@ memories_instantiate(AOTModuleInstance *module_inst, AOTModuleInstance *parent, /* Check memory data */ /* check offset since length might negative */ if (base_offset > memory_inst->memory_data_size) { - LOG_DEBUG("base_offset(%d) > memory_data_size(%d)", base_offset, - memory_inst->memory_data_size); + LOG_DEBUG("base_offset(%d) > memory_data_size(%" PRIu64 ")", + base_offset, memory_inst->memory_data_size); #if WASM_ENABLE_REF_TYPES != 0 set_error_buf(error_buf, error_buf_size, "out of bounds memory access"); @@ -1085,7 +1086,8 @@ memories_instantiate(AOTModuleInstance *module_inst, AOTModuleInstance *parent, /* check offset + length(could be zero) */ length = data_seg->byte_count; if (base_offset + length > memory_inst->memory_data_size) { - LOG_DEBUG("base_offset(%d) + length(%d) > memory_data_size(%d)", + LOG_DEBUG("base_offset(%d) + length(%d) > memory_data_size(%" PRIu64 + ")", base_offset, length, memory_inst->memory_data_size); #if WASM_ENABLE_REF_TYPES != 0 set_error_buf(error_buf, error_buf_size, @@ -1719,6 +1721,7 @@ aot_instantiate(AOTModule *module, AOTModuleInstance *parent, bh_assert(table_init_data); + bh_assert(table_init_data->table_index < module_inst->table_count); table = module_inst->tables[table_init_data->table_index]; bh_assert(table); @@ -1726,8 +1729,9 @@ aot_instantiate(AOTModule *module, AOTModuleInstance *parent, bh_assert(table_data); wasm_runtime_get_table_inst_elem_type( - (WASMModuleInstanceCommon *)module_inst, i, &tbl_elem_type, - &tbl_elem_ref_type, &tbl_init_size, &tbl_max_size); + (WASMModuleInstanceCommon *)module_inst, + table_init_data->table_index, &tbl_elem_type, &tbl_elem_ref_type, + &tbl_init_size, &tbl_max_size); if (!wasm_elem_is_declarative(table_init_data->mode) && !wasm_reftype_is_subtype_of( @@ -1963,8 +1967,6 @@ invoke_native_with_hw_bound_check(WASMExecEnv *exec_env, void *func_ptr, AOTModuleInstance *module_inst = (AOTModuleInstance *)exec_env->module_inst; WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls(); WASMJmpBuf jmpbuf_node = { 0 }, *jmpbuf_node_pop; - uint32 page_size = os_getpagesize(); - uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; #ifdef BH_PLATFORM_WINDOWS int result; bool has_exception; @@ -1975,10 +1977,7 @@ invoke_native_with_hw_bound_check(WASMExecEnv *exec_env, void *func_ptr, /* Check native stack overflow firstly to ensure we have enough native stack to run the following codes before actually calling the aot function in invokeNative function. */ - RECORD_STACK_USAGE(exec_env, (uint8 *)&module_inst); - if ((uint8 *)&module_inst < exec_env->native_stack_boundary - + page_size * (guard_page_count + 1)) { - aot_set_exception_with_id(module_inst, EXCE_NATIVE_STACK_OVERFLOW); + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { return false; } @@ -2523,7 +2522,8 @@ aot_module_malloc_internal(AOTModuleInstance *module_inst, aot_set_exception(module_inst, "app heap corrupted"); } else { - LOG_WARNING("warning: allocate %u bytes memory failed", size); + LOG_WARNING("warning: allocate %" PRIu64 " bytes memory failed", + size); } return 0; } @@ -2785,9 +2785,7 @@ aot_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 table_elem_idx, exec_env->native_stack_boundary must have been set, we don't set it again */ - RECORD_STACK_USAGE(exec_env, (uint8 *)&module_inst); - if ((uint8 *)&module_inst < exec_env->native_stack_boundary) { - aot_set_exception_with_id(module_inst, EXCE_NATIVE_STACK_OVERFLOW); + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { goto fail; } @@ -2806,7 +2804,7 @@ aot_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 table_elem_idx, } #if WASM_ENABLE_GC == 0 - func_idx = tbl_elem_val; + func_idx = (uint32)tbl_elem_val; #else func_idx = wasm_func_obj_get_func_idx_bound((WASMFuncObjectRef)tbl_elem_val); @@ -3363,39 +3361,49 @@ aot_table_fill(AOTModuleInstance *module_inst, uint32 tbl_idx, uint32 length, } uint32 -aot_table_grow(AOTModuleInstance *module_inst, uint32 tbl_idx, - uint32 inc_entries, table_elem_type_t init_val) +aot_table_grow(AOTModuleInstance *module_inst, uint32 tbl_idx, uint32 inc_size, + table_elem_type_t init_val) { - uint32 entry_count, i, orig_tbl_sz; AOTTableInstance *tbl_inst; + uint32 i, orig_size, total_size; tbl_inst = module_inst->tables[tbl_idx]; if (!tbl_inst) { return (uint32)-1; } - orig_tbl_sz = tbl_inst->cur_size; + orig_size = tbl_inst->cur_size; - if (!inc_entries) { - return orig_tbl_sz; + if (!inc_size) { + return orig_size; } - if (tbl_inst->cur_size > UINT32_MAX - inc_entries) { + if (tbl_inst->cur_size > UINT32_MAX - inc_size) { +#if WASM_ENABLE_SPEC_TEST == 0 + LOG_WARNING("table grow (%" PRIu32 "-> %" PRIu32 + ") failed because of integer overflow", + tbl_inst->cur_size, inc_size); +#endif return (uint32)-1; } - entry_count = tbl_inst->cur_size + inc_entries; - if (entry_count > tbl_inst->max_size) { + total_size = tbl_inst->cur_size + inc_size; + if (total_size > tbl_inst->max_size) { +#if WASM_ENABLE_SPEC_TEST == 0 + LOG_WARNING("table grow (%" PRIu32 "-> %" PRIu32 + ") failed because of over max size", + tbl_inst->cur_size, inc_size); +#endif return (uint32)-1; } /* fill in */ - for (i = 0; i < inc_entries; ++i) { + for (i = 0; i < inc_size; ++i) { tbl_inst->elems[tbl_inst->cur_size + i] = init_val; } - tbl_inst->cur_size = entry_count; - return orig_tbl_sz; + tbl_inst->cur_size = total_size; + return orig_size; } #endif /* WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ @@ -3697,14 +3705,14 @@ aot_create_call_stack(struct WASMExecEnv *exec_env) frame.instance = module_inst; frame.module_offset = 0; - frame.func_index = cur_frame->func_index; - frame.func_offset = cur_frame->ip_offset; - frame.func_name_wp = - get_func_name_from_index(module_inst, cur_frame->func_index); + frame.func_index = (uint32)cur_frame->func_index; + frame.func_offset = (uint32)cur_frame->ip_offset; + frame.func_name_wp = get_func_name_from_index( + module_inst, (uint32)cur_frame->func_index); if (cur_frame->func_index >= module->import_func_count) { uint32 aot_func_idx = - cur_frame->func_index - module->import_func_count; + (uint32)(cur_frame->func_index - module->import_func_count); max_local_cell_num = module->max_local_cell_nums[aot_func_idx]; max_stack_cell_num = module->max_stack_cell_nums[aot_func_idx]; } @@ -4474,6 +4482,22 @@ aot_obj_is_instance_of(AOTModuleInstance *module_inst, WASMObjectRef gc_obj, return wasm_obj_is_instance_of(gc_obj, type_index, types, type_count); } +bool +aot_func_type_is_super_of(AOTModuleInstance *module_inst, uint32 type_idx1, + uint32 type_idx2) +{ + AOTModule *aot_module = (AOTModule *)module_inst->module; + AOTType **types = aot_module->types; + + if (type_idx1 == type_idx2) + return true; + + bh_assert(types[type_idx1]->type_flag == WASM_TYPE_FUNC); + bh_assert(types[type_idx2]->type_flag == WASM_TYPE_FUNC); + return wasm_func_type_is_super_of((WASMFuncType *)types[type_idx1], + (WASMFuncType *)types[type_idx2]); +} + WASMRttTypeRef aot_rtt_type_new(AOTModuleInstance *module_inst, uint32 type_index) { @@ -4721,12 +4745,12 @@ aot_set_module_name(AOTModule *module, const char *name, char *error_buf, if (!name) return false; - module->name = - aot_const_str_set_insert((const uint8 *)name, strlen(name) + 1, module, + module->name = aot_const_str_set_insert((const uint8 *)name, + (uint32)(strlen(name) + 1), module, #if (WASM_ENABLE_WORD_ALIGN_READ != 0) - false, + false, #endif - error_buf, error_buf_size); + error_buf, error_buf_size); return module->name != NULL; } diff --git a/core/iwasm/aot/aot_runtime.h b/core/iwasm/aot/aot_runtime.h index 519c1edc1..63f8c872a 100644 --- a/core/iwasm/aot/aot_runtime.h +++ b/core/iwasm/aot/aot_runtime.h @@ -444,8 +444,8 @@ typedef struct LLVMProfileData_64 { * @return return AOT module loaded, NULL if failed */ AOTModule * -aot_load_from_aot_file(const uint8 *buf, uint32 size, char *error_buf, - uint32 error_buf_size); +aot_load_from_aot_file(const uint8 *buf, uint32 size, const LoadArgs *args, + char *error_buf, uint32 error_buf_size); /** * Load a AOT module from a specified AOT section list. @@ -752,6 +752,11 @@ bool aot_obj_is_instance_of(AOTModuleInstance *module_inst, WASMObjectRef gc_obj, uint32 type_index); +/* Whether func type1 is one of super types of func type2 */ +bool +aot_func_type_is_super_of(AOTModuleInstance *module_inst, uint32 type_idx1, + uint32 type_idx2); + WASMRttTypeRef aot_rtt_type_new(AOTModuleInstance *module_inst, uint32 type_index); diff --git a/core/iwasm/aot/debug/elf_parser.c b/core/iwasm/aot/debug/elf_parser.c index 657f9530c..7b0c57b81 100644 --- a/core/iwasm/aot/debug/elf_parser.c +++ b/core/iwasm/aot/debug/elf_parser.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include diff --git a/core/iwasm/aot/debug/jit_debug.c b/core/iwasm/aot/debug/jit_debug.c index 4b0e46f1d..261c20546 100644 --- a/core/iwasm/aot/debug/jit_debug.c +++ b/core/iwasm/aot/debug/jit_debug.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -56,6 +55,12 @@ typedef struct JITDescriptor { JITCodeEntry *first_entry_; } JITDescriptor; +#if defined(_WIN32) || defined(_WIN32_) +#define attribute_noinline __declspec(noinline) +#else +#define attribute_noinline __attribute__((noinline)) +#endif + /* LLVM has already define this */ #if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) /** @@ -63,9 +68,11 @@ typedef struct JITDescriptor { * To prevent GCC from inlining or removing it we place noinline attribute * and inline assembler statement inside. */ -void __attribute__((noinline)) __jit_debug_register_code(); +void attribute_noinline +__jit_debug_register_code(); -void __attribute__((noinline)) __jit_debug_register_code() +void attribute_noinline +__jit_debug_register_code() { int x; *(char *)&x = '\0'; diff --git a/core/iwasm/common/gc/gc_common.c b/core/iwasm/common/gc/gc_common.c index 80936f34a..99fca86d4 100644 --- a/core/iwasm/common/gc/gc_common.c +++ b/core/iwasm/common/gc/gc_common.c @@ -304,7 +304,12 @@ wasm_defined_type_equal(WASMType *const def_type1, WASMType *const def_type2, } #endif #if WASM_ENABLE_AOT != 0 - /* TODO */ + if (module->module_type == Wasm_Module_AoT) { + AOTModule *aot_module = (AOTModule *)module; + + types = aot_module->types; + type_count = aot_module->type_count; + } #endif bh_assert(types); diff --git a/core/iwasm/common/gc/gc_type.c b/core/iwasm/common/gc/gc_type.c index 0c9271c87..5ade1cb27 100644 --- a/core/iwasm/common/gc/gc_type.c +++ b/core/iwasm/common/gc/gc_type.c @@ -148,7 +148,7 @@ wasm_dump_func_type(const WASMFuncType *type) os_printf("] -> ["); - for (; i < type->param_count + type->result_count; i++) { + for (; i < (uint32)(type->param_count + type->result_count); i++) { if (wasm_is_type_multi_byte_type(type->types[i])) { bh_assert(j < type->ref_type_map_count); bh_assert(i == type->ref_type_maps[j].index); @@ -250,6 +250,51 @@ wasm_value_types_is_subtype_of(const uint8 *types1, return true; } +static bool +rec_ref_type_equal(const WASMRefType *ref_type1, const WASMRefType *ref_type2, + uint32 rec_begin_type_idx1, uint32 rec_begin_type_idx2, + uint32 rec_count, const WASMTypePtr *types, + uint32 type_count) +{ + uint32 type_idx1, type_idx2; + + if (!wasm_is_refheaptype_typeidx(&ref_type1->ref_ht_common) + || !wasm_is_refheaptype_typeidx(&ref_type2->ref_ht_common)) + return ref_type1->ref_ht_common.heap_type + == ref_type2->ref_ht_common.heap_type + ? true + : false; + + /* Now both ref types are type of (ref type_idx) */ + type_idx1 = ref_type1->ref_ht_typeidx.type_idx; + type_idx2 = ref_type2->ref_ht_typeidx.type_idx; + + if (type_idx1 >= rec_begin_type_idx1 + && type_idx1 < rec_begin_type_idx1 + rec_count) { + /* The converted iso-recursive types should be the same */ + bool ret = (type_idx2 >= rec_begin_type_idx2 + && type_idx2 < rec_begin_type_idx2 + rec_count + && type_idx1 - rec_begin_type_idx1 + == type_idx2 - rec_begin_type_idx2) + ? true + : false; + return ret; + } + else if (type_idx2 >= rec_begin_type_idx2 + && type_idx2 < rec_begin_type_idx2 + rec_count) { + /* The converted iso-recursive types should be the same */ + bool ret = (type_idx1 >= rec_begin_type_idx1 + && type_idx1 < rec_begin_type_idx1 + rec_count + && type_idx1 - rec_begin_type_idx1 + == type_idx2 - rec_begin_type_idx2) + ? true + : false; + return ret; + } + + return types[type_idx1] == types[type_idx2] ? true : false; +} + bool wasm_func_type_equal(const WASMFuncType *type1, const WASMFuncType *type2, const WASMTypePtr *types, uint32 type_count) @@ -264,7 +309,7 @@ wasm_func_type_equal(const WASMFuncType *type1, const WASMFuncType *type2, || type1->ref_type_map_count != type2->ref_type_map_count) return false; - for (i = 0; i < type1->param_count + type1->result_count; i++) { + for (i = 0; i < (uint32)(type1->param_count + type1->result_count); i++) { if (type1->types[i] != type2->types[i]) return false; @@ -277,9 +322,11 @@ wasm_func_type_equal(const WASMFuncType *type1, const WASMFuncType *type2, ref_type1 = type1->ref_type_maps[j].ref_type; ref_type2 = type2->ref_type_maps[j].ref_type; - if (!wasm_reftype_equal(ref_type1->ref_type, ref_type1, - ref_type2->ref_type, ref_type2, types, - type_count)) + + if (!rec_ref_type_equal( + ref_type1, ref_type2, type1->base_type.rec_begin_type_idx, + type2->base_type.rec_begin_type_idx, + type1->base_type.rec_count, types, type_count)) return false; j++; @@ -316,9 +363,11 @@ wasm_struct_type_equal(const WASMStructType *type1, const WASMStructType *type2, ref_type1 = type1->ref_type_maps[j].ref_type; ref_type2 = type2->ref_type_maps[j].ref_type; - if (!wasm_reftype_equal(ref_type1->ref_type, ref_type1, - ref_type2->ref_type, ref_type2, types, - type_count)) + + if (!rec_ref_type_equal( + ref_type1, ref_type2, type1->base_type.rec_begin_type_idx, + type2->base_type.rec_begin_type_idx, + type1->base_type.rec_count, types, type_count)) return false; j++; @@ -338,21 +387,67 @@ wasm_array_type_equal(const WASMArrayType *type1, const WASMArrayType *type2, if (type1->elem_flags != type2->elem_flags) return false; - return wasm_reftype_equal(type1->elem_type, type1->elem_ref_type, - type2->elem_type, type2->elem_ref_type, types, - type_count); + if (type1->elem_type != type2->elem_type) + return false; + + if (!wasm_is_type_multi_byte_type(type1->elem_type)) + return true; + + return rec_ref_type_equal(type1->elem_ref_type, type2->elem_ref_type, + type1->base_type.rec_begin_type_idx, + type2->base_type.rec_begin_type_idx, + type1->base_type.rec_count, types, type_count); } bool wasm_type_equal(const WASMType *type1, const WASMType *type2, const WASMTypePtr *types, uint32 type_count) { + uint32 rec_begin_type_idx1 = type1->rec_begin_type_idx; + uint32 rec_begin_type_idx2 = type2->rec_begin_type_idx; + uint32 parent_type_idx1, parent_type_idx2, rec_count; + if (type1 == type2) return true; - if (type1->type_flag != type2->type_flag) + if (!(type1->type_flag == type2->type_flag + && type1->is_sub_final == type2->is_sub_final + && type1->rec_count == type2->rec_count + && type1->rec_idx == type2->rec_idx)) return false; + rec_count = type1->rec_count; + + parent_type_idx1 = type1->parent_type_idx; + parent_type_idx2 = type2->parent_type_idx; + + if (parent_type_idx1 >= rec_begin_type_idx1 + && parent_type_idx1 < rec_begin_type_idx1 + rec_count) { + /* The converted iso-recursive types should be the same */ + if (!(parent_type_idx2 >= rec_begin_type_idx2 + && parent_type_idx2 < rec_begin_type_idx2 + rec_count + && parent_type_idx1 - rec_begin_type_idx1 + == parent_type_idx2 - rec_begin_type_idx2)) { + return false; + } + } + else if (parent_type_idx2 >= rec_begin_type_idx2 + && parent_type_idx2 < rec_begin_type_idx2 + rec_count) { + /* The converted iso-recursive types should be the same */ + if (!(parent_type_idx1 >= rec_begin_type_idx1 + && parent_type_idx1 < rec_begin_type_idx1 + rec_count + && parent_type_idx1 - rec_begin_type_idx1 + == parent_type_idx2 - rec_begin_type_idx2)) { + return false; + } + } + else if (type1->parent_type != type2->parent_type) { + /* The parent types should be same since they have been + normalized and equivalence types with different type + indexes are referring to a same WASMType */ + return false; + } + if (wasm_type_is_func_type(type1)) return wasm_func_type_equal((WASMFuncType *)type1, (WASMFuncType *)type2, types, type_count); @@ -399,7 +494,7 @@ wasm_func_type_is_subtype_of(const WASMFuncType *type1, } } - for (; i < type1->param_count + type1->result_count; i++) { + for (; i < (uint32)(type1->param_count + type1->result_count); i++) { if (wasm_is_type_multi_byte_type(type1->types[i])) { bh_assert(j1 < type1->ref_type_map_count); ref_type1 = type1->ref_type_maps[j1++].ref_type; @@ -653,12 +748,6 @@ wasm_reftype_struct_size(const WASMRefType *ref_type) return (uint32)sizeof(RefHeapType_Common); } -static bool -type_idx_equal(uint32 type_idx1, uint32 type_idx2) -{ - return (type_idx1 == type_idx2) ? true : false; -} - bool wasm_refheaptype_equal(const RefHeapType_Common *ref_heap_type1, const RefHeapType_Common *ref_heap_type2, @@ -673,8 +762,16 @@ wasm_refheaptype_equal(const RefHeapType_Common *ref_heap_type1, if (ref_heap_type1->heap_type != ref_heap_type2->heap_type) { if (wasm_is_refheaptype_typeidx(ref_heap_type1) && wasm_is_refheaptype_typeidx(ref_heap_type2)) { - return type_idx_equal(ref_heap_type1->heap_type, - ref_heap_type2->heap_type); + if (ref_heap_type1->heap_type == ref_heap_type2->heap_type) + return true; + else + /* the type_count may be 0 when called from reftype_equal */ + return ((uint32)ref_heap_type1->heap_type < type_count + && (uint32)ref_heap_type2->heap_type < type_count + && types[ref_heap_type1->heap_type] + == types[ref_heap_type2->heap_type]) + ? true + : false; } return false; } @@ -835,6 +932,13 @@ wasm_type_is_supers_of(const WASMType *type1, const WASMType *type2) return false; } +bool +wasm_func_type_is_super_of(const WASMFuncType *type1, const WASMFuncType *type2) +{ + return wasm_type_is_supers_of((const WASMType *)type1, + (const WASMType *)type2); +} + bool wasm_reftype_is_subtype_of(uint8 type1, const WASMRefType *ref_type1, uint8 type2, const WASMRefType *ref_type2, @@ -914,14 +1018,15 @@ wasm_reftype_is_subtype_of(uint8 type1, const WASMRefType *ref_type1, #endif else if (type1 == REF_TYPE_HT_NULLABLE) { if (wasm_is_refheaptype_typeidx(&ref_type1->ref_ht_common)) { + bh_assert((uint32)ref_type1->ref_ht_typeidx.type_idx < type_count); /* reftype1 is (ref null $t) */ if (type2 == REF_TYPE_HT_NULLABLE && ref_type2 != NULL && wasm_is_refheaptype_typeidx(&ref_type2->ref_ht_common)) { - return type_idx_equal(ref_type1->ref_ht_typeidx.type_idx, - ref_type2->ref_ht_typeidx.type_idx) - || wasm_type_is_supers_of( - types[ref_type2->ref_ht_typeidx.type_idx], - types[ref_type1->ref_ht_typeidx.type_idx]); + bh_assert((uint32)ref_type2->ref_ht_typeidx.type_idx + < type_count); + return wasm_type_is_supers_of( + types[ref_type2->ref_ht_typeidx.type_idx], + types[ref_type1->ref_ht_typeidx.type_idx]); } else if (types[ref_type1->ref_ht_typeidx.type_idx]->type_flag == WASM_TYPE_STRUCT) @@ -963,16 +1068,17 @@ wasm_reftype_is_subtype_of(uint8 type1, const WASMRefType *ref_type1, else if (type1 == REF_TYPE_HT_NON_NULLABLE) { bh_assert(ref_type1); if (wasm_is_refheaptype_typeidx(&ref_type1->ref_ht_common)) { + bh_assert((uint32)ref_type1->ref_ht_typeidx.type_idx < type_count); /* reftype1 is (ref $t) */ if ((type2 == REF_TYPE_HT_NULLABLE || type2 == REF_TYPE_HT_NON_NULLABLE) && ref_type2 != NULL && wasm_is_refheaptype_typeidx(&ref_type2->ref_ht_common)) { - return type_idx_equal(ref_type1->ref_ht_typeidx.type_idx, - ref_type2->ref_ht_typeidx.type_idx) - || wasm_type_is_supers_of( - types[ref_type2->ref_ht_typeidx.type_idx], - types[ref_type1->ref_ht_typeidx.type_idx]); + bh_assert((uint32)ref_type2->ref_ht_typeidx.type_idx + < type_count); + return wasm_type_is_supers_of( + types[ref_type2->ref_ht_typeidx.type_idx], + types[ref_type1->ref_ht_typeidx.type_idx]); } else if (types[ref_type1->ref_ht_typeidx.type_idx]->type_flag == WASM_TYPE_STRUCT) { diff --git a/core/iwasm/common/gc/gc_type.h b/core/iwasm/common/gc/gc_type.h index 5b3840e45..919c8e501 100644 --- a/core/iwasm/common/gc/gc_type.h +++ b/core/iwasm/common/gc/gc_type.h @@ -47,6 +47,12 @@ wasm_func_type_is_subtype_of(const WASMFuncType *type1, const WASMFuncType *type2, const WASMTypePtr *types, uint32 type_count); +/* Whether func type1 is one of super types of func type2, + used for the func type check in call_indirect/call_ref opcodes */ +bool +wasm_func_type_is_super_of(const WASMFuncType *type1, + const WASMFuncType *type2); + /* Whether func type1's result types are subtype of func type2's result types */ bool diff --git a/core/iwasm/common/wasm_application.c b/core/iwasm/common/wasm_application.c index 13ad2b1a6..f19cb00e4 100644 --- a/core/iwasm/common/wasm_application.c +++ b/core/iwasm/common/wasm_application.c @@ -578,8 +578,7 @@ execute_func(WASMModuleInstanceCommon *module_inst, const char *name, is_anyref = true; } - if (wasm_is_type_multi_byte_type( - type->types[type->param_count + i])) { + if (wasm_is_type_multi_byte_type(type->types[i])) { WASMRefType *ref_type = ref_type_map->ref_type; if (wasm_is_refheaptype_common( &ref_type->ref_ht_common)) { diff --git a/core/iwasm/common/wasm_c_api.c b/core/iwasm/common/wasm_c_api.c index 10ceb7583..456ce505e 100644 --- a/core/iwasm/common/wasm_c_api.c +++ b/core/iwasm/common/wasm_c_api.c @@ -2234,7 +2234,8 @@ quit: #endif /* WASM_ENABLE_WASM_CACHE != 0 */ wasm_module_t * -wasm_module_new(wasm_store_t *store, const wasm_byte_vec_t *binary) +wasm_module_new_ex(wasm_store_t *store, const wasm_byte_vec_t *binary, + const LoadArgs *args) { char error_buf[128] = { 0 }; wasm_module_ex_t *module_ex = NULL; @@ -2290,8 +2291,8 @@ wasm_module_new(wasm_store_t *store, const wasm_byte_vec_t *binary) if (!module_ex->binary->data) goto free_binary; - module_ex->module_comm_rt = wasm_runtime_load( - (uint8 *)module_ex->binary->data, (uint32)module_ex->binary->size, + module_ex->module_comm_rt = wasm_runtime_load_ex( + (uint8 *)module_ex->binary->data, (uint32)module_ex->binary->size, args, error_buf, (uint32)sizeof(error_buf)); if (!(module_ex->module_comm_rt)) { LOG_ERROR("%s", error_buf); @@ -2337,6 +2338,14 @@ quit: return NULL; } +wasm_module_t * +wasm_module_new(wasm_store_t *store, const wasm_byte_vec_t *binary) +{ + LoadArgs args = { 0 }; + args.name = ""; + return wasm_module_new_ex(store, binary, &args); +} + bool wasm_module_validate(wasm_store_t *store, const wasm_byte_vec_t *binary) { @@ -3987,7 +3996,7 @@ wasm_table_get(const wasm_table_t *table, wasm_table_size_t index) if (index >= table_interp->cur_size) { return NULL; } - ref_idx = table_interp->elems[index]; + ref_idx = (uint32)table_interp->elems[index]; } #endif @@ -3998,7 +4007,7 @@ wasm_table_get(const wasm_table_t *table, wasm_table_size_t index) if (index >= table_aot->cur_size) { return NULL; } - ref_idx = table_aot->elems[index]; + ref_idx = (uint32)table_aot->elems[index]; } #endif diff --git a/core/iwasm/common/wasm_exec_env.h b/core/iwasm/common/wasm_exec_env.h index f96242332..53d248755 100644 --- a/core/iwasm/common/wasm_exec_env.h +++ b/core/iwasm/common/wasm_exec_env.h @@ -117,6 +117,9 @@ typedef struct WASMExecEnv { /* whether current thread is detached */ bool thread_is_detached; + + /* whether the aux stack is allocated */ + bool is_aux_stack_allocated; #endif #if WASM_ENABLE_GC != 0 diff --git a/core/iwasm/common/wasm_memory.c b/core/iwasm/common/wasm_memory.c index 1d2cd1677..c49c1825a 100644 --- a/core/iwasm/common/wasm_memory.c +++ b/core/iwasm/common/wasm_memory.c @@ -29,24 +29,43 @@ static void *enlarge_memory_error_user_data; #if WASM_MEM_ALLOC_WITH_USER_DATA != 0 static void *allocator_user_data = NULL; -static void *(*malloc_func)(void *user_data, unsigned int size) = NULL; -static void *(*realloc_func)(void *user_data, void *ptr, - unsigned int size) = NULL; -static void (*free_func)(void *user_data, void *ptr) = NULL; -#else -static void *(*malloc_func)(unsigned int size) = NULL; -static void *(*realloc_func)(void *ptr, unsigned int size) = NULL; -static void (*free_func)(void *ptr) = NULL; #endif +static void *(*malloc_func)( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + mem_alloc_usage_t usage, +#endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + void *user_data, +#endif + unsigned int size) = NULL; + +static void *(*realloc_func)( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + mem_alloc_usage_t usage, bool full_size_mmaped, +#endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + void *user_data, +#endif + void *ptr, unsigned int size) = NULL; + +static void (*free_func)( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + mem_alloc_usage_t usage, +#endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + void *user_data, +#endif + void *ptr) = NULL; + static unsigned int global_pool_size; -static uint32 +static uint64 align_as_and_cast(uint64 size, uint64 alignment) { uint64 aligned_size = (size + alignment - 1) & ~(alignment - 1); - return aligned_size > UINT32_MAX ? UINT32_MAX : (uint32)aligned_size; + return aligned_size; } static bool @@ -177,11 +196,14 @@ wasm_runtime_malloc_internal(unsigned int size) return mem_allocator_malloc(pool_allocator, size); } else if (memory_mode == MEMORY_MODE_ALLOCATOR) { -#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 - return malloc_func(allocator_user_data, size); -#else - return malloc_func(size); + return malloc_func( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + Alloc_For_Runtime, #endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + allocator_user_data, +#endif + size); } else { return os_malloc(size); @@ -201,11 +223,14 @@ wasm_runtime_realloc_internal(void *ptr, unsigned int size) } else if (memory_mode == MEMORY_MODE_ALLOCATOR) { if (realloc_func) -#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 - return realloc_func(allocator_user_data, ptr, size); -#else - return realloc_func(ptr, size); + return realloc_func( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + Alloc_For_Runtime, false, #endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + allocator_user_data, +#endif + ptr, size); else return NULL; } @@ -233,11 +258,14 @@ wasm_runtime_free_internal(void *ptr) mem_allocator_free(pool_allocator, ptr); } else if (memory_mode == MEMORY_MODE_ALLOCATOR) { -#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 - free_func(allocator_user_data, ptr); -#else - free_func(ptr); + free_func( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + Alloc_For_Runtime, #endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + allocator_user_data, +#endif + ptr); } else { os_free(ptr); @@ -765,6 +793,29 @@ wasm_enlarge_memory_internal(WASMModuleInstance *module, uint32 inc_page_count) bh_assert(total_size_new <= GET_MAX_LINEAR_MEMORY_SIZE(memory->is_memory64)); +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + if (!(memory_data_new = + realloc_func(Alloc_For_LinearMemory, full_size_mmaped, +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + NULL, +#endif + memory_data_old, total_size_new))) { + ret = false; + goto return_func; + } + if (heap_size > 0) { + if (mem_allocator_migrate(memory->heap_handle, + (char *)heap_data_old + + (memory_data_new - memory_data_old), + heap_size) + != 0) { + ret = false; + } + } + memory->heap_data = memory_data_new + (heap_data_old - memory_data_old); + memory->heap_data_end = memory->heap_data + heap_size; + memory->memory_data = memory_data_new; +#else if (full_size_mmaped) { #ifdef BH_PLATFORM_WINDOWS if (!os_mem_commit(memory->memory_data_end, @@ -823,6 +874,7 @@ wasm_enlarge_memory_internal(WASMModuleInstance *module, uint32 inc_page_count) os_writegsbase(memory_data_new); #endif } +#endif /* end of WASM_MEM_ALLOC_WITH_USAGE */ memory->num_bytes_per_page = num_bytes_per_page; memory->cur_page_count = total_page_count; @@ -903,8 +955,19 @@ wasm_deallocate_linear_memory(WASMMemoryInstance *memory_inst) #else map_size = 8 * (uint64)BH_GB; #endif + +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + (void)map_size; + free_func(Alloc_For_LinearMemory, +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + NULL, +#endif + memory_inst->memory_data); +#else wasm_munmap_linear_memory(memory_inst->memory_data, memory_inst->memory_data_size, map_size); +#endif + memory_inst->memory_data = NULL; } @@ -951,13 +1014,24 @@ wasm_allocate_linear_memory(uint8 **data, bool is_shared_memory, { bh_assert(*memory_data_size <= MAX_LINEAR_MEMORY_SIZE); } - align_as_and_cast(*memory_data_size, page_size); + *memory_data_size = align_as_and_cast(*memory_data_size, page_size); if (map_size > 0) { +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + (void)wasm_mmap_linear_memory; + if (!(*data = malloc_func(Alloc_For_LinearMemory, +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + NULL, +#endif + *memory_data_size))) { + return BHT_ERROR; + } +#else if (!(*data = wasm_mmap_linear_memory(map_size, *memory_data_size))) { return BHT_ERROR; } +#endif } return BHT_OK; -} \ No newline at end of file +} diff --git a/core/iwasm/common/wasm_native.c b/core/iwasm/common/wasm_native.c index 14b295ee7..394dfd2b5 100644 --- a/core/iwasm/common/wasm_native.c +++ b/core/iwasm/common/wasm_native.c @@ -84,9 +84,9 @@ compare_type_with_signautre(uint8 type, const char signature) if ('r' == signature #if WASM_ENABLE_GC != 0 #if WASM_ENABLE_STRINGREF != 0 - && (type >= REF_TYPE_STRINGVIEWITER && type <= REF_TYPE_FUNCREF) + && (type >= REF_TYPE_STRINGVIEWITER && type <= REF_TYPE_NULLFUNCREF) #else - && (type >= REF_TYPE_NULLREF && type <= REF_TYPE_FUNCREF) + && (type >= REF_TYPE_HT_NULLABLE && type <= REF_TYPE_NULLFUNCREF) #endif #else && type == VALUE_TYPE_EXTERNREF diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index e3b4ca7b5..2b481710c 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -65,7 +65,7 @@ #if WASM_ENABLE_MULTI_MODULE != 0 /** * A safety insurance to prevent - * circular depencies which leads stack overflow + * circular dependencies which leads stack overflow * try to break early */ typedef struct LoadingModule { @@ -219,7 +219,7 @@ runtime_signal_handler(void *sig_addr) os_longjmp(jmpbuf_node->jmpbuf, 1); } #if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 - else if (stack_min_addr - page_size <= (uint8 *)sig_addr + else if (stack_min_addr <= (uint8 *)sig_addr && (uint8 *)sig_addr < stack_min_addr + page_size * guard_page_count) { /* The address which causes segmentation fault is inside @@ -275,11 +275,11 @@ decode_insn(uint8 *insn) buffer, sizeof(buffer), runtime_address); +#if 0 /* Print current instruction */ - /* os_printf("%012" PRIX64 " ", runtime_address); puts(buffer); - */ +#endif return instruction.length; } @@ -1043,7 +1043,7 @@ wasm_runtime_register_module_internal(const char *module_name, /* module hasn't been registered */ node = runtime_malloc(sizeof(WASMRegisteredModule), NULL, NULL, 0); if (!node) { - LOG_DEBUG("malloc WASMRegisteredModule failed. SZ=%d", + LOG_DEBUG("malloc WASMRegisteredModule failed. SZ=%zu", sizeof(WASMRegisteredModule)); return false; } @@ -1333,11 +1333,15 @@ register_module_with_null_name(WASMModuleCommon *module_common, char *error_buf, } WASMModuleCommon * -wasm_runtime_load(uint8 *buf, uint32 size, char *error_buf, - uint32 error_buf_size) +wasm_runtime_load_ex(uint8 *buf, uint32 size, const LoadArgs *args, + char *error_buf, uint32 error_buf_size) { WASMModuleCommon *module_common = NULL; + if (!args) { + return NULL; + } + if (get_package_type(buf, size) == Wasm_Module_Bytecode) { #if WASM_ENABLE_INTERP != 0 module_common = @@ -1345,13 +1349,13 @@ wasm_runtime_load(uint8 *buf, uint32 size, char *error_buf, #if WASM_ENABLE_MULTI_MODULE != 0 true, #endif - error_buf, error_buf_size); + args, error_buf, error_buf_size); #endif } else if (get_package_type(buf, size) == Wasm_Module_AoT) { #if WASM_ENABLE_AOT != 0 module_common = (WASMModuleCommon *)aot_load_from_aot_file( - buf, size, error_buf, error_buf_size); + buf, size, args, error_buf, error_buf_size); #endif } else { @@ -1367,10 +1371,21 @@ wasm_runtime_load(uint8 *buf, uint32 size, char *error_buf, LOG_DEBUG("WASM module load failed"); return NULL; } + + /*TODO: use file name as name and register with name? */ return register_module_with_null_name(module_common, error_buf, error_buf_size); } +WASMModuleCommon * +wasm_runtime_load(uint8 *buf, uint32 size, char *error_buf, + uint32 error_buf_size) +{ + LoadArgs args = { 0 }; + args.name = ""; + return wasm_runtime_load_ex(buf, size, &args, error_buf, error_buf_size); +} + WASMModuleCommon * wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot, char *error_buf, uint32 error_buf_size) @@ -1780,7 +1795,7 @@ wasm_runtime_dump_mem_consumption(WASMExecEnv *exec_env) wasm_runtime_dump_module_inst_mem_consumption(module_inst_common); wasm_runtime_dump_exec_env_mem_consumption(exec_env); os_printf("\nTotal memory consumption of module, module inst and " - "exec env: %u\n", + "exec env: %" PRIu64 "\n", total_size); os_printf("Total interpreter stack used: %u\n", exec_env->max_wasm_stack_used); @@ -3712,6 +3727,213 @@ static union { #define is_little_endian() (__ue.b == 1) /* NOLINT */ +int32 +wasm_runtime_get_import_count(WASMModuleCommon *const module) +{ + if (!module) { + bh_assert(0); + return -1; + } + +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + const AOTModule *aot_module = (const AOTModule *)module; + return (int32)(aot_module->import_func_count + + aot_module->import_global_count + + aot_module->import_table_count + + aot_module->import_memory_count); + } +#endif +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + const WASMModule *wasm_module = (const WASMModule *)module; + return (int32)wasm_module->import_count; + } +#endif + + return -1; +} + +void +wasm_runtime_get_import_type(WASMModuleCommon *const module, int32 import_index, + wasm_import_type *import_type) +{ + if (!import_type) { + bh_assert(0); + return; + } + + memset(import_type, 0, sizeof(wasm_import_type)); + + if (!module) { + bh_assert(0); + return; + } + +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + const AOTModule *aot_module = (const AOTModule *)module; + + uint32 func_index = (uint32)import_index; + if (func_index < aot_module->import_func_count) { + const AOTImportFunc *aot_import_func = + &aot_module->import_funcs[func_index]; + import_type->module_name = aot_import_func->module_name; + import_type->name = aot_import_func->func_name; + import_type->kind = WASM_IMPORT_EXPORT_KIND_FUNC; + import_type->linked = + aot_import_func->func_ptr_linked ? true : false; + return; + } + + uint32 global_index = func_index - aot_module->import_func_count; + if (global_index < aot_module->import_global_count) { + const AOTImportGlobal *aot_import_global = + &aot_module->import_globals[global_index]; + import_type->module_name = aot_import_global->module_name; + import_type->name = aot_import_global->global_name; + import_type->kind = WASM_IMPORT_EXPORT_KIND_GLOBAL; + import_type->linked = aot_import_global->is_linked; + return; + } + + uint32 table_index = global_index - aot_module->import_global_count; + if (table_index < aot_module->import_table_count) { + const AOTImportTable *aot_import_table = + &aot_module->import_tables[table_index]; + import_type->module_name = aot_import_table->module_name; + import_type->name = aot_import_table->table_name; + import_type->kind = WASM_IMPORT_EXPORT_KIND_TABLE; + import_type->linked = false; + return; + } + + uint32 memory_index = table_index - aot_module->import_table_count; + if (memory_index < aot_module->import_memory_count) { + const AOTImportMemory *aot_import_memory = + &aot_module->import_memories[memory_index]; + import_type->module_name = aot_import_memory->module_name; + import_type->name = aot_import_memory->memory_name; + import_type->kind = WASM_IMPORT_EXPORT_KIND_MEMORY; + import_type->linked = false; + return; + } + + bh_assert(0); + return; + } +#endif +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + const WASMModule *wasm_module = (const WASMModule *)module; + + if ((uint32)import_index >= wasm_module->import_count) { + bh_assert(0); + return; + } + + const WASMImport *wasm_import = &wasm_module->imports[import_index]; + + import_type->module_name = wasm_import->u.names.module_name; + import_type->name = wasm_import->u.names.field_name; + import_type->kind = wasm_import->kind; + switch (import_type->kind) { + case WASM_IMPORT_EXPORT_KIND_FUNC: + import_type->linked = wasm_import->u.function.func_ptr_linked; + break; + case WASM_IMPORT_EXPORT_KIND_GLOBAL: + import_type->linked = wasm_import->u.global.is_linked; + break; + case WASM_IMPORT_EXPORT_KIND_TABLE: + /* not supported */ + import_type->linked = false; + break; + case WASM_IMPORT_EXPORT_KIND_MEMORY: + /* not supported */ + import_type->linked = false; + break; + default: + bh_assert(0); + break; + } + + return; + } +#endif +} + +int32 +wasm_runtime_get_export_count(WASMModuleCommon *const module) +{ + if (!module) { + bh_assert(0); + return -1; + } + +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + const AOTModule *aot_module = (const AOTModule *)module; + return (int32)aot_module->export_count; + } +#endif +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + const WASMModule *wasm_module = (const WASMModule *)module; + return (int32)wasm_module->export_count; + } +#endif + + return -1; +} + +void +wasm_runtime_get_export_type(WASMModuleCommon *const module, int32 export_index, + wasm_export_type *export_type) +{ + if (!export_type) { + bh_assert(0); + return; + } + + memset(export_type, 0, sizeof(wasm_export_type)); + + if (!module) { + bh_assert(0); + return; + } + +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + const AOTModule *aot_module = (const AOTModule *)module; + + if ((uint32)export_index >= aot_module->export_count) { + bh_assert(0); + return; + } + + const AOTExport *aot_export = &aot_module->exports[export_index]; + export_type->name = aot_export->name; + export_type->kind = aot_export->kind; + return; + } +#endif +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + const WASMModule *wasm_module = (const WASMModule *)module; + + if ((uint32)export_index >= wasm_module->export_count) { + bh_assert(0); + return; + } + + const WASMExport *wasm_export = &wasm_module->exports[export_index]; + export_type->name = wasm_export->name; + export_type->kind = wasm_export->kind; + return; + } +#endif +} + bool wasm_runtime_register_natives(const char *module_name, NativeSymbol *native_symbols, @@ -5488,6 +5710,7 @@ wasm_externref_set_cleanup(WASMModuleInstanceCommon *module_inst, if (lookup_user_data.found) { void *key = (void *)(uintptr_t)lookup_user_data.externref_idx; ExternRefMapNode *node = bh_hash_map_find(externref_map, key); + bh_assert(node); node->cleanup = extern_obj_cleanup; ok = true; } @@ -6500,6 +6723,7 @@ wasm_runtime_load_depended_module(const WASMModuleCommon *parent_module, bool ret = false; uint8 *buffer = NULL; uint32 buffer_size = 0; + LoadArgs args = { 0 }; /* check the registered module list of the parent */ sub_module = wasm_runtime_search_sub_module(parent_module, sub_module_name); @@ -6539,23 +6763,25 @@ wasm_runtime_load_depended_module(const WASMModuleCommon *parent_module, if (!ret) { LOG_DEBUG("read the file of %s failed", sub_module_name); set_error_buf_v(parent_module, error_buf, error_buf_size, - "unknown import", sub_module_name); + "unknown import %s", sub_module_name); goto delete_loading_module; } if (get_package_type(buffer, buffer_size) != parent_module->module_type) { LOG_DEBUG("moudle %s type error", sub_module_name); - goto delete_loading_module; + goto destroy_file_buffer; } + + args.name = (char *)sub_module_name; if (get_package_type(buffer, buffer_size) == Wasm_Module_Bytecode) { #if WASM_ENABLE_INTERP != 0 - sub_module = (WASMModuleCommon *)wasm_load(buffer, buffer_size, false, - error_buf, error_buf_size); + sub_module = (WASMModuleCommon *)wasm_load( + buffer, buffer_size, false, &args, error_buf, error_buf_size); #endif } else if (get_package_type(buffer, buffer_size) == Wasm_Module_AoT) { #if WASM_ENABLE_AOT != 0 sub_module = (WASMModuleCommon *)aot_load_from_aot_file( - buffer, buffer_size, error_buf, error_buf_size); + buffer, buffer_size, &args, error_buf, error_buf_size); #endif } if (!sub_module) { @@ -6650,7 +6876,7 @@ wasm_runtime_sub_module_instantiate(WASMModuleCommon *module, sub_module_inst_list_node = loader_malloc(sizeof(WASMSubModInstNode), error_buf, error_buf_size); if (!sub_module_inst_list_node) { - LOG_DEBUG("Malloc WASMSubModInstNode failed, SZ:%d", + LOG_DEBUG("Malloc WASMSubModInstNode failed, SZ: %zu", sizeof(WASMSubModInstNode)); if (sub_module_inst) wasm_runtime_deinstantiate_internal(sub_module_inst, false); @@ -6789,3 +7015,59 @@ wasm_runtime_get_module_name(wasm_module_t module) return ""; } + +/* + * wasm_runtime_detect_native_stack_overflow + * + * - raise "native stack overflow" exception if available native stack + * at this point is less than WASM_STACK_GUARD_SIZE. in that case, + * return false. + * + * - update native_stack_top_min. + */ +bool +wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env) +{ + uint8 *boundary = exec_env->native_stack_boundary; + RECORD_STACK_USAGE(exec_env, (uint8 *)&boundary); + if (boundary == NULL) { + /* the platfrom doesn't support os_thread_get_stack_boundary */ + return true; + } +#if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + uint32 page_size = os_getpagesize(); + uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; + boundary = boundary + page_size * guard_page_count; +#endif + if ((uint8 *)&boundary < boundary) { + wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), + "native stack overflow"); + return false; + } + return true; +} + +bool +wasm_runtime_detect_native_stack_overflow_size(WASMExecEnv *exec_env, + uint32 requested_size) +{ + uint8 *boundary = exec_env->native_stack_boundary; + RECORD_STACK_USAGE(exec_env, (uint8 *)&boundary); + if (boundary == NULL) { + /* the platfrom doesn't support os_thread_get_stack_boundary */ + return true; + } +#if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + uint32 page_size = os_getpagesize(); + uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; + boundary = boundary + page_size * guard_page_count; +#endif + /* adjust the boundary for the requested size */ + boundary = boundary - WASM_STACK_GUARD_SIZE + requested_size; + if ((uint8 *)&boundary < boundary) { + wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), + "native stack overflow"); + return false; + } + return true; +} diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h index 62c35473a..abaa33011 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h @@ -1189,6 +1189,13 @@ wasm_runtime_end_blocking_op(WASMExecEnv *exec_env); void wasm_runtime_interrupt_blocking_op(WASMExecEnv *exec_env); +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env); + +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_detect_native_stack_overflow_size(WASMExecEnv *exec_env, + uint32 requested_size); + #if WASM_ENABLE_LINUX_PERF != 0 bool wasm_runtime_get_linux_perf(void); diff --git a/core/iwasm/compilation/aot_compiler.c b/core/iwasm/compilation/aot_compiler.c index 9740cd0d1..5c257742a 100644 --- a/core/iwasm/compilation/aot_compiler.c +++ b/core/iwasm/compilation/aot_compiler.c @@ -330,7 +330,7 @@ aot_gen_commit_values(AOTCompFrame *frame) if (!p->dirty) continue; - n = p - frame->lp; + n = (uint32)(p - frame->lp); /* Commit reference flag */ if (comp_ctx->enable_gc) { @@ -432,7 +432,7 @@ aot_gen_commit_values(AOTCompFrame *frame) continue; p->dirty = 0; - n = p - frame->lp; + n = (uint32)(p - frame->lp); /* Commit values */ switch (p->type) { @@ -538,7 +538,7 @@ aot_gen_commit_values(AOTCompFrame *frame) /* Clear reference flags for unused stack slots. */ for (p = frame->sp; p < end; p++) { bh_assert(!p->ref); - n = p - frame->lp; + n = (uint32)(p - frame->lp); /* Commit reference flag. */ if (p->ref != p->committed_ref - 1) { @@ -621,7 +621,7 @@ aot_gen_commit_sp_ip(AOTCompFrame *frame, bool commit_sp, bool commit_ip) } if (commit_sp) { - n = sp - frame->lp; + n = (uint32)(sp - frame->lp); value = I32_CONST(offset_of_local(comp_ctx, n)); if (!value) { aot_set_last_error("llvm build const failed"); @@ -3452,16 +3452,6 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) break; } - case SIMD_i32x4_narrow_i64x2_s: - case SIMD_i32x4_narrow_i64x2_u: - { - if (!aot_compile_simd_i32x4_narrow_i64x2( - comp_ctx, func_ctx, - SIMD_i32x4_narrow_i64x2_s == opcode)) - return false; - break; - } - case SIMD_i32x4_extend_low_i16x8_s: case SIMD_i32x4_extend_high_i16x8_s: { @@ -3501,16 +3491,6 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) break; } - case SIMD_i32x4_add_sat_s: - case SIMD_i32x4_add_sat_u: - { - if (!aot_compile_simd_i32x4_saturate( - comp_ctx, func_ctx, V128_ADD, - opcode == SIMD_i32x4_add_sat_s)) - return false; - break; - } - case SIMD_i32x4_sub: { if (!aot_compile_simd_i32x4_arith(comp_ctx, func_ctx, @@ -3519,16 +3499,6 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) break; } - case SIMD_i32x4_sub_sat_s: - case SIMD_i32x4_sub_sat_u: - { - if (!aot_compile_simd_i32x4_saturate( - comp_ctx, func_ctx, V128_SUB, - opcode == SIMD_i32x4_add_sat_s)) - return false; - break; - } - case SIMD_i32x4_mul: { if (!aot_compile_simd_i32x4_arith(comp_ctx, func_ctx, @@ -3565,13 +3535,6 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) break; } - case SIMD_i32x4_avgr_u: - { - if (!aot_compile_simd_i32x4_avgr_u(comp_ctx, func_ctx)) - return false; - break; - } - case SIMD_i32x4_extmul_low_i16x8_s: case SIMD_i32x4_extmul_high_i16x8_s: { @@ -3728,13 +3691,6 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) break; } - case SIMD_f32x4_round: - { - if (!aot_compile_simd_f32x4_round(comp_ctx, func_ctx)) - return false; - break; - } - case SIMD_f32x4_sqrt: { if (!aot_compile_simd_f32x4_sqrt(comp_ctx, func_ctx)) @@ -3788,13 +3744,6 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) break; } - case SIMD_f64x2_round: - { - if (!aot_compile_simd_f64x2_round(comp_ctx, func_ctx)) - return false; - break; - } - case SIMD_f64x2_sqrt: { if (!aot_compile_simd_f64x2_sqrt(comp_ctx, func_ctx)) diff --git a/core/iwasm/compilation/aot_emit_aot_file.c b/core/iwasm/compilation/aot_emit_aot_file.c index 52637686f..426171984 100644 --- a/core/iwasm/compilation/aot_emit_aot_file.c +++ b/core/iwasm/compilation/aot_emit_aot_file.c @@ -484,15 +484,15 @@ static uint32 get_func_type_size(AOTCompContext *comp_ctx, AOTFuncType *func_type) { #if WASM_ENABLE_GC != 0 - /* type flag + is_sub_final + parent_type_idx + rec_count + rec_idx + param - * count + result count - * + ref_type_map_count + types + context of ref_type_map */ + /* type flag + equivalence type flag + is_sub_final + parent_type_idx + + rec_count + rec_idx + param count + result count + + ref_type_map_count + types + context of ref_type_map */ if (comp_ctx->enable_gc) { uint32 size = 0; /* type flag */ size += sizeof(func_type->base_type.type_flag); - /* is_sub_final */ + /* equivalence type flag + is_sub_final */ size += sizeof(uint16); /* parent_type_idx */ size += sizeof(func_type->base_type.parent_type_idx); @@ -529,12 +529,12 @@ static uint32 get_struct_type_size(AOTCompContext *comp_ctx, AOTStructType *struct_type) { uint32 size = 0; - /* type flag + is_sub_final + parent_type_idx + rec_count + rec_idx + field - * count + fields */ + /* type flag + equivalence type flag + is_sub_final + parent_type_idx + + rec_count + rec_idx + field count + fields */ /* type flag */ size += sizeof(struct_type->base_type.type_flag); - /* is_sub_final */ + /* equivalence type flag + is_sub_final */ size += sizeof(uint16); /* parent_type_idx */ size += sizeof(struct_type->base_type.parent_type_idx); @@ -558,12 +558,12 @@ static uint32 get_array_type_size(AOTCompContext *comp_ctx, AOTArrayType *array_type) { uint32 size = 0; - /* type flag + is_sub_final + parent_type_idx + rec_count + rec_idx + - elem_flags + elem_type + elem_ref_type */ + /* type flag + equivalence type flag + is_sub_final + parent_type_idx + + rec_count + rec_idx + elem_flags + elem_type + elem_ref_type */ /* type flag */ size += sizeof(array_type->base_type.type_flag); - /* is_sub_final */ + /* equivalence type flag + is_sub_final */ size += sizeof(uint16); /* parent_type_idx (u32) */ size += sizeof(array_type->base_type.parent_type_idx); @@ -597,7 +597,22 @@ get_type_info_size(AOTCompContext *comp_ctx, AOTCompData *comp_data) #if WASM_ENABLE_GC != 0 if (comp_ctx->enable_gc) { for (i = 0; i < comp_data->type_count; i++) { + uint32 j; + size = align_uint(size, 4); + + /* Emit simple info if there is an equivalence type */ + for (j = 0; j < i; j++) { + if (comp_data->types[j] == comp_data->types[i]) { + /* type_flag (2 bytes) + equivalence type flag (1 byte) + + padding (1 byte) + equivalence type index */ + size += 8; + break; + } + } + if (j < i) + continue; + if (comp_data->types[i]->type_flag == WASM_TYPE_FUNC) size += get_func_type_size(comp_ctx, (AOTFuncType *)comp_data->types[i]); @@ -2093,13 +2108,32 @@ aot_emit_type_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, #if WASM_ENABLE_GC != 0 if (comp_ctx->enable_gc) { - int32 idx; AOTType **types = comp_data->types; + int32 idx; + uint32 j; for (i = 0; i < comp_data->type_count; i++) { offset = align_uint(offset, 4); + + /* Emit simple info if there is an equivalence type */ + for (j = 0; j < i; j++) { + if (types[j] == types[i]) { + EMIT_U16(types[i]->type_flag); + /* equivalence type flag is true */ + EMIT_U8(1); + EMIT_U8(0); + /* equivalence type index */ + EMIT_U32(j); + break; + } + } + if (j < i) + continue; + EMIT_U16(types[i]->type_flag); - EMIT_U16(types[i]->is_sub_final); + /* equivalence type flag is false */ + EMIT_U8(0); + EMIT_U8(types[i]->is_sub_final); EMIT_U32(types[i]->parent_type_idx); EMIT_U16(types[i]->rec_count); @@ -2593,7 +2627,7 @@ aot_emit_func_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, if (comp_ctx->enable_gc) { /* emit func_local_ref_flag arrays for both import and AOTed funcs */ AOTFuncType *func_type; - uint32 j, local_ref_flags_cell_num; + uint32 j, local_ref_flags_cell_num, paddings; for (i = 0; i < comp_data->import_func_count; i++) { func_type = comp_data->import_funcs[i].func_type; @@ -2603,6 +2637,8 @@ aot_emit_func_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, local_ref_flags_cell_num += wasm_value_type_cell_num_internal( func_type->types[j], comp_ctx->pointer_size); } + paddings = + local_ref_flags_cell_num < 2 ? 2 - local_ref_flags_cell_num : 0; local_ref_flags_cell_num = local_ref_flags_cell_num > 2 ? local_ref_flags_cell_num : 2; @@ -2614,7 +2650,7 @@ aot_emit_func_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, func_type->types[j])) return false; } - for (; j < 2; j++) + for (j = 0; j < paddings; j++) EMIT_U8(0); } diff --git a/core/iwasm/compilation/aot_emit_control.c b/core/iwasm/compilation/aot_emit_control.c index 4e28babc3..24511ffd0 100644 --- a/core/iwasm/compilation/aot_emit_control.c +++ b/core/iwasm/compilation/aot_emit_control.c @@ -1269,6 +1269,7 @@ aot_compile_op_br_table(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, PUSH(values[j], target_block->result_types[j]); } wasm_runtime_free(values); + values = NULL; } target_block->is_reachable = true; if (i == br_count) @@ -1294,6 +1295,7 @@ aot_compile_op_br_table(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, PUSH(values[j], target_block->param_types[j]); } wasm_runtime_free(values); + values = NULL; } if (i == br_count) default_llvm_block = target_block->llvm_entry_block; diff --git a/core/iwasm/compilation/aot_emit_function.c b/core/iwasm/compilation/aot_emit_function.c index 224173163..cf3824e9a 100644 --- a/core/iwasm/compilation/aot_emit_function.c +++ b/core/iwasm/compilation/aot_emit_function.c @@ -1826,6 +1826,52 @@ fail: return ret; } +#if WASM_ENABLE_GC != 0 +static LLVMValueRef +call_aot_func_type_is_super_of_func(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + LLVMValueRef type_idx1, + LLVMValueRef type_idx2) +{ + LLVMValueRef param_values[3], ret_value, value, func; + LLVMTypeRef param_types[3], ret_type, func_type, func_ptr_type; + + param_types[0] = comp_ctx->aot_inst_type; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + ret_type = INT8_TYPE; + +#if WASM_ENABLE_JIT != 0 + if (comp_ctx->is_jit_mode) + GET_AOT_FUNCTION(llvm_jit_func_type_is_super_of, 3); + else +#endif + GET_AOT_FUNCTION(aot_func_type_is_super_of, 3); + + param_values[0] = func_ctx->aot_inst; + param_values[1] = type_idx1; + param_values[2] = type_idx2; + + if (!(ret_value = + LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + 3, "call_aot_func_type_is_super_of"))) { + aot_set_last_error("llvm build call failed."); + return NULL; + } + + if (!(ret_value = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, ret_value, + I8_ZERO, "check_fail"))) { + aot_set_last_error("llvm build icmp failed."); + return NULL; + } + + return ret_value; + +fail: + return NULL; +} +#endif + static bool call_aot_call_indirect_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, AOTFuncType *aot_func_type, @@ -2018,15 +2064,23 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return false; } - /* Find the equivalent function type whose type index is the smallest: - the callee function's type index is also converted to the smallest - one in wasm loader, so we can just check whether the two type indexes - are equal (the type index of call_indirect opcode and callee func), - we don't need to check whether the whole function types are equal, - including param types and result types. */ - type_idx = - wasm_get_smallest_type_idx((WASMTypePtr *)comp_ctx->comp_data->types, - comp_ctx->comp_data->type_count, type_idx); + if (!comp_ctx->enable_gc) { + /* Find the equivalent function type whose type index is the smallest: + the callee function's type index is also converted to the smallest + one in wasm loader, so we can just check whether the two type indexes + are equal (the type index of call_indirect opcode and callee func), + we don't need to check whether the whole function types are equal, + including param types and result types. */ + type_idx = wasm_get_smallest_type_idx( + (WASMTypePtr *)comp_ctx->comp_data->types, + comp_ctx->comp_data->type_count, type_idx); + } + else { + /* Call aot_func_type_is_super_of to check whether the func type + provided in the bytecode is a super type of the func type of + the function to call */ + } + ftype_idx_const = I32_CONST(type_idx); CHECK_LLVM_CONST(ftype_idx_const); @@ -2254,11 +2308,23 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, goto fail; } - /* Check if function type index not equal */ - if (!(cmp_ftype_idx = LLVMBuildICmp(comp_ctx->builder, LLVMIntNE, ftype_idx, - ftype_idx_const, "cmp_ftype_idx"))) { - aot_set_last_error("llvm build icmp failed."); - goto fail; +#if WASM_ENABLE_GC != 0 + if (comp_ctx->enable_gc) { + if (!(cmp_ftype_idx = call_aot_func_type_is_super_of_func( + comp_ctx, func_ctx, ftype_idx_const, ftype_idx))) { + goto fail; + } + } + else +#endif + { + /* Check if function type index not equal */ + if (!(cmp_ftype_idx = + LLVMBuildICmp(comp_ctx->builder, LLVMIntNE, ftype_idx, + ftype_idx_const, "cmp_ftype_idx"))) { + aot_set_last_error("llvm build icmp failed."); + goto fail; + } } /* Throw exception if ftype_idx != ftype_idx_const */ diff --git a/core/iwasm/compilation/aot_llvm.c b/core/iwasm/compilation/aot_llvm.c index 79a39d06a..3af56e8b6 100644 --- a/core/iwasm/compilation/aot_llvm.c +++ b/core/iwasm/compilation/aot_llvm.c @@ -85,7 +85,7 @@ aot_add_llvm_func1(const AOTCompContext *comp_ctx, LLVMModuleRef module, uint32 func_index, uint32 param_count, LLVMTypeRef func_type, const char *prefix) { - char func_name[48]; + char func_name[48] = { 0 }; LLVMValueRef func; LLVMValueRef local_value; uint32 i, j; @@ -674,7 +674,8 @@ aot_add_llvm_func(AOTCompContext *comp_ctx, LLVMModuleRef module, uint32 backend_thread_num, compile_thread_num; /* Check function parameter types and result types */ - for (i = 0; i < aot_func_type->param_count + aot_func_type->result_count; + for (i = 0; + i < (uint32)(aot_func_type->param_count + aot_func_type->result_count); i++) { if (!check_wasm_type(comp_ctx, aot_func_type->types[i])) return NULL; @@ -2547,6 +2548,9 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option) aot_set_last_error("create LLVM module failed."); goto fail; } +#if LLVM_VERSION_MAJOR >= 19 + LLVMSetIsNewDbgInfoFormat(comp_ctx->module, true); +#endif #if WASM_ENABLE_LINUX_PERF != 0 if (wasm_runtime_get_linux_perf()) { diff --git a/core/iwasm/compilation/debug/dwarf_extractor.cpp b/core/iwasm/compilation/debug/dwarf_extractor.cpp index da33fc432..e2e515ba0 100644 --- a/core/iwasm/compilation/debug/dwarf_extractor.cpp +++ b/core/iwasm/compilation/debug/dwarf_extractor.cpp @@ -295,6 +295,28 @@ lldb_function_to_function_dbi(const AOTCompContext *comp_ctx, const size_t num_function_args = function_args.GetSize(); dwarf_extractor *extractor; + /* + * Process only known languages. + * We have a few assumptions which might not be true for non-C functions. + * + * At least it's known broken for C++ and Rust: + * https://github.com/bytecodealliance/wasm-micro-runtime/issues/3187 + * https://github.com/bytecodealliance/wasm-micro-runtime/issues/3163 + */ + LanguageType language_type = function.GetLanguage(); + switch (language_type) { + case eLanguageTypeC89: + case eLanguageTypeC: + case eLanguageTypeC99: + case eLanguageTypeC11: + case eLanguageTypeC17: + break; + default: + LOG_WARNING("func %s has unsuppoted language_type 0x%x", + function_name, (int)language_type); + return NULL; + } + if (!(extractor = TO_EXTACTOR(comp_ctx->comp_data->extractor))) return NULL; @@ -313,6 +335,17 @@ lldb_function_to_function_dbi(const AOTCompContext *comp_ctx, if (function_arg_type.IsValid()) { ParamTypes[function_arg_idx + 1] = lldb_type_to_type_dbi(comp_ctx, function_arg_type); + if (ParamTypes[function_arg_idx + 1] == NULL) { + LOG_WARNING( + "func %s arg %" PRIu32 + " has a type not implemented by lldb_type_to_type_dbi", + function_name, function_arg_idx); + } + } + else { + LOG_WARNING("func %s arg %" PRIu32 ": GetTypeAtIndex failed", + function_name, function_arg_idx); + ParamTypes[function_arg_idx + 1] = NULL; } } @@ -354,27 +387,10 @@ lldb_function_to_function_dbi(const AOTCompContext *comp_ctx, LLVMDIBuilderCreateExpression(DIB, NULL, 0); auto variable_list = function.GetBlock().GetVariables(extractor->target, true, false, false); - unsigned int variable_offset = 0; if (num_function_args != variable_list.GetSize()) { - // A hack to detect C++ "this" pointer. - // - // REVISIT: is there a more reliable way? - // At the DWARF level, we can probably look at DW_AT_object_pointer - // and DW_AT_artificial. I'm not sure how it can be done via the - // LLDB API though. - if (num_function_args + 1 == variable_list.GetSize()) { - SBValue variable(variable_list.GetValueAtIndex(0)); - const char *varname = variable.GetName(); - if (varname != NULL && !strcmp(varname, "this")) { - variable_offset = 1; - } - } - if (!variable_offset) { - LOG_ERROR("function args number dismatch!:function %s %s value " - "number=%d, function args=%d", - function_name, function.GetMangledName(), - variable_list.GetSize(), num_function_args); - } + LOG_ERROR( + "function args number dismatch!:value number=%d, function args=%d", + variable_list.GetSize(), num_function_args); } LLVMMetadataRef ParamLocation = LLVMDIBuilderCreateDebugLocation( @@ -395,11 +411,10 @@ lldb_function_to_function_dbi(const AOTCompContext *comp_ctx, LLVMDIBuilderInsertDbgValueAtEnd(DIB, Param, ParamVar, ParamExpression, ParamLocation, block_curr); - for (uint32_t function_arg_idx = 0; function_arg_idx < num_function_args; - ++function_arg_idx) { - uint32_t variable_idx = variable_offset + function_arg_idx; - SBValue variable(variable_list.GetValueAtIndex(variable_idx)); - if (variable.IsValid()) { + for (uint32_t function_arg_idx = 0; + function_arg_idx < variable_list.GetSize(); ++function_arg_idx) { + SBValue variable(variable_list.GetValueAtIndex(function_arg_idx)); + if (variable.IsValid() && ParamTypes[function_arg_idx + 1] != NULL) { SBDeclaration dec(variable.GetDeclaration()); auto valtype = variable.GetType(); LLVMMetadataRef ParamLocation = LLVMDIBuilderCreateDebugLocation( @@ -408,11 +423,12 @@ lldb_function_to_function_dbi(const AOTCompContext *comp_ctx, const char *varname = variable.GetName(); LLVMMetadataRef ParamVar = LLVMDIBuilderCreateParameterVariable( DIB, FunctionMetadata, varname, varname ? strlen(varname) : 0, - variable_idx + 1 + 1, + function_arg_idx + 1 + 1, File, // starts form 1, and 1 is exenv, dec.GetLine(), ParamTypes[function_arg_idx + 1], true, LLVMDIFlagZero); - LLVMValueRef Param = LLVMGetParam(func_ctx->func, variable_idx + 1); + LLVMValueRef Param = + LLVMGetParam(func_ctx->func, function_arg_idx + 1); LLVMDIBuilderInsertDbgValueAtEnd(DIB, Param, ParamVar, ParamExpression, ParamLocation, block_curr); diff --git a/core/iwasm/compilation/simd/simd_conversions.c b/core/iwasm/compilation/simd/simd_conversions.c index 8e4c17ed3..042e28089 100644 --- a/core/iwasm/compilation/simd/simd_conversions.c +++ b/core/iwasm/compilation/simd/simd_conversions.c @@ -226,15 +226,6 @@ aot_compile_simd_i16x8_narrow_i32x4(AOTCompContext *comp_ctx, } } -bool -aot_compile_simd_i32x4_narrow_i64x2(AOTCompContext *comp_ctx, - AOTFuncContext *func_ctx, bool is_signed) -{ - /* TODO: x86 intrinsics */ - return simd_integer_narrow_common(comp_ctx, func_ctx, e_sat_i64x2, - is_signed); -} - enum integer_extend_type { e_ext_i8x16, e_ext_i16x8, diff --git a/core/iwasm/compilation/simd/simd_conversions.h b/core/iwasm/compilation/simd/simd_conversions.h index 87b8bd684..e3a1a3521 100644 --- a/core/iwasm/compilation/simd/simd_conversions.h +++ b/core/iwasm/compilation/simd/simd_conversions.h @@ -20,10 +20,6 @@ bool aot_compile_simd_i16x8_narrow_i32x4(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, bool is_signed); -bool -aot_compile_simd_i32x4_narrow_i64x2(AOTCompContext *comp_ctx, - AOTFuncContext *func_ctx, bool is_signed); - bool aot_compile_simd_i16x8_extend_i8x16(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, bool is_low, diff --git a/core/iwasm/compilation/simd/simd_floating_point.c b/core/iwasm/compilation/simd/simd_floating_point.c index 7fcc1ab65..536ef5b28 100644 --- a/core/iwasm/compilation/simd/simd_floating_point.c +++ b/core/iwasm/compilation/simd/simd_floating_point.c @@ -129,20 +129,6 @@ aot_compile_simd_f64x2_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) "llvm.fabs.v2f64"); } -bool -aot_compile_simd_f32x4_round(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) -{ - return simd_float_intrinsic(comp_ctx, func_ctx, V128_f32x4_TYPE, - "llvm.round.v4f32"); -} - -bool -aot_compile_simd_f64x2_round(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) -{ - return simd_float_intrinsic(comp_ctx, func_ctx, V128_f64x2_TYPE, - "llvm.round.v2f64"); -} - bool aot_compile_simd_f32x4_sqrt(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) { diff --git a/core/iwasm/compilation/simd/simd_floating_point.h b/core/iwasm/compilation/simd/simd_floating_point.h index 213b4391f..39e37c872 100644 --- a/core/iwasm/compilation/simd/simd_floating_point.h +++ b/core/iwasm/compilation/simd/simd_floating_point.h @@ -32,14 +32,6 @@ aot_compile_simd_f32x4_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); bool aot_compile_simd_f64x2_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); -bool -aot_compile_simd_f32x4_round(AOTCompContext *comp_ctx, - AOTFuncContext *func_ctx); - -bool -aot_compile_simd_f64x2_round(AOTCompContext *comp_ctx, - AOTFuncContext *func_ctx); - bool aot_compile_simd_f32x4_sqrt(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); diff --git a/core/iwasm/compilation/simd/simd_int_arith.c b/core/iwasm/compilation/simd/simd_int_arith.c index 1d0e6967b..6a1902d1f 100644 --- a/core/iwasm/compilation/simd/simd_int_arith.c +++ b/core/iwasm/compilation/simd/simd_int_arith.c @@ -243,7 +243,6 @@ aot_compile_simd_i64x2_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) enum integer_avgr_u { e_avgr_u_i8x16, e_avgr_u_i16x8, - e_avgr_u_i32x4, }; /* TODO: try int_x86_mmx_pavg_b and int_x86_mmx_pavg_w */ @@ -257,9 +256,8 @@ simd_v128_avg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMTypeRef vector_type[] = { V128_i8x16_TYPE, V128_i16x8_TYPE, - V128_i32x4_TYPE, }; - unsigned lanes[] = { 16, 8, 4 }; + unsigned lanes[] = { 16, 8 }; if (!(rhs = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type[itype], "rhs")) @@ -325,13 +323,6 @@ aot_compile_simd_i16x8_avgr_u(AOTCompContext *comp_ctx, return simd_v128_avg(comp_ctx, func_ctx, e_avgr_u_i16x8); } -bool -aot_compile_simd_i32x4_avgr_u(AOTCompContext *comp_ctx, - AOTFuncContext *func_ctx) -{ - return simd_v128_avg(comp_ctx, func_ctx, e_avgr_u_i32x4); -} - bool aot_compile_simd_i32x4_dot_i16x8(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) diff --git a/core/iwasm/compilation/simd/simd_int_arith.h b/core/iwasm/compilation/simd/simd_int_arith.h index a7a21170a..49827d51d 100644 --- a/core/iwasm/compilation/simd/simd_int_arith.h +++ b/core/iwasm/compilation/simd/simd_int_arith.h @@ -76,10 +76,6 @@ bool aot_compile_simd_i16x8_avgr_u(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); -bool -aot_compile_simd_i32x4_avgr_u(AOTCompContext *comp_ctx, - AOTFuncContext *func_ctx); - bool aot_compile_simd_i32x4_dot_i16x8(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); diff --git a/core/iwasm/compilation/simd/simd_sat_int_arith.c b/core/iwasm/compilation/simd/simd_sat_int_arith.c index 1de4520a7..ea250b7e0 100644 --- a/core/iwasm/compilation/simd/simd_sat_int_arith.c +++ b/core/iwasm/compilation/simd/simd_sat_int_arith.c @@ -64,18 +64,3 @@ aot_compile_simd_i16x8_saturate(AOTCompContext *comp_ctx, is_signed ? intrinsics[arith_op][0] : intrinsics[arith_op][1]); } - -bool -aot_compile_simd_i32x4_saturate(AOTCompContext *comp_ctx, - AOTFuncContext *func_ctx, - V128Arithmetic arith_op, bool is_signed) -{ - char *intrinsics[][2] = { - { "llvm.sadd.sat.v4i32", "llvm.uadd.sat.v4i32" }, - { "llvm.ssub.sat.v4i32", "llvm.usub.sat.v4i32" }, - }; - - return simd_sat_int_arith(comp_ctx, func_ctx, V128_i16x8_TYPE, - is_signed ? intrinsics[arith_op][0] - : intrinsics[arith_op][1]); -} diff --git a/core/iwasm/compilation/simd/simd_sat_int_arith.h b/core/iwasm/compilation/simd/simd_sat_int_arith.h index e30acaaf4..67c602fc5 100644 --- a/core/iwasm/compilation/simd/simd_sat_int_arith.h +++ b/core/iwasm/compilation/simd/simd_sat_int_arith.h @@ -22,10 +22,6 @@ aot_compile_simd_i16x8_saturate(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, V128Arithmetic arith_op, bool is_signed); -bool -aot_compile_simd_i32x4_saturate(AOTCompContext *comp_ctx, - AOTFuncContext *func_ctx, - V128Arithmetic arith_op, bool is_signed); #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/core/iwasm/fast-jit/cg/x86-64/jit_codegen_x86_64.cpp b/core/iwasm/fast-jit/cg/x86-64/jit_codegen_x86_64.cpp index f5605b6f2..79c72503e 100644 --- a/core/iwasm/fast-jit/cg/x86-64/jit_codegen_x86_64.cpp +++ b/core/iwasm/fast-jit/cg/x86-64/jit_codegen_x86_64.cpp @@ -7511,7 +7511,7 @@ at_rmw_xor_r_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, CHECK_KIND(r3, JIT_REG_KIND_I64); \ } \ /* r0: read/return value r2: memory base addr can't be const */ \ - /* already check it's not const in LOAD_4ARGS(); */ \ + /* already check it's not const in LOAD_4ARGS() */ \ reg_no_dst = jit_reg_no(r0); \ CHECK_REG_NO(reg_no_dst, jit_reg_kind(r0)); \ /* mem_data base address has to be non-const */ \ @@ -9293,8 +9293,8 @@ jit_codegen_init() imm.setValue(INT32_MAX); a.jne(imm); - char *stream = (char *)a.code()->sectionById(0)->buffer().data() - + a.code()->sectionById(0)->buffer().size(); + char *stream_old = (char *)a.code()->sectionById(0)->buffer().data() + + a.code()->sectionById(0)->buffer().size(); /* If yes, call jit_set_exception_with_id to throw exception, and then set eax to JIT_INTERP_ACTION_THROWN, and jump to @@ -9319,7 +9319,7 @@ jit_codegen_init() /* Patch the offset of jne instruction */ char *stream_new = (char *)a.code()->sectionById(0)->buffer().data() + a.code()->sectionById(0)->buffer().size(); - *(int32 *)(stream - 4) = (int32)(stream_new - stream); + *(int32 *)(stream_old - 4) = (int32)(stream_new - stream_old); } /* Load compiled func ptr and call it */ @@ -9419,7 +9419,7 @@ static uint8 hreg_info_F64[3][16] = { 1, 1, 1, 1, 1, 1, 1, 0 }, /* caller_saved_jitted */ }; -static const JitHardRegInfo hreg_info = { +static const JitHardRegInfo g_hreg_info = { { { 0, NULL, NULL, NULL }, /* VOID */ @@ -9459,7 +9459,7 @@ static const JitHardRegInfo hreg_info = { const JitHardRegInfo * jit_codegen_get_hreg_info() { - return &hreg_info; + return &g_hreg_info; } static const char *reg_names_i32[] = { diff --git a/core/iwasm/fast-jit/fe/jit_emit_memory.c b/core/iwasm/fast-jit/fe/jit_emit_memory.c index 0a977c1d6..ea245ba34 100644 --- a/core/iwasm/fast-jit/fe/jit_emit_memory.c +++ b/core/iwasm/fast-jit/fe/jit_emit_memory.c @@ -636,7 +636,7 @@ wasm_init_memory(WASMModuleInstance *inst, uint32 mem_idx, uint32 seg_idx, /* if d + n > the length of mem.data */ mem_inst = inst->memories[mem_idx]; - mem_size = mem_inst->cur_page_count * mem_inst->num_bytes_per_page; + mem_size = mem_inst->cur_page_count * (uint64)mem_inst->num_bytes_per_page; if (mem_size < mem_offset || mem_size - mem_offset < len) goto out_of_bounds; @@ -724,8 +724,10 @@ wasm_copy_memory(WASMModuleInstance *inst, uint32 src_mem_idx, src_mem = inst->memories[src_mem_idx]; dst_mem = inst->memories[dst_mem_idx]; - src_mem_size = src_mem->cur_page_count * src_mem->num_bytes_per_page; - dst_mem_size = dst_mem->cur_page_count * dst_mem->num_bytes_per_page; + src_mem_size = + src_mem->cur_page_count * (uint64)src_mem->num_bytes_per_page; + dst_mem_size = + dst_mem->cur_page_count * (uint64)dst_mem->num_bytes_per_page; /* if s + n > the length of mem.data */ if (src_mem_size < src_offset || src_mem_size - src_offset < len) @@ -788,7 +790,7 @@ wasm_fill_memory(WASMModuleInstance *inst, uint32 mem_idx, uint32 len, uint8 *dst_addr; mem_inst = inst->memories[mem_idx]; - mem_size = mem_inst->cur_page_count * mem_inst->num_bytes_per_page; + mem_size = mem_inst->cur_page_count * (uint64)mem_inst->num_bytes_per_page; if (mem_size < dst || mem_size - dst < len) goto out_of_bounds; diff --git a/core/iwasm/include/aot_export.h b/core/iwasm/include/aot_export.h index c1a03d86c..d06fef1dd 100644 --- a/core/iwasm/include/aot_export.h +++ b/core/iwasm/include/aot_export.h @@ -3,6 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +/** + * @file aot_export.h + * + * @brief This file defines the exported AOT compilation APIs + */ + #ifndef _AOT_EXPORT_H #define _AOT_EXPORT_H diff --git a/core/iwasm/include/gc_export.h b/core/iwasm/include/gc_export.h index 3eb88dbab..777551edc 100644 --- a/core/iwasm/include/gc_export.h +++ b/core/iwasm/include/gc_export.h @@ -3,6 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +/** + * @file gc_export.h + * + * @brief This file defines the exported GC APIs + */ + #ifndef _GC_EXPORT_H #define _GC_EXPORT_H diff --git a/core/iwasm/include/lib_export.h b/core/iwasm/include/lib_export.h index e4829e4fe..0ca668f52 100644 --- a/core/iwasm/include/lib_export.h +++ b/core/iwasm/include/lib_export.h @@ -3,6 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +/** + * @file lib_export.h + * + */ + #ifndef _LIB_EXPORT_H_ #define _LIB_EXPORT_H_ diff --git a/core/iwasm/include/wasm_c_api.h b/core/iwasm/include/wasm_c_api.h index 606b9ff82..63d18f3ae 100644 --- a/core/iwasm/include/wasm_c_api.h +++ b/core/iwasm/include/wasm_c_api.h @@ -1,5 +1,11 @@ // WebAssembly C API +/** + * @file wasm_c_api.h + * + * @brief This file defines the WebAssembly C APIs + */ + #ifndef _WASM_C_API_H_ #define _WASM_C_API_H_ @@ -517,10 +523,21 @@ struct WASMModuleCommon; typedef struct WASMModuleCommon *wasm_module_t; #endif +#ifndef LOAD_ARGS_OPTION_DEFINED +#define LOAD_ARGS_OPTION_DEFINED +typedef struct LoadArgs { + char *name; + /* TODO: more fields? */ +} LoadArgs; +#endif /* LOAD_ARGS_OPTION_DEFINED */ WASM_API_EXTERN own wasm_module_t* wasm_module_new( wasm_store_t*, const wasm_byte_vec_t* binary); +// please refer to wasm_runtime_load_ex(...) in core/iwasm/include/wasm_export.h +WASM_API_EXTERN own wasm_module_t* wasm_module_new_ex( + wasm_store_t*, const wasm_byte_vec_t* binary, const LoadArgs *args); + WASM_API_EXTERN void wasm_module_delete(own wasm_module_t*); WASM_API_EXTERN bool wasm_module_validate(wasm_store_t*, const wasm_byte_vec_t* binary); diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h index b40a3440a..c7513396f 100644 --- a/core/iwasm/include/wasm_export.h +++ b/core/iwasm/include/wasm_export.h @@ -3,6 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +/** + * @file wasm_export.h + * + * @brief This file defines the exported common runtime APIs + */ + #ifndef _WASM_EXPORT_H #define _WASM_EXPORT_H @@ -26,10 +32,7 @@ extern "C" { #endif -/* clang-format off */ - -#define get_module_inst(exec_env) \ - wasm_runtime_get_module_inst(exec_env) +#define get_module_inst(exec_env) wasm_runtime_get_module_inst(exec_env) #define validate_app_addr(offset, size) \ wasm_runtime_validate_app_addr(module_inst, offset, size) @@ -46,8 +49,7 @@ extern "C" { #define module_malloc(size, p_native_addr) \ wasm_runtime_module_malloc(module_inst, size, p_native_addr) -#define module_free(offset) \ - wasm_runtime_module_free(module_inst, offset) +#define module_free(offset) wasm_runtime_module_free(module_inst, offset) #define native_raw_return_type(type, args) type *raw_ret = (type *)(args) @@ -63,6 +65,25 @@ struct WASMModuleCommon; typedef struct WASMModuleCommon *wasm_module_t; #endif +typedef enum { + WASM_IMPORT_EXPORT_KIND_FUNC, + WASM_IMPORT_EXPORT_KIND_TABLE, + WASM_IMPORT_EXPORT_KIND_MEMORY, + WASM_IMPORT_EXPORT_KIND_GLOBAL +} wasm_import_export_kind_t; + +typedef struct wasm_import_type { + const char *module_name; + const char *name; + wasm_import_export_kind_t kind; + bool linked; +} wasm_import_type; + +typedef struct wasm_export_type { + const char *name; + wasm_import_export_kind_t kind; +} wasm_export_type; + /* Instantiated WASM module */ struct WASMModuleInstanceCommon; typedef struct WASMModuleInstanceCommon *wasm_module_inst_t; @@ -107,6 +128,8 @@ typedef enum { Alloc_With_System_Allocator, } mem_alloc_type_t; +typedef enum { Alloc_For_Runtime, Alloc_For_LinearMemory } mem_alloc_usage_t; + /* Memory allocator option */ typedef union MemAllocOption { struct { @@ -114,6 +137,9 @@ typedef union MemAllocOption { uint32_t heap_size; } pool; struct { + /* the function signature is varied when + WASM_MEM_ALLOC_WITH_USER_DATA and + WASM_MEM_ALLOC_WITH_USAGE are defined */ void *malloc_func; void *realloc_func; void *free_func; @@ -183,6 +209,14 @@ typedef struct RuntimeInitArgs { bool enable_linux_perf; } RuntimeInitArgs; +#ifndef LOAD_ARGS_OPTION_DEFINED +#define LOAD_ARGS_OPTION_DEFINED +typedef struct LoadArgs { + char *name; + /* TODO: more fields? */ +} LoadArgs; +#endif /* LOAD_ARGS_OPTION_DEFINED */ + #ifndef INSTANTIATION_ARGS_OPTION_DEFINED #define INSTANTIATION_ARGS_OPTION_DEFINED /* WASM module instantiation arguments */ @@ -351,8 +385,8 @@ wasm_runtime_is_xip_file(const uint8_t *buf, uint32_t size); * Callback to load a module file into a buffer in multi-module feature */ typedef bool (*module_reader)(package_type_t module_type, - const char *module_name, - uint8_t **p_buffer, uint32_t *p_size); + const char *module_name, uint8_t **p_buffer, + uint32_t *p_size); /** * Callback to release the buffer loaded by module_reader callback @@ -416,8 +450,15 @@ wasm_runtime_find_module_registered(const char *module_name); * @return return WASM module loaded, NULL if failed */ WASM_RUNTIME_API_EXTERN wasm_module_t -wasm_runtime_load(uint8_t *buf, uint32_t size, - char *error_buf, uint32_t error_buf_size); +wasm_runtime_load(uint8_t *buf, uint32_t size, char *error_buf, + uint32_t error_buf_size); + +/** + * Load a WASM module with specified load argument. + */ +WASM_RUNTIME_API_EXTERN wasm_module_t +wasm_runtime_load_ex(uint8_t *buf, uint32_t size, const LoadArgs *args, + char *error_buf, uint32_t error_buf_size); /** * Load a WASM module from a specified WASM or AOT section list. @@ -486,12 +527,12 @@ wasm_runtime_get_module_hash(wasm_module_t module); * for STDERR is used. */ WASM_RUNTIME_API_EXTERN void -wasm_runtime_set_wasi_args_ex(wasm_module_t module, - const char *dir_list[], uint32_t dir_count, - const char *map_dir_list[], uint32_t map_dir_count, - const char *env[], uint32_t env_count, - char *argv[], int argc, int64_t stdinfd, - int64_t stdoutfd, int64_t stderrfd); +wasm_runtime_set_wasi_args_ex(wasm_module_t module, const char *dir_list[], + uint32_t dir_count, const char *map_dir_list[], + uint32_t map_dir_count, const char *env[], + uint32_t env_count, char *argv[], int argc, + int64_t stdinfd, int64_t stdoutfd, + int64_t stderrfd); /** * Set WASI parameters. @@ -499,34 +540,34 @@ wasm_runtime_set_wasi_args_ex(wasm_module_t module, * Same as wasm_runtime_set_wasi_args_ex but with default stdio handles */ WASM_RUNTIME_API_EXTERN void -wasm_runtime_set_wasi_args(wasm_module_t module, - const char *dir_list[], uint32_t dir_count, - const char *map_dir_list[], uint32_t map_dir_count, - const char *env[], uint32_t env_count, - char *argv[], int argc); +wasm_runtime_set_wasi_args(wasm_module_t module, const char *dir_list[], + uint32_t dir_count, const char *map_dir_list[], + uint32_t map_dir_count, const char *env[], + uint32_t env_count, char *argv[], int argc); WASM_RUNTIME_API_EXTERN void wasm_runtime_set_wasi_addr_pool(wasm_module_t module, const char *addr_pool[], uint32_t addr_pool_size); WASM_RUNTIME_API_EXTERN void -wasm_runtime_set_wasi_ns_lookup_pool(wasm_module_t module, const char *ns_lookup_pool[], +wasm_runtime_set_wasi_ns_lookup_pool(wasm_module_t module, + const char *ns_lookup_pool[], uint32_t ns_lookup_pool_size); /** * Instantiate a WASM module. * * @param module the WASM module to instantiate - * @param default_stack_size the default stack size of the module instance when the - * exec env's operation stack isn't created by user, e.g. API + * @param default_stack_size the default stack size of the module instance when + * the exec env's operation stack isn't created by user, e.g. API * wasm_application_execute_main() and wasm_application_execute_func() * create the operation stack internally with the stack size specified * here. And API wasm_runtime_create_exec_env() creates the operation * stack with stack size specified by its parameter, the stack size * specified here is ignored. - * @param host_managed_heap_size the default heap size of the module instance, a heap will - * be created besides the app memory space. Both wasm app and native - * function can allocate memory from the heap. + * @param host_managed_heap_size the default heap size of the module instance, + * a heap will be created besides the app memory space. Both wasm app + * and native function can allocate memory from the heap. * @param error_buf buffer to output the error info if failed * @param error_buf_size the size of the error buffer * @@ -534,18 +575,20 @@ wasm_runtime_set_wasi_ns_lookup_pool(wasm_module_t module, const char *ns_lookup */ WASM_RUNTIME_API_EXTERN wasm_module_inst_t wasm_runtime_instantiate(const wasm_module_t module, - uint32_t default_stack_size, uint32_t host_managed_heap_size, - char *error_buf, uint32_t error_buf_size); + uint32_t default_stack_size, + uint32_t host_managed_heap_size, char *error_buf, + uint32_t error_buf_size); /** * Instantiate a WASM module, with specified instantiation arguments * - * Same as wasm_runtime_instantiate, but it also allows overwriting maximum memory + * Same as wasm_runtime_instantiate, but it also allows overwriting maximum + * memory */ WASM_RUNTIME_API_EXTERN wasm_module_inst_t wasm_runtime_instantiate_ex(const wasm_module_t module, - const InstantiationArgs *args, - char *error_buf, uint32_t error_buf_size); + const InstantiationArgs *args, char *error_buf, + uint32_t error_buf_size); /** * Set the running mode of a WASM module instance, override the @@ -730,7 +773,8 @@ wasm_runtime_get_exec_env_singleton(wasm_module_inst_t module_inst); * @return debug port if success, 0 otherwise. */ WASM_RUNTIME_API_EXTERN uint32_t -wasm_runtime_start_debug_instance_with_port(wasm_exec_env_t exec_env, int32_t port); +wasm_runtime_start_debug_instance_with_port(wasm_exec_env_t exec_env, + int32_t port); /** * Same as wasm_runtime_start_debug_instance_with_port(env, -1). @@ -811,8 +855,7 @@ wasm_runtime_set_module_inst(wasm_exec_env_t exec_env, * info. */ WASM_RUNTIME_API_EXTERN bool -wasm_runtime_call_wasm(wasm_exec_env_t exec_env, - wasm_function_inst_t function, +wasm_runtime_call_wasm(wasm_exec_env_t exec_env, wasm_function_inst_t function, uint32_t argc, uint32_t argv[]); /** @@ -833,9 +876,9 @@ wasm_runtime_call_wasm(wasm_exec_env_t exec_env, */ WASM_RUNTIME_API_EXTERN bool wasm_runtime_call_wasm_a(wasm_exec_env_t exec_env, - wasm_function_inst_t function, - uint32_t num_results, wasm_val_t results[], - uint32_t num_args, wasm_val_t *args); + wasm_function_inst_t function, uint32_t num_results, + wasm_val_t results[], uint32_t num_args, + wasm_val_t *args); /** * Call the given WASM function of a WASM module instance with @@ -855,9 +898,8 @@ wasm_runtime_call_wasm_a(wasm_exec_env_t exec_env, */ WASM_RUNTIME_API_EXTERN bool wasm_runtime_call_wasm_v(wasm_exec_env_t exec_env, - wasm_function_inst_t function, - uint32_t num_results, wasm_val_t results[], - uint32_t num_args, ...); + wasm_function_inst_t function, uint32_t num_results, + wasm_val_t results[], uint32_t num_args, ...); /** * Call a function reference of a given WASM runtime instance with @@ -899,8 +941,8 @@ wasm_runtime_call_indirect(wasm_exec_env_t exec_env, uint32_t element_index, * the exception info. */ WASM_RUNTIME_API_EXTERN bool -wasm_application_execute_main(wasm_module_inst_t module_inst, - int32_t argc, char *argv[]); +wasm_application_execute_main(wasm_module_inst_t module_inst, int32_t argc, + char *argv[]); /** * Find the specified function in argv[0] from a WASM module instance @@ -918,8 +960,8 @@ wasm_application_execute_main(wasm_module_inst_t module_inst, * to get the exception info. */ WASM_RUNTIME_API_EXTERN bool -wasm_application_execute_func(wasm_module_inst_t module_inst, - const char *name, int32_t argc, char *argv[]); +wasm_application_execute_func(wasm_module_inst_t module_inst, const char *name, + int32_t argc, char *argv[]); /** * Get exception info of the WASM module instance. @@ -978,8 +1020,7 @@ wasm_runtime_terminate(wasm_module_inst_t module_inst); * @param custom_data the custom data to be set */ WASM_RUNTIME_API_EXTERN void -wasm_runtime_set_custom_data(wasm_module_inst_t module_inst, - void *custom_data); +wasm_runtime_set_custom_data(wasm_module_inst_t module_inst, void *custom_data); /** * Get the custom data within a WASM module instance. @@ -998,8 +1039,7 @@ wasm_runtime_get_custom_data(wasm_module_inst_t module_inst); * @param enable the flag to enable/disable the memory bounds checks */ WASM_RUNTIME_API_EXTERN void -wasm_runtime_set_bounds_checks(wasm_module_inst_t module_inst, - bool enable); +wasm_runtime_set_bounds_checks(wasm_module_inst_t module_inst, bool enable); /** * Check if the memory bounds checks flag is enabled for a WASM module instance. @@ -1008,8 +1048,7 @@ wasm_runtime_set_bounds_checks(wasm_module_inst_t module_inst, * @return true if the memory bounds checks flag is enabled, false otherwise */ WASM_RUNTIME_API_EXTERN bool -wasm_runtime_is_bounds_checks_enabled( - wasm_module_inst_t module_inst); +wasm_runtime_is_bounds_checks_enabled(wasm_module_inst_t module_inst); /** * Allocate memory from the heap of WASM module instance @@ -1057,8 +1096,8 @@ wasm_runtime_module_free(wasm_module_inst_t module_inst, uint64_t ptr); * Return non-zero if success, zero if failed. */ WASM_RUNTIME_API_EXTERN uint64_t -wasm_runtime_module_dup_data(wasm_module_inst_t module_inst, - const char *src, uint64_t size); +wasm_runtime_module_dup_data(wasm_module_inst_t module_inst, const char *src, + uint64_t size); /** * Validate the app address, check whether it belongs to WASM module @@ -1176,6 +1215,48 @@ wasm_runtime_get_native_addr_range(wasm_module_inst_t module_inst, uint8_t **p_native_start_addr, uint8_t **p_native_end_addr); +/** + * Get the number of import items for a WASM module + * + * @param module the WASM module + * + * @return the number of imports (zero for none), or -1 for failure + */ +WASM_RUNTIME_API_EXTERN int32_t +wasm_runtime_get_import_count(const wasm_module_t module); + +/** + * Get information about a specific WASM module import + * + * @param module the WASM module + * @param import_index the desired import index + * @param import_type the location to store information about the import + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_get_import_type(const wasm_module_t module, int32_t import_index, + wasm_import_type *import_type); + +/** + * Get the number of export items for a WASM module + * + * @param module the WASM module + * + * @return the number of exports (zero for none), or -1 for failure + */ +WASM_RUNTIME_API_EXTERN int32_t +wasm_runtime_get_export_count(const wasm_module_t module); + +/** + * Get information about a specific WASM module export + * + * @param module the WASM module + * @param export_index the desired export index + * @param export_type the location to store information about the export + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_get_export_type(const wasm_module_t module, int32_t export_index, + wasm_export_type *export_type); + /** * Register native functions with same module name * @@ -1195,7 +1276,8 @@ wasm_runtime_get_native_addr_range(wasm_module_inst_t module_inst, * 'I': the parameter is i64 type * 'f': the parameter is f32 type * 'F': the parameter is f64 type - * 'r': the parameter is externref type, it should be a uintptr_t in host + * 'r': the parameter is externref type, it should be a uintptr_t + * in host * '*': the parameter is a pointer (i32 in WASM), and runtime will * auto check its boundary before calling the native function. * If it is followed by '~', the checked length of the pointer @@ -1229,7 +1311,6 @@ wasm_runtime_register_natives_raw(const char *module_name, NativeSymbol *native_symbols, uint32_t n_native_symbols); - /** * Undo wasm_runtime_register_natives or wasm_runtime_register_natives_raw * @@ -1306,7 +1387,7 @@ wasm_runtime_sum_wasm_exec_time(wasm_module_inst_t module_inst); /** * Return execution time in ms of a given wasm funciton with -* func_name. If the function is not found, return 0. + * func_name. If the function is not found, return 0. * * @param module_inst the WASM module instance to profile * @param func_name could be an export name or a name in the @@ -1385,8 +1466,8 @@ wasm_runtime_join_thread(wasm_thread_t tid, void **retval); * @return true if success, false otherwise */ WASM_RUNTIME_API_EXTERN bool -wasm_externref_obj2ref(wasm_module_inst_t module_inst, - void *extern_obj, uint32_t *p_externref_idx); +wasm_externref_obj2ref(wasm_module_inst_t module_inst, void *extern_obj, + uint32_t *p_externref_idx); /** * Delete external object registered by `wasm_externref_obj2ref`. @@ -1513,7 +1594,6 @@ WASM_RUNTIME_API_EXTERN const uint8_t * wasm_runtime_get_custom_section(wasm_module_t const module_comm, const char *name, uint32_t *len); - /** * Get WAMR semantic version */ @@ -1529,8 +1609,8 @@ wasm_runtime_is_import_func_linked(const char *module_name, const char *func_name); /** - * Check whether an import global `(import (global ...))` - * is linked or not with runtime registered natvie globals + * Check whether an import global `(import + * (global ...))` is linked or not with runtime registered natvie globals */ WASM_RUNTIME_API_EXTERN bool wasm_runtime_is_import_global_linked(const char *module_name, @@ -1544,8 +1624,7 @@ typedef enum { typedef void (*enlarge_memory_error_callback_t)( uint32_t inc_page_count, uint64_t current_memory_size, uint32_t memory_index, enlarge_memory_error_reason_t failure_reason, - wasm_module_inst_t instance, wasm_exec_env_t exec_env, - void* user_data); + wasm_module_inst_t instance, wasm_exec_env_t exec_env, void *user_data); /** * Setup callback invoked when memory.grow fails @@ -1607,8 +1686,8 @@ wasm_runtime_set_enlarge_mem_error_callback( */ WASM_RUNTIME_API_EXTERN void * -wasm_runtime_create_context_key( - void (*dtor)(wasm_module_inst_t inst, void *ctx)); +wasm_runtime_create_context_key(void (*dtor)(wasm_module_inst_t inst, + void *ctx)); WASM_RUNTIME_API_EXTERN void wasm_runtime_destroy_context_key(void *key); @@ -1669,16 +1748,64 @@ wasm_runtime_begin_blocking_op(wasm_exec_env_t exec_env); WASM_RUNTIME_API_EXTERN void wasm_runtime_end_blocking_op(wasm_exec_env_t exec_env); - WASM_RUNTIME_API_EXTERN bool wasm_runtime_set_module_name(wasm_module_t module, const char *name, char *error_buf, uint32_t error_buf_size); /* return the most recently set module name or "" if never set before */ -WASM_RUNTIME_API_EXTERN const char* +WASM_RUNTIME_API_EXTERN const char * wasm_runtime_get_module_name(wasm_module_t module); -/* clang-format on */ +/* + * wasm_runtime_detect_native_stack_overflow + * + * Detect native stack shortage. + * Ensure that the calling thread still has a reasonable amount of + * native stack (WASM_STACK_GUARD_SIZE bytes) available. + * + * If enough stack is left, this function returns true. + * Otherwise, this function raises a "native stack overflow" trap and + * returns false. + * + * Note: please do not expect a very strict detection. it's a good idea + * to give some margins. wasm_runtime_detect_native_stack_overflow itself + * requires a small amount of stack to run. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_detect_native_stack_overflow(wasm_exec_env_t exec_env); + +/* + * wasm_runtime_detect_native_stack_overflow_size + * + * Similar to wasm_runtime_detect_native_stack_overflow, + * but use the caller-specified size instead of WASM_STACK_GUARD_SIZE. + * + * An expected usage: + * ```c + * __attribute__((noinline)) // inlining can break the stack check + * void stack_hog(void) + * { + * // consume a lot of stack here + * } + * + * void + * stack_hog_wrapper(exec_env) { + * // the amount of stack stack_hog would consume, + * // plus a small margin + * uint32_t size = 10000000; + * + * if (!wasm_runtime_detect_native_stack_overflow_size(exec_env, size)) { + * // wasm_runtime_detect_native_stack_overflow_size has raised + * // a trap. + * return; + * } + * stack_hog(); + * } + * ``` + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_detect_native_stack_overflow_size(wasm_exec_env_t exec_env, + uint32_t required_size); #ifdef __cplusplus } diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index 5fd86b572..5c436cbfa 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/core/iwasm/interpreter/wasm.h @@ -274,7 +274,7 @@ typedef struct InitializerExpression { */ typedef struct RefHeapType_TypeIdx { /* ref_type is REF_TYPE_HT_NULLABLE or - REF_TYPE_HT_NON_NULLABLE, (0x6C or 0x6B) */ + REF_TYPE_HT_NON_NULLABLE, (0x63 or 0x64) */ uint8 ref_type; /* true if ref_type is REF_TYPE_HT_NULLABLE */ bool nullable; @@ -288,7 +288,7 @@ typedef struct RefHeapType_TypeIdx { */ typedef struct RefHeapType_Common { /* ref_type is REF_TYPE_HT_NULLABLE or - REF_TYPE_HT_NON_NULLABLE (0x6C or 0x6B) */ + REF_TYPE_HT_NON_NULLABLE (0x63 or 0x64) */ uint8 ref_type; /* true if ref_type is REF_TYPE_HT_NULLABLE */ bool nullable; @@ -338,18 +338,24 @@ typedef struct WASMType { uint16 type_flag; bool is_sub_final; + /* How many types are referring to this type */ + uint16 ref_count; /* The inheritance depth */ - uint32 inherit_depth; + uint16 inherit_depth; /* The root type */ struct WASMType *root_type; /* The parent type */ struct WASMType *parent_type; uint32 parent_type_idx; - /* number of internal types in the current rec group, if the type is not in - * a recursive group, rec_count = 0 */ + /* The number of internal types in the current rec group, and if + the type is not in a recursive group, rec_count is 1 since a + single type definition is reinterpreted as a short-hand for a + recursive group containing just one type */ uint16 rec_count; uint16 rec_idx; + /* The index of the begin type of this group */ + uint32 rec_begin_type_idx; } WASMType, *WASMTypePtr; #endif /* end of WASM_ENABLE_GC */ @@ -375,9 +381,6 @@ typedef struct WASMFuncType { uint16 ref_type_map_count; WASMRefTypeMap *ref_type_maps; WASMRefTypeMap *result_ref_type_maps; - /* minimal type index of the type equal to this type, - used in type equal check in call_indirect opcode */ - uint32 min_type_idx_normalized; #else uint16 ref_count; #endif @@ -1329,8 +1332,8 @@ block_type_get_param_types(BlockType *block_type, uint8 **p_param_types, param_count = func_type->param_count; #if WASM_ENABLE_GC != 0 *p_param_reftype_maps = func_type->ref_type_maps; - *p_param_reftype_map_count = - func_type->result_ref_type_maps - func_type->ref_type_maps; + *p_param_reftype_map_count = (uint32)(func_type->result_ref_type_maps + - func_type->ref_type_maps); #endif } else { diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index ca972fd4b..2763bc2be 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -1159,6 +1159,10 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst, uint8 *frame_ref; #endif + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { + return; + } + all_cell_num = local_cell_num; #if WASM_ENABLE_GC != 0 all_cell_num += (local_cell_num + 3) / 4; @@ -1290,6 +1294,14 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, uintptr_t aux_stack_origin_boundary = 0; uintptr_t aux_stack_origin_bottom = 0; + /* + * perform stack overflow check before calling + * wasm_interp_call_func_bytecode recursively. + */ + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { + return; + } + if (!sub_func_inst) { snprintf(buf, sizeof(buf), "failed to call unlinked import function (%s, %s)", @@ -1509,6 +1521,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, WASMStringviewIterObjectRef stringview_iter_obj; #endif #endif +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + bool is_return_call = false; +#endif #if WASM_ENABLE_MEMORY64 != 0 /* TODO: multi-memories for now assuming the memory idx type is consistent * across multi-memories */ @@ -2209,6 +2224,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, WASMFuncType *cur_type, *cur_func_type; WASMTableInstance *tbl_inst; uint32 tbl_idx; + #if WASM_ENABLE_TAIL_CALL != 0 opcode = *(frame_ip - 1); #endif @@ -2279,8 +2295,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, goto got_exception; } #else - if (cur_type->min_type_idx_normalized - != cur_func_type->min_type_idx_normalized) { + if (!wasm_func_type_is_super_of(cur_type, cur_func_type)) { wasm_set_exception(module, "indirect call type mismatch"); goto got_exception; } @@ -4199,7 +4214,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, else #endif { - *(uint32 *)global_addr = aux_stack_top; + *(uint32 *)global_addr = (uint32)aux_stack_top; frame_sp--; } #if WASM_ENABLE_MEMORY_PROFILING != 0 @@ -6227,6 +6242,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, frame_ip = frame->ip; frame_sp = frame->sp; frame_csp = frame->csp; +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + is_return_call = false; +#endif goto call_func_from_entry; } @@ -6320,6 +6338,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, } FREE_FRAME(exec_env, frame); wasm_exec_env_set_cur_frame(exec_env, prev_frame); + is_return_call = true; goto call_func_from_entry; } #endif @@ -6333,6 +6352,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, } SYNC_ALL_TO_FRAME(); prev_frame = frame; +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + is_return_call = false; +#endif } call_func_from_entry: @@ -6342,15 +6364,27 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (cur_func->import_func_inst) { wasm_interp_call_func_import(module, exec_env, cur_func, prev_frame); +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + if (is_return_call) { + /* the frame was freed before tail calling and + the prev_frame was set as exec_env's cur_frame, + so here we recover context from prev_frame */ + RECOVER_CONTEXT(prev_frame); + } + else +#endif + { + prev_frame = frame->prev_frame; + cur_func = frame->function; + UPDATE_ALL_FROM_FRAME(); + } + #if WASM_ENABLE_EXCE_HANDLING != 0 char uncaught_exception[128] = { 0 }; bool has_exception = wasm_copy_exception(module, uncaught_exception); if (has_exception && strstr(uncaught_exception, "uncaught wasm exception")) { - /* fix framesp */ - UPDATE_ALL_FROM_FRAME(); - uint32 import_exception; /* initialize imported exception index to be invalid */ SET_INVALID_TAGINDEX(import_exception); @@ -6392,12 +6426,22 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, { wasm_interp_call_func_native(module, exec_env, cur_func, prev_frame); +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + if (is_return_call) { + /* the frame was freed before tail calling and + the prev_frame was set as exec_env's cur_frame, + so here we recover context from prev_frame */ + RECOVER_CONTEXT(prev_frame); + } + else +#endif + { + prev_frame = frame->prev_frame; + cur_func = frame->function; + UPDATE_ALL_FROM_FRAME(); + } } - prev_frame = frame->prev_frame; - cur_func = frame->function; - UPDATE_ALL_FROM_FRAME(); - /* update memory size, no need to update memory ptr as it isn't changed in wasm_enlarge_memory */ #if !defined(OS_ENABLE_HW_BOUND_CHECK) \ @@ -7076,12 +7120,13 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, } argc = function->param_cell_num; - RECORD_STACK_USAGE(exec_env, (uint8 *)&prev_frame); -#if !(defined(OS_ENABLE_HW_BOUND_CHECK) \ - && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0) - if ((uint8 *)&prev_frame < exec_env->native_stack_boundary) { - wasm_set_exception((WASMModuleInstance *)exec_env->module_inst, - "native stack overflow"); +#if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + /* + * wasm_runtime_detect_native_stack_overflow is done by + * call_wasm_with_hw_bound_check. + */ +#else + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { return; } #endif diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index 21412046e..b861b271e 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -1167,6 +1167,10 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst, all_cell_num += (local_cell_num + 3) / 4; #endif + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { + return; + } + if (!(frame = ALLOC_FRAME(exec_env, wasm_interp_interp_frame_size(all_cell_num), prev_frame))) @@ -1275,6 +1279,14 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, uintptr_t aux_stack_origin_boundary = 0; uintptr_t aux_stack_origin_bottom = 0; + /* + * perform stack overflow check before calling + * wasm_interp_call_func_bytecode recursively. + */ + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { + return; + } + if (!sub_func_inst) { snprintf(buf, sizeof(buf), "failed to call unlinked import function (%s, %s)", @@ -1501,6 +1513,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, WASMStringviewIterObjectRef stringview_iter_obj; #endif #endif +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + bool is_return_call = false; +#endif #if WASM_ENABLE_LABELS_AS_VALUES != 0 #define HANDLE_OPCODE(op) &&HANDLE_##op @@ -1693,7 +1708,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, /* clang-format off */ #if WASM_ENABLE_GC == 0 - fidx = tbl_inst->elems[val]; + fidx = (uint32)tbl_inst->elems[val]; if (fidx == (uint32)-1) { wasm_set_exception(module, "uninitialized element"); goto got_exception; @@ -1733,8 +1748,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, goto got_exception; } #else - if (cur_type->min_type_idx_normalized - != cur_func_type->min_type_idx_normalized) { + if (!wasm_func_type_is_super_of(cur_type, cur_func_type)) { wasm_set_exception(module, "indirect call type mismatch"); goto got_exception; } @@ -5618,6 +5632,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, { frame = prev_frame; frame_ip = frame->ip; +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + is_return_call = false; +#endif goto call_func_from_entry; } @@ -5766,6 +5783,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, FREE_FRAME(exec_env, frame); frame_ip += cur_func->param_count * sizeof(int16); wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame *)prev_frame); + is_return_call = true; goto call_func_from_entry; } #endif /* WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 */ @@ -5838,6 +5856,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, } SYNC_ALL_TO_FRAME(); prev_frame = frame; +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + is_return_call = false; +#endif } call_func_from_entry: @@ -5855,9 +5876,20 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, prev_frame); } - prev_frame = frame->prev_frame; - cur_func = frame->function; - UPDATE_ALL_FROM_FRAME(); +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + if (is_return_call) { + /* the frame was freed before tail calling and + the prev_frame was set as exec_env's cur_frame, + so here we recover context from prev_frame */ + RECOVER_CONTEXT(prev_frame); + } + else +#endif + { + prev_frame = frame->prev_frame; + cur_func = frame->function; + UPDATE_ALL_FROM_FRAME(); + } /* update memory size, no need to update memory ptr as it isn't changed in wasm_enlarge_memory */ @@ -6061,12 +6093,13 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, } argc = function->param_cell_num; - RECORD_STACK_USAGE(exec_env, (uint8 *)&prev_frame); -#if !(defined(OS_ENABLE_HW_BOUND_CHECK) \ - && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0) - if ((uint8 *)&prev_frame < exec_env->native_stack_boundary) { - wasm_set_exception((WASMModuleInstance *)exec_env->module_inst, - "native stack overflow"); +#if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + /* + * wasm_runtime_detect_native_stack_overflow is done by + * call_wasm_with_hw_bound_check. + */ +#else + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { return; } #endif diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index 51384cb69..aa92f96ee 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -36,6 +36,21 @@ #define TEMPLATE_READ_VALUE(Type, p) \ (p += sizeof(Type), *(Type *)(p - sizeof(Type))) +#if WASM_ENABLE_MEMORY64 != 0 +static bool +has_module_memory64(WASMModule *module) +{ + /* TODO: multi-memories for now assuming the memory idx type is consistent + * across multi-memories */ + if (module->import_memory_count > 0) + return !!(module->import_memories[0].u.memory.flags & MEMORY64_FLAG); + else if (module->memory_count > 0) + return !!(module->memories[0].flags & MEMORY64_FLAG); + + return false; +} +#endif + static void set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) { @@ -278,7 +293,10 @@ type2str(uint8 type) static bool is_32bit_type(uint8 type) { - if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32 + if (type == VALUE_TYPE_I32 + || type == VALUE_TYPE_F32 + /* the operand stack is in polymorphic state */ + || type == VALUE_TYPE_ANY #if WASM_ENABLE_GC != 0 || (sizeof(uintptr_t) == 4 && wasm_is_type_reftype(type)) #elif WASM_ENABLE_REF_TYPES != 0 @@ -394,10 +412,10 @@ memory_realloc(void *mem_old, uint32 size_old, uint32 size_new, char *error_buf, #if WASM_ENABLE_GC != 0 static bool -check_type_index(const WASMModule *module, uint32 type_index, char *error_buf, - uint32 error_buf_size) +check_type_index(const WASMModule *module, uint32 type_count, uint32 type_index, + char *error_buf, uint32 error_buf_size) { - if (type_index >= module->type_count) { + if (type_index >= type_count) { set_error_buf_v(error_buf, error_buf_size, "unknown type %d", type_index); return false; @@ -409,7 +427,8 @@ static bool check_array_type(const WASMModule *module, uint32 type_index, char *error_buf, uint32 error_buf_size) { - if (!check_type_index(module, type_index, error_buf, error_buf_size)) { + if (!check_type_index(module, module->type_count, type_index, error_buf, + error_buf_size)) { return false; } if (module->types[type_index]->type_flag != WASM_TYPE_ARRAY) { @@ -775,8 +794,8 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end, if (!is_byte_a_type(type1)) { p--; read_leb_uint32(p, p_end, type_idx); - if (!check_type_index(module, type_idx, error_buf, - error_buf_size)) + if (!check_type_index(module, module->type_count, type_idx, + error_buf, error_buf_size)) goto fail; wasm_set_refheaptype_typeidx(&cur_ref_type.ref_ht_typeidx, @@ -902,7 +921,8 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end, uint32 field_count; read_leb_uint32(p, p_end, type_idx); - if (!check_type_index(module, type_idx, error_buf, + if (!check_type_index(module, module->type_count, + type_idx, error_buf, error_buf_size)) { goto fail; } @@ -966,7 +986,8 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end, read_leb_uint32(p, p_end, cur_value.type_index); type_idx = cur_value.type_index; - if (!check_type_index(module, type_idx, error_buf, + if (!check_type_index(module, module->type_count, + type_idx, error_buf, error_buf_size)) { goto fail; } @@ -1001,7 +1022,8 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end, read_leb_uint32(p, p_end, cur_value.type_index); type_idx = cur_value.type_index; - if (!check_type_index(module, type_idx, error_buf, + if (!check_type_index(module, module->type_count, + type_idx, error_buf, error_buf_size)) { goto fail; } @@ -1275,6 +1297,13 @@ destroy_array_type(WASMArrayType *type) static void destroy_wasm_type(WASMType *type) { + if (type->ref_count > 1) { + /* The type is referenced by other types + of current wasm module */ + type->ref_count--; + return; + } + if (type->type_flag == WASM_TYPE_FUNC) destroy_func_type((WASMFuncType *)type); else if (type->type_flag == WASM_TYPE_STRUCT) @@ -1289,8 +1318,9 @@ destroy_wasm_type(WASMType *type) /* Resolve (ref null ht) or (ref ht) */ static bool resolve_reftype_htref(const uint8 **p_buf, const uint8 *buf_end, - WASMModule *module, bool nullable, WASMRefType *ref_type, - char *error_buf, uint32 error_buf_size) + WASMModule *module, uint32 type_count, bool nullable, + WASMRefType *ref_type, char *error_buf, + uint32 error_buf_size) { const uint8 *p = *p_buf, *p_end = buf_end; @@ -1301,8 +1331,9 @@ resolve_reftype_htref(const uint8 **p_buf, const uint8 *buf_end, if (wasm_is_refheaptype_typeidx(&ref_type->ref_ht_common)) { /* heap type is (type i), i : typeidx, >= 0 */ - if (!check_type_index(module, ref_type->ref_ht_typeidx.type_idx, - error_buf, error_buf_size)) { + if (!check_type_index(module, type_count, + ref_type->ref_ht_typeidx.type_idx, error_buf, + error_buf_size)) { return false; } } @@ -1320,9 +1351,10 @@ fail: static bool resolve_value_type(const uint8 **p_buf, const uint8 *buf_end, - WASMModule *module, bool *p_need_ref_type_map, - WASMRefType *ref_type, bool allow_packed_type, - char *error_buf, uint32 error_buf_size) + WASMModule *module, uint32 type_count, + bool *p_need_ref_type_map, WASMRefType *ref_type, + bool allow_packed_type, char *error_buf, + uint32 error_buf_size) { const uint8 *p = *p_buf, *p_end = buf_end; uint8 type; @@ -1334,8 +1366,8 @@ resolve_value_type(const uint8 **p_buf, const uint8 *buf_end, if (wasm_is_reftype_htref_nullable(type)) { /* (ref null ht) */ - if (!resolve_reftype_htref(&p, p_end, module, true, ref_type, error_buf, - error_buf_size)) + if (!resolve_reftype_htref(&p, p_end, module, type_count, true, + ref_type, error_buf, error_buf_size)) return false; if (!wasm_is_refheaptype_common(&ref_type->ref_ht_common)) *p_need_ref_type_map = true; @@ -1351,8 +1383,8 @@ resolve_value_type(const uint8 **p_buf, const uint8 *buf_end, } else if (wasm_is_reftype_htref_non_nullable(type)) { /* (ref ht) */ - if (!resolve_reftype_htref(&p, p_end, module, false, ref_type, - error_buf, error_buf_size)) + if (!resolve_reftype_htref(&p, p_end, module, type_count, false, + ref_type, error_buf, error_buf_size)) return false; *p_need_ref_type_map = true; #if WASM_ENABLE_STRINGREF != 0 @@ -1401,7 +1433,8 @@ reftype_set_insert(HashMap *ref_type_set, const WASMRefType *ref_type, static bool resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module, - uint32 type_idx, char *error_buf, uint32 error_buf_size) + uint32 type_count, uint32 type_idx, char *error_buf, + uint32 error_buf_size) { const uint8 *p = *p_buf, *p_end = buf_end, *p_org; uint32 param_count, result_count, i, j = 0; @@ -1417,8 +1450,9 @@ resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module, read_leb_uint32(p, p_end, param_count); p_org = p; for (i = 0; i < param_count; i++) { - if (!resolve_value_type(&p, p_end, module, &need_ref_type_map, - &ref_type, false, error_buf, error_buf_size)) { + if (!resolve_value_type(&p, p_end, module, type_count, + &need_ref_type_map, &ref_type, false, error_buf, + error_buf_size)) { return false; } if (need_ref_type_map) @@ -1427,8 +1461,9 @@ resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module, read_leb_uint32(p, p_end, result_count); for (i = 0; i < result_count; i++) { - if (!resolve_value_type(&p, p_end, module, &need_ref_type_map, - &ref_type, false, error_buf, error_buf_size)) { + if (!resolve_value_type(&p, p_end, module, type_count, + &need_ref_type_map, &ref_type, false, error_buf, + error_buf_size)) { return false; } if (need_ref_type_map) { @@ -1468,8 +1503,9 @@ resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module, } for (i = 0; i < param_count; i++) { - if (!resolve_value_type(&p, p_end, module, &need_ref_type_map, - &ref_type, false, error_buf, error_buf_size)) { + if (!resolve_value_type(&p, p_end, module, type_count, + &need_ref_type_map, &ref_type, false, error_buf, + error_buf_size)) { goto fail; } type->types[i] = ref_type.ref_type; @@ -1485,8 +1521,9 @@ resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module, read_leb_uint32(p, p_end, result_count); for (i = 0; i < result_count; i++) { - if (!resolve_value_type(&p, p_end, module, &need_ref_type_map, - &ref_type, false, error_buf, error_buf_size)) { + if (!resolve_value_type(&p, p_end, module, type_count, + &need_ref_type_map, &ref_type, false, error_buf, + error_buf_size)) { goto fail; } type->types[param_count + i] = ref_type.ref_type; @@ -1521,24 +1558,12 @@ resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module, #endif #if WASM_ENABLE_WAMR_COMPILER != 0 - for (i = 0; i < type->param_count + type->result_count; i++) { + for (i = 0; i < (uint32)(type->param_count + type->result_count); i++) { if (type->types[i] == VALUE_TYPE_V128) module->is_simd_used = true; } #endif - /* Calculate the minimal type index of the type equal to this type */ - type->min_type_idx_normalized = type_idx; - for (i = 0; i < type_idx; i++) { - WASMFuncType *func_type = (WASMFuncType *)module->types[i]; - if (func_type->base_type.type_flag == WASM_TYPE_FUNC - && wasm_func_type_equal(type, func_type, module->types, - type_idx + 1)) { - type->min_type_idx_normalized = i; - break; - } - } - *p_buf = p; module->types[type_idx] = (WASMType *)type; @@ -1552,8 +1577,8 @@ fail: static bool resolve_struct_type(const uint8 **p_buf, const uint8 *buf_end, - WASMModule *module, uint32 type_idx, char *error_buf, - uint32 error_buf_size) + WASMModule *module, uint32 type_count, uint32 type_idx, + char *error_buf, uint32 error_buf_size) { const uint8 *p = *p_buf, *p_end = buf_end, *p_org; uint32 field_count, ref_type_map_count = 0, ref_field_count = 0; @@ -1569,8 +1594,9 @@ resolve_struct_type(const uint8 **p_buf, const uint8 *buf_end, read_leb_uint32(p, p_end, field_count); p_org = p; for (i = 0; i < field_count; i++) { - if (!resolve_value_type(&p, p_end, module, &need_ref_type_map, - &ref_type, true, error_buf, error_buf_size)) { + if (!resolve_value_type(&p, p_end, module, type_count, + &need_ref_type_map, &ref_type, true, error_buf, + error_buf_size)) { return false; } if (need_ref_type_map) @@ -1617,8 +1643,9 @@ resolve_struct_type(const uint8 **p_buf, const uint8 *buf_end, offset = (uint32)offsetof(WASMStructObject, field_data); for (i = 0; i < field_count; i++) { - if (!resolve_value_type(&p, p_end, module, &need_ref_type_map, - &ref_type, true, error_buf, error_buf_size)) { + if (!resolve_value_type(&p, p_end, module, type_count, + &need_ref_type_map, &ref_type, true, error_buf, + error_buf_size)) { goto fail; } type->fields[i].field_type = ref_type.ref_type; @@ -1671,8 +1698,8 @@ fail: static bool resolve_array_type(const uint8 **p_buf, const uint8 *buf_end, - WASMModule *module, uint32 type_idx, char *error_buf, - uint32 error_buf_size) + WASMModule *module, uint32 type_count, uint32 type_idx, + char *error_buf, uint32 error_buf_size) { const uint8 *p = *p_buf, *p_end = buf_end; uint8 mutable; @@ -1680,8 +1707,8 @@ resolve_array_type(const uint8 **p_buf, const uint8 *buf_end, WASMRefType ref_type; WASMArrayType *type = NULL; - if (!resolve_value_type(&p, p_end, module, &need_ref_type_map, &ref_type, - true, error_buf, error_buf_size)) { + if (!resolve_value_type(&p, p_end, module, type_count, &need_ref_type_map, + &ref_type, true, error_buf, error_buf_size)) { return false; } @@ -1730,7 +1757,8 @@ init_ref_type(WASMModule *module, WASMRefType *ref_type, bool nullable, int32 heap_type, char *error_buf, uint32 error_buf_size) { if (heap_type >= 0) { - if (!check_type_index(module, heap_type, error_buf, error_buf_size)) { + if (!check_type_index(module, module->type_count, heap_type, error_buf, + error_buf_size)) { return false; } wasm_set_refheaptype_typeidx(&ref_type->ref_ht_typeidx, nullable, @@ -1929,8 +1957,8 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, } #else /* else of WASM_ENABLE_GC == 0 */ for (i = 0; i < type_count; i++) { - uint32 super_type_count = 0, parent_type_idx = (uint32)-1, - rec_count = 1, j; + uint32 super_type_count = 0, parent_type_idx = (uint32)-1; + uint32 rec_count = 1, j; bool is_sub_final = true; CHECK_BUF(p, p_end, 1); @@ -1942,10 +1970,22 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, if (rec_count > 1) { uint64 new_total_size; + /* integer overflow */ + if (rec_count - 1 > UINT32_MAX - module->type_count) { + set_error_buf(error_buf, error_buf_size, + "recursive type count too large"); + return false; + } module->type_count += rec_count - 1; new_total_size = sizeof(WASMFuncType *) * (uint64)module->type_count; - MEM_REALLOC(module->types, total_size, new_total_size); + if (new_total_size > UINT32_MAX) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + return false; + } + MEM_REALLOC(module->types, (uint32)total_size, + (uint32)new_total_size); total_size = new_total_size; } @@ -1998,6 +2038,7 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, if (flag == DEFINED_TYPE_FUNC) { if (!resolve_func_type(&p, buf_end, module, + processed_type_count + rec_count, processed_type_count + j, error_buf, error_buf_size)) { return false; @@ -2005,6 +2046,7 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, } else if (flag == DEFINED_TYPE_STRUCT) { if (!resolve_struct_type(&p, buf_end, module, + processed_type_count + rec_count, processed_type_count + j, error_buf, error_buf_size)) { return false; @@ -2012,6 +2054,7 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, } else if (flag == DEFINED_TYPE_ARRAY) { if (!resolve_array_type(&p, buf_end, module, + processed_type_count + rec_count, processed_type_count + j, error_buf, error_buf_size)) { return false; @@ -2025,13 +2068,13 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, cur_type = module->types[processed_type_count + j]; + cur_type->ref_count = 1; cur_type->parent_type_idx = parent_type_idx; cur_type->is_sub_final = is_sub_final; - if (rec_count > 1) { - cur_type->rec_count = rec_count; - cur_type->rec_idx = j; - } + cur_type->rec_count = rec_count; + cur_type->rec_idx = j; + cur_type->rec_begin_type_idx = processed_type_count; } /* resolve subtyping relationship in current rec group */ @@ -2043,6 +2086,11 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, module->types[cur_type->parent_type_idx]; cur_type->parent_type = parent_type; cur_type->root_type = parent_type->root_type; + if (parent_type->inherit_depth == UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "parent type's inherit depth too large"); + return false; + } cur_type->inherit_depth = parent_type->inherit_depth + 1; } else { @@ -2068,6 +2116,49 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, } } + /* If there is already an equivalence type or a group of equivalence + recursive types created, use it or them instead */ + for (j = 0; j < processed_type_count;) { + WASMType *src_type = module->types[j]; + WASMType *cur_type = module->types[processed_type_count]; + uint32 k, src_rec_count; + + src_rec_count = src_type->rec_count; + if (src_rec_count != rec_count) { + /* no type equivalence */ + j += src_rec_count; + continue; + } + + for (k = 0; k < rec_count; k++) { + src_type = module->types[j + k]; + cur_type = module->types[processed_type_count + k]; + if (!wasm_type_equal(src_type, cur_type, module->types, + module->type_count)) { + break; + } + } + if (k < rec_count) { + /* no type equivalence */ + j += src_rec_count; + continue; + } + + /* type equivalence */ + for (k = 0; k < rec_count; k++) { + if (module->types[j + k]->ref_count == UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "wasm type's ref count too large"); + return false; + } + destroy_wasm_type(module->types[processed_type_count + k]); + module->types[processed_type_count + k] = + module->types[j + k]; + module->types[j + k]->ref_count++; + } + break; + } + if (rec_count > 1) { LOG_VERBOSE("Finished processing rec group [%d-%d]", processed_type_count, @@ -2499,8 +2590,9 @@ load_table_import(const uint8 **p_buf, const uint8 *buf_end, return false; } #else /* else of WASM_ENABLE_GC == 0 */ - if (!resolve_value_type(&p, p_end, parent_module, &need_ref_type_map, - &ref_type, false, error_buf, error_buf_size)) { + if (!resolve_value_type(&p, p_end, parent_module, parent_module->type_count, + &need_ref_type_map, &ref_type, false, error_buf, + error_buf_size)) { return false; } if (wasm_is_reftype_htref_non_nullable(ref_type.ref_type)) { @@ -2935,8 +3027,9 @@ load_global_import(const uint8 **p_buf, const uint8 *buf_end, declare_type = read_uint8(p); declare_mutable = read_uint8(p); #else - if (!resolve_value_type(&p, p_end, parent_module, &need_ref_type_map, - &ref_type, false, error_buf, error_buf_size)) { + if (!resolve_value_type(&p, p_end, parent_module, parent_module->type_count, + &need_ref_type_map, &ref_type, false, error_buf, + error_buf_size)) { return false; } declare_type = ref_type.ref_type; @@ -3038,8 +3131,9 @@ load_table(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module, return false; } #else /* else of WASM_ENABLE_GC == 0 */ - if (!resolve_value_type(&p, p_end, module, &need_ref_type_map, &ref_type, - false, error_buf, error_buf_size)) { + if (!resolve_value_type(&p, p_end, module, module->type_count, + &need_ref_type_map, &ref_type, false, error_buf, + error_buf_size)) { return false; } table->elem_type = ref_type.ref_type; @@ -3524,7 +3618,8 @@ load_function_section(const uint8 *buf, const uint8 *buf_end, type_index_org = type_index; #endif -#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) +#if (WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0) \ + && WASM_ENABLE_GC == 0 type_index = wasm_get_smallest_type_idx( module->types, module->type_count, type_index); #endif @@ -3565,8 +3660,9 @@ load_function_section(const uint8 *buf, const uint8 *buf_end, #endif #else if (!resolve_value_type(&p_code, buf_code_end, module, - &need_ref_type_map, &ref_type, false, - error_buf, error_buf_size)) { + module->type_count, &need_ref_type_map, + &ref_type, false, error_buf, + error_buf_size)) { return false; } local_count += sub_local_count; @@ -3652,8 +3748,9 @@ load_function_section(const uint8 *buf, const uint8 *buf_end, } #else if (!resolve_value_type(&p_code, buf_code_end, module, - &need_ref_type_map, &ref_type, false, - error_buf, error_buf_size)) { + module->type_count, &need_ref_type_map, + &ref_type, false, error_buf, + error_buf_size)) { return false; } if (need_ref_type_map) { @@ -3911,9 +4008,9 @@ load_global_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, global->type = read_uint8(p); mutable = read_uint8(p); #else - if (!resolve_value_type(&p, p_end, module, &need_ref_type_map, - &ref_type, false, error_buf, - error_buf_size)) { + if (!resolve_value_type(&p, p_end, module, module->type_count, + &need_ref_type_map, &ref_type, false, + error_buf, error_buf_size)) { return false; } global->type = ref_type.ref_type; @@ -4219,8 +4316,8 @@ load_elem_type(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end, #else p--; if (!resolve_value_type((const uint8 **)&p, p_end, module, - &need_ref_type_map, &elem_ref_type, false, - error_buf, error_buf_size)) { + module->type_count, &need_ref_type_map, + &elem_ref_type, false, error_buf, error_buf_size)) { return false; } if (!wasm_is_type_reftype(elem_ref_type.ref_type)) { @@ -4534,7 +4631,7 @@ load_data_segment_section(const uint8 *buf, const uint8 *buf_end, bool is_passive = false; uint32 mem_flag; #endif - uint8 mem_offset_type; + uint8 mem_offset_type = VALUE_TYPE_I32; read_leb_uint32(p, p_end, data_seg_count); @@ -5574,12 +5671,13 @@ load_from_sections(WASMModule *module, WASMSection *sections, *buf_func = NULL, *buf_func_end = NULL; WASMGlobal *aux_data_end_global = NULL, *aux_heap_base_global = NULL; WASMGlobal *aux_stack_top_global = NULL, *global; - uint64 aux_data_end = (uint64)-1, aux_heap_base = (uint64)-1, - aux_stack_top = (uint64)-1; + uint64 aux_data_end = (uint64)-1LL, aux_heap_base = (uint64)-1LL, + aux_stack_top = (uint64)-1LL; uint32 global_index, func_index, i; uint32 aux_data_end_global_index = (uint32)-1; uint32 aux_heap_base_global_index = (uint32)-1; WASMFuncType *func_type; + uint8 malloc_free_io_type = VALUE_TYPE_I32; /* Find code and function sections if have */ while (section) { @@ -5715,7 +5813,7 @@ load_from_sections(WASMModule *module, WASMSection *sections, aux_heap_base_global = global; aux_heap_base = (uint64)(uint32)global->init_expr.u.i32; aux_heap_base_global_index = export->index; - LOG_VERBOSE("Found aux __heap_base global, value: %d", + LOG_VERBOSE("Found aux __heap_base global, value: %" PRIu64, aux_heap_base); } } @@ -5728,7 +5826,7 @@ load_from_sections(WASMModule *module, WASMSection *sections, aux_data_end_global = global; aux_data_end = (uint64)(uint32)global->init_expr.u.i32; aux_data_end_global_index = export->index; - LOG_VERBOSE("Found aux __data_end global, value: %d", + LOG_VERBOSE("Found aux __data_end global, value: %" PRIu64, aux_data_end); aux_data_end = align_uint64(aux_data_end, 16); @@ -5778,10 +5876,11 @@ load_from_sections(WASMModule *module, WASMSection *sections, aux_stack_top > aux_data_end ? (uint32)(aux_stack_top - aux_data_end) : (uint32)aux_stack_top; - LOG_VERBOSE("Found aux stack top global, value: %d, " - "global index: %d, stack size: %d", - aux_stack_top, global_index, - module->aux_stack_size); + LOG_VERBOSE( + "Found aux stack top global, value: %" PRIu64 ", " + "global index: %d, stack size: %d", + aux_stack_top, global_index, + module->aux_stack_size); break; } } @@ -5807,6 +5906,10 @@ load_from_sections(WASMModule *module, WASMSection *sections, module->retain_function = (uint32)-1; /* Resolve malloc/free function exported by wasm module */ +#if WASM_ENABLE_MEMORY64 != 0 + if (has_module_memory64(module)) + malloc_free_io_type = VALUE_TYPE_I64; +#endif export = module->exports; for (i = 0; i < module->export_count; i++, export ++) { if (export->kind == EXPORT_KIND_FUNC) { @@ -5815,8 +5918,8 @@ load_from_sections(WASMModule *module, WASMSection *sections, func_index = export->index - module->import_function_count; func_type = module->functions[func_index]->func_type; if (func_type->param_count == 1 && func_type->result_count == 1 - && func_type->types[0] == VALUE_TYPE_I32 - && func_type->types[1] == VALUE_TYPE_I32) { + && func_type->types[0] == malloc_free_io_type + && func_type->types[1] == malloc_free_io_type) { bh_assert(module->malloc_function == (uint32)-1); module->malloc_function = export->index; LOG_VERBOSE("Found malloc function, name: %s, index: %u", @@ -5829,9 +5932,9 @@ load_from_sections(WASMModule *module, WASMSection *sections, func_index = export->index - module->import_function_count; func_type = module->functions[func_index]->func_type; if (func_type->param_count == 2 && func_type->result_count == 1 - && func_type->types[0] == VALUE_TYPE_I32 + && func_type->types[0] == malloc_free_io_type && func_type->types[1] == VALUE_TYPE_I32 - && func_type->types[2] == VALUE_TYPE_I32) { + && func_type->types[2] == malloc_free_io_type) { uint32 j; WASMExport *export_tmp; @@ -5855,8 +5958,8 @@ load_from_sections(WASMModule *module, WASMSection *sections, module->functions[func_index]->func_type; if (func_type->param_count == 1 && func_type->result_count == 1 - && func_type->types[0] == VALUE_TYPE_I32 - && func_type->types[1] == VALUE_TYPE_I32) { + && func_type->types[0] == malloc_free_io_type + && func_type->types[1] == malloc_free_io_type) { bh_assert(module->retain_function == (uint32)-1); module->retain_function = export_tmp->index; @@ -5882,7 +5985,7 @@ load_from_sections(WASMModule *module, WASMSection *sections, func_index = export->index - module->import_function_count; func_type = module->functions[func_index]->func_type; if (func_type->param_count == 1 && func_type->result_count == 0 - && func_type->types[0] == VALUE_TYPE_I32) { + && func_type->types[0] == malloc_free_io_type) { bh_assert(module->free_function == (uint32)-1); module->free_function = export->index; LOG_VERBOSE("Found free function, name: %s, index: %u", @@ -5929,9 +6032,10 @@ load_from_sections(WASMModule *module, WASMSection *sections, * memory_import->init_page_count; if (shrunk_memory_size <= init_memory_size) { /* Reset memory info to decrease memory usage */ - memory_import->num_bytes_per_page = shrunk_memory_size; + memory_import->num_bytes_per_page = + (uint32)shrunk_memory_size; memory_import->init_page_count = 1; - LOG_VERBOSE("Shrink import memory size to %d", + LOG_VERBOSE("Shrink import memory size to %" PRIu64, shrunk_memory_size); } } @@ -5942,9 +6046,9 @@ load_from_sections(WASMModule *module, WASMSection *sections, * memory->init_page_count; if (shrunk_memory_size <= init_memory_size) { /* Reset memory info to decrease memory usage */ - memory->num_bytes_per_page = shrunk_memory_size; + memory->num_bytes_per_page = (uint32)shrunk_memory_size; memory->init_page_count = 1; - LOG_VERBOSE("Shrink memory size to %d", + LOG_VERBOSE("Shrink memory size to %" PRIu64, shrunk_memory_size); } } @@ -6029,7 +6133,7 @@ load_from_sections(WASMModule *module, WASMSection *sections, } static WASMModule * -create_module(char *error_buf, uint32 error_buf_size) +create_module(char *name, char *error_buf, uint32 error_buf_size) { WASMModule *module = loader_malloc(sizeof(WASMModule), error_buf, error_buf_size); @@ -6044,7 +6148,7 @@ create_module(char *error_buf, uint32 error_buf_size) /* Set start_function to -1, means no start function */ module->start_function = (uint32)-1; - module->name = ""; + module->name = name; #if WASM_ENABLE_FAST_INTERP == 0 module->br_table_cache_list = &module->br_table_cache_list_head; @@ -6124,7 +6228,7 @@ WASMModule * wasm_loader_load_from_sections(WASMSection *section_list, char *error_buf, uint32 error_buf_size) { - WASMModule *module = create_module(error_buf, error_buf_size); + WASMModule *module = create_module("", error_buf, error_buf_size); if (!module) return NULL; @@ -6465,9 +6569,9 @@ wasm_loader_load(uint8 *buf, uint32 size, #if WASM_ENABLE_MULTI_MODULE != 0 bool main_module, #endif - char *error_buf, uint32 error_buf_size) + const LoadArgs *args, char *error_buf, uint32 error_buf_size) { - WASMModule *module = create_module(error_buf, error_buf_size); + WASMModule *module = create_module(args->name, error_buf, error_buf_size); if (!module) { return NULL; } @@ -6654,7 +6758,7 @@ wasm_loader_unload(WASMModule *module) #if WASM_ENABLE_STRINGREF != 0 if (module->string_literal_ptrs) { - wasm_runtime_free(module->string_literal_ptrs); + wasm_runtime_free((void *)module->string_literal_ptrs); } if (module->string_literal_lengths) { wasm_runtime_free(module->string_literal_lengths); @@ -8356,12 +8460,12 @@ wasm_loader_pop_nullable_ht(WASMLoaderContext *ctx, uint8 *p_type, } /* Convert to related (ref ht) and return */ - if ((type >= REF_TYPE_EQREF && type <= REF_TYPE_FUNCREF) - || (type >= REF_TYPE_NULLREF && type <= REF_TYPE_I31REF)) { - /* Return (ref func/extern/any/eq/i31/nofunc/noextern/struct/array/none) + if (type >= REF_TYPE_ARRAYREF && type <= REF_TYPE_NULLFUNCREF) { + /* Return (ref array/struct/i31/eq/any/extern/func/none/noextern/nofunc) */ wasm_set_refheaptype_common(&ref_ht_ret->ref_ht_common, false, - HEAP_TYPE_FUNC + (type - REF_TYPE_FUNCREF)); + HEAP_TYPE_ARRAY + + (type - REF_TYPE_ARRAYREF)); type = ref_ht_ret->ref_type; } else if (wasm_is_reftype_htref_nullable(type) @@ -10067,8 +10171,8 @@ wasm_loader_check_br(WASMLoaderContext *loader_ctx, uint32 depth, uint8 opcode, loader_ctx->stack_cell_num = stack_cell_num_old; loader_ctx->frame_ref = loader_ctx->frame_ref_bottom + stack_cell_num_old; - total_size = (uint32)sizeof(uint8) - * (frame_ref_old - frame_ref_after_popped); + total_size = (uint32)(sizeof(uint8) + * (frame_ref_old - frame_ref_after_popped)); bh_memcpy_s((uint8 *)loader_ctx->frame_ref - total_size, total_size, frame_ref_buf, total_size); @@ -10079,9 +10183,9 @@ wasm_loader_check_br(WASMLoaderContext *loader_ctx, uint32 depth, uint8 opcode, loader_ctx->reftype_map_num = reftype_map_num_old; loader_ctx->frame_reftype_map = loader_ctx->frame_reftype_map_bottom + reftype_map_num_old; - total_size = - (uint32)sizeof(WASMRefTypeMap) - * (frame_reftype_map_old - frame_reftype_map_after_popped); + total_size = (uint32)(sizeof(WASMRefTypeMap) + * (frame_reftype_map_old + - frame_reftype_map_after_popped)); bh_memcpy_s((uint8 *)loader_ctx->frame_reftype_map - total_size, total_size, frame_reftype_map_buf, total_size); #endif @@ -10089,8 +10193,9 @@ wasm_loader_check_br(WASMLoaderContext *loader_ctx, uint32 depth, uint8 opcode, #if WASM_ENABLE_FAST_INTERP != 0 loader_ctx->frame_offset = loader_ctx->frame_offset_bottom + stack_cell_num_old; - total_size = (uint32)sizeof(int16) - * (frame_offset_old - frame_offset_after_popped); + total_size = + (uint32)(sizeof(int16) + * (frame_offset_old - frame_offset_after_popped)); bh_memcpy_s((uint8 *)loader_ctx->frame_offset - total_size, total_size, frame_offset_buf, total_size); (loader_ctx->frame_csp - 1)->dynamic_offset = dynamic_offset_old; @@ -10164,7 +10269,7 @@ fail: #endif #if WASM_ENABLE_FAST_INTERP != 0 if (frame_offset_buf && frame_offset_buf != frame_offset_tmp) - wasm_runtime_free(frame_offset_tmp); + wasm_runtime_free(frame_offset_buf); #endif return ret; @@ -10220,7 +10325,7 @@ check_branch_block_for_delegate(WASMLoaderContext *loader_ctx, uint8 **p_buf, } frame_csp_tmp = loader_ctx->frame_csp - depth - 2; #if WASM_ENABLE_FAST_INTERP != 0 - emit_br_info(frame_csp_tmp); + emit_br_info(frame_csp_tmp, false); #endif *p_buf = p; @@ -10655,14 +10760,7 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, func->param_cell_num, func->local_cell_num, func->ret_cell_num); #endif #if WASM_ENABLE_MEMORY64 != 0 - bool is_memory64 = false; - /* TODO: multi-memories for now assuming the memory idx type is consistent - * across multi-memories */ - if (module->import_memory_count > 0) - is_memory64 = module->import_memories[0].u.memory.flags & MEMORY64_FLAG; - else if (module->memory_count > 0) - is_memory64 = module->memories[0].flags & MEMORY64_FLAG; - + bool is_memory64 = has_module_memory64(module); mem_offset_type = is_memory64 ? VALUE_TYPE_I64 : VALUE_TYPE_I32; #else mem_offset_type = VALUE_TYPE_I32; @@ -10812,7 +10910,8 @@ re_scan: p_org = p; p--; if (!resolve_value_type((const uint8 **)&p, p_end, - module, &need_ref_type_map, + module, module->type_count, + &need_ref_type_map, &wasm_ref_type, false, error_buf, error_buf_size)) { goto fail; @@ -11239,8 +11338,10 @@ re_scan: BlockType block_type; if (loader_ctx->csp_num < 2 - || (loader_ctx->frame_csp - 1)->label_type - != LABEL_TYPE_IF) { + /* the matched if isn't found */ + || (loader_ctx->frame_csp - 1)->label_type != LABEL_TYPE_IF + /* duplicated else is found */ + || (loader_ctx->frame_csp - 1)->else_addr) { set_error_buf( error_buf, error_buf_size, "opcode else found without matched opcode if"); @@ -11288,8 +11389,8 @@ re_scan: bh_memcpy_s(loader_ctx->frame_offset, size, block->param_frame_offsets, size); loader_ctx->frame_offset += (size / sizeof(int16)); - loader_ctx->dynamic_offset = block->start_dynamic_offset; } + loader_ctx->dynamic_offset = block->start_dynamic_offset; #endif break; @@ -11435,16 +11536,17 @@ re_scan: #endif POP_I32(); - /* Get the default depth and check it */ + /* Get each depth and check it */ p_org = p; for (i = 0; i <= count; i++) { read_leb_uint32(p, p_end, depth); - } - if (loader_ctx->csp_num < depth + 1) { - set_error_buf(error_buf, error_buf_size, - "unknown label, " - "unexpected end of section or function"); - goto fail; + bh_assert(loader_ctx->csp_num > 0); + if (loader_ctx->csp_num - 1 < depth) { + set_error_buf(error_buf, error_buf_size, + "unknown label, " + "unexpected end of section or function"); + goto fail; + } } p = p_org; @@ -11460,12 +11562,6 @@ re_scan: for (i = 0; i <= count; i++) { p_org = p; read_leb_uint32(p, p_end, depth); - if (loader_ctx->csp_num < depth + 1) { - set_error_buf(error_buf, error_buf_size, - "unknown label, " - "unexpected end of section or function"); - goto fail; - } p = p_org; /* Get the target block's arity and check it */ @@ -11583,6 +11679,15 @@ re_scan: if (opcode == WASM_OP_CALL_REF || opcode == WASM_OP_RETURN_CALL_REF) { read_leb_uint32(p, p_end, type_idx1); + if (!check_type_index(module, module->type_count, type_idx1, + error_buf, error_buf_size)) { + goto fail; + } + if (module->types[type_idx1]->type_flag != WASM_TYPE_FUNC) { + set_error_buf(error_buf, error_buf_size, + "unkown function type"); + goto fail; + } if (!wasm_loader_pop_nullable_typeidx(loader_ctx, &type, &type_idx, error_buf, error_buf_size)) { @@ -11591,8 +11696,8 @@ re_scan: if (type == VALUE_TYPE_ANY) { type_idx = type_idx1; } - if (!check_type_index(module, type_idx, error_buf, - error_buf_size)) { + if (!check_type_index(module, module->type_count, type_idx, + error_buf, error_buf_size)) { goto fail; } if (module->types[type_idx]->type_flag != WASM_TYPE_FUNC) { @@ -11600,7 +11705,9 @@ re_scan: "unkown function type"); goto fail; } - if (type_idx != type_idx1) { + if (!wasm_func_type_is_super_of( + (WASMFuncType *)module->types[type_idx1], + (WASMFuncType *)module->types[type_idx])) { set_error_buf(error_buf, error_buf_size, "function type mismatch"); goto fail; @@ -11856,8 +11963,7 @@ re_scan: loader_ctx->reftype_map_num--; } #endif - if (is_32bit_type(*(loader_ctx->frame_ref - 1)) - || *(loader_ctx->frame_ref - 1) == VALUE_TYPE_ANY) { + if (is_32bit_type(*(loader_ctx->frame_ref - 1))) { loader_ctx->frame_ref--; loader_ctx->stack_cell_num--; #if WASM_ENABLE_FAST_INTERP != 0 @@ -11935,6 +12041,7 @@ re_scan: switch (*(loader_ctx->frame_ref - 1)) { case VALUE_TYPE_I32: case VALUE_TYPE_F32: + case VALUE_TYPE_ANY: break; case VALUE_TYPE_I64: case VALUE_TYPE_F64: @@ -12040,8 +12147,9 @@ re_scan: #else p_org = p + 1; if (!resolve_value_type((const uint8 **)&p, p_end, module, - &need_ref_type_map, &wasm_ref_type, - false, error_buf, error_buf_size)) { + module->type_count, &need_ref_type_map, + &wasm_ref_type, false, error_buf, + error_buf_size)) { goto fail; } type = wasm_ref_type.ref_type; @@ -12208,8 +12316,8 @@ re_scan: #else read_leb_int32(p, p_end, heap_type); if (heap_type >= 0) { - if (!check_type_index(module, heap_type, error_buf, - error_buf_size)) { + if (!check_type_index(module, module->type_count, heap_type, + error_buf, error_buf_size)) { goto fail; } wasm_set_refheaptype_typeidx(&wasm_ref_type.ref_ht_typeidx, @@ -12300,33 +12408,58 @@ re_scan: goto fail; } - /* Refer to a forward-declared function */ - if (func_idx >= cur_func_idx + module->import_function_count) { + /* Refer to a forward-declared function: + the function must be an import, exported, or present in + a table elem segment or global initializer to be used as + the operand to ref.func */ + if (func_idx >= module->import_function_count) { WASMTableSeg *table_seg = module->table_segments; bool func_declared = false; uint32 j; - /* Check whether the function is declared in table segs, - note that it doesn't matter whether the table seg's mode - is passive, active or declarative. */ - for (i = 0; i < module->table_seg_count; i++, table_seg++) { - if (table_seg->elem_type == VALUE_TYPE_FUNCREF + for (i = 0; i < module->global_count; i++) { + if (module->globals[i].type == VALUE_TYPE_FUNCREF + && module->globals[i].init_expr.init_expr_type + == INIT_EXPR_TYPE_FUNCREF_CONST + && module->globals[i].init_expr.u.u32 == func_idx) { + func_declared = true; + break; + } + } + + if (!func_declared) { + /* Check whether the function is declared in table segs, + note that it doesn't matter whether the table seg's + mode is passive, active or declarative. */ + for (i = 0; i < module->table_seg_count; + i++, table_seg++) { + if (table_seg->elem_type == VALUE_TYPE_FUNCREF #if WASM_ENABLE_GC != 0 - || (table_seg->elem_type == REF_TYPE_HT_NON_NULLABLE - && table_seg->elem_ref_type->ref_ht_common - .heap_type - == HEAP_TYPE_FUNC) + /* elem type is (ref null? func) or + (ref null? $t) */ + || ((table_seg->elem_type + == REF_TYPE_HT_NON_NULLABLE + || table_seg->elem_type + == REF_TYPE_HT_NULLABLE) + && (table_seg->elem_ref_type->ref_ht_common + .heap_type + == HEAP_TYPE_FUNC + || table_seg->elem_ref_type + ->ref_ht_common.heap_type + > 0)) #endif - ) { - for (j = 0; j < table_seg->value_count; j++) { - if (table_seg->init_values[j].u.ref_index - == func_idx) { - func_declared = true; - break; + ) { + for (j = 0; j < table_seg->value_count; j++) { + if (table_seg->init_values[j].u.ref_index + == func_idx) { + func_declared = true; + break; + } } } } } + if (!func_declared) { /* Check whether the function is exported */ for (i = 0; i < module->export_count; i++) { @@ -12993,6 +13126,7 @@ re_scan: break; case WASM_OP_F32_CONST: + CHECK_BUF(p, p_end, sizeof(float32)); p += sizeof(float32); #if WASM_ENABLE_FAST_INTERP != 0 skip_label(); @@ -13011,6 +13145,7 @@ re_scan: break; case WASM_OP_F64_CONST: + CHECK_BUF(p, p_end, sizeof(float64)); p += sizeof(float64); #if WASM_ENABLE_FAST_INTERP != 0 skip_label(); @@ -13271,7 +13406,8 @@ re_scan: #if WASM_ENABLE_FAST_INTERP != 0 emit_uint32(loader_ctx, type_idx); #endif - if (!check_type_index(module, type_idx, error_buf, + if (!check_type_index(module, module->type_count, + type_idx, error_buf, error_buf_size)) { goto fail; } @@ -13357,7 +13493,8 @@ re_scan: #if WASM_ENABLE_FAST_INTERP != 0 emit_uint32(loader_ctx, type_idx); #endif - if (!check_type_index(module, type_idx, error_buf, + if (!check_type_index(module, module->type_count, + type_idx, error_buf, error_buf_size)) { goto fail; } @@ -13770,7 +13907,8 @@ re_scan: emit_uint32(loader_ctx, (uint32)heap_type); #endif if (heap_type >= 0) { - if (!check_type_index(module, heap_type, error_buf, + if (!check_type_index(module, module->type_count, + heap_type, error_buf, error_buf_size)) { goto fail; } @@ -14341,6 +14479,7 @@ re_scan: } case WASM_OP_MEMORY_COPY: { + CHECK_BUF(p, p_end, sizeof(int16)); /* both src and dst memory index should be 0 */ if (*(int16 *)p != 0x0000) goto fail_zero_byte_expected; @@ -15114,13 +15253,6 @@ re_scan: break; } - case SIMD_i32x4_narrow_i64x2_s: - case SIMD_i32x4_narrow_i64x2_u: - { - POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); - break; - } - case SIMD_i32x4_extend_low_i16x8_s: case SIMD_i32x4_extend_high_i16x8_s: case SIMD_i32x4_extend_low_i16x8_u: @@ -15147,7 +15279,6 @@ re_scan: case SIMD_i32x4_max_s: case SIMD_i32x4_max_u: case SIMD_i32x4_dot_i16x8_s: - case SIMD_i32x4_avgr_u: case SIMD_i32x4_extmul_low_i16x8_s: case SIMD_i32x4_extmul_high_i16x8_s: case SIMD_i32x4_extmul_low_i16x8_u: @@ -15211,7 +15342,6 @@ re_scan: /* f32x4 operation */ case SIMD_f32x4_abs: case SIMD_f32x4_neg: - case SIMD_f32x4_round: case SIMD_f32x4_sqrt: { POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); @@ -15234,7 +15364,6 @@ re_scan: /* f64x2 operation */ case SIMD_f64x2_abs: case SIMD_f64x2_neg: - case SIMD_f64x2_round: case SIMD_f64x2_sqrt: { POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); diff --git a/core/iwasm/interpreter/wasm_loader.h b/core/iwasm/interpreter/wasm_loader.h index 8b0dc77d6..676770ee2 100644 --- a/core/iwasm/interpreter/wasm_loader.h +++ b/core/iwasm/interpreter/wasm_loader.h @@ -28,7 +28,7 @@ wasm_loader_load(uint8 *buf, uint32 size, #if WASM_ENABLE_MULTI_MODULE != 0 bool main_module, #endif - char *error_buf, uint32 error_buf_size); + const LoadArgs *args, char *error_buf, uint32 error_buf_size); /** * Load a WASM module from a specified WASM section list. diff --git a/core/iwasm/interpreter/wasm_mini_loader.c b/core/iwasm/interpreter/wasm_mini_loader.c index f1023fa01..fa4cd0881 100644 --- a/core/iwasm/interpreter/wasm_mini_loader.c +++ b/core/iwasm/interpreter/wasm_mini_loader.c @@ -25,6 +25,21 @@ #define TEMPLATE_READ_VALUE(Type, p) \ (p += sizeof(Type), *(Type *)(p - sizeof(Type))) +#if WASM_ENABLE_MEMORY64 != 0 +static bool +has_module_memory64(WASMModule *module) +{ + /* TODO: multi-memories for now assuming the memory idx type is consistent + * across multi-memories */ + if (module->import_memory_count > 0) + return !!(module->import_memories[0].u.memory.flags & MEMORY64_FLAG); + else if (module->memory_count > 0) + return !!(module->memories[0].flags & MEMORY64_FLAG); + + return false; +} +#endif + static void set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) { @@ -52,7 +67,10 @@ set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) static bool is_32bit_type(uint8 type) { - if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32 + if (type == VALUE_TYPE_I32 + || type == VALUE_TYPE_F32 + /* the operand stack is in polymorphic state */ + || type == VALUE_TYPE_ANY #if WASM_ENABLE_REF_TYPES != 0 || type == VALUE_TYPE_FUNCREF || type == VALUE_TYPE_EXTERNREF #endif @@ -1761,7 +1779,7 @@ load_data_segment_section(const uint8 *buf, const uint8 *buf_end, bool is_passive = false; uint32 mem_flag; #endif - uint8 mem_offset_type; + uint8 mem_offset_type = VALUE_TYPE_I32; read_leb_uint32(p, p_end, data_seg_count); @@ -2567,12 +2585,13 @@ load_from_sections(WASMModule *module, WASMSection *sections, *buf_func = NULL, *buf_func_end = NULL; WASMGlobal *aux_data_end_global = NULL, *aux_heap_base_global = NULL; WASMGlobal *aux_stack_top_global = NULL, *global; - uint64 aux_data_end = (uint64)-1, aux_heap_base = (uint64)-1, - aux_stack_top = (uint64)-1; + uint64 aux_data_end = (uint64)-1LL, aux_heap_base = (uint64)-1LL, + aux_stack_top = (uint64)-1LL; uint32 global_index, func_index, i; uint32 aux_data_end_global_index = (uint32)-1; uint32 aux_heap_base_global_index = (uint32)-1; WASMFuncType *func_type; + uint8 malloc_free_io_type = VALUE_TYPE_I32; /* Find code and function sections if have */ while (section) { @@ -2689,7 +2708,7 @@ load_from_sections(WASMModule *module, WASMSection *sections, aux_heap_base_global = global; aux_heap_base = (uint64)(uint32)global->init_expr.u.i32; aux_heap_base_global_index = export->index; - LOG_VERBOSE("Found aux __heap_base global, value: %d", + LOG_VERBOSE("Found aux __heap_base global, value: %" PRIu64, aux_heap_base); } } @@ -2702,7 +2721,7 @@ load_from_sections(WASMModule *module, WASMSection *sections, aux_data_end_global = global; aux_data_end = (uint64)(uint32)global->init_expr.u.i32; aux_data_end_global_index = export->index; - LOG_VERBOSE("Found aux __data_end global, value: %d", + LOG_VERBOSE("Found aux __data_end global, value: %" PRIu64, aux_data_end); aux_data_end = align_uint64(aux_data_end, 16); } @@ -2751,10 +2770,11 @@ load_from_sections(WASMModule *module, WASMSection *sections, aux_stack_top > aux_data_end ? (uint32)(aux_stack_top - aux_data_end) : (uint32)aux_stack_top; - LOG_VERBOSE("Found aux stack top global, value: %d, " - "global index: %d, stack size: %d", - aux_stack_top, global_index, - module->aux_stack_size); + LOG_VERBOSE( + "Found aux stack top global, value: %" PRIu64 ", " + "global index: %d, stack size: %d", + aux_stack_top, global_index, + module->aux_stack_size); break; } } @@ -2780,6 +2800,10 @@ load_from_sections(WASMModule *module, WASMSection *sections, module->retain_function = (uint32)-1; /* Resolve malloc/free function exported by wasm module */ +#if WASM_ENABLE_MEMORY64 != 0 + if (has_module_memory64(module)) + malloc_free_io_type = VALUE_TYPE_I64; +#endif export = module->exports; for (i = 0; i < module->export_count; i++, export ++) { if (export->kind == EXPORT_KIND_FUNC) { @@ -2788,8 +2812,8 @@ load_from_sections(WASMModule *module, WASMSection *sections, func_index = export->index - module->import_function_count; func_type = module->functions[func_index]->func_type; if (func_type->param_count == 1 && func_type->result_count == 1 - && func_type->types[0] == VALUE_TYPE_I32 - && func_type->types[1] == VALUE_TYPE_I32) { + && func_type->types[0] == malloc_free_io_type + && func_type->types[1] == malloc_free_io_type) { bh_assert(module->malloc_function == (uint32)-1); module->malloc_function = export->index; LOG_VERBOSE("Found malloc function, name: %s, index: %u", @@ -2802,9 +2826,9 @@ load_from_sections(WASMModule *module, WASMSection *sections, func_index = export->index - module->import_function_count; func_type = module->functions[func_index]->func_type; if (func_type->param_count == 2 && func_type->result_count == 1 - && func_type->types[0] == VALUE_TYPE_I32 + && func_type->types[0] == malloc_free_io_type && func_type->types[1] == VALUE_TYPE_I32 - && func_type->types[2] == VALUE_TYPE_I32) { + && func_type->types[2] == malloc_free_io_type) { uint32 j; WASMExport *export_tmp; @@ -2828,8 +2852,8 @@ load_from_sections(WASMModule *module, WASMSection *sections, module->functions[func_index]->func_type; if (func_type->param_count == 1 && func_type->result_count == 1 - && func_type->types[0] == VALUE_TYPE_I32 - && func_type->types[1] == VALUE_TYPE_I32) { + && func_type->types[0] == malloc_free_io_type + && func_type->types[1] == malloc_free_io_type) { bh_assert(module->retain_function == (uint32)-1); module->retain_function = export_tmp->index; @@ -2855,7 +2879,7 @@ load_from_sections(WASMModule *module, WASMSection *sections, func_index = export->index - module->import_function_count; func_type = module->functions[func_index]->func_type; if (func_type->param_count == 1 && func_type->result_count == 0 - && func_type->types[0] == VALUE_TYPE_I32) { + && func_type->types[0] == malloc_free_io_type) { bh_assert(module->free_function == (uint32)-1); module->free_function = export->index; LOG_VERBOSE("Found free function, name: %s, index: %u", @@ -2901,7 +2925,7 @@ load_from_sections(WASMModule *module, WASMSection *sections, /* Reset memory info to decrease memory usage */ memory_import->num_bytes_per_page = shrunk_memory_size; memory_import->init_page_count = 1; - LOG_VERBOSE("Shrink import memory size to %d", + LOG_VERBOSE("Shrink import memory size to %" PRIu64, shrunk_memory_size); } } @@ -2914,7 +2938,7 @@ load_from_sections(WASMModule *module, WASMSection *sections, /* Reset memory info to decrease memory usage */ memory->num_bytes_per_page = shrunk_memory_size; memory->init_page_count = 1; - LOG_VERBOSE("Shrink memory size to %d", + LOG_VERBOSE("Shrink memory size to %" PRIu64, shrunk_memory_size); } } @@ -2993,7 +3017,7 @@ load_from_sections(WASMModule *module, WASMSection *sections, } static WASMModule * -create_module(char *error_buf, uint32 error_buf_size) +create_module(char *name, char *error_buf, uint32 error_buf_size) { WASMModule *module = loader_malloc(sizeof(WASMModule), error_buf, error_buf_size); @@ -3008,7 +3032,7 @@ create_module(char *error_buf, uint32 error_buf_size) /* Set start_function to -1, means no start function */ module->start_function = (uint32)-1; - module->name = ""; + module->name = name; #if WASM_ENABLE_FAST_INTERP == 0 module->br_table_cache_list = &module->br_table_cache_list_head; @@ -3034,7 +3058,7 @@ WASMModule * wasm_loader_load_from_sections(WASMSection *section_list, char *error_buf, uint32 error_buf_size) { - WASMModule *module = create_module(error_buf, error_buf_size); + WASMModule *module = create_module("", error_buf, error_buf_size); if (!module) return NULL; @@ -3205,10 +3229,10 @@ load(const uint8 *buf, uint32 size, WASMModule *module, char *error_buf, } WASMModule * -wasm_loader_load(uint8 *buf, uint32 size, char *error_buf, +wasm_loader_load(uint8 *buf, uint32 size, const LoadArgs *args, char *error_buf, uint32 error_buf_size) { - WASMModule *module = create_module(error_buf, error_buf_size); + WASMModule *module = create_module(args->name, error_buf, error_buf_size); if (!module) { return NULL; } @@ -5158,10 +5182,21 @@ fail: goto fail; \ } while (0) -#define PUSH_MEM_OFFSET() PUSH_OFFSET_TYPE(mem_offset_type) +#define PUSH_MEM_OFFSET() \ + do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, mem_offset_type, \ + disable_emit, operand_offset, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) #define PUSH_PAGE_COUNT() PUSH_MEM_OFFSET() -#define POP_MEM_OFFSET() POP_OFFSET_TYPE(mem_offset_type) +#define POP_MEM_OFFSET() \ + do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, mem_offset_type, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) #define POP_AND_PUSH(type_pop, type_push) \ do { \ @@ -5646,7 +5681,7 @@ fail: wasm_runtime_free(frame_ref_buf); #if WASM_ENABLE_FAST_INTERP != 0 if (frame_offset_buf && frame_offset_buf != frame_offset_tmp) - wasm_runtime_free(frame_offset_tmp); + wasm_runtime_free(frame_offset_buf); #endif return ret; @@ -5905,14 +5940,7 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, func->param_cell_num, func->local_cell_num, func->ret_cell_num); #endif #if WASM_ENABLE_MEMORY64 != 0 - bool is_memory64 = false; - /* TODO: multi-memories for now assuming the memory idx type is consistent - * across multi-memories */ - if (module->import_memory_count > 0) - is_memory64 = module->import_memories[0].u.memory.flags & MEMORY64_FLAG; - else if (module->memory_count > 0) - is_memory64 = module->memories[0].flags & MEMORY64_FLAG; - + bool is_memory64 = has_module_memory64(module); mem_offset_type = is_memory64 ? VALUE_TYPE_I64 : VALUE_TYPE_I32; #else mem_offset_type = VALUE_TYPE_I32; @@ -6189,8 +6217,11 @@ re_scan: BranchBlock *block = NULL; BlockType block_type = (loader_ctx->frame_csp - 1)->block_type; bh_assert(loader_ctx->csp_num >= 2 + /* the matched if is found */ && (loader_ctx->frame_csp - 1)->label_type - == LABEL_TYPE_IF); + == LABEL_TYPE_IF + /* duplicated else isn't found */ + && !(loader_ctx->frame_csp - 1)->else_addr); block = loader_ctx->frame_csp - 1; /* check whether if branch's stack matches its result type */ @@ -6225,8 +6256,8 @@ re_scan: bh_memcpy_s(loader_ctx->frame_offset, size, block->param_frame_offsets, size); loader_ctx->frame_offset += (size / sizeof(int16)); - loader_ctx->dynamic_offset = block->start_dynamic_offset; } + loader_ctx->dynamic_offset = block->start_dynamic_offset; #endif break; @@ -6323,13 +6354,11 @@ re_scan: case WASM_OP_BR_TABLE: { uint8 *ret_types = NULL; - uint32 ret_count = 0; + uint32 ret_count = 0, depth = 0; #if WASM_ENABLE_FAST_INTERP == 0 - uint8 *p_depth_begin, *p_depth; - uint32 depth, j; BrTableCache *br_table_cache = NULL; - - p_org = p - 1; + uint8 *p_depth_begin, *p_depth, *p_opcode = p - 1; + uint32 j; #endif read_leb_uint32(p, p_end, count); @@ -6338,6 +6367,16 @@ re_scan: #endif POP_I32(); + /* Get each depth and check it */ + p_org = p; + for (i = 0; i <= count; i++) { + read_leb_uint32(p, p_end, depth); + bh_assert(loader_ctx->csp_num > 0); + bh_assert(loader_ctx->csp_num - 1 >= depth); + (void)depth; + } + p = p_org; + #if WASM_ENABLE_FAST_INTERP == 0 p_depth_begin = p_depth = p; #endif @@ -6363,8 +6402,8 @@ re_scan: error_buf, error_buf_size))) { goto fail; } - *p_org = EXT_OP_BR_TABLE_CACHE; - br_table_cache->br_table_op_addr = p_org; + *p_opcode = EXT_OP_BR_TABLE_CACHE; + br_table_cache->br_table_op_addr = p_opcode; br_table_cache->br_count = count; /* Copy previous depths which are one byte */ for (j = 0; j < i; j++) { @@ -6595,8 +6634,7 @@ re_scan: && !cur_block->is_stack_polymorphic)); if (available_stack_cell > 0) { - if (is_32bit_type(*(loader_ctx->frame_ref - 1)) - || *(loader_ctx->frame_ref - 1) == VALUE_TYPE_ANY) { + if (is_32bit_type(*(loader_ctx->frame_ref - 1))) { loader_ctx->frame_ref--; loader_ctx->stack_cell_num--; #if WASM_ENABLE_FAST_INTERP != 0 @@ -6658,6 +6696,7 @@ re_scan: switch (*(loader_ctx->frame_ref - 1)) { case REF_I32: case REF_F32: + case REF_ANY: break; case REF_I64_2: case REF_F64_2: @@ -6902,26 +6941,43 @@ re_scan: goto fail; } - /* Refer to a forward-declared function */ - if (func_idx >= cur_func_idx + module->import_function_count) { + /* Refer to a forward-declared function: + the function must be an import, exported, or present in + a table elem segment or global initializer to be used as + the operand to ref.func */ + if (func_idx >= module->import_function_count) { WASMTableSeg *table_seg = module->table_segments; bool func_declared = false; uint32 j; - /* Check whether the function is declared in table segs, - note that it doesn't matter whether the table seg's mode - is passive, active or declarative. */ - for (i = 0; i < module->table_seg_count; i++, table_seg++) { - if (table_seg->elem_type == VALUE_TYPE_FUNCREF) { - for (j = 0; j < table_seg->value_count; j++) { - if (table_seg->init_values[j].u.ref_index - == func_idx) { - func_declared = true; - break; + for (i = 0; i < module->global_count; i++) { + if (module->globals[i].type == VALUE_TYPE_FUNCREF + && module->globals[i].init_expr.init_expr_type + == INIT_EXPR_TYPE_FUNCREF_CONST + && module->globals[i].init_expr.u.u32 == func_idx) { + func_declared = true; + break; + } + } + + if (!func_declared) { + /* Check whether the function is declared in table segs, + note that it doesn't matter whether the table seg's + mode is passive, active or declarative. */ + for (i = 0; i < module->table_seg_count; + i++, table_seg++) { + if (table_seg->elem_type == VALUE_TYPE_FUNCREF) { + for (j = 0; j < table_seg->value_count; j++) { + if (table_seg->init_values[j].u.ref_index + == func_idx) { + func_declared = true; + break; + } } } } } + if (!func_declared) { /* Check whether the function is exported */ for (i = 0; i < module->export_count; i++) { @@ -7350,6 +7406,7 @@ re_scan: break; case WASM_OP_F32_CONST: + CHECK_BUF(p, p_end, sizeof(float32)); p += sizeof(float32); #if WASM_ENABLE_FAST_INTERP != 0 skip_label(); @@ -7368,6 +7425,7 @@ re_scan: break; case WASM_OP_F64_CONST: + CHECK_BUF(p, p_end, sizeof(float64)); p += sizeof(float64); #if WASM_ENABLE_FAST_INTERP != 0 skip_label(); @@ -7675,6 +7733,7 @@ re_scan: } case WASM_OP_MEMORY_COPY: { + CHECK_BUF(p, p_end, sizeof(int16)); /* both src and dst memory index should be 0 */ bh_assert(*(int16 *)p == 0x0000); p += 2; diff --git a/core/iwasm/interpreter/wasm_opcode.h b/core/iwasm/interpreter/wasm_opcode.h index 98e5b1325..db5e5e40b 100644 --- a/core/iwasm/interpreter/wasm_opcode.h +++ b/core/iwasm/interpreter/wasm_opcode.h @@ -593,8 +593,8 @@ typedef enum WASMSimdEXTOpcode { /* placeholder = 0xa2 */ SIMD_i32x4_all_true = 0xa3, SIMD_i32x4_bitmask = 0xa4, - SIMD_i32x4_narrow_i64x2_s = 0xa5, - SIMD_i32x4_narrow_i64x2_u = 0xa6, + /* placeholder = 0xa5 */ + /* placeholder = 0xa6 */ SIMD_i32x4_extend_low_i16x8_s = 0xa7, SIMD_i32x4_extend_high_i16x8_s = 0xa8, SIMD_i32x4_extend_low_i16x8_u = 0xa9, @@ -603,19 +603,19 @@ typedef enum WASMSimdEXTOpcode { SIMD_i32x4_shr_s = 0xac, SIMD_i32x4_shr_u = 0xad, SIMD_i32x4_add = 0xae, - SIMD_i32x4_add_sat_s = 0xaf, - SIMD_i32x4_add_sat_u = 0xb0, + /* placeholder = 0xaf */ + /* placeholder = 0xb0 */ SIMD_i32x4_sub = 0xb1, - SIMD_i32x4_sub_sat_s = 0xb2, - SIMD_i32x4_sub_sat_u = 0xb3, - /* placeholder = 0xb4 */ + /* placeholder = 0xb2 */ + /* placeholder = 0xb3 */ + /* placeholder = 0xb4 */ SIMD_i32x4_mul = 0xb5, SIMD_i32x4_min_s = 0xb6, SIMD_i32x4_min_u = 0xb7, SIMD_i32x4_max_s = 0xb8, SIMD_i32x4_max_u = 0xb9, SIMD_i32x4_dot_i16x8_s = 0xba, - SIMD_i32x4_avgr_u = 0xbb, + /* placeholder = 0xbb */ SIMD_i32x4_extmul_low_i16x8_s = 0xbc, SIMD_i32x4_extmul_high_i16x8_s = 0xbd, SIMD_i32x4_extmul_low_i16x8_u = 0xbe, @@ -658,7 +658,7 @@ typedef enum WASMSimdEXTOpcode { /* f32x4 operation */ SIMD_f32x4_abs = 0xe0, SIMD_f32x4_neg = 0xe1, - SIMD_f32x4_round = 0xe2, + /* placeholder = 0xe2 */ SIMD_f32x4_sqrt = 0xe3, SIMD_f32x4_add = 0xe4, SIMD_f32x4_sub = 0xe5, @@ -672,7 +672,7 @@ typedef enum WASMSimdEXTOpcode { /* f64x2 operation */ SIMD_f64x2_abs = 0xec, SIMD_f64x2_neg = 0xed, - SIMD_f64x2_round = 0xee, + /* placeholder = 0xee */ SIMD_f64x2_sqrt = 0xef, SIMD_f64x2_add = 0xf0, SIMD_f64x2_sub = 0xf1, diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index 71e7d54ee..a17d07327 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/core/iwasm/interpreter/wasm_runtime.c @@ -60,13 +60,13 @@ wasm_load(uint8 *buf, uint32 size, #if WASM_ENABLE_MULTI_MODULE != 0 bool main_module, #endif - char *error_buf, uint32 error_buf_size) + const LoadArgs *name, char *error_buf, uint32 error_buf_size) { return wasm_loader_load(buf, size, #if WASM_ENABLE_MULTI_MODULE != 0 main_module, #endif - error_buf, error_buf_size); + name, error_buf, error_buf_size); } WASMModule * @@ -273,7 +273,7 @@ memory_instantiate(WASMModuleInstance *module_inst, WASMModuleInstance *parent, /* For memory32, the global value should be i32 */ *(uint32 *)global_addr = (uint32)aux_heap_base; } - LOG_VERBOSE("Reset __heap_base global to %lu", aux_heap_base); + LOG_VERBOSE("Reset __heap_base global to %" PRIu64, aux_heap_base); } else { /* Insert app heap before new page */ @@ -300,7 +300,8 @@ memory_instantiate(WASMModuleInstance *module_inst, WASMModuleInstance *parent, LOG_VERBOSE("Memory instantiate:"); LOG_VERBOSE(" page bytes: %u, init pages: %u, max pages: %u", num_bytes_per_page, init_page_count, max_page_count); - LOG_VERBOSE(" heap offset: %u, heap size: %d\n", heap_offset, heap_size); + LOG_VERBOSE(" heap offset: %" PRIu64 ", heap size: %u\n", heap_offset, + heap_size); max_memory_data_size = (uint64)num_bytes_per_page * max_page_count; bh_assert(max_memory_data_size @@ -1399,30 +1400,39 @@ fail: static bool execute_malloc_function(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, WASMFunctionInstance *malloc_func, - WASMFunctionInstance *retain_func, uint32 size, - uint32 *p_result) + WASMFunctionInstance *retain_func, uint64 size, + uint64 *p_result) { #ifdef OS_ENABLE_HW_BOUND_CHECK WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls(); #endif WASMExecEnv *exec_env_created = NULL; WASMModuleInstanceCommon *module_inst_old = NULL; - uint32 argv[2], argc; + uint32 argv[3], argc; bool ret; - - argv[0] = size; - argc = 1; +#if WASM_ENABLE_MEMORY64 != 0 + bool is_memory64 = module_inst->memories[0]->is_memory64; + if (is_memory64) { + argc = 2; + PUT_I64_TO_ADDR(&argv[0], size); + } + else +#endif + { + argc = 1; + argv[0] = (uint32)size; + } /* if __retain is exported, then this module is compiled by assemblyscript, the memory should be managed by as's runtime, in this case we need to call the retain function after malloc the memory */ if (retain_func) { - /* the malloc functino from assemblyscript is: + /* the malloc function from assemblyscript is: function __new(size: usize, id: u32) id = 0 means this is an ArrayBuffer object */ - argv[1] = 0; - argc = 2; + argv[argc] = 0; + argc++; } if (exec_env) { @@ -1474,24 +1484,42 @@ execute_malloc_function(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, if (exec_env_created) wasm_exec_env_destroy(exec_env_created); - if (ret) - *p_result = argv[0]; + if (ret) { +#if WASM_ENABLE_MEMORY64 != 0 + if (is_memory64) + *p_result = GET_I64_FROM_ADDR(&argv[0]); + else +#endif + { + *p_result = argv[0]; + } + } return ret; } static bool execute_free_function(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, - WASMFunctionInstance *free_func, uint32 offset) + WASMFunctionInstance *free_func, uint64 offset) { #ifdef OS_ENABLE_HW_BOUND_CHECK WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls(); #endif WASMExecEnv *exec_env_created = NULL; WASMModuleInstanceCommon *module_inst_old = NULL; - uint32 argv[2]; + uint32 argv[2], argc; bool ret; - argv[0] = offset; +#if WASM_ENABLE_MEMORY64 != 0 + if (module_inst->memories[0]->is_memory64) { + PUT_I64_TO_ADDR(&argv[0], offset); + argc = 2; + } + else +#endif + { + argv[0] = (uint32)offset; + argc = 1; + } if (exec_env) { #ifdef OS_ENABLE_HW_BOUND_CHECK @@ -1530,7 +1558,7 @@ execute_free_function(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, } } - ret = wasm_call_function(exec_env, free_func, 1, argv); + ret = wasm_call_function(exec_env, free_func, argc, argv); if (module_inst_old) /* Restore the existing exec_env's module inst */ @@ -2379,8 +2407,13 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent, /* check offset */ if (base_offset > memory_size) { - LOG_DEBUG("base_offset(%d) > memory_size(%d)", base_offset, +#if WASM_ENABLE_MEMORY64 != 0 + LOG_DEBUG("base_offset(%" PRIu64 ") > memory_size(%" PRIu64 ")", + base_offset, memory_size); +#else + LOG_DEBUG("base_offset(%u) > memory_size(%" PRIu64 ")", base_offset, memory_size); +#endif #if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 set_error_buf(error_buf, error_buf_size, "out of bounds memory access"); @@ -2394,8 +2427,14 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent, /* check offset + length(could be zero) */ length = data_seg->data_length; if ((uint64)base_offset + length > memory_size) { - LOG_DEBUG("base_offset(%d) + length(%d) > memory_size(%d)", +#if WASM_ENABLE_MEMORY64 != 0 + LOG_DEBUG("base_offset(%" PRIu64 + ") + length(%d) > memory_size(%" PRIu64 ")", base_offset, length, memory_size); +#else + LOG_DEBUG("base_offset(%u) + length(%d) > memory_size(%" PRIu64 ")", + base_offset, length, memory_size); +#endif #if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 set_error_buf(error_buf, error_buf_size, "out of bounds memory access"); @@ -3100,8 +3139,6 @@ call_wasm_with_hw_bound_check(WASMModuleInstance *module_inst, { WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls(); WASMJmpBuf jmpbuf_node = { 0 }, *jmpbuf_node_pop; - uint32 page_size = os_getpagesize(); - uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; WASMRuntimeFrame *prev_frame = wasm_exec_env_get_cur_frame(exec_env); uint8 *prev_top = exec_env->wasm_stack.top; #ifdef BH_PLATFORM_WINDOWS @@ -3114,10 +3151,7 @@ call_wasm_with_hw_bound_check(WASMModuleInstance *module_inst, /* Check native stack overflow firstly to ensure we have enough native stack to run the following codes before actually calling the aot function in invokeNative function. */ - RECORD_STACK_USAGE(exec_env, (uint8 *)&exec_env_tls); - if ((uint8 *)&exec_env_tls < exec_env->native_stack_boundary - + page_size * (guard_page_count + 1)) { - wasm_set_exception(module_inst, "native stack overflow"); + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { return; } @@ -3324,7 +3358,7 @@ wasm_module_malloc_internal(WASMModuleInstance *module_inst, { WASMMemoryInstance *memory = wasm_get_default_memory(module_inst); uint8 *addr = NULL; - uint32 offset = 0; + uint64 offset = 0; /* TODO: Memory64 size check based on memory idx type */ bh_assert(size <= UINT32_MAX); @@ -3340,7 +3374,7 @@ wasm_module_malloc_internal(WASMModuleInstance *module_inst, else if (module_inst->e->malloc_function && module_inst->e->free_function) { if (!execute_malloc_function( module_inst, exec_env, module_inst->e->malloc_function, - module_inst->e->retain_function, (uint32)size, &offset)) { + module_inst->e->retain_function, size, &offset)) { return 0; } /* If we use app's malloc function, @@ -3356,7 +3390,8 @@ wasm_module_malloc_internal(WASMModuleInstance *module_inst, wasm_set_exception(module_inst, "app heap corrupted"); } else { - LOG_WARNING("warning: allocate %u bytes memory failed", size); + LOG_WARNING("warning: allocate %" PRIu64 " bytes memory failed", + size); } return 0; } @@ -3439,7 +3474,7 @@ wasm_module_free_internal(WASMModuleInstance *module_inst, && module_inst->e->free_function && memory->memory_data <= addr && addr < memory_data_end) { execute_free_function(module_inst, exec_env, - module_inst->e->free_function, (uint32)ptr); + module_inst->e->free_function, ptr); } } } @@ -3555,7 +3590,7 @@ call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 tbl_elem_idx, } #if WASM_ENABLE_GC == 0 - func_idx = tbl_elem_val; + func_idx = (uint32)tbl_elem_val; #else func_idx = wasm_func_obj_get_func_idx_bound((WASMFuncObjectRef)tbl_elem_val); @@ -4340,11 +4375,21 @@ llvm_jit_table_grow(WASMModuleInstance *module_inst, uint32 tbl_idx, } if (tbl_inst->cur_size > UINT32_MAX - inc_size) { /* integer overflow */ +#if WASM_ENABLE_SPEC_TEST == 0 + LOG_WARNING("table grow (%" PRIu32 "-> %" PRIu32 + ") failed because of integer overflow", + tbl_inst->cur_size, inc_size); +#endif return (uint32)-1; } total_size = tbl_inst->cur_size + inc_size; if (total_size > tbl_inst->max_size) { +#if WASM_ENABLE_SPEC_TEST == 0 + LOG_WARNING("table grow (%" PRIu32 "-> %" PRIu32 + ") failed because of over max size", + tbl_inst->cur_size, inc_size); +#endif return (uint32)-1; } @@ -4381,6 +4426,22 @@ llvm_jit_obj_is_instance_of(WASMModuleInstance *module_inst, return wasm_obj_is_instance_of(gc_obj, type_index, types, type_count); } +bool +llvm_jit_func_type_is_super_of(WASMModuleInstance *module_inst, + uint32 type_idx1, uint32 type_idx2) +{ + WASMModule *module = module_inst->module; + WASMType **types = module->types; + + if (type_idx1 == type_idx2) + return true; + + bh_assert(types[type_idx1]->type_flag == WASM_TYPE_FUNC); + bh_assert(types[type_idx2]->type_flag == WASM_TYPE_FUNC); + return wasm_func_type_is_super_of((WASMFuncType *)types[type_idx1], + (WASMFuncType *)types[type_idx2]); +} + WASMRttTypeRef llvm_jit_rtt_type_new(WASMModuleInstance *module_inst, uint32 type_index) { @@ -4586,8 +4647,8 @@ wasm_set_module_name(WASMModule *module, const char *name, char *error_buf, return false; module->name = - wasm_const_str_list_insert((const uint8 *)name, strlen(name), module, - false, error_buf, error_buf_size); + wasm_const_str_list_insert((const uint8 *)name, (uint32)strlen(name), + module, false, error_buf, error_buf_size); return module->name != NULL; } @@ -4595,4 +4656,4 @@ const char * wasm_get_module_name(WASMModule *module) { return module->name; -} \ No newline at end of file +} diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h index 3b01f05cd..4249eb5c1 100644 --- a/core/iwasm/interpreter/wasm_runtime.h +++ b/core/iwasm/interpreter/wasm_runtime.h @@ -508,7 +508,7 @@ wasm_load(uint8 *buf, uint32 size, #if WASM_ENABLE_MULTI_MODULE != 0 bool main_module, #endif - char *error_buf, uint32 error_buf_size); + const LoadArgs *args, char *error_buf, uint32 error_buf_size); WASMModule * wasm_load_from_sections(WASMSection *section_list, char *error_buf, @@ -811,6 +811,11 @@ bool llvm_jit_obj_is_instance_of(WASMModuleInstance *module_inst, WASMObjectRef gc_obj, uint32 type_index); +/* Whether func type1 is one of super types of func type2 */ +bool +llvm_jit_func_type_is_super_of(WASMModuleInstance *module_inst, + uint32 type_idx1, uint32 type_idx2); + WASMRttTypeRef llvm_jit_rtt_type_new(WASMModuleInstance *module_inst, uint32 type_index); diff --git a/core/iwasm/libraries/debug-engine/handler.c b/core/iwasm/libraries/debug-engine/handler.c index 8d451b1a3..905ca2f7c 100644 --- a/core/iwasm/libraries/debug-engine/handler.c +++ b/core/iwasm/libraries/debug-engine/handler.c @@ -309,9 +309,11 @@ handle_general_query(WASMGDBServer *server, char *payload) } if (!strcmp(name, "WasmData")) { + write_packet(server, ""); } if (!strcmp(name, "WasmMem")) { + write_packet(server, ""); } if (!strcmp(name, "Symbol")) { @@ -447,7 +449,7 @@ send_thread_stop_status(WASMGDBServer *server, uint32 status, korp_tid tid) "thread-pcs:%" PRIx64 ";00:%s;reason:%s;", pc, pc_string, "trace"); } - else if (status > 0) { + else { /* status > 0 (== 0 is checked at the function beginning) */ len += snprintf(tmpbuf + len, MAX_PACKET_SIZE - len, "thread-pcs:%" PRIx64 ";00:%s;reason:%s;", pc, pc_string, "signal"); diff --git a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c index 30055e634..b3fa57d72 100644 --- a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c +++ b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c @@ -1123,7 +1123,8 @@ posix_memalign_wrapper(wasm_exec_env_t exec_env, void **memptr, int32 align, wasm_module_inst_t module_inst = get_module_inst(exec_env); void *p = NULL; - *((int32 *)memptr) = module_malloc(size, (void **)&p); + /* TODO: for memory 64, module_malloc may return uint64 offset */ + *((uint32 *)memptr) = (uint32)module_malloc(size, (void **)&p); if (!p) return -1; diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/blocking_op.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/blocking_op.h index 9c36d7df6..a32e5d662 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/blocking_op.h +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/blocking_op.h @@ -3,6 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +#ifndef _BLOCKING_OP_H_ +#define _BLOCKING_OP_H_ + #include "bh_platform.h" #include "wasm_export.h" @@ -57,3 +60,5 @@ __wasi_errno_t blocking_op_poll(wasm_exec_env_t exec_env, struct pollfd *pfds, nfds_t nfds, int timeout, int *retp); #endif + +#endif /* end of _BLOCKING_OP_H_ */ diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.c b/core/iwasm/libraries/thread-mgr/thread_manager.c index ac8957501..ac4deb92c 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.c +++ b/core/iwasm/libraries/thread-mgr/thread_manager.c @@ -558,6 +558,7 @@ wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env) aux_stack_size)) { goto fail3; } + new_exec_env->is_aux_stack_allocated = true; /* Inherit suspend_flags of parent thread */ new_exec_env->suspend_flags.flags = @@ -603,7 +604,9 @@ wasm_cluster_destroy_spawned_exec_env(WASMExecEnv *exec_env) exec_env_tls = exec_env; } - /* Free aux stack space */ + /* Free aux stack space which was allocated in + wasm_cluster_spawn_exec_env */ + bh_assert(exec_env_tls->is_aux_stack_allocated); wasm_cluster_free_aux_stack(exec_env_tls, (uint64)exec_env->aux_stack_bottom); @@ -655,7 +658,9 @@ thread_manager_start_routine(void *arg) #endif /* Free aux stack space */ - wasm_cluster_free_aux_stack(exec_env, (uint64)exec_env->aux_stack_bottom); + if (exec_env->is_aux_stack_allocated) + wasm_cluster_free_aux_stack(exec_env, + (uint64)exec_env->aux_stack_bottom); os_mutex_lock(&cluster_list_lock); @@ -723,11 +728,13 @@ wasm_cluster_create_thread(WASMExecEnv *exec_env, aux_stack_size)) { goto fail2; } + new_exec_env->is_aux_stack_allocated = true; } else { /* Disable aux stack */ new_exec_env->aux_stack_boundary = 0; new_exec_env->aux_stack_bottom = UINTPTR_MAX; + new_exec_env->is_aux_stack_allocated = false; } /* Inherit suspend_flags of parent thread */ @@ -1049,7 +1056,9 @@ wasm_cluster_exit_thread(WASMExecEnv *exec_env, void *retval) #endif /* Free aux stack space */ - wasm_cluster_free_aux_stack(exec_env, (uint64)exec_env->aux_stack_bottom); + if (exec_env->is_aux_stack_allocated) + wasm_cluster_free_aux_stack(exec_env, + (uint64)exec_env->aux_stack_bottom); /* App exit the thread, free the resources before exit native thread */ diff --git a/core/shared/mem-alloc/ems/ems_alloc.c b/core/shared/mem-alloc/ems/ems_alloc.c index b667fbe9f..4863527d6 100644 --- a/core/shared/mem-alloc/ems/ems_alloc.c +++ b/core/shared/mem-alloc/ems/ems_alloc.c @@ -785,8 +785,8 @@ gc_alloc_wo_internal(void *vheap, gc_size_t size, const char *file, int line) if (!hmu) goto finish; - /* Do we need to memset the memory to 0? */ - /* memset((char *)hmu + sizeof(*hmu), 0, tot_size - sizeof(*hmu)); */ + /* Don't memset the memory to improve performance, the caller should + decide whether to memset it or not */ bh_assert(hmu_get_size(hmu) >= tot_size); /* the total size allocated may be larger than diff --git a/core/shared/mem-alloc/ems/ems_gc.c b/core/shared/mem-alloc/ems/ems_gc.c index b0f14772b..26e83a975 100644 --- a/core/shared/mem-alloc/ems/ems_gc.c +++ b/core/shared/mem-alloc/ems/ems_gc.c @@ -114,8 +114,8 @@ sweep_instance_heap(gc_heap_t *heap) else { /* current block is still live */ if (last) { - tot_free += (char *)cur - (char *)last; - gci_add_fc(heap, last, (char *)cur - (char *)last); + tot_free += (gc_size_t)((char *)cur - (char *)last); + gci_add_fc(heap, last, (gc_size_t)((char *)cur - (char *)last)); hmu_mark_pinuse(last); last = NULL; } @@ -132,8 +132,8 @@ sweep_instance_heap(gc_heap_t *heap) bh_assert(cur == end); if (last) { - tot_free += (char *)cur - (char *)last; - gci_add_fc(heap, last, (char *)cur - (char *)last); + tot_free += (gc_size_t)((char *)cur - (char *)last); + gci_add_fc(heap, last, (gc_size_t)((char *)cur - (char *)last)); hmu_mark_pinuse(last); } @@ -449,7 +449,9 @@ gci_gc_heap(void *h) LOG_VERBOSE("#reclaim instance heap %p", heap); - gct_vm_gc_prepare(); + /* TODO: get exec_env of current thread when GC multi-threading + is enabled, and pass it to runtime */ + gct_vm_gc_prepare(NULL); gct_vm_mutex_lock(&heap->lock); heap->is_doing_reclaim = 1; @@ -459,7 +461,9 @@ gci_gc_heap(void *h) heap->is_doing_reclaim = 0; gct_vm_mutex_unlock(&heap->lock); - gct_vm_gc_finished(); + /* TODO: get exec_env of current thread when GC multi-threading + is enabled, and pass it to runtime */ + gct_vm_gc_finished(NULL); LOG_VERBOSE("#reclaim instance heap %p done", heap); diff --git a/core/shared/mem-alloc/mem_alloc.c b/core/shared/mem-alloc/mem_alloc.c index 1f9e03d5a..df1a4de4c 100644 --- a/core/shared/mem-alloc/mem_alloc.c +++ b/core/shared/mem-alloc/mem_alloc.c @@ -77,13 +77,13 @@ mem_allocator_free_with_gc(mem_allocator_t allocator, void *ptr) void mem_allocator_enable_gc_reclaim(mem_allocator_t allocator, void *exec_env) { - return gc_enable_gc_reclaim((gc_handle_t)allocator, exec_env); + gc_enable_gc_reclaim((gc_handle_t)allocator, exec_env); } #else void mem_allocator_enable_gc_reclaim(mem_allocator_t allocator, void *cluster) { - return gc_enable_gc_reclaim((gc_handle_t)allocator, cluster); + gc_enable_gc_reclaim((gc_handle_t)allocator, cluster); } #endif diff --git a/core/shared/platform/common/posix/posix_file.c b/core/shared/platform/common/posix/posix_file.c index 20f94fba3..ac7e58537 100644 --- a/core/shared/platform/common/posix/posix_file.c +++ b/core/shared/platform/common/posix/posix_file.c @@ -823,7 +823,7 @@ os_fadvise(os_file_handle handle, __wasi_filesize_t offset, int ret = posix_fadvise(handle, (off_t)offset, (off_t)length, nadvice); - if (ret < 0) + if (ret != 0) return convert_errno(ret); return __WASI_ESUCCESS; @@ -920,7 +920,12 @@ os_readdir(os_dir_stream dir_stream, __wasi_dirent_t *entry, if (dent == NULL) { *d_name = NULL; - return convert_errno(errno); + if (errno != 0) { + return convert_errno(errno); + } + else { + return 0; + } } long offset = (__wasi_dircookie_t)telldir(dir_stream); diff --git a/core/shared/platform/common/posix/posix_thread.c b/core/shared/platform/common/posix/posix_thread.c index 1195d80eb..189092e9b 100644 --- a/core/shared/platform/common/posix/posix_thread.c +++ b/core/shared/platform/common/posix/posix_thread.c @@ -445,9 +445,6 @@ os_thread_get_stack_boundary() pthread_attr_destroy(&attr); if (stack_size > max_stack_size) addr = addr + stack_size - max_stack_size; - if (guard_size < (size_t)page_size) - /* Reserved 1 guard page at least for safety */ - guard_size = (size_t)page_size; addr += guard_size; } (void)stack_size; @@ -466,8 +463,6 @@ os_thread_get_stack_boundary() stack_size = max_stack_size; addr -= stack_size; - /* Reserved 1 guard page at least for safety */ - addr += page_size; } #endif diff --git a/core/shared/platform/esp-idf/espidf_clock.c b/core/shared/platform/esp-idf/espidf_clock.c new file mode 100644 index 000000000..41413211c --- /dev/null +++ b/core/shared/platform/esp-idf/espidf_clock.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2023 Amazon Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "libc_errno.h" +#include "platform_api_extension.h" + +#define NANOSECONDS_PER_SECOND 1000000000ULL + +static __wasi_errno_t +wasi_clockid_to_clockid(__wasi_clockid_t in, clockid_t *out) +{ + switch (in) { + case __WASI_CLOCK_MONOTONIC: + *out = CLOCK_MONOTONIC; + return __WASI_ESUCCESS; + case __WASI_CLOCK_REALTIME: + *out = CLOCK_REALTIME; + return __WASI_ESUCCESS; + case __WASI_CLOCK_PROCESS_CPUTIME_ID: +#if defined(CLOCK_PROCESS_CPUTIME_ID) + *out = CLOCK_PROCESS_CPUTIME_ID; + return __WASI_ESUCCESS; +#else + return __WASI_ENOTSUP; +#endif + case __WASI_CLOCK_THREAD_CPUTIME_ID: +#if defined(CLOCK_THREAD_CPUTIME_ID) + *out = CLOCK_THREAD_CPUTIME_ID; + return __WASI_ESUCCESS; +#else + return __WASI_ENOTSUP; +#endif + default: + return __WASI_EINVAL; + } +} + +static __wasi_timestamp_t +timespec_to_nanoseconds(const struct timespec *ts) +{ + if (ts->tv_sec < 0) + return 0; + if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / NANOSECONDS_PER_SECOND) + return UINT64_MAX; + return (__wasi_timestamp_t)ts->tv_sec * NANOSECONDS_PER_SECOND + + (__wasi_timestamp_t)ts->tv_nsec; +} + +__wasi_errno_t +os_clock_res_get(__wasi_clockid_t clock_id, __wasi_timestamp_t *resolution) +{ + clockid_t nclock_id; + __wasi_errno_t error = wasi_clockid_to_clockid(clock_id, &nclock_id); + + if (error != __WASI_ESUCCESS) + return error; + + struct timespec ts; + if (clock_getres(nclock_id, &ts) < 0) + return convert_errno(errno); + + *resolution = timespec_to_nanoseconds(&ts); + + return error; +} + +__wasi_errno_t +os_clock_time_get(__wasi_clockid_t clock_id, __wasi_timestamp_t precision, + __wasi_timestamp_t *time) +{ + clockid_t nclock_id; + __wasi_errno_t error = wasi_clockid_to_clockid(clock_id, &nclock_id); + + (void)precision; + + if (error != __WASI_ESUCCESS) + return error; + + struct timespec ts; + if (clock_gettime(nclock_id, &ts) < 0) + return convert_errno(errno); + + *time = timespec_to_nanoseconds(&ts); + + return error; +} diff --git a/core/shared/platform/esp-idf/espidf_file.c b/core/shared/platform/esp-idf/espidf_file.c new file mode 100644 index 000000000..ac7e58537 --- /dev/null +++ b/core/shared/platform/esp-idf/espidf_file.c @@ -0,0 +1,1014 @@ +/* + * Copyright (C) 2023 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_extension.h" +#include "libc_errno.h" +#include + +#if !defined(__APPLE__) && !defined(ESP_PLATFORM) +#define CONFIG_HAS_PWRITEV 1 +#define CONFIG_HAS_PREADV 1 +#else +#define CONFIG_HAS_PWRITEV 0 +#define CONFIG_HAS_PREADV 0 +#endif + +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(ESP_PLATFORM) +#define CONFIG_HAS_FDATASYNC 1 +#else +#define CONFIG_HAS_FDATASYNC 0 +#endif + +/* + * For NuttX, CONFIG_HAS_ISATTY is provided by its platform header. + * (platform_internal.h) + */ +#if !defined(CONFIG_HAS_D_INO) +#if !defined(__NuttX__) +#define CONFIG_HAS_D_INO 1 +#define CONFIG_HAS_ISATTY 1 +#else +#define CONFIG_HAS_D_INO 0 +#endif +#endif + +#if !defined(__APPLE__) && !defined(ESP_PLATFORM) && !defined(__COSMOPOLITAN__) +#define CONFIG_HAS_POSIX_FALLOCATE 1 +#else +#define CONFIG_HAS_POSIX_FALLOCATE 0 +#endif + +#if defined(O_DSYNC) +#define CONFIG_HAS_O_DSYNC +#endif + +// POSIX requires O_RSYNC to be defined, but Linux explicitly doesn't support +// it. +#if defined(O_RSYNC) && !defined(__linux__) +#define CONFIG_HAS_O_RSYNC +#endif + +#if defined(O_SYNC) +#define CONFIG_HAS_O_SYNC +#endif + +// Converts a POSIX timespec to a WASI timestamp. +static __wasi_timestamp_t +convert_timespec(const struct timespec *ts) +{ + if (ts->tv_sec < 0) + return 0; + if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / 1000000000) + return UINT64_MAX; + return (__wasi_timestamp_t)ts->tv_sec * 1000000000 + + (__wasi_timestamp_t)ts->tv_nsec; +} + +// Converts a POSIX stat structure to a WASI filestat structure +static void +convert_stat(os_file_handle handle, const struct stat *in, + __wasi_filestat_t *out) +{ + out->st_dev = in->st_dev; + out->st_ino = in->st_ino; + out->st_nlink = (__wasi_linkcount_t)in->st_nlink; + out->st_size = (__wasi_filesize_t)in->st_size; +#ifdef __APPLE__ + out->st_atim = convert_timespec(&in->st_atimespec); + out->st_mtim = convert_timespec(&in->st_mtimespec); + out->st_ctim = convert_timespec(&in->st_ctimespec); +#else + out->st_atim = convert_timespec(&in->st_atim); + out->st_mtim = convert_timespec(&in->st_mtim); + out->st_ctim = convert_timespec(&in->st_ctim); +#endif + + // Convert the file type. In the case of sockets there is no way we + // can easily determine the exact socket type. + if (S_ISBLK(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_BLOCK_DEVICE; + } + else if (S_ISCHR(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_CHARACTER_DEVICE; + } + else if (S_ISDIR(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_DIRECTORY; + } + else if (S_ISFIFO(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; + } + else if (S_ISLNK(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_SYMBOLIC_LINK; + } + else if (S_ISREG(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_REGULAR_FILE; + } + else if (S_ISSOCK(in->st_mode)) { + int socktype; + socklen_t socktypelen = sizeof(socktype); + + if (getsockopt(handle, SOL_SOCKET, SO_TYPE, &socktype, &socktypelen) + < 0) { + out->st_filetype = __WASI_FILETYPE_UNKNOWN; + return; + } + + switch (socktype) { + case SOCK_DGRAM: + out->st_filetype = __WASI_FILETYPE_SOCKET_DGRAM; + break; + case SOCK_STREAM: + out->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; + break; + default: + out->st_filetype = __WASI_FILETYPE_UNKNOWN; + return; + } + } + else { + out->st_filetype = __WASI_FILETYPE_UNKNOWN; + } +} + +static void +convert_timestamp(__wasi_timestamp_t in, struct timespec *out) +{ + // Store sub-second remainder. +#if defined(__SYSCALL_SLONG_TYPE) + out->tv_nsec = (__SYSCALL_SLONG_TYPE)(in % 1000000000); +#else + out->tv_nsec = (long)(in % 1000000000); +#endif + in /= 1000000000; + + // Clamp to the maximum in case it would overflow our system's time_t. + out->tv_sec = (time_t)in < BH_TIME_T_MAX ? (time_t)in : BH_TIME_T_MAX; +} + +// Converts the provided timestamps and flags to a set of arguments for +// futimens() and utimensat(). +static void +convert_utimens_arguments(__wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags, struct timespec *ts) +{ + if ((fstflags & __WASI_FILESTAT_SET_ATIM_NOW) != 0) { + ts[0].tv_nsec = UTIME_NOW; + } + else if ((fstflags & __WASI_FILESTAT_SET_ATIM) != 0) { + convert_timestamp(st_atim, &ts[0]); + } + else { + ts[0].tv_nsec = UTIME_OMIT; + } + + if ((fstflags & __WASI_FILESTAT_SET_MTIM_NOW) != 0) { + ts[1].tv_nsec = UTIME_NOW; + } + else if ((fstflags & __WASI_FILESTAT_SET_MTIM) != 0) { + convert_timestamp(st_mtim, &ts[1]); + } + else { + ts[1].tv_nsec = UTIME_OMIT; + } +} + +__wasi_errno_t +os_fstat(os_file_handle handle, struct __wasi_filestat_t *buf) +{ + struct stat stat_buf; + int ret = fstat(handle, &stat_buf); + + if (ret < 0) + return convert_errno(errno); + + convert_stat(handle, &stat_buf, buf); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fstatat(os_file_handle handle, const char *path, + struct __wasi_filestat_t *buf, __wasi_lookupflags_t lookup_flags) +{ + struct stat stat_buf; + int ret = fstatat(handle, path, &stat_buf, + (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) + ? AT_SYMLINK_FOLLOW + : AT_SYMLINK_NOFOLLOW); + + if (ret < 0) + return convert_errno(errno); + + convert_stat(handle, &stat_buf, buf); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_file_get_fdflags(os_file_handle handle, __wasi_fdflags_t *flags) +{ + int ret = fcntl(handle, F_GETFL); + + if (ret < 0) + return convert_errno(errno); + + *flags = 0; + + if ((ret & O_APPEND) != 0) + *flags |= __WASI_FDFLAG_APPEND; +#ifdef CONFIG_HAS_O_DSYNC + if ((ret & O_DSYNC) != 0) + *flags |= __WASI_FDFLAG_DSYNC; +#endif + if ((ret & O_NONBLOCK) != 0) + *flags |= __WASI_FDFLAG_NONBLOCK; +#ifdef CONFIG_HAS_O_RSYNC + if ((ret & O_RSYNC) != 0) + *flags |= __WASI_FDFLAG_RSYNC; +#endif +#ifdef CONFIG_HAS_O_SYNC + if ((ret & O_SYNC) != 0) + *flags |= __WASI_FDFLAG_SYNC; +#endif + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_file_set_fdflags(os_file_handle handle, __wasi_fdflags_t flags) +{ + int fcntl_flags = 0; + + if ((flags & __WASI_FDFLAG_APPEND) != 0) + fcntl_flags |= O_APPEND; + if ((flags & __WASI_FDFLAG_DSYNC) != 0) +#ifdef CONFIG_HAS_O_DSYNC + fcntl_flags |= O_DSYNC; +#else + return __WASI_ENOTSUP; +#endif + if ((flags & __WASI_FDFLAG_NONBLOCK) != 0) + fcntl_flags |= O_NONBLOCK; + if ((flags & __WASI_FDFLAG_RSYNC) != 0) +#ifdef CONFIG_HAS_O_RSYNC + fcntl_flags |= O_RSYNC; +#else + return __WASI_ENOTSUP; +#endif + if ((flags & __WASI_FDFLAG_SYNC) != 0) +#ifdef CONFIG_HAS_O_SYNC + fcntl_flags |= O_SYNC; +#else + return __WASI_ENOTSUP; +#endif + + int ret = fcntl(handle, F_SETFL, fcntl_flags); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fdatasync(os_file_handle handle) +{ +#if CONFIG_HAS_FDATASYNC + int ret = fdatasync(handle); +#else + int ret = fsync(handle); +#endif + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fsync(os_file_handle handle) +{ + int ret = fsync(handle); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_open_preopendir(const char *path, os_file_handle *out) +{ + + int fd = open(path, O_RDONLY | O_DIRECTORY, 0); + + if (fd < 0) + return convert_errno(errno); + + *out = fd; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_openat(os_file_handle handle, const char *path, __wasi_oflags_t oflags, + __wasi_fdflags_t fs_flags, __wasi_lookupflags_t lookup_flags, + wasi_libc_file_access_mode read_write_mode, os_file_handle *out) +{ + int open_flags = 0; + + // Convert open flags. + if ((oflags & __WASI_O_CREAT) != 0) { + open_flags |= O_CREAT; + } + if ((oflags & __WASI_O_DIRECTORY) != 0) + open_flags |= O_DIRECTORY; + if ((oflags & __WASI_O_EXCL) != 0) + open_flags |= O_EXCL; + if ((oflags & __WASI_O_TRUNC) != 0) { + open_flags |= O_TRUNC; + } + + // Convert file descriptor flags. + if ((fs_flags & __WASI_FDFLAG_APPEND) != 0) + open_flags |= O_APPEND; + if ((fs_flags & __WASI_FDFLAG_DSYNC) != 0) { +#ifdef CONFIG_HAS_O_DSYNC + open_flags |= O_DSYNC; +#else + return __WASI_ENOTSUP; +#endif + } + if ((fs_flags & __WASI_FDFLAG_NONBLOCK) != 0) + open_flags |= O_NONBLOCK; + if ((fs_flags & __WASI_FDFLAG_RSYNC) != 0) { +#ifdef CONFIG_HAS_O_RSYNC + open_flags |= O_RSYNC; +#else + return __WASI_ENOTSUP; +#endif + } + if ((fs_flags & __WASI_FDFLAG_SYNC) != 0) { +#ifdef CONFIG_HAS_O_SYNC + open_flags |= O_SYNC; +#else + return __WASI_ENOTSUP; +#endif + } + + if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0) { + open_flags |= O_NOFOLLOW; + } + + switch (read_write_mode) { + case WASI_LIBC_ACCESS_MODE_READ_WRITE: + open_flags |= O_RDWR; + break; + case WASI_LIBC_ACCESS_MODE_READ_ONLY: + open_flags |= O_RDONLY; + break; + case WASI_LIBC_ACCESS_MODE_WRITE_ONLY: + open_flags |= O_WRONLY; + break; + default: + return __WASI_EINVAL; + } + + int fd = openat(handle, path, open_flags, 0666); + + if (fd < 0) { + int openat_errno = errno; + // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket. + if (openat_errno == ENXIO) { + struct stat sb; + int ret = fstatat(handle, path, &sb, + (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) + ? 0 + : AT_SYMLINK_NOFOLLOW); + return ret == 0 && S_ISSOCK(sb.st_mode) ? __WASI_ENOTSUP + : __WASI_ENXIO; + } + // Linux returns ENOTDIR instead of ELOOP when using + // O_NOFOLLOW|O_DIRECTORY on a symlink. + if (openat_errno == ENOTDIR + && (open_flags & (O_NOFOLLOW | O_DIRECTORY)) != 0) { + struct stat sb; + int ret = fstatat(handle, path, &sb, AT_SYMLINK_NOFOLLOW); + if (S_ISLNK(sb.st_mode)) { + return __WASI_ELOOP; + } + (void)ret; + } + // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on + // a symlink. + if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0 + && openat_errno == EMLINK) + return __WASI_ELOOP; + + return convert_errno(openat_errno); + } + + *out = fd; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_file_get_access_mode(os_file_handle handle, + wasi_libc_file_access_mode *access_mode) +{ + int ret = fcntl(handle, F_GETFL, 0); + + if (ret < 0) + return convert_errno(errno); + + switch (ret & O_ACCMODE) { + case O_RDONLY: + *access_mode = WASI_LIBC_ACCESS_MODE_READ_ONLY; + break; + case O_WRONLY: + *access_mode = WASI_LIBC_ACCESS_MODE_WRITE_ONLY; + break; + case O_RDWR: + *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE; + break; + default: + return __WASI_EINVAL; + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_close(os_file_handle handle, bool is_stdio) +{ + if (is_stdio) + return __WASI_ESUCCESS; + + int ret = close(handle); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_preadv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nread) +{ +#if CONFIG_HAS_PREADV + ssize_t len = + preadv(handle, (const struct iovec *)iov, (int)iovcnt, (off_t)offset); + if (len < 0) + return convert_errno(errno); + + *nread = (size_t)len; + return __WASI_ESUCCESS; +#else + if (iovcnt == 1) { + ssize_t len = pread(handle, iov->buf, iov->buf_len, offset); + + if (len < 0) + return convert_errno(errno); + + *nread = len; + return __WASI_ESUCCESS; + } + + // Allocate a single buffer to fit all data. + size_t totalsize = 0; + for (int i = 0; i < iovcnt; ++i) + totalsize += iov[i].buf_len; + + char *buf = BH_MALLOC(totalsize); + + if (buf == NULL) { + return __WASI_ENOMEM; + } + + // Perform a single read operation. + ssize_t len = pread(handle, buf, totalsize, offset); + + if (len < 0) { + BH_FREE(buf); + return convert_errno(errno); + } + + // Copy data back to vectors. + size_t bufoff = 0; + for (int i = 0; i < iovcnt; ++i) { + if (bufoff + iov[i].buf_len < (size_t)len) { + memcpy(iov[i].buf, buf + bufoff, iov[i].buf_len); + bufoff += iov[i].buf_len; + } + else { + memcpy(iov[i].buf, buf + bufoff, len - bufoff); + break; + } + } + BH_FREE(buf); + *nread = len; + + return __WASI_ESUCCESS; +#endif +} + +__wasi_errno_t +os_pwritev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nwritten) +{ + if (iovcnt == 0) + return __WASI_EINVAL; + + ssize_t len = 0; +#if CONFIG_HAS_PWRITEV + len = + pwritev(handle, (const struct iovec *)iov, (int)iovcnt, (off_t)offset); +#else + if (iovcnt == 1) { + len = pwrite(handle, iov->buf, iov->buf_len, offset); + } + else { + // Allocate a single buffer to fit all data. + size_t totalsize = 0; + for (int i = 0; i < iovcnt; ++i) + totalsize += iov[i].buf_len; + char *buf = BH_MALLOC(totalsize); + if (buf == NULL) { + return __WASI_ENOMEM; + } + size_t bufoff = 0; + for (int i = 0; i < iovcnt; ++i) { + memcpy(buf + bufoff, iov[i].buf, iov[i].buf_len); + bufoff += iov[i].buf_len; + } + + // Perform a single write operation. + len = pwrite(handle, buf, totalsize, offset); + BH_FREE(buf); + } +#endif + if (len < 0) + return convert_errno(errno); + + *nwritten = (size_t)len; + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_readv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt, + size_t *nread) +{ + ssize_t len = readv(handle, (const struct iovec *)iov, (int)iovcnt); + + if (len < 0) + return convert_errno(errno); + + *nread = (size_t)len; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_writev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt, + size_t *nwritten) +{ + ssize_t len = writev(handle, (const struct iovec *)iov, (int)iovcnt); + + if (len < 0) + return convert_errno(errno); + + *nwritten = (size_t)len; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fallocate(os_file_handle handle, __wasi_filesize_t offset, + __wasi_filesize_t length) +{ +#if CONFIG_HAS_POSIX_FALLOCATE + int ret = posix_fallocate(handle, (off_t)offset, (off_t)length); +#else + // At least ensure that the file is grown to the right size. + // TODO(ed): See if this can somehow be implemented without any race + // conditions. We may end up shrinking the file right now. + struct stat sb; + int ret = fstat(handle, &sb); + off_t newsize = (off_t)(offset + length); + + if (ret == 0 && sb.st_size < newsize) + ret = ftruncate(handle, newsize); +#endif + + if (ret != 0) + return convert_errno(ret); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_ftruncate(os_file_handle handle, __wasi_filesize_t size) +{ + int ret = ftruncate(handle, (off_t)size); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_futimens(os_file_handle handle, __wasi_timestamp_t access_time, + __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags) +{ + struct timespec ts[2]; + convert_utimens_arguments(access_time, modification_time, fstflags, ts); + + int ret = futimens(handle, ts); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_utimensat(os_file_handle handle, const char *path, + __wasi_timestamp_t access_time, + __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags, + __wasi_lookupflags_t lookup_flags) +{ + struct timespec ts[2]; + convert_utimens_arguments(access_time, modification_time, fstflags, ts); + + int ret = utimensat(handle, path, ts, + (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) + ? 0 + : AT_SYMLINK_NOFOLLOW); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_readlinkat(os_file_handle handle, const char *path, char *buf, + size_t bufsize, size_t *nread) +{ + // Linux requires that the buffer size is positive. whereas POSIX does + // not. Use a fake buffer to store the results if the size is zero. + char fakebuf[1]; + ssize_t len = readlinkat(handle, path, bufsize == 0 ? fakebuf : buf, + bufsize == 0 ? sizeof(fakebuf) : bufsize); + + if (len < 0) + return convert_errno(errno); + + *nread = (size_t)len < bufsize ? (size_t)len : bufsize; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_linkat(os_file_handle from_handle, const char *from_path, + os_file_handle to_handle, const char *to_path, + __wasi_lookupflags_t lookup_flags) +{ + int ret = linkat( + from_handle, from_path, to_handle, to_path, + (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) ? AT_SYMLINK_FOLLOW : 0); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_symlinkat(const char *old_path, os_file_handle handle, const char *new_path) +{ + int ret = symlinkat(old_path, handle, new_path); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_mkdirat(os_file_handle handle, const char *path) +{ + int ret = mkdirat(handle, path, 0777); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_renameat(os_file_handle old_handle, const char *old_path, + os_file_handle new_handle, const char *new_path) +{ + + int ret = renameat(old_handle, old_path, new_handle, new_path); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_unlinkat(os_file_handle handle, const char *path, bool is_dir) +{ + int ret = unlinkat(handle, path, is_dir ? AT_REMOVEDIR : 0); + +#ifndef __linux__ + if (ret < 0) { + // Non-Linux implementations may return EPERM when attempting to remove + // a directory without REMOVEDIR. While that's what POSIX specifies, + // it's less useful. Adjust this to EISDIR. It doesn't matter that this + // is not atomic with the unlinkat, because if the file is removed and a + // directory is created before fstatat sees it, we're racing with that + // change anyway and unlinkat could have legitimately seen the directory + // if the race had turned out differently. + if (errno == EPERM) { + struct stat statbuf; + if (fstatat(handle, path, &statbuf, AT_SYMLINK_NOFOLLOW) == 0 + && S_ISDIR(statbuf.st_mode)) { + errno = EISDIR; + } + } + // POSIX permits either EEXIST or ENOTEMPTY when the directory is not + // empty. Map it to ENOTEMPTY. + else if (errno == EEXIST) { + errno = ENOTEMPTY; + } + + return convert_errno(errno); + } +#endif + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_lseek(os_file_handle handle, __wasi_filedelta_t offset, + __wasi_whence_t whence, __wasi_filesize_t *new_offset) +{ + int nwhence; + + switch (whence) { + case __WASI_WHENCE_CUR: + nwhence = SEEK_CUR; + break; + case __WASI_WHENCE_END: + nwhence = SEEK_END; + break; + case __WASI_WHENCE_SET: + nwhence = SEEK_SET; + break; + default: + return __WASI_EINVAL; + } + + off_t ret = lseek(handle, offset, nwhence); + + if (ret < 0) + return convert_errno(errno); + + *new_offset = (__wasi_filesize_t)ret; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fadvise(os_file_handle handle, __wasi_filesize_t offset, + __wasi_filesize_t length, __wasi_advice_t advice) +{ +#ifdef POSIX_FADV_NORMAL + int nadvice; + switch (advice) { + case __WASI_ADVICE_DONTNEED: + nadvice = POSIX_FADV_DONTNEED; + break; + case __WASI_ADVICE_NOREUSE: + nadvice = POSIX_FADV_NOREUSE; + break; + case __WASI_ADVICE_NORMAL: + nadvice = POSIX_FADV_NORMAL; + break; + case __WASI_ADVICE_RANDOM: + nadvice = POSIX_FADV_RANDOM; + break; + case __WASI_ADVICE_SEQUENTIAL: + nadvice = POSIX_FADV_SEQUENTIAL; + break; + case __WASI_ADVICE_WILLNEED: + nadvice = POSIX_FADV_WILLNEED; + break; + default: + return __WASI_EINVAL; + } + + int ret = posix_fadvise(handle, (off_t)offset, (off_t)length, nadvice); + + if (ret != 0) + return convert_errno(ret); + + return __WASI_ESUCCESS; +#else + // Advisory information can be safely ignored if not supported + switch (advice) { + case __WASI_ADVICE_DONTNEED: + case __WASI_ADVICE_NOREUSE: + case __WASI_ADVICE_NORMAL: + case __WASI_ADVICE_RANDOM: + case __WASI_ADVICE_SEQUENTIAL: + case __WASI_ADVICE_WILLNEED: + return __WASI_ESUCCESS; + default: + return __WASI_EINVAL; + } +#endif +} + +__wasi_errno_t +os_isatty(os_file_handle handle) +{ +#if CONFIG_HAS_ISATTY + int ret = isatty(handle); + + if (ret == 1) + return __WASI_ESUCCESS; + + return __WASI_ENOTTY; +#else + return __WASI_ENOTSUP; +#endif +} + +os_file_handle +os_convert_stdin_handle(os_raw_file_handle raw_stdin) +{ +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + return raw_stdin >= 0 ? raw_stdin : STDIN_FILENO; +} + +os_file_handle +os_convert_stdout_handle(os_raw_file_handle raw_stdout) +{ +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif + return raw_stdout >= 0 ? raw_stdout : STDOUT_FILENO; +} + +os_file_handle +os_convert_stderr_handle(os_raw_file_handle raw_stderr) +{ +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + return raw_stderr >= 0 ? raw_stderr : STDERR_FILENO; +} + +__wasi_errno_t +os_fdopendir(os_file_handle handle, os_dir_stream *dir_stream) +{ + *dir_stream = fdopendir(handle); + + if (*dir_stream == NULL) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_rewinddir(os_dir_stream dir_stream) +{ + rewinddir(dir_stream); + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_seekdir(os_dir_stream dir_stream, __wasi_dircookie_t position) +{ + seekdir(dir_stream, (long)position); + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_readdir(os_dir_stream dir_stream, __wasi_dirent_t *entry, + const char **d_name) +{ + errno = 0; + + struct dirent *dent = readdir(dir_stream); + + if (dent == NULL) { + *d_name = NULL; + if (errno != 0) { + return convert_errno(errno); + } + else { + return 0; + } + } + + long offset = (__wasi_dircookie_t)telldir(dir_stream); + + size_t namlen = strlen(dent->d_name); + + *d_name = dent->d_name; + entry->d_next = offset; + entry->d_namlen = (__wasi_dirnamlen_t)namlen; +#if CONFIG_HAS_D_INO + entry->d_ino = dent->d_ino; +#else + entry->d_ino = 0; +#endif + + switch (dent->d_type) { + case DT_BLK: + entry->d_type = __WASI_FILETYPE_BLOCK_DEVICE; + break; + case DT_CHR: + entry->d_type = __WASI_FILETYPE_CHARACTER_DEVICE; + break; + case DT_DIR: + entry->d_type = __WASI_FILETYPE_DIRECTORY; + break; + case DT_FIFO: + entry->d_type = __WASI_FILETYPE_SOCKET_STREAM; + break; + case DT_LNK: + entry->d_type = __WASI_FILETYPE_SYMBOLIC_LINK; + break; + case DT_REG: + entry->d_type = __WASI_FILETYPE_REGULAR_FILE; + break; +#ifdef DT_SOCK + case DT_SOCK: + // Technically not correct, but good enough. + entry->d_type = __WASI_FILETYPE_SOCKET_STREAM; + break; +#endif + default: + entry->d_type = __WASI_FILETYPE_UNKNOWN; + break; + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_closedir(os_dir_stream dir_stream) +{ + int ret = closedir(dir_stream); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +os_dir_stream +os_get_invalid_dir_stream() +{ + return NULL; +} + +bool +os_is_dir_stream_valid(os_dir_stream *dir_stream) +{ + assert(dir_stream != NULL); + + return *dir_stream != NULL; +} + +bool +os_is_handle_valid(os_file_handle *handle) +{ + assert(handle != NULL); + + return *handle > -1; +} + +char * +os_realpath(const char *path, char *resolved_path) +{ + return realpath(path, resolved_path); +} \ No newline at end of file diff --git a/core/shared/platform/esp-idf/espidf_memmap.c b/core/shared/platform/esp-idf/espidf_memmap.c index 6b1b6f045..a8298125b 100644 --- a/core/shared/platform/esp-idf/espidf_memmap.c +++ b/core/shared/platform/esp-idf/espidf_memmap.c @@ -9,11 +9,10 @@ #include "soc/mmu.h" #include "rom/cache.h" -#define MEM_DUAL_BUS_OFFSET (IRAM0_CACHE_ADDRESS_LOW - DRAM0_CACHE_ADDRESS_LOW) +#define MEM_DUAL_BUS_OFFSET (SOC_IROM_LOW - SOC_IROM_HIGH) -#define in_ibus_ext(addr) \ - (((uint32)addr >= IRAM0_CACHE_ADDRESS_LOW) \ - && ((uint32)addr < IRAM0_CACHE_ADDRESS_HIGH)) +#define in_ibus_ext(addr) \ + (((uint32)addr >= SOC_IROM_LOW) && ((uint32)addr < SOC_IROM_HIGH)) static portMUX_TYPE s_spinlock = portMUX_INITIALIZER_UNLOCKED; #endif @@ -55,7 +54,24 @@ os_mmap(void *hint, size_t size, int prot, int flags, os_file_handle file) #else uint32_t mem_caps = MALLOC_CAP_8BIT; #endif - return heap_caps_malloc(size, mem_caps); + void *buf_origin = + heap_caps_malloc(size + 4 + sizeof(uintptr_t), mem_caps); + if (!buf_origin) { + return NULL; + } + + // Memory allocation with MALLOC_CAP_SPIRAM or MALLOC_CAP_8BIT will + // return 4-byte aligned Reserve extra 4 byte to fixup alignment and + // size for the pointer to the originally allocated address + void *buf_fixed = buf_origin + sizeof(void *); + if ((uintptr_t)buf_fixed & (uintptr_t)0x7) { + buf_fixed = (void *)((uintptr_t)(buf_fixed + 4) & (~(uintptr_t)7)); + } + + uintptr_t *addr_field = buf_fixed - sizeof(uintptr_t); + *addr_field = (uintptr_t)buf_origin; + + return buf_fixed; } } diff --git a/core/shared/platform/esp-idf/espidf_platform.c b/core/shared/platform/esp-idf/espidf_platform.c index 9c0d02e62..d5f821d07 100644 --- a/core/shared/platform/esp-idf/espidf_platform.c +++ b/core/shared/platform/esp-idf/espidf_platform.c @@ -3,9 +3,25 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +#include "sdkconfig.h" #include "platform_api_vmcore.h" #include "platform_api_extension.h" +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) \ + && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0)) +#define UTIMENSAT_TIMESPEC_POINTER 1 +#define FUTIMENS_TIMESPEC_POINTER 1 +#endif + +#if CONFIG_LITTLEFS_OPEN_DIR && CONFIG_LITTLEFS_FCNTL_GET_PATH +#define OPENAT_SUPPORT 1 + +#undef F_GETPATH +#define F_GETPATH CONFIG_LITTLEFS_FCNTL_F_GETPATH_VALUE + +#define DIR_PATH_LEN (CONFIG_LITTLEFS_OBJ_NAME_LEN + 1) +#endif + int bh_platform_init() { @@ -177,12 +193,40 @@ writev(int fildes, const struct iovec *iov, int iovcnt) return ntotal; } +#if OPENAT_SUPPORT +int +openat(int fd, const char *pathname, int flags, ...) +{ + int new_fd; + int ret; + char dir_path[DIR_PATH_LEN]; + char *full_path; + + ret = fcntl(fd, F_GETPATH, dir_path); + if (ret != 0) { + errno = -EINVAL; + return -1; + } + + ret = asprintf(&full_path, "%s/%s", dir_path, pathname); + if (ret < 0) { + errno = ENOMEM; + return -1; + } + + new_fd = open(full_path, flags); + free(full_path); + + return new_fd; +} +#else int openat(int fd, const char *path, int oflags, ...) { errno = ENOSYS; return -1; } +#endif int fstatat(int fd, const char *path, struct stat *buf, int flag) @@ -234,7 +278,13 @@ unlinkat(int fd, const char *path, int flag) } int -utimensat(int fd, const char *path, const struct timespec ts[2], int flag) +utimensat(int fd, const char *path, +#if UTIMENSAT_TIMESPEC_POINTER + const struct timespec *ts, +#else + const struct timespec ts[2], +#endif + int flag) { errno = ENOSYS; return -1; @@ -257,7 +307,13 @@ ftruncate(int fd, off_t length) #endif int -futimens(int fd, const struct timespec times[2]) +futimens(int fd, +#if FUTIMENS_TIMESPEC_POINTER + const struct timespec *times +#else + const struct timespec times[2] +#endif +) { errno = ENOSYS; return -1; diff --git a/core/shared/platform/esp-idf/espidf_socket.c b/core/shared/platform/esp-idf/espidf_socket.c index a75d82975..8c6509464 100644 --- a/core/shared/platform/esp-idf/espidf_socket.c +++ b/core/shared/platform/esp-idf/espidf_socket.c @@ -8,19 +8,44 @@ #include "libc_errno.h" #include +#include +#include +#include -static void -textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr_in *out) +static bool +textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr *out, + socklen_t *out_len) { + struct sockaddr_in *v4; +#ifdef IPPROTO_IPV6 + struct sockaddr_in6 *v6; +#endif + assert(textual); - out->sin_family = AF_INET; - out->sin_port = htons(port); - out->sin_addr.s_addr = inet_addr(textual); + v4 = (struct sockaddr_in *)out; + if (inet_pton(AF_INET, textual, &v4->sin_addr.s_addr) == 1) { + v4->sin_family = AF_INET; + v4->sin_port = htons(port); + *out_len = sizeof(struct sockaddr_in); + return true; + } + +#ifdef IPPROTO_IPV6 + v6 = (struct sockaddr_in6 *)out; + if (inet_pton(AF_INET6, textual, &v6->sin6_addr.s6_addr) == 1) { + v6->sin6_family = AF_INET6; + v6->sin6_port = htons(port); + *out_len = sizeof(struct sockaddr_in6); + return true; + } +#endif + + return false; } static int -sockaddr_to_bh_sockaddr(const struct sockaddr *sockaddr, socklen_t socklen, +sockaddr_to_bh_sockaddr(const struct sockaddr *sockaddr, bh_sockaddr_t *bh_sockaddr) { switch (sockaddr->sa_family) { @@ -28,31 +53,82 @@ sockaddr_to_bh_sockaddr(const struct sockaddr *sockaddr, socklen_t socklen, { struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; - assert(socklen >= sizeof(struct sockaddr_in)); - bh_sockaddr->port = ntohs(addr->sin_port); bh_sockaddr->addr_buffer.ipv4 = ntohl(addr->sin_addr.s_addr); bh_sockaddr->is_ipv4 = true; return BHT_OK; } +#ifdef IPPROTO_IPV6 + case AF_INET6: + { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)sockaddr; + size_t i; + + bh_sockaddr->port = ntohs(addr->sin6_port); + + for (i = 0; i < sizeof(bh_sockaddr->addr_buffer.ipv6) + / sizeof(bh_sockaddr->addr_buffer.ipv6[0]); + i++) { + uint16 part_addr = addr->sin6_addr.s6_addr[i * 2] + | (addr->sin6_addr.s6_addr[i * 2 + 1] << 8); + bh_sockaddr->addr_buffer.ipv6[i] = ntohs(part_addr); + } + + bh_sockaddr->is_ipv4 = false; + return BHT_OK; + } +#endif default: errno = EAFNOSUPPORT; return BHT_ERROR; } } +static void +bh_sockaddr_to_sockaddr(const bh_sockaddr_t *bh_sockaddr, + struct sockaddr_storage *sockaddr, socklen_t *socklen) +{ + if (bh_sockaddr->is_ipv4) { + struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; + addr->sin_port = htons(bh_sockaddr->port); + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = htonl(bh_sockaddr->addr_buffer.ipv4); + *socklen = sizeof(*addr); + } +#ifdef IPPROTO_IPV6 + else { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)sockaddr; + size_t i; + addr->sin6_port = htons(bh_sockaddr->port); + addr->sin6_family = AF_INET6; + + for (i = 0; i < sizeof(bh_sockaddr->addr_buffer.ipv6) + / sizeof(bh_sockaddr->addr_buffer.ipv6[0]); + i++) { + uint16 part_addr = htons(bh_sockaddr->addr_buffer.ipv6[i]); + addr->sin6_addr.s6_addr[i * 2] = 0xff & part_addr; + addr->sin6_addr.s6_addr[i * 2 + 1] = (0xff00 & part_addr) >> 8; + } + + *socklen = sizeof(*addr); + } +#endif +} + int os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp) { + int af = is_ipv4 ? AF_INET : AF_INET6; + if (!sock) { return BHT_ERROR; } if (is_tcp) { - *sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + *sock = socket(af, SOCK_STREAM, IPPROTO_TCP); } else { - *sock = socket(AF_INET, SOCK_DGRAM, 0); + *sock = socket(af, SOCK_DGRAM, 0); } return (*sock == -1) ? BHT_ERROR : BHT_OK; @@ -61,28 +137,47 @@ os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp) int os_socket_bind(bh_socket_t socket, const char *host, int *port) { - struct sockaddr_in addr; + struct sockaddr_storage addr = { 0 }; + struct linger ling; socklen_t socklen; int ret; assert(host); assert(port); - addr.sin_addr.s_addr = inet_addr(host); - addr.sin_port = htons(*port); - addr.sin_family = AF_INET; + ling.l_onoff = 1; + ling.l_linger = 0; - ret = bind(socket, (struct sockaddr *)&addr, sizeof(addr)); + if (!textual_addr_to_sockaddr(host, *port, (struct sockaddr *)&addr, + &socklen)) { + goto fail; + } + + ret = setsockopt(socket, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)); + if (ret < 0) { + goto fail; + } + + ret = bind(socket, (struct sockaddr *)&addr, socklen); if (ret < 0) { goto fail; } socklen = sizeof(addr); - if (getsockname(socket, (struct sockaddr *)&addr, &socklen) == -1) { + if (getsockname(socket, (void *)&addr, &socklen) == -1) { goto fail; } - *port = ntohs(addr.sin_port); + if (addr.ss_family == AF_INET) { + *port = ntohs(((struct sockaddr_in *)&addr)->sin_port); + } + else { +#ifdef IPPROTO_IPV6 + *port = ntohs(((struct sockaddr_in6 *)&addr)->sin6_port); +#else + goto fail; +#endif + } return BHT_OK; @@ -120,10 +215,7 @@ int os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, unsigned int *addrlen) { - struct sockaddr addr_tmp; - socklen_t len = sizeof(struct sockaddr); - - *sock = accept(server_sock, (struct sockaddr *)&addr_tmp, &len); + *sock = accept(server_sock, addr, (socklen_t *)addrlen); if (*sock < 0) { return BHT_ERROR; @@ -135,11 +227,14 @@ os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, int os_socket_connect(bh_socket_t socket, const char *addr, int port) { - struct sockaddr_in addr_in = { 0 }; - socklen_t addr_len = sizeof(struct sockaddr_in); + struct sockaddr_storage addr_in = { 0 }; + socklen_t addr_len; int ret = 0; - textual_addr_to_sockaddr(addr, port, &addr_in); + if (!textual_addr_to_sockaddr(addr, port, (struct sockaddr *)&addr_in, + &addr_len)) { + return BHT_ERROR; + } ret = connect(socket, (struct sockaddr *)&addr_in, addr_len); if (ret == -1) { @@ -155,12 +250,53 @@ os_socket_recv(bh_socket_t socket, void *buf, unsigned int len) return recv(socket, buf, len, 0); } +int +os_socket_recv_from(bh_socket_t socket, void *buf, unsigned int len, int flags, + bh_sockaddr_t *src_addr) +{ + struct sockaddr_storage sock_addr = { 0 }; + socklen_t socklen = sizeof(sock_addr); + int ret; + + ret = recvfrom(socket, buf, len, flags, (struct sockaddr *)&sock_addr, + &socklen); + + if (ret < 0) { + return ret; + } + + if (src_addr && socklen > 0) { + if (sockaddr_to_bh_sockaddr((struct sockaddr *)&sock_addr, src_addr) + == BHT_ERROR) { + return -1; + } + } + else if (src_addr) { + memset(src_addr, 0, sizeof(*src_addr)); + } + + return ret; +} + int os_socket_send(bh_socket_t socket, const void *buf, unsigned int len) { return send(socket, buf, len, 0); } +int +os_socket_send_to(bh_socket_t socket, const void *buf, unsigned int len, + int flags, const bh_sockaddr_t *dest_addr) +{ + struct sockaddr_storage sock_addr = { 0 }; + socklen_t socklen = 0; + + bh_sockaddr_to_sockaddr(dest_addr, &sock_addr, &socklen); + + return sendto(socket, buf, len, flags, (const struct sockaddr *)&sock_addr, + socklen); +} + int os_socket_close(bh_socket_t socket) { @@ -191,41 +327,701 @@ os_socket_inet_network(bool is_ipv4, const char *cp, bh_ip_addr_buffer_t *out) out->ipv4 = ntohl(out->ipv4); } else { +#ifdef IPPROTO_IPV6 if (inet_pton(AF_INET6, cp, out->ipv6) != 1) { return BHT_ERROR; } for (int i = 0; i < 8; i++) { out->ipv6[i] = ntohs(out->ipv6[i]); } +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + + return BHT_OK; +} + +static int +getaddrinfo_error_to_errno(int error) +{ + switch (error) { + case EAI_AGAIN: + return EAGAIN; + case EAI_FAIL: + return EFAULT; + case EAI_MEMORY: + return ENOMEM; + default: + return EINVAL; + } +} + +static int +is_addrinfo_supported(struct addrinfo *info) +{ + return + // Allow only IPv4 and IPv6 + (info->ai_family == AF_INET || info->ai_family == AF_INET6) + // Allow only UDP and TCP + && (info->ai_socktype == SOCK_DGRAM || info->ai_socktype == SOCK_STREAM) + && (info->ai_protocol == IPPROTO_TCP + || info->ai_protocol == IPPROTO_UDP); +} + +int +os_socket_addr_resolve(const char *host, const char *service, + uint8_t *hint_is_tcp, uint8_t *hint_is_ipv4, + bh_addr_info_t *addr_info, size_t addr_info_size, + size_t *max_info_size) +{ + struct addrinfo hints = { 0 }, *res, *result; + int hints_enabled = hint_is_tcp || hint_is_ipv4; + int ret; + size_t pos = 0; + + if (hints_enabled) { + if (hint_is_ipv4) { + hints.ai_family = *hint_is_ipv4 ? AF_INET : AF_INET6; + } + if (hint_is_tcp) { + hints.ai_socktype = *hint_is_tcp ? SOCK_STREAM : SOCK_DGRAM; + } + } + + ret = getaddrinfo(host, strlen(service) == 0 ? NULL : service, + hints_enabled ? &hints : NULL, &result); + if (ret != BHT_OK) { + errno = getaddrinfo_error_to_errno(ret); + return BHT_ERROR; + } + + res = result; + while (res) { + if (addr_info_size > pos) { + if (!is_addrinfo_supported(res)) { + res = res->ai_next; + continue; + } + + ret = + sockaddr_to_bh_sockaddr(res->ai_addr, &addr_info[pos].sockaddr); + + if (ret == BHT_ERROR) { + freeaddrinfo(result); + return BHT_ERROR; + } + + addr_info[pos].is_tcp = res->ai_socktype == SOCK_STREAM; + } + + pos++; + res = res->ai_next; + } + + *max_info_size = pos; + freeaddrinfo(result); + + return BHT_OK; +} + +static int +os_socket_setbooloption(bh_socket_t socket, int level, int optname, + bool is_enabled) +{ + int option = (int)is_enabled; + if (setsockopt(socket, level, optname, &option, sizeof(option)) != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +static int +os_socket_getbooloption(bh_socket_t socket, int level, int optname, + bool *is_enabled) +{ + assert(is_enabled); + + int optval; + socklen_t optval_size = sizeof(optval); + if (getsockopt(socket, level, optname, &optval, &optval_size) != 0) { + return BHT_ERROR; + } + *is_enabled = (bool)optval; + return BHT_OK; +} + +int +os_socket_set_send_buf_size(bh_socket_t socket, size_t bufsiz) +{ + int buf_size_int = (int)bufsiz; + if (setsockopt(socket, SOL_SOCKET, SO_SNDBUF, &buf_size_int, + sizeof(buf_size_int)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_send_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + assert(bufsiz); + + int buf_size_int; + socklen_t bufsiz_len = sizeof(buf_size_int); + if (getsockopt(socket, SOL_SOCKET, SO_SNDBUF, &buf_size_int, &bufsiz_len) + != 0) { + return BHT_ERROR; + } + *bufsiz = (size_t)buf_size_int; + + return BHT_OK; +} + +int +os_socket_set_recv_buf_size(bh_socket_t socket, size_t bufsiz) +{ + int buf_size_int = (int)bufsiz; + if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, &buf_size_int, + sizeof(buf_size_int)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_recv_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + assert(bufsiz); + + int buf_size_int; + socklen_t bufsiz_len = sizeof(buf_size_int); + if (getsockopt(socket, SOL_SOCKET, SO_RCVBUF, &buf_size_int, &bufsiz_len) + != 0) { + return BHT_ERROR; + } + *bufsiz = (size_t)buf_size_int; + + return BHT_OK; +} + +int +os_socket_set_keep_alive(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_KEEPALIVE, + is_enabled); +} + +int +os_socket_get_keep_alive(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_KEEPALIVE, + is_enabled); +} + +int +os_socket_set_reuse_addr(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_REUSEADDR, + is_enabled); +} + +int +os_socket_get_reuse_addr(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_REUSEADDR, + is_enabled); +} + +int +os_socket_set_reuse_port(bh_socket_t socket, bool is_enabled) +{ +#if defined(SO_REUSEPORT) /* NuttX doesn't have SO_REUSEPORT */ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_REUSEPORT, + is_enabled); +#else + errno = ENOTSUP; + return BHT_ERROR; +#endif /* defined(SO_REUSEPORT) */ +} + +int +os_socket_get_reuse_port(bh_socket_t socket, bool *is_enabled) +{ +#if defined(SO_REUSEPORT) /* NuttX doesn't have SO_REUSEPORT */ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_REUSEPORT, + is_enabled); +#else + errno = ENOTSUP; + return BHT_ERROR; +#endif /* defined(SO_REUSEPORT) */ +} + +int +os_socket_set_linger(bh_socket_t socket, bool is_enabled, int linger_s) +{ + struct linger linger_opts = { .l_onoff = (int)is_enabled, + .l_linger = linger_s }; + if (setsockopt(socket, SOL_SOCKET, SO_LINGER, &linger_opts, + sizeof(linger_opts)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_linger(bh_socket_t socket, bool *is_enabled, int *linger_s) +{ + assert(is_enabled); + assert(linger_s); + + struct linger linger_opts; + socklen_t linger_opts_len = sizeof(linger_opts); + if (getsockopt(socket, SOL_SOCKET, SO_LINGER, &linger_opts, + &linger_opts_len) + != 0) { + return BHT_ERROR; + } + *linger_s = linger_opts.l_linger; + *is_enabled = (bool)linger_opts.l_onoff; + return BHT_OK; +} + +int +os_socket_set_tcp_no_delay(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_NODELAY, + is_enabled); +} + +int +os_socket_get_tcp_no_delay(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_NODELAY, + is_enabled); +} + +int +os_socket_set_tcp_quick_ack(bh_socket_t socket, bool is_enabled) +{ +#ifdef TCP_QUICKACK + return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_QUICKACK, + is_enabled); +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_quick_ack(bh_socket_t socket, bool *is_enabled) +{ +#ifdef TCP_QUICKACK + return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_QUICKACK, + is_enabled); +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_tcp_keep_idle(bh_socket_t socket, uint32 time_s) +{ + int time_s_int = (int)time_s; +#ifdef TCP_KEEPIDLE + if (setsockopt(socket, IPPROTO_TCP, TCP_KEEPIDLE, &time_s_int, + sizeof(time_s_int)) + != 0) { + return BHT_ERROR; + } + return BHT_OK; +#elif defined(TCP_KEEPALIVE) + if (setsockopt(socket, IPPROTO_TCP, TCP_KEEPALIVE, &time_s_int, + sizeof(time_s_int)) + != 0) { + return BHT_ERROR; + } + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_keep_idle(bh_socket_t socket, uint32 *time_s) +{ + assert(time_s); + int time_s_int; + socklen_t time_s_len = sizeof(time_s_int); +#ifdef TCP_KEEPIDLE + if (getsockopt(socket, IPPROTO_TCP, TCP_KEEPIDLE, &time_s_int, &time_s_len) + != 0) { + return BHT_ERROR; + } + *time_s = (uint32)time_s_int; + return BHT_OK; +#elif defined(TCP_KEEPALIVE) + if (getsockopt(socket, IPPROTO_TCP, TCP_KEEPALIVE, &time_s_int, &time_s_len) + != 0) { + return BHT_ERROR; + } + *time_s = (uint32)time_s_int; + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_tcp_keep_intvl(bh_socket_t socket, uint32 time_s) +{ + int time_s_int = (int)time_s; +#ifdef TCP_KEEPINTVL + if (setsockopt(socket, IPPROTO_TCP, TCP_KEEPINTVL, &time_s_int, + sizeof(time_s_int)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_keep_intvl(bh_socket_t socket, uint32 *time_s) +{ +#ifdef TCP_KEEPINTVL + assert(time_s); + int time_s_int; + socklen_t time_s_len = sizeof(time_s_int); + if (getsockopt(socket, IPPROTO_TCP, TCP_KEEPINTVL, &time_s_int, &time_s_len) + != 0) { + return BHT_ERROR; + } + *time_s = (uint32)time_s_int; + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_tcp_fastopen_connect(bh_socket_t socket, bool is_enabled) +{ +#ifdef TCP_FASTOPEN_CONNECT + return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, + is_enabled); +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_fastopen_connect(bh_socket_t socket, bool *is_enabled) +{ +#ifdef TCP_FASTOPEN_CONNECT + return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, + is_enabled); +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool is_enabled) +{ + if (ipv6) { +#ifdef IPPROTO_IPV6 + return os_socket_setbooloption(socket, IPPROTO_IPV6, + IPV6_MULTICAST_LOOP, is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + return os_socket_setbooloption(socket, IPPROTO_IP, IP_MULTICAST_LOOP, + is_enabled); + } +} + +int +os_socket_get_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool *is_enabled) +{ + if (ipv6) { +#ifdef IPPROTO_IPV6 + return os_socket_getbooloption(socket, IPPROTO_IPV6, + IPV6_MULTICAST_LOOP, is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + return os_socket_getbooloption(socket, IPPROTO_IP, IP_MULTICAST_LOOP, + is_enabled); + } +} + +int +os_socket_set_ip_add_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) +{ + assert(imr_multiaddr); + if (is_ipv6) { +#if defined(IPPROTO_IPV6) && !defined(BH_PLATFORM_COSMOPOLITAN) + struct ipv6_mreq mreq; + for (int i = 0; i < 8; i++) { + ((uint16_t *)mreq.ipv6mr_multiaddr.s6_addr)[i] = + imr_multiaddr->ipv6[i]; + } + mreq.ipv6mr_interface = imr_interface; + if (setsockopt(socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = imr_multiaddr->ipv4; + mreq.imr_interface.s_addr = imr_interface; + if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } } return BHT_OK; } int -os_socket_addr_remote(bh_socket_t socket, bh_sockaddr_t *sockaddr) +os_socket_set_ip_drop_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) { - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); + assert(imr_multiaddr); + if (is_ipv6) { +#if defined(IPPROTO_IPV6) && !defined(BH_PLATFORM_COSMOPOLITAN) + struct ipv6_mreq mreq; + for (int i = 0; i < 8; i++) { + ((uint16_t *)mreq.ipv6mr_multiaddr.s6_addr)[i] = + imr_multiaddr->ipv6[i]; + } + mreq.ipv6mr_interface = imr_interface; + if (setsockopt(socket, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = imr_multiaddr->ipv4; + mreq.imr_interface.s_addr = imr_interface; + if (setsockopt(socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } + } - if (getpeername(socket, (struct sockaddr *)&addr, &addr_len) == -1) { + return BHT_OK; +} + +int +os_socket_set_ip_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + if (setsockopt(socket, IPPROTO_IP, IP_TTL, &ttl_s, sizeof(ttl_s)) != 0) { return BHT_ERROR; } - return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr, addr_len, - sockaddr); + return BHT_OK; +} + +int +os_socket_get_ip_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + socklen_t opt_len = sizeof(*ttl_s); + if (getsockopt(socket, IPPROTO_IP, IP_TTL, ttl_s, &opt_len) != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_set_ip_multicast_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl_s, sizeof(ttl_s)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_ip_multicast_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + socklen_t opt_len = sizeof(*ttl_s); + if (getsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, ttl_s, &opt_len) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_set_ipv6_only(bh_socket_t socket, bool is_enabled) +{ +#ifdef IPPROTO_IPV6 + return os_socket_setbooloption(socket, IPPROTO_IPV6, IPV6_V6ONLY, + is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif +} + +int +os_socket_get_ipv6_only(bh_socket_t socket, bool *is_enabled) +{ +#ifdef IPPROTO_IPV6 + return os_socket_getbooloption(socket, IPPROTO_IPV6, IPV6_V6ONLY, + is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif +} + +int +os_socket_set_broadcast(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_BROADCAST, + is_enabled); +} + +int +os_socket_get_broadcast(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_BROADCAST, + is_enabled); +} + +int +os_socket_set_send_timeout(bh_socket_t socket, uint64 timeout_us) +{ + struct timeval tv; + tv.tv_sec = timeout_us / 1000000UL; + tv.tv_usec = timeout_us % 1000000UL; + if (setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) { + return BHT_ERROR; + } + return BHT_OK; +} + +int +os_socket_get_send_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + struct timeval tv; + socklen_t tv_len = sizeof(tv); + if (getsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, &tv_len) != 0) { + return BHT_ERROR; + } + *timeout_us = (tv.tv_sec * 1000000UL) + tv.tv_usec; + return BHT_OK; +} + +int +os_socket_set_recv_timeout(bh_socket_t socket, uint64 timeout_us) +{ + struct timeval tv; + tv.tv_sec = timeout_us / 1000000UL; + tv.tv_usec = timeout_us % 1000000UL; + if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) { + return BHT_ERROR; + } + return BHT_OK; +} + +int +os_socket_get_recv_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + struct timeval tv; + socklen_t tv_len = sizeof(tv); + if (getsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, &tv_len) != 0) { + return BHT_ERROR; + } + *timeout_us = (tv.tv_sec * 1000000UL) + tv.tv_usec; + return BHT_OK; } int os_socket_addr_local(bh_socket_t socket, bh_sockaddr_t *sockaddr) { - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); + struct sockaddr_storage addr_storage = { 0 }; + socklen_t addr_len = sizeof(addr_storage); + int ret; - if (getsockname(socket, (struct sockaddr *)&addr, &addr_len) == -1) { + ret = getsockname(socket, (struct sockaddr *)&addr_storage, &addr_len); + + if (ret != BHT_OK) { return BHT_ERROR; } - return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr, addr_len, - sockaddr); + return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, sockaddr); +} + +int +os_socket_addr_remote(bh_socket_t socket, bh_sockaddr_t *sockaddr) +{ + struct sockaddr_storage addr_storage = { 0 }; + socklen_t addr_len = sizeof(addr_storage); + int ret; + + ret = getpeername(socket, (struct sockaddr *)&addr_storage, &addr_len); + + if (ret != BHT_OK) { + return BHT_ERROR; + } + + return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, sockaddr); } diff --git a/core/shared/platform/esp-idf/espidf_thread.c b/core/shared/platform/esp-idf/espidf_thread.c index 637cd4177..768d823a8 100644 --- a/core/shared/platform/esp-idf/espidf_thread.c +++ b/core/shared/platform/esp-idf/espidf_thread.c @@ -230,4 +230,59 @@ int os_cond_broadcast(korp_cond *cond) { return pthread_cond_broadcast(cond); -} \ No newline at end of file +} + +int +os_rwlock_init(korp_rwlock *lock) +{ + assert(lock); + + if (pthread_rwlock_init(lock, NULL) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_rwlock_rdlock(korp_rwlock *lock) +{ + assert(lock); + + if (pthread_rwlock_rdlock(lock) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_rwlock_wrlock(korp_rwlock *lock) +{ + assert(lock); + + if (pthread_rwlock_wrlock(lock) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_rwlock_unlock(korp_rwlock *lock) +{ + assert(lock); + + if (pthread_rwlock_unlock(lock) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_rwlock_destroy(korp_rwlock *lock) +{ + assert(lock); + + if (pthread_rwlock_destroy(lock) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} diff --git a/core/shared/platform/esp-idf/platform_internal.h b/core/shared/platform/esp-idf/platform_internal.h index 0f873810e..e0091bee1 100644 --- a/core/shared/platform/esp-idf/platform_internal.h +++ b/core/shared/platform/esp-idf/platform_internal.h @@ -56,39 +56,29 @@ typedef unsigned int korp_sem; #define UTIME_OMIT ((1l << 30) - 2l) #endif -#ifdef DT_UNKNOWN -#undef DT_UNKNOWN -#endif - -#ifdef DT_REG -#undef DT_REG -#endif - -#ifdef DT_DIR -#undef DT_DIR -#endif - /* Below parts of d_type define are ported from Nuttx, under Apache License v2.0 */ -/* File type code for the d_type field in dirent structure. - * Note that because of the simplified filesystem organization of the NuttX, - * top-level, pseudo-file system, an inode can be BOTH a file and a directory +/* Following macros are defined in espressif GCC of esp-idf v5.3 */ #define DTYPE_UNKNOWN 0 -#define DTYPE_FIFO 1 -#define DTYPE_CHR 2 -#define DTYPE_SEM 3 -#define DTYPE_DIRECTORY 4 -#define DTYPE_MQ 5 -#define DTYPE_BLK 6 -#define DTYPE_SHM 7 -#define DTYPE_FILE 8 -#define DTYPE_MTD 9 +#define DTYPE_FILE 1 +#define DTYPE_DIRECTORY 2 +#define DTYPE_CHR 4 +#define DTYPE_BLK 5 +#define DTYPE_FIFO 8 #define DTYPE_LINK 10 #define DTYPE_SOCK 12 +/* Following macros are not defined in espressif GCC of esp-idf v5.3 + */ + +#define DTYPE_SEM 100 +#define DTYPE_MQ 101 +#define DTYPE_SHM 102 +#define DTYPE_MTD 103 + /* The d_type field of the dirent structure is not specified by POSIX. It * is a non-standard, 4.5BSD extension that is implemented by most OSs. A * POSIX compliant OS may not implement the d_type field at all. Many OS's @@ -96,18 +86,53 @@ typedef unsigned int korp_sem; * type names: */ +#ifndef DT_UNKNOWN #define DT_UNKNOWN DTYPE_UNKNOWN +#endif + +#ifndef DT_FIFO #define DT_FIFO DTYPE_FIFO +#endif + +#ifndef DT_CHR #define DT_CHR DTYPE_CHR +#endif + +#ifndef DT_SEM #define DT_SEM DTYPE_SEM +#endif + +#ifndef DT_DIR #define DT_DIR DTYPE_DIRECTORY +#endif + +#ifndef DT_MQ #define DT_MQ DTYPE_MQ +#endif + +#ifndef DT_BLK #define DT_BLK DTYPE_BLK +#endif + +#ifndef DT_SHM #define DT_SHM DTYPE_SHM +#endif + +#ifndef DT_REG #define DT_REG DTYPE_FILE +#endif + +#ifndef DT_MTD #define DT_MTD DTYPE_MTD +#endif + +#ifndef DT_LNK #define DT_LNK DTYPE_LINK +#endif + +#ifndef DT_SOCK #define DT_SOCK DTYPE_SOCK +#endif static inline int os_getpagesize() diff --git a/core/shared/platform/linux-sgx/sgx_ipfs.c b/core/shared/platform/linux-sgx/sgx_ipfs.c index 322688980..4f4bbef9b 100644 --- a/core/shared/platform/linux-sgx/sgx_ipfs.c +++ b/core/shared/platform/linux-sgx/sgx_ipfs.c @@ -350,7 +350,7 @@ ipfs_fopen(int fd, int flags) errno = __WASI_ECANCELED; sgx_fclose(sgx_file); os_printf("An error occurred while inserting the IPFS file pointer in " - "the map."); + "the map.\n"); return NULL; } @@ -529,4 +529,4 @@ ipfs_ftruncate(int fd, off_t len) return 0; } -#endif /* end of WASM_ENABLE_SGX_IPFS */ \ No newline at end of file +#endif /* end of WASM_ENABLE_SGX_IPFS */ diff --git a/core/shared/platform/linux-sgx/sgx_platform.c b/core/shared/platform/linux-sgx/sgx_platform.c index 3a4a19245..d97883a8e 100644 --- a/core/shared/platform/linux-sgx/sgx_platform.c +++ b/core/shared/platform/linux-sgx/sgx_platform.c @@ -154,8 +154,8 @@ os_mmap(void *hint, size_t size, int prot, int flags, os_file_handle file) ret = sgx_alloc_rsrv_mem(aligned_size); if (ret == NULL) { - os_printf("os_mmap(size=%u, aligned size=%lu, prot=0x%x) failed.", size, - aligned_size, prot); + os_printf("os_mmap(size=%u, aligned size=%lu, prot=0x%x) failed.\n", + size, aligned_size, prot); return NULL; } @@ -168,7 +168,7 @@ os_mmap(void *hint, size_t size, int prot, int flags, os_file_handle file) st = sgx_tprotect_rsrv_mem(ret, aligned_size, mprot); if (st != SGX_SUCCESS) { - os_printf("os_mmap(size=%u, prot=0x%x) failed to set protect.", size, + os_printf("os_mmap(size=%u, prot=0x%x) failed to set protect.\n", size, prot); sgx_free_rsrv_mem(ret, aligned_size); return NULL; @@ -205,7 +205,8 @@ os_mprotect(void *addr, size_t size, int prot) mprot |= SGX_PROT_EXEC; st = sgx_tprotect_rsrv_mem(addr, aligned_size, mprot); if (st != SGX_SUCCESS) - os_printf("os_mprotect(addr=0x%" PRIx64 ", size=%u, prot=0x%x) failed.", + os_printf("os_mprotect(addr=0x%" PRIx64 + ", size=%u, prot=0x%x) failed.\n", (uintptr_t)addr, size, prot); return (st == SGX_SUCCESS ? 0 : -1); diff --git a/core/shared/platform/zephyr/zephyr_thread.c b/core/shared/platform/zephyr/zephyr_thread.c index 105d53993..53ca71f62 100644 --- a/core/shared/platform/zephyr/zephyr_thread.c +++ b/core/shared/platform/zephyr/zephyr_thread.c @@ -577,4 +577,34 @@ os_thread_get_stack_boundary() void os_thread_jit_write_protect_np(bool enabled) -{} \ No newline at end of file +{} + +int +os_thread_detach(korp_tid thread) +{ + (void)thread; + return BHT_OK; +} + +void +os_thread_exit(void *retval) +{ + (void)retval; + os_thread_cleanup(); + k_thread_abort(k_current_get()); +} + +int +os_cond_broadcast(korp_cond *cond) +{ + os_thread_wait_node *node; + k_mutex_lock(&cond->wait_list_lock, K_FOREVER); + node = cond->thread_wait_list; + while (node) { + os_thread_wait_node *next = node->next; + k_sem_give(&node->sem); + node = next; + } + k_mutex_unlock(&cond->wait_list_lock); + return BHT_OK; +} diff --git a/core/shared/utils/runtime_timer.c b/core/shared/utils/runtime_timer.c index b9ace567f..9d390c214 100644 --- a/core/shared/utils/runtime_timer.c +++ b/core/shared/utils/runtime_timer.c @@ -394,7 +394,7 @@ handle_expired_timers(timer_ctx_t ctx, app_timer_t *expired) operation may change expired->next */ expired = expired->next; if (t->is_periodic) { - /* if it is repeating, then reschedule it; */ + /* if it is repeating, then reschedule it */ reschedule_timer(ctx, t); } else { diff --git a/core/version.h b/core/version.h index 8f9f757a6..de3f56abc 100644 --- a/core/version.h +++ b/core/version.h @@ -5,7 +5,7 @@ #ifndef _WAMR_VERSION_H_ #define _WAMR_VERSION_H_ -#define WAMR_VERSION_MAJOR 1 -#define WAMR_VERSION_MINOR 3 -#define WAMR_VERSION_PATCH 2 +#define WAMR_VERSION_MAJOR 2 +#define WAMR_VERSION_MINOR 0 +#define WAMR_VERSION_PATCH 0 #endif diff --git a/doc/build_wamr.md b/doc/build_wamr.md index 0d372e0d7..5598ea364 100644 --- a/doc/build_wamr.md +++ b/doc/build_wamr.md @@ -76,6 +76,11 @@ cmake -DWAMR_BUILD_PLATFORM=linux -DWAMR_BUILD_TARGET=ARM #### **Enable bulk memory feature** - **WAMR_BUILD_BULK_MEMORY**=1/0, default to disable if not set +#### **Enable memory64 feature** +- **WAMR_BUILD_MEMORY64**=1/0, default to disable if not set + +> Note: Currently, the memory64 feature is only supported in classic interpreter running mode. + #### **Enable thread manager** - **WAMR_BUILD_THREAD_MGR**=1/0, default to disable if not set @@ -129,6 +134,14 @@ cmake -DWAMR_BUILD_PLATFORM=linux -DWAMR_BUILD_TARGET=ARM - **WAMR_BUILD_SIMD**=1/0, default to enable if not set > Note: only supported in AOT mode x86-64 target. +#### **Enable Exception Handling** +- **WAMR_BUILD_EXCE_HANDLING**=1/0, default to disable if not set + +> Note: Currently, the exception handling feature is only supported in classic interpreter running mode. + +#### **Enable Garbage Collection** +- **WAMR_BUILD_GC**=1/0, default to disable if not set + #### **Configure Debug** - **WAMR_BUILD_CUSTOM_NAME_SECTION**=1/0, load the function name from custom name section, default to disable if not set @@ -241,6 +254,10 @@ Currently we only profile the memory consumption of module, module_instance and > See [Enable segue optimization for wamrc when generating the aot file](./perf_tune.md#3-enable-segue-optimization-for-wamrc-when-generating-the-aot-file) for more details. +#### **User defined linear memory allocator** +- **WAMR_BUILD_ALLOC_WITH_USAGE**=1/0, default to disable if not set +> Notes: by default, the linear memory is allocated by system. when it's set to 1 and Alloc_With_Allocator is selected, it will be allocated by customer. + #### **Enable running PGO(Profile-Guided Optimization) instrumented AOT file** - **WAMR_BUILD_STATIC_PGO**=1/0, default to disable if not set > Note: See [Use the AOT static PGO method](./perf_tune.md#5-use-the-aot-static-pgo-method) for more details. diff --git a/doc/export_native_api.md b/doc/export_native_api.md index b8f77f262..0f7f1669d 100644 --- a/doc/export_native_api.md +++ b/doc/export_native_api.md @@ -89,7 +89,7 @@ Each letter in the "()" represents a parameter type, and the one following after - '**I**': i64 - '**f**': f32 - '**F**': f64 -- '**r**': externref (has to be the value of a `uintptr_t` variable) +- '**r**': externref (has to be the value of a `uintptr_t` variable), or all kinds of GC reference types when GC feature is enabled - '**\***': the parameter is a buffer address in WASM application - '**~**': the parameter is the byte length of WASM buffer as referred by preceding argument "\*". It must follow after '*', otherwise, registration will fail - '**$**': the parameter is a string in WASM application diff --git a/doc/perf_tune.md b/doc/perf_tune.md index 7858cc854..893279710 100644 --- a/doc/perf_tune.md +++ b/doc/perf_tune.md @@ -28,7 +28,7 @@ emcc -msimd128 -O3 -o - Reduce the footprint of JIT/AOT, the JIT/AOT code generated is smaller - Reduce the compilation time of JIT/AOT -Currently it is supported on linux x86-64, developer can use `--enable-segue=[]` for wamrc: +Currently it is only supported on linux x86-64, developer can use `--enable-segue=[]` for wamrc: ```bash wamrc --enable-segue -o aot_file wasm_file @@ -50,6 +50,8 @@ iwasm --enable-segue wasm_file (iwasm is built with llvm-jit enabled) iwasm --enable-segue=[] wasm_file ``` +> Note: Currently it is only supported on linux x86-64. + ## 5. Use the AOT static PGO method LLVM PGO (Profile-Guided Optimization) allows the compiler to better optimize code for how it actually runs. WAMR supports AOT static PGO, currently it is tested on Linux x86-64 and x86-32. The basic steps are: diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 000000000..ff25b32cb --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,15 @@ +version: "2.0.0" +description: WebAssembly Micro Runtime - A lightweight standalone WebAssembly (Wasm) runtime with small footprint, high performance and highly configurable features +url: https://bytecodealliance.org/ +repository: https://github.com/bytecodealliance/wasm-micro-runtime.git +documentation: https://wamr.gitbook.io/ +issues: https://github.com/bytecodealliance/wasm-micro-runtime/issues +dependencies: + idf: ">=4.4" +targets: + - esp32 + - esp32s3 + - esp32c3 + - esp32c6 +examples: + - path: product-mini/platforms/esp-idf \ No newline at end of file diff --git a/product-mini/platforms/esp-idf/CMakeLists.txt b/product-mini/platforms/esp-idf/CMakeLists.txt index d8a3d2f96..8472df8dd 100644 --- a/product-mini/platforms/esp-idf/CMakeLists.txt +++ b/product-mini/platforms/esp-idf/CMakeLists.txt @@ -6,7 +6,4 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set (COMPONENTS ${IDF_TARGET} main freertos esptool_py wamr) -list(APPEND EXTRA_COMPONENT_DIRS "$ENV{WAMR_PATH}/build-scripts/esp-idf") - project(wamr-simple) \ No newline at end of file diff --git a/product-mini/platforms/esp-idf/main/CMakeLists.txt b/product-mini/platforms/esp-idf/main/CMakeLists.txt index 55e725670..1bb61bad9 100644 --- a/product-mini/platforms/esp-idf/main/CMakeLists.txt +++ b/product-mini/platforms/esp-idf/main/CMakeLists.txt @@ -2,5 +2,4 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception idf_component_register(SRCS "main.c" - INCLUDE_DIRS "." - REQUIRES wamr) + INCLUDE_DIRS ".") diff --git a/product-mini/platforms/esp-idf/main/idf_component.yml b/product-mini/platforms/esp-idf/main/idf_component.yml new file mode 100644 index 000000000..1c05f476e --- /dev/null +++ b/product-mini/platforms/esp-idf/main/idf_component.yml @@ -0,0 +1,7 @@ +## IDF Component Manager Manifest File +dependencies: + wasm-micro-runtime: + version: "^2" + override_path: "../../../.." + idf: + version: ">=4.4" \ No newline at end of file diff --git a/product-mini/platforms/esp-idf/main/main.c b/product-mini/platforms/esp-idf/main/main.c index fbfb04c21..1a34096d7 100644 --- a/product-mini/platforms/esp-idf/main/main.c +++ b/product-mini/platforms/esp-idf/main/main.c @@ -12,11 +12,7 @@ #include "esp_log.h" -#ifdef CONFIG_IDF_TARGET_ESP32S3 #define IWASM_MAIN_STACK_SIZE 5120 -#else -#define IWASM_MAIN_STACK_SIZE 4096 -#endif #define LOG_TAG "wamr" diff --git a/product-mini/platforms/nuttx/CMakeLists.txt b/product-mini/platforms/nuttx/CMakeLists.txt index f83e79916..e9fe5a9e3 100644 --- a/product-mini/platforms/nuttx/CMakeLists.txt +++ b/product-mini/platforms/nuttx/CMakeLists.txt @@ -129,6 +129,10 @@ if(CONFIG_INTERPRETERS_WAMR_GLOBAL_HEAP_POOL) set(WAMR_BUILD_GLOBAL_HEAP_SIZE ${_HEAP_SIZE_}) endif() +if (CONFIG_INTERPRETERS_WAMR_MEM_ALLOC_WITH_USAGE) + set(WAMR_BUILD_MEM_ALLOC_WITH_USAGE 1) +endif() + if(CONFIG_INTERPRETERS_WAMR_ENABLE_SPEC_TEST) set(WAMR_BUILD_SPEC_TEST 1) endif() diff --git a/product-mini/platforms/nuttx/wamr.mk b/product-mini/platforms/nuttx/wamr.mk index e414a7cda..7aac0e358 100644 --- a/product-mini/platforms/nuttx/wamr.mk +++ b/product-mini/platforms/nuttx/wamr.mk @@ -373,6 +373,11 @@ CFLAGS += -DWASM_ENABLE_GLOBAL_HEAP_POOL=1 CFLAGS += -DWASM_GLOBAL_HEAP_SIZE="$(CONFIG_INTERPRETERS_WAMR_GLOBAL_HEAP_POOL_SIZE) * 1024" else CFLAGS += -DWASM_ENABLE_GLOBAL_HEAP_POOL=0 +ifeq ($(CONFIG_INTERPRETERS_WAMR_MEM_ALLOC_WITH_USAGE),y) +CFLAGS += -DWASM_MEM_ALLOC_WITH_USAGE=1 +else +CFLAGS += -DWASM_MEM_ALLOC_WITH_USAGE=0 +endif endif ifeq ($(CONFIG_INTERPRETERS_WAMR_ENABLE_SPEC_TEST),y) diff --git a/product-mini/platforms/posix/main.c b/product-mini/platforms/posix/main.c index 37ee0cb87..cb0581ce2 100644 --- a/product-mini/platforms/posix/main.c +++ b/product-mini/platforms/posix/main.c @@ -445,6 +445,9 @@ static char global_heap_buf[WASM_GLOBAL_HEAP_SIZE] = { 0 }; #else static void * malloc_func( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + mem_alloc_usage_t usage, +#endif #if WASM_MEM_ALLOC_WITH_USER_DATA != 0 void *user_data, #endif @@ -455,6 +458,9 @@ malloc_func( static void * realloc_func( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + mem_alloc_usage_t usage, bool full_size_mmaped, +#endif #if WASM_MEM_ALLOC_WITH_USER_DATA != 0 void *user_data, #endif @@ -465,6 +471,9 @@ realloc_func( static void free_func( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + mem_alloc_usage_t usage, +#endif #if WASM_MEM_ALLOC_WITH_USER_DATA != 0 void *user_data, #endif @@ -675,7 +684,7 @@ main(int argc, char *argv[]) #endif #if WASM_ENABLE_GC != 0 else if (!strncmp(argv[0], "--gc-heap-size=", 15)) { - if (argv[0][21] == '\0') + if (argv[0][15] == '\0') return print_help(); gc_heap_size = atoi(argv[0] + 15); } @@ -851,7 +860,8 @@ main(int argc, char *argv[]) #if WASM_ENABLE_DEBUG_INTERP != 0 init_args.instance_port = instance_port; if (ip_addr) - strcpy(init_args.ip_addr, ip_addr); + /* ensure that init_args.ip_addr is null terminated */ + strncpy(init_args.ip_addr, ip_addr, sizeof(init_args.ip_addr) - 1); #endif /* initialize runtime environment */ diff --git a/product-mini/platforms/windows/main.c b/product-mini/platforms/windows/main.c index 6461e9172..35a489721 100644 --- a/product-mini/platforms/windows/main.c +++ b/product-mini/platforms/windows/main.c @@ -464,7 +464,9 @@ main(int argc, char *argv[]) #if WASM_ENABLE_DEBUG_INTERP != 0 init_args.instance_port = instance_port; if (ip_addr) - strcpy(init_args.ip_addr, ip_addr); + /* ensure that init_args.ip_addr is null terminated */ + strncpy_s(init_args.ip_addr, sizeof(init_args.ip_addr) - 1, ip_addr, + strlen(ip_addr)); #endif /* initialize runtime environment */ diff --git a/product-mini/platforms/zephyr/simple/prj.conf b/product-mini/platforms/zephyr/simple/prj.conf index 7f4a32832..c269b8ab4 100644 --- a/product-mini/platforms/zephyr/simple/prj.conf +++ b/product-mini/platforms/zephyr/simple/prj.conf @@ -4,3 +4,4 @@ CONFIG_STACK_SENTINEL=y CONFIG_PRINTK=y CONFIG_LOG=y +CONFIG_LOG_BUFFER_SIZE=4096 diff --git a/product-mini/platforms/zephyr/simple/src/main.c b/product-mini/platforms/zephyr/simple/src/main.c index 3b389826f..f6941d7f0 100644 --- a/product-mini/platforms/zephyr/simple/src/main.c +++ b/product-mini/platforms/zephyr/simple/src/main.c @@ -16,30 +16,17 @@ #endif /* end of BUILD_TARGET_RISCV64_LP64 || BUILD_TARGET_RISCV32_ILP32 */ #if defined(BUILD_TARGET_RISCV64_LP64) || defined(BUILD_TARGET_RISCV32_ILP32) -#if defined(BUILD_TARGET_RISCV64_LP64) -#define CONFIG_GLOBAL_HEAP_BUF_SIZE 4360 -#define CONFIG_APP_STACK_SIZE 288 -#define CONFIG_MAIN_THREAD_STACK_SIZE 2400 -#else #define CONFIG_GLOBAL_HEAP_BUF_SIZE 5120 #define CONFIG_APP_STACK_SIZE 512 -#define CONFIG_MAIN_THREAD_STACK_SIZE 4096 -#endif -#define CONFIG_APP_HEAP_SIZE 256 +#define CONFIG_APP_HEAP_SIZE 512 #else /* else of BUILD_TARGET_RISCV64_LP64 || BUILD_TARGET_RISCV32_ILP32 */ - #define CONFIG_GLOBAL_HEAP_BUF_SIZE WASM_GLOBAL_HEAP_SIZE #define CONFIG_APP_STACK_SIZE 8192 #define CONFIG_APP_HEAP_SIZE 8192 - -#ifdef CONFIG_NO_OPTIMIZATIONS -#define CONFIG_MAIN_THREAD_STACK_SIZE 8192 -#else -#define CONFIG_MAIN_THREAD_STACK_SIZE 4096 -#endif - #endif /* end of BUILD_TARGET_RISCV64_LP64 || BUILD_TARGET_RISCV32_ILP32 */ +#define CONFIG_MAIN_THREAD_STACK_SIZE 8192 + static int app_argc; static char **app_argv; diff --git a/samples/debug-tools/CMakeLists.txt b/samples/debug-tools/CMakeLists.txt index 5143462a3..ce06029a5 100644 --- a/samples/debug-tools/CMakeLists.txt +++ b/samples/debug-tools/CMakeLists.txt @@ -7,6 +7,14 @@ include(CheckPIESupported) project(debug_tools_sample) +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) +find_package(WASISDK REQUIRED) + +option(SOURCE_MAP_DEMO "Enable source map demo" OFF) +if (SOURCE_MAP_DEMO) + find_package(EMSCRIPTEN 3.1.50 REQUIRED) +endif () + ################ runtime settings ################ string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) if (APPLE) @@ -61,7 +69,30 @@ include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) ################ wasm application ################ -add_subdirectory(wasm-apps) +include(ExternalProject) + +# wasm32-wasi +ExternalProject_Add(wasm33-wasi + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps" + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps -B build + -DWASI_SDK_PREFIX=${WASISDK_HOME} + -DCMAKE_TOOLCHAIN_FILE=${WASISDK_TOOLCHAIN} + BUILD_COMMAND ${CMAKE_COMMAND} --build build + INSTALL_COMMAND ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR} +) + +if (EMSCRIPTEN_FOUND) + # wasm32-emscripten + ExternalProject_Add(wasm32-emscripten + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps" + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps -B build + -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN_TOOLCHAIN} + -DCMAKE_VERBOSE_MAKEFILE=On + -DSOURCE_MAP_DEMO=On + BUILD_COMMAND ${CMAKE_COMMAND} --build build + INSTALL_COMMAND ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR}/emscripten + ) +endif () ################ wamr runtime ################ include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) diff --git a/samples/debug-tools/README.md b/samples/debug-tools/README.md index 304778596..b0358b9e4 100644 --- a/samples/debug-tools/README.md +++ b/samples/debug-tools/README.md @@ -63,7 +63,7 @@ The output should be something like: at wasm-micro-runtime/samples/debug-tools/wasm-apps/trap.c:17:12 3: main at wasm-micro-runtime/samples/debug-tools/wasm-apps/trap.c:24:5 -4: +4: __main_void at unknown:?:? 5: _start ``` @@ -79,3 +79,55 @@ $ python3 ../../../test-tools/addr2line/addr2line.py \ --wasm-file wasm-apps/trap.wasm \ call_stack.txt --no-addr ``` + +#### sourcemap + +This script also supports _sourcemap_ which is produced by [_emscripten_](https://emscripten.org/docs/tools_reference/emcc.html). The _sourcemap_ is used to map the wasm function to the original source file. To use it, add `-gsource-map` option to _emcc_ command line. The output should be a section named "sourceMappingURL" and a separated file named "_.map_. + +If the wasm file is with _sourcemap_, the script will use it to get the source file and line info. It needs an extra command line option `--emsdk` to specify the path of _emsdk_. The script will use _emsymbolizer_ to query the source file and line info. + +````bash +$ python3 ../../../test-tools/addr2line/addr2line.py \ + --wasi-sdk /opt/wasi-sdk \ + --wabt /opt/wabt \ + --wasm-file emscripten/wasm-apps/trap.wasm \ + --emsdk /opt/emsdk \ + call_stack.from_wasm_w_sourcemap.txt + +The output should be something like: + +```text +1: c + at ../../../../../wasm-apps/trap.c:5:1 +2: b + at ../../../../../wasm-apps/trap.c:11:12 +3: a + at ../../../../../wasm-apps/trap.c:17:12 +4: main + at ../../../../../wasm-apps/trap.c:24:5 +5: __main_void + at ../../../../../../../../../emsdk/emscripten/system/lib/standalone/__main_void.c:53:10 +6: _start + at ../../../../../../../../../emsdk/emscripten/system/lib/libc/crt1.c:27:3 +```` + +> The script assume the separated map file _.map_ is in the same directory as the wasm file. + +### Another approach + +If the wasm file is with "name" section, it is able to output function name in the stack trace. To achieve that, need to enable `WAMR_BUILD_LOAD_CUSTOM_SECTION` and `WAMR_BUILD_CUSTOM_NAME_SECTION`. If using .aot file, need to add `--emit-custom-sections=name` into wamrc command line options. + +Then the output should be something like + +```text +#00: 0x0159 - c +#01: 0x01b2 - b +#02: 0x0200 - a +#03: 0x026b - main +#04: 0x236b - __main_void +#05: 0x011f - _start + +Exception: unreachable +``` + +Also, it is able to use _addr2line.py_ to add file and line info to the stack trace. diff --git a/samples/debug-tools/cmake/FindEMSCRIPTEN.cmake b/samples/debug-tools/cmake/FindEMSCRIPTEN.cmake new file mode 100644 index 000000000..8f63ec545 --- /dev/null +++ b/samples/debug-tools/cmake/FindEMSCRIPTEN.cmake @@ -0,0 +1,45 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include(FindPackageHandleStandardArgs) + +find_path(EMSCRIPTEN_HOME + NAMES upstream/emscripten + PATHS /opt/emsdk + NO_DEFAULT_PATH + NO_CMAKE_PATH + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_FIND_ROOT_PATH + REQUIRED +) + +find_file(EMSCRIPTEN_VERSION_FILE + NAMES emscripten-version.txt + PATHS ${EMSCRIPTEN_HOME}/upstream/emscripten + NO_DEFAULT_PATH + NO_CMAKE_PATH + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_FIND_ROOT_PATH + REQUIRED +) + +file(READ ${EMSCRIPTEN_VERSION_FILE} EMSCRIPTEN_VERSION_FILE_CONTENT) + +string(REGEX + MATCH + "[0-9]+\.[0-9]+(\.[0-9]+)*" + EMSCRIPTEN_VERSION + ${EMSCRIPTEN_VERSION_FILE_CONTENT} +) + +find_package_handle_standard_args(EMSCRIPTEN + REQUIRED_VARS EMSCRIPTEN_HOME + VERSION_VAR EMSCRIPTEN_VERSION + HANDLE_VERSION_RANGE +) + +if(EMSCRIPTEN_FOUND) + set(EMSCRIPTEN_TOOLCHAIN ${EMSCRIPTEN_HOME}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake) + set(EMCC ${EMSCRIPTEN_HOME}/upstream/emscripten/emcc) +endif() +mark_as_advanced(EMSCRIPTEN_TOOLCHAIN EMCC) diff --git a/samples/debug-tools/cmake/FindWAMRC.cmake b/samples/debug-tools/cmake/FindWAMRC.cmake new file mode 100644 index 000000000..20f9416f7 --- /dev/null +++ b/samples/debug-tools/cmake/FindWAMRC.cmake @@ -0,0 +1,27 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include(FindPackageHandleStandardArgs) + +find_path(WAMRC_HOME + wamr-compiler + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/../../.. + NO_DEFAULT_PATH + NO_CMAKE_PATH + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_FIND_ROOT_PATH + REQUIRED +) + +find_file(WAMRC_BIN + wamrc + HINTS ${WAMRC_HOME}/wamr-compiler/build + NO_DEFAULT_PATH + NO_CMAKE_PATH + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_FIND_ROOT_PATH + REQUIRED +) + +find_package_handle_standard_args(WAMRC REQUIRED_VARS WAMRC_BIN) +mark_as_advanced(WAMRC_BIN) diff --git a/samples/debug-tools/cmake/FindWASISDK.cmake b/samples/debug-tools/cmake/FindWASISDK.cmake new file mode 100644 index 000000000..0caf374df --- /dev/null +++ b/samples/debug-tools/cmake/FindWASISDK.cmake @@ -0,0 +1,24 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include(FindPackageHandleStandardArgs) + +file(GLOB WASISDK_SEARCH_PATH "/opt/wasi-sdk-*") +find_path(WASISDK_HOME + NAMES share/wasi-sysroot + PATHS ${WASISDK_SEARCH_PATH} + NO_DEFAULT_PATH + REQUIRED +) + +string(REGEX MATCH [0-9]+\.[0-9]+\.*[0-9]* WASISDK_VERSION ${WASISDK_HOME}) + +find_package_handle_standard_args(WASISDK REQUIRED_VARS WASISDK_HOME VERSION_VAR WASISDK_VERSION) + +if(WASISDK_FOUND) + set(WASISDK_CC_COMMAND ${WASISDK_HOME}/bin/clang) + set(WASISDK_CXX_COMMAND ${WASISDK_HOME}/bin/clang++) + set(WASISDK_TOOLCHAIN ${WASISDK_HOME}/share/cmake/wasi-sdk.cmake) + set(WASISDK_SYSROOT ${WASISDK_HOME}/share/wasi-sysroot) +endif() +mark_as_advanced(WASISDK_CC_COMMAND WASISDK_CXX_COMMAND WASISDK_TOOLCHAIN WASISDK_SYSROOT WASISDK_HOME) diff --git a/samples/debug-tools/wasm-apps/CMakeLists.txt b/samples/debug-tools/wasm-apps/CMakeLists.txt index 3ca8aff2a..527b5f37a 100644 --- a/samples/debug-tools/wasm-apps/CMakeLists.txt +++ b/samples/debug-tools/wasm-apps/CMakeLists.txt @@ -1,91 +1,58 @@ # Copyright (C) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -if (APPLE) - set (HAVE_FLAG_SEARCH_PATHS_FIRST 0) - set (CMAKE_C_LINK_FLAGS "") - set (CMAKE_CXX_LINK_FLAGS "") +cmake_minimum_required (VERSION 3.14) + +project (debut_tools_wasm) + +set (CMAKE_BUILD_TYPE Debug) # Otherwise no debug symbols (addr2line) + +list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../cmake) +find_package (WAMRC REQUIRED) + +option(SOURCE_MAP_DEMO "Enable source map demo" OFF) +if (SOURCE_MAP_DEMO) + find_package(EMSCRIPTEN 3.1.50 REQUIRED) endif () -if (NOT DEFINED WASI_SDK_DIR) - set (WASI_SDK_DIR "/opt/wasi-sdk") -endif () - -if (DEFINED WASI_SYSROOT) - set (CMAKE_SYSROOT "${WASI_SYSROOT}") -endif () - -set (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") -set (CMAKE_ASM_COMPILER "${WASI_SDK_DIR}/bin/clang") -set (CMAKE_EXE_LINKER_FLAGS "-target wasm32-wasi") - -################ wabt and wamrc dependencies ################ -message(CHECK_START "Detecting WABT") -if(NOT (DEFINED WABT_DIR OR DEFINED CACHE{WABT_DIR})) - find_path(WABT_DIR - wabt - PATHS /opt - NO_DEFAULT_PATH - NO_CMAKE_FIND_ROOT_PATH - ) - if(DEFINED WABT_DIR) - set(WABT_DIR ${WABT_DIR}/wabt) - endif() -endif() -if(WABT_DIR) - message(CHECK_PASS "found") -else() - message(CHECK_FAIL "not found") -endif() - -message(CHECK_START "Detecting WASM_OBJDUMP at ${WABT_DIR}") -find_program(WASM_OBJDUMP - wasm-objdump - PATHS "${WABT_DIR}/bin" - NO_DEFAULT_PATH - NO_CMAKE_FIND_ROOT_PATH -) -if(WASM_OBJDUMP) - message(CHECK_PASS "found") -else() - message(CHECK_FAIL "not found") -endif() -if((NOT EXISTS ${WASM_OBJDUMP}) ) - message(FATAL_ERROR "Please make sure to have wasm-objdump under the path=${WABT_DIR}/bin ") -endif() - -set(WAMR_COMPILER_DIR ${CMAKE_CURRENT_LIST_DIR}/../../wamr-compiler/build) -message(CHECK_START "Detecting WAMR_COMPILER at ${WAMR_COMPILER_DIR}") -find_file(WAMR_COMPILER - wamrc - PATHS "${CMAKE_CURRENT_LIST_DIR}/../../../wamr-compiler/build" - NO_DEFAULT_PATH - NO_CMAKE_FIND_ROOT_PATH -) -if(WAMR_COMPILER) - message(CHECK_PASS "found") -else() - message(CHECK_FAIL "not found") -endif() -if((NOT EXISTS ${WAMR_COMPILER}) ) - message(FATAL_ERROR "Please build wamrc under the path=${WAMR_ROOT_DIR}/wamr-compiler/") -endif() - ################ wasm and aot compilation ################ function (compile_sample SOURCE_FILE) get_filename_component (FILE_NAME ${SOURCE_FILE} NAME_WLE) - set (WASM_MODULE ${FILE_NAME}.wasm) - add_executable (${WASM_MODULE} ${SOURCE_FILE}) - add_custom_target( - wasm_to_aot + ## wasm + set (WASM_FILE ${FILE_NAME}.wasm) + add_executable (${FILE_NAME} ${SOURCE_FILE}) + set_target_properties (${FILE_NAME} PROPERTIES SUFFIX .wasm) + + ## aot + set (AOT_FILE ${FILE_NAME}.aot) + add_custom_target ( + ${FILE_NAME}_aot ALL - DEPENDS ${WAMR_COMPILER} ${WASM_MODULE} + DEPENDS ${WAMRC_BIN} ${WASM_FILE} # Use --enable-dump-call-stack to generate stack trace (addr2line) - COMMAND ${WAMR_COMPILER} --size-level=0 --enable-dump-call-stack -o wasm-apps/trap.aot wasm-apps/trap.wasm - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND ${WAMRC_BIN} --size-level=0 --enable-dump-call-stack -o ${AOT_FILE} ${WASM_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) + + ## wasm + sourcemap + if (DEFINED EMSCRIPTEN) + add_custom_target( + ${FILE_NAME}_w_sourcemap + ALL + DEPENDS ${SOURCE_FILE} + COMMAND ${EMCC} -O0 -gsource-map -o ${FILE_NAME}.sourcemap.wasm ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + endif () + + ## install both + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${WASM_FILE} DESTINATION wasm-apps) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${AOT_FILE} DESTINATION wasm-apps) + if (DEFINED EMSCRIPTEN) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.sourcemap.wasm DESTINATION wasm-apps) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.sourcemap.wasm.map DESTINATION wasm-apps) + endif () endfunction () -set(CMAKE_BUILD_TYPE Debug) # Otherwise no debug symbols (addr2line) -compile_sample(trap.c) \ No newline at end of file +compile_sample(trap.c) diff --git a/samples/linux-perf/CMakeLists.txt b/samples/linux-perf/CMakeLists.txt new file mode 100644 index 000000000..e9882c835 --- /dev/null +++ b/samples/linux-perf/CMakeLists.txt @@ -0,0 +1,63 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project(linux_perf_sample) + +if(NOT CMAKE_HOST_LINUX) + message(FATAL_ERROR "This sample only works on linux") +endif() + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +set(CMAKE_CXX_STANDARD 17) + +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) +find_package(WASISDK REQUIRED) + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +include(CheckPIESupported) + +# AOT and JIT byd default +set(WAMR_BUILD_AOT 1) +set(WAMR_BUILD_INTERP 0) +set(WAMR_BUILD_JIT 1) +# wasm32-wasi +set(WAMR_BUILD_LIBC_BUILTIN 0) +set(WAMR_BUILD_LIBC_WASI 1) +# mvp +set(WAMR_BUILD_BULK_MEMORY 1) +set(WAMR_BUILD_REF_TYPES 1) +set(WAMR_BUILD_SIMD 1) +set(WAMR_BUILD_TAIL_CALL 1) +# trap information +set(WAMR_BUILD_DUMP_CALL_STACK 1) +# linux perf +set(WAMR_BUILD_LINUX_PERF 1) +# +#set(WAMR_BUILD_THREAD_MGR 0) + +# vmlib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include(${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib SHARED ${WAMR_RUNTIME_LIB_SOURCE}) +target_include_directories(vmlib INTERFACE ${WAMR_ROOT_DIR}/core/iwasm/include) +target_link_libraries (vmlib ${LLVM_AVAILABLE_LIBS} -lm -ldl) + +################ host ################ +add_executable(${PROJECT_NAME} host/demo.c) +target_link_libraries(${PROJECT_NAME} vmlib) + +################ aot + wasm ################ +include(ExternalProject) +ExternalProject_Add(wasm + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wasm" + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/wasm -B build + -DCMAKE_TOOLCHAIN_FILE=${WASISDK_TOOLCHAIN} + BUILD_COMMAND ${CMAKE_COMMAND} --build build + INSTALL_COMMAND ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR} +) \ No newline at end of file diff --git a/samples/linux-perf/README.md b/samples/linux-perf/README.md new file mode 100644 index 000000000..5a8dc578f --- /dev/null +++ b/samples/linux-perf/README.md @@ -0,0 +1,90 @@ +# linux perf sample introduction + +This is a sample to show how to use the Linux perf tool to profile the execution of a WebAssembly application. And how to use the [Flamegraph](https://www.brendangregg.com/flamegraphs.html) tool to visualize the profiling result. + +## Build and run the sample + +There are two Wasm modules and their instance will be created and run in the sample. [The first module](./wasm/fib.c) is a simple Wasm module that calculates the Fibonacci number. [The second module](./wasm/ackermann.c) is a simple Wasm module that execute the Ackermann function. The target is enable to profile the execution of both two modules separately. + +```bash +$ cmake -S . -B build +$ cmake --build build +``` + +### Profile the execution + +```bash +$ cd build +$ perf record -k mono -g --output=perf.data -- ./linux_perf_sample +``` + +Enable to use `perf report --stdio` to do a quick analysis of the profiling result. + +### Visualize the profiling result + +Need to download Flamegraph tool from [Flamegraph](https://github.com/brendangregg/FlameGraph/releases/tag/v1.0) firstly. + +```bash +$ perf script > out.perf +$ ./FlameGraph/stackcollapse-perf.pl out.perf > out.folded +$ ./FlameGraph/flamegraph.pl out.folded > perf.svg +``` + +In this result, you'll see two modules's profiling result and all wasm functions are named as "aot_func#N" which is a little hard to distinguish. + +![perf.png](./pics/perf.png) + +### Separate profiling result + +[process_folded_data.py](../../test-tools/flame-graph-helper/process_folded_data.py) is a script can a) translate "aot_func#N" into its original function name in name sections, b) separate the profiling result of different modules. + +In this sample, we want to separate `fib` and `ackermann` profiling data from _out.folded_. In [demo](host/demo.c), we decide to name the module of `fib1.wasm` as `fib2` and the module of `ackermann1.wasm` as `ackermann2`. + +```bash +$ python process_folded_data.py --wabt_home /opt/wabt --wasm_names fib2=./fib1.wasm,ackermann2=./ackermann1.wasm out.folded +-> write into out.fib2.translated +-> write into out.ackermann2.translated +-> write into out.translated +``` + +More scenarios: + +if only using one wasm during profiling, the script can be used like this: + +```bash +$ python process_folded_data.py --wabt_home /opt/wabt --wasm --folded +``` + +if only using one wasm during profiling and specify the module name via APIs, the script can be used like this: + +```bash +$ python process_folded_data.py --wabt_home /opt/wabt --wasm_names = --folded +``` + +if only using one wasm during profiling and specify the module name, which is same with the basename of wasm file, via APIs, the script can be used like this: + +```bash +$ python process_folded_data.py --wabt_home /opt/wabt --wasm --folded +``` + +if using multiple wasm during profiling and specify module names, which are same with basename of wasm files, via APIs, the script can be used like this: + +```bash +$ python process_folded_data.py --wabt_home /opt/wabt --wasm --wasm --wasm --folded +``` + +if using multiple wasm during profiling and specify module names via APIs, the script can be used like this: + +```bash +$ python process_folded_data.py --wabt_home /opt/wabt --wasm_names =,=,= --folded +``` + +Now we have two flame-graphs for two wasm modules: + +![fib.svg](./pics/perf.fib.svg) + +![ackermann.svg](./pics/perf.ackermann.svg) + +## Reference + +- [perf_tune](../../doc/perf_tune.md) diff --git a/samples/linux-perf/cmake/FindWAMRC.cmake b/samples/linux-perf/cmake/FindWAMRC.cmake new file mode 100644 index 000000000..586263edd --- /dev/null +++ b/samples/linux-perf/cmake/FindWAMRC.cmake @@ -0,0 +1,14 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include(FindPackageHandleStandardArgs) + +find_file(WAMRC_BIN + NAMES wamrc + DOC "search wamrc" + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/../../../wamr-compiler/build + REQUIRED +) + +find_package_handle_standard_args(WAMRC REQUIRED_VARS WAMRC_BIN) +mark_as_advanced(WAMRC_BIN) \ No newline at end of file diff --git a/samples/linux-perf/cmake/FindWASISDK.cmake b/samples/linux-perf/cmake/FindWASISDK.cmake new file mode 100644 index 000000000..5cdfea41e --- /dev/null +++ b/samples/linux-perf/cmake/FindWASISDK.cmake @@ -0,0 +1,23 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include(FindPackageHandleStandardArgs) + +file(GLOB WASISDK_SEARCH_PATH "/opt/wasi-sdk-*") +find_path(WASISDK_HOME + NAMES share/wasi-sysroot + PATHS ${WASISDK_SEARCH_PATH} + NO_DEFAULT_PATH + REQUIRED +) + +string(REGEX MATCH [0-9]+\.[0-9]+\.*[0-9]* WASISDK_VERSION ${WASISDK_HOME}) + +find_package_handle_standard_args(WASISDK REQUIRED_VARS WASISDK_HOME VERSION_VAR WASISDK_VERSION) + +if(WASISDK_FOUND) + set(WASISDK_CC_COMMAND ${WASISDK_HOME}/bin/clang) + set(WASISDK_CXX_COMMAND ${WASISDK_HOME}/bin/clang++) + set(WASISDK_TOOLCHAIN ${WASISDK_HOME}/share/cmake/wasi-sdk.cmake) + set(WASISDK_SYSROOT ${WASISDK_HOME}/share/wasi-sysroot) +endif() diff --git a/samples/linux-perf/host/demo.c b/samples/linux-perf/host/demo.c new file mode 100644 index 000000000..8ea446ed4 --- /dev/null +++ b/samples/linux-perf/host/demo.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + +/* return a copy of the file stem of a file path */ +static own char * +stem(const char *file_path) +{ + char *base_name = basename(file_path); + char *s = strdup(base_name); + char *dot = strchr(s, '.'); + assert(dot); + *dot = '\0'; + return s; +} + +static void +guest_i32_to_wasm_i32_array(int *args, unsigned argc, wasm_val_t *data, + unsigned datac) +{ + for (unsigned i = 0; i < argc && i < datac; i++) { + memset(&data[i], 0, sizeof(wasm_val_t)); + data[i].kind = WASM_I32; + data[i].of.i32 = args[i]; + } +} + +int +load_run_wasm_file(wasm_engine_t *engine, const char *file_path, int *args, + unsigned argc) +{ + wasm_store_t *store = wasm_store_new(engine); + // Load binary. + printf("Loading binary...\n"); + FILE *file = fopen(file_path, "rb"); + assert(file); + + int ret = fseek(file, 0L, SEEK_END); + assert(ret == 0); + + long file_size = ftell(file); + assert(file_size != -1); + + ret = fseek(file, 0L, SEEK_SET); + assert(ret == 0); + + wasm_byte_vec_t binary = { 0 }; + wasm_byte_vec_new_uninitialized(&binary, file_size); + + size_t nread = fread(binary.data, file_size, 1, file); + fclose(file); + + // Compile. + printf("Compiling module...\n"); + + // Use its file name as the module name + char *file_name = stem(file_path); + assert(file_name); + + LoadArgs load_args = { 0 }; + load_args.name = file_name; + own wasm_module_t *module = wasm_module_new_ex(store, &binary, &load_args); + wasm_byte_vec_delete(&binary); + assert(module); + + // Use export type to find the function index to call later + wasm_exporttype_vec_t export_types = { 0 }; + wasm_module_exports(module, &export_types); + int func_to_call = -1; + for (unsigned i = 0; i < export_types.num_elems; i++) { + const wasm_name_t *name = wasm_exporttype_name(export_types.data[i]); + if (strncmp(name->data, "run", 3) == 0) { + func_to_call = i; + break; + } + } + assert(func_to_call != -1); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_vec_t imports = WASM_EMPTY_VEC; + own wasm_instance_t *instance = wasm_instance_new_with_args( + store, module, &imports, NULL, 16 * 1024 * 1024, 1 * 1024 * 1024); + assert(instance); + + // Extract export. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + assert(exports.size); + + assert(wasm_extern_kind(exports.data[func_to_call]) == WASM_EXTERN_FUNC); + const wasm_func_t *run_func = + wasm_extern_as_func(exports.data[func_to_call]); + assert(run_func); + + wasm_module_delete(module); + wasm_instance_delete(instance); + + // Call. + printf("Calling export...\n"); + wasm_val_t as[4] = { 0 }; + guest_i32_to_wasm_i32_array(args, argc, as, 4); + + wasm_val_vec_t params = WASM_ARRAY_VEC(as); + wasm_val_t rs[1] = { WASM_I32_VAL(0) }; + wasm_val_vec_t results = WASM_ARRAY_VEC(rs); + wasm_trap_t *trap = wasm_func_call(run_func, ¶ms, &results); + assert(!trap); + + wasm_extern_vec_delete(&exports); + free(file_name); + wasm_store_delete(store); + + { + nread = nread; + ret = ret; + trap = trap; + } + return 0; +} + +void * +load_run_fib_wasm(void *arg) +{ + wasm_engine_t *engine = (wasm_engine_t *)arg; + int args[] = { 40 }; + load_run_wasm_file(engine, "./fib1.wasm", args, 1); + return NULL; +} + +void * +load_run_fib_aot(void *arg) +{ + wasm_engine_t *engine = (wasm_engine_t *)arg; + int args[] = { 40 }; + load_run_wasm_file(engine, "./fib2.aot", args, 1); + return NULL; +} + +void * +load_run_ackermann_wasm(void *arg) +{ + wasm_engine_t *engine = (wasm_engine_t *)arg; + int args[] = { 3, 12 }; + load_run_wasm_file(engine, "./ackermann1.wasm", args, 2); + return NULL; +} + +void * +load_run_ackermann_aot(void *arg) +{ + wasm_engine_t *engine = (wasm_engine_t *)arg; + int args[] = { 3, 12 }; + load_run_wasm_file(engine, "./ackermann2.aot", args, 2); + return NULL; +} + +int +main(int argc, const char *argv[]) +{ + // Initialize. + printf("Initializing...\n"); + wasm_config_t *config = wasm_config_new(); + wasm_config_set_linux_perf_opt(config, true); + wasm_engine_t *engine = wasm_engine_new_with_config(config); + + pthread_t tid[4] = { 0 }; + /* FIXME: uncomment when it is able to run two modules with llvm-jit */ + // pthread_create(&tid[0], NULL, load_run_fib_wasm, (void *)engine); + // pthread_create(&tid[2], NULL, load_run_ackermann_wasm, (void *)engine); + + pthread_create(&tid[1], NULL, load_run_fib_aot, (void *)engine); + pthread_create(&tid[3], NULL, load_run_ackermann_aot, (void *)engine); + + for (unsigned i = 0; i < sizeof(tid) / sizeof(tid[0]); i++) + pthread_join(tid[i], NULL); + + // Shut down. + printf("Shutting down...\n"); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/samples/linux-perf/pics/perf.ackermann.svg b/samples/linux-perf/pics/perf.ackermann.svg new file mode 100644 index 000000000..c2e1d8747 --- /dev/null +++ b/samples/linux-perf/pics/perf.ackermann.svg @@ -0,0 +1,1349 @@ + + + + + + + + + + + + + + +Flame Graph + +Reset Zoom +Search +ic + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +load_run_wasm_file (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +invoke_ii_i (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +load_run_ackermann_aot (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] run (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +wasm_runtime_call_wasm (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +all (11,485,868,643 samples, 100%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +aot_call_function (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +linux_perf_samp (11,485,868,643 samples, 100.00%) +linux_perf_samp + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +wasm_func_call (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +invoke_native_with_hw_bound_check (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +start_thread (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + + diff --git a/samples/linux-perf/pics/perf.fib.svg b/samples/linux-perf/pics/perf.fib.svg new file mode 100644 index 000000000..a1db059a7 --- /dev/null +++ b/samples/linux-perf/pics/perf.fib.svg @@ -0,0 +1,605 @@ + + + + + + + + + + + + + + +Flame Graph + +Reset Zoom +Search +ic + + + +[Wasm] [fib2] fibonacci (1,321,095,222 samples, 93.80%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (382,407,564 samples, 27.15%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (15,340,273 samples, 1.09%) + + + +[Wasm] [fib2] fibonacci (1,359,552,763 samples, 96.53%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,408,481,525 samples, 100.00%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (27,274,310 samples, 1.94%) +[.. + + +[Wasm] [fib2] fibonacci (62,450,767 samples, 4.43%) +[Wasm.. + + +[Wasm] [fib2] fibonacci (1,408,481,525 samples, 100.00%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,388,674,508 samples, 98.59%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,170,751,868 samples, 83.12%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,408,481,525 samples, 100.00%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (120,820,158 samples, 8.58%) +[Wasm] [fib2.. + + +invoke_i_i (1,408,481,525 samples, 100.00%) +invoke_i_i + + +[Wasm] [fib2] fibonacci (1,375,872,224 samples, 97.68%) +[Wasm] [fib2] fibonacci + + +load_run_wasm_file (1,408,481,525 samples, 100.00%) +load_run_wasm_file + + +load_run_fib_aot (1,408,481,525 samples, 100.00%) +load_run_fib_aot + + +[Wasm] [fib2] run (1,408,481,525 samples, 100.00%) +[Wasm] [fib2] run + + +[Wasm] [fib2] fibonacci (42,420,273 samples, 3.01%) +[Wa.. + + +[Wasm] [fib2] fibonacci (1,266,323,684 samples, 89.91%) +[Wasm] [fib2] fibonacci + + +linux_perf_samp (1,408,481,525 samples, 100.00%) +linux_perf_samp + + +[Wasm] [fib2] fibonacci (280,259,464 samples, 19.90%) +[Wasm] [fib2] fibonacci + + +start_thread (1,408,481,525 samples, 100.00%) +start_thread + + +[Wasm] [fib2] fibonacci (2,334,521 samples, 0.17%) + + + +[Wasm] [fib2] fibonacci (666,394,609 samples, 47.31%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (943,121,736 samples, 66.96%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,169,581 samples, 0.08%) + + + +[Wasm] [fib2] fibonacci (1,169,581 samples, 0.08%) + + + +[Wasm] [fib2] fibonacci (194,755,877 samples, 13.83%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,406,148,966 samples, 99.83%) +[Wasm] [fib2] fibonacci + + +all (1,408,481,525 samples, 100%) + + + +wasm_func_call (1,408,481,525 samples, 100.00%) +wasm_func_call + + +[Wasm] [fib2] fibonacci (5,943,602 samples, 0.42%) + + + +[Wasm] [fib2] fibonacci (1,408,481,525 samples, 100.00%) +[Wasm] [fib2] fibonacci + + +wasm_runtime_call_wasm (1,408,481,525 samples, 100.00%) +wasm_runtime_call_wasm + + +[Wasm] [fib2] fibonacci (1,401,486,191 samples, 99.50%) +[Wasm] [fib2] fibonacci + + +aot_call_function (1,408,481,525 samples, 100.00%) +aot_call_function + + +[Wasm] [fib2] fibonacci (531,941,563 samples, 37.77%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,406,148,966 samples, 99.83%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,061,055,435 samples, 75.33%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,408,481,525 samples, 100.00%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,403,816,880 samples, 99.67%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (800,646,766 samples, 56.84%) +[Wasm] [fib2] fibonacci + + +invoke_native_with_hw_bound_check (1,408,481,525 samples, 100.00%) +invoke_native_with_hw_bound_check + + + diff --git a/samples/linux-perf/pics/perf.png b/samples/linux-perf/pics/perf.png new file mode 100755 index 000000000..fc4c0930f Binary files /dev/null and b/samples/linux-perf/pics/perf.png differ diff --git a/samples/linux-perf/wasm/CMakeLists.txt b/samples/linux-perf/wasm/CMakeLists.txt new file mode 100644 index 000000000..8d36e28df --- /dev/null +++ b/samples/linux-perf/wasm/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project(linux_perf_sample_wasm) + +include(CMakePrintHelpers) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../cmake) +find_package(WAMRC REQUIRED) + +################ wasm ################ +add_executable(fib_wasm fib.c) +set_target_properties(fib_wasm PROPERTIES SUFFIX .wasm) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fib_wasm.wasm DESTINATION . RENAME fib1.wasm) + +add_executable(ackermann_wasm ackermann.c) +set_target_properties(ackermann_wasm PROPERTIES SUFFIX .wasm) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ackermann_wasm.wasm DESTINATION . RENAME ackermann1.wasm) + + +################ aot ################ +add_custom_target(fib_aot + ALL + COMMAND ${WAMRC_BIN} --enable-linux-perf -o fib2.aot fib_wasm.wasm + DEPENDS fib_wasm + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fib2.aot DESTINATION .) + +add_custom_target(ackermann_aot + ALL + COMMAND ${WAMRC_BIN} --enable-linux-perf -o ackermann2.aot ackermann_wasm.wasm + DEPENDS ackermann_wasm + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ackermann2.aot DESTINATION .) diff --git a/samples/linux-perf/wasm/ackermann.c b/samples/linux-perf/wasm/ackermann.c new file mode 100644 index 000000000..d6eb4346f --- /dev/null +++ b/samples/linux-perf/wasm/ackermann.c @@ -0,0 +1,38 @@ +#include + +// Ackermann function +unsigned long +ackermann(unsigned long m, unsigned long n) +{ + if (m == 0) { + return n + 1; + } + else if (n == 0) { + return ackermann(m - 1, 1); + } + else { + return ackermann(m - 1, ackermann(m, n - 1)); + } +} + +__attribute__((export_name("run"))) int +run(int m, int n) +{ + int result = ackermann(m, n); + printf("ackermann(%d, %d)=%d\n", m, n, result); + return result; +} + +int +main() +{ + unsigned long m, n, result; + + // Example usage: + m = 3; + n = 2; + result = ackermann(m, n); + printf("Ackermann(%lu, %lu) = %lu\n", m, n, result); + + return 0; +} diff --git a/samples/linux-perf/wasm/fib.c b/samples/linux-perf/wasm/fib.c new file mode 100644 index 000000000..cb928f65d --- /dev/null +++ b/samples/linux-perf/wasm/fib.c @@ -0,0 +1,32 @@ +#include +#include + +int +fibonacci(int n) +{ + if (n <= 0) + return 0; + + if (n == 1) + return 1; + + return fibonacci(n - 1) + fibonacci(n - 2); +} + +__attribute__((export_name("run"))) int +run(int n) +{ + int result = fibonacci(n); + printf("fibonacci(%d)=%d\n", n, result); + return result; +} + +int +main(int argc, char **argv) +{ + int n = atoi(argv[1]); + + printf("fibonacci(%d)=%d\n", n, fibonacci(n)); + + return 0; +} diff --git a/samples/native-stack-overflow/.gitignore b/samples/native-stack-overflow/.gitignore new file mode 100644 index 000000000..0fa8a76bd --- /dev/null +++ b/samples/native-stack-overflow/.gitignore @@ -0,0 +1 @@ +/out/ \ No newline at end of file diff --git a/samples/native-stack-overflow/CMakeLists.txt b/samples/native-stack-overflow/CMakeLists.txt new file mode 100644 index 000000000..efe5b5dc0 --- /dev/null +++ b/samples/native-stack-overflow/CMakeLists.txt @@ -0,0 +1,99 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) + +if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows") + project (native-stack-overflow) +else() + project (native-stack-overflow C ASM) +endif() + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_EXTENSIONS YES) + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# WAMR features switch + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", +# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)") + set (WAMR_BUILD_TARGET "AARCH64") + elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") + set (WAMR_BUILD_TARGET "RISCV64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + else () + message(SEND_ERROR "Unsupported build target platform!") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set (CMAKE_BUILD_TYPE Release) +endif () + +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_AOT 1) +set (WAMR_BUILD_JIT 0) +set (WAMR_BUILD_LIBC_BUILTIN 0) +set (WAMR_BUILD_LIBC_WASI 1) + +if (NOT MSVC) + # linker flags + if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") + endif () + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") + if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register") + endif () + endif () +endif () + +if (CMAKE_C_COMPILER_ID MATCHES "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER 13.0.0) +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-usage") +endif () + +# GCC doesn't have disable_tail_calls attribute +if (CMAKE_C_COMPILER_ID MATCHES "GNU") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-optimize-sibling-calls") +endif () + +# build out vmlib +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +################ application related ################ +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (native-stack-overflow src/main.c src/native_impl.c ${UNCOMMON_SHARED_SOURCE}) + +check_pie_supported() +set_target_properties (native-stack-overflow PROPERTIES POSITION_INDEPENDENT_CODE ON) + +if (APPLE) + target_link_libraries (native-stack-overflow vmlib -lm -ldl -lpthread) +else () + target_link_libraries (native-stack-overflow vmlib -lm -ldl -lpthread -lrt) +endif () diff --git a/samples/native-stack-overflow/README.md b/samples/native-stack-overflow/README.md new file mode 100644 index 000000000..431c5876e --- /dev/null +++ b/samples/native-stack-overflow/README.md @@ -0,0 +1,4 @@ +The "native-stack-overflow" sample project +========================================== + +This sample examines native stack overflow detection mechanisms. diff --git a/samples/native-stack-overflow/build.sh b/samples/native-stack-overflow/build.sh new file mode 100755 index 000000000..ac9f410f4 --- /dev/null +++ b/samples/native-stack-overflow/build.sh @@ -0,0 +1,75 @@ +#! /bin/sh + +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +CURR_DIR=$PWD +WAMR_DIR=${PWD}/../.. +OUT_DIR=${PWD}/out + +WASM_APPS=${PWD}/wasm-apps + + +rm -rf ${OUT_DIR} +mkdir ${OUT_DIR} +mkdir ${OUT_DIR}/wasm-apps + + +echo "##################### build (default)" +cd ${CURR_DIR} +mkdir -p cmake_build +cd cmake_build +cmake .. +make -j 4 +if [ $? != 0 ];then + echo "BUILD_FAIL native-stack-overflow exit as $?\n" + exit 2 +fi +cp -a native-stack-overflow ${OUT_DIR} + +echo "##################### build (WAMR_DISABLE_HW_BOUND_CHECK=1)" +cd ${CURR_DIR} +mkdir -p cmake_build_disable_hw_bound +cd cmake_build_disable_hw_bound +cmake -D WAMR_DISABLE_HW_BOUND_CHECK=1 .. +make -j 4 +if [ $? != 0 ];then + echo "BUILD_FAIL native-stack-overflow exit as $?\n" + exit 2 +fi +cp -a native-stack-overflow ${OUT_DIR}/native-stack-overflow.WAMR_DISABLE_HW_BOUND_CHECK + +echo + +echo "##################### build wasm apps" + +cd ${WASM_APPS} + +for i in `ls *.c` +do +APP_SRC="$i" +OUT_FILE=${i%.*}.wasm + +# use WAMR SDK to build out the .wasm binary +/opt/wasi-sdk/bin/clang \ + -mexec-model=reactor \ + -Os -z stack-size=4096 -Wl,--initial-memory=65536 \ + -Wl,--allow-undefined \ + -o ${OUT_DIR}/wasm-apps/${OUT_FILE} ${APP_SRC} + +if [ -f ${OUT_DIR}/wasm-apps/${OUT_FILE} ]; then + echo "build ${OUT_FILE} success" +else + echo "build ${OUT_FILE} fail" +fi +done +echo "#################### build wasm apps done" + +echo "#################### aot-compile" +WAMRC=${WAMR_DIR}/wamr-compiler/build/wamrc +${WAMRC} -o ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot --size-level=0 ${OUT_DIR}/wasm-apps/${OUT_FILE} + +echo "#################### aot-compile (--bounds-checks=1)" +${WAMRC} -o ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot.bounds-checks --size-level=0 --bounds-checks=1 ${OUT_DIR}/wasm-apps/${OUT_FILE} diff --git a/samples/native-stack-overflow/clean.sh b/samples/native-stack-overflow/clean.sh new file mode 100755 index 000000000..54c0325bb --- /dev/null +++ b/samples/native-stack-overflow/clean.sh @@ -0,0 +1 @@ +rm -rf cmake_build cmake_build_disable_hw_bound out diff --git a/samples/native-stack-overflow/run.sh b/samples/native-stack-overflow/run.sh new file mode 100755 index 000000000..3386ea1ef --- /dev/null +++ b/samples/native-stack-overflow/run.sh @@ -0,0 +1,20 @@ +#! /bin/sh + +set -e + +NAME=${1:-test1} + +echo "====== Interpreter ${NAME}" +out/native-stack-overflow out/wasm-apps/testapp.wasm ${NAME} + +echo +echo "====== Interpreter WAMR_DISABLE_HW_BOUND_CHECK=1 ${NAME}" +out/native-stack-overflow.WAMR_DISABLE_HW_BOUND_CHECK out/wasm-apps/testapp.wasm ${NAME} + +echo +echo "====== AOT ${NAME}" +out/native-stack-overflow out/wasm-apps/testapp.wasm.aot ${NAME} + +echo +echo "====== AOT WAMR_DISABLE_HW_BOUND_CHECK=1 ${NAME}" +out/native-stack-overflow.WAMR_DISABLE_HW_BOUND_CHECK out/wasm-apps/testapp.wasm.aot.bounds-checks ${NAME} diff --git a/samples/native-stack-overflow/src/main.c b/samples/native-stack-overflow/src/main.c new file mode 100644 index 000000000..8d42cc0aa --- /dev/null +++ b/samples/native-stack-overflow/src/main.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2024 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "bh_read_file.h" + +uint32_t +host_consume_stack_and_call_indirect(wasm_exec_env_t exec_env, uint32_t funcidx, + uint32_t x, uint32_t stack); +uint32_t +host_consume_stack(wasm_exec_env_t exec_env, uint32_t stack); + +extern unsigned int nest; + +static NativeSymbol native_symbols[] = { + { "host_consume_stack_and_call_indirect", + host_consume_stack_and_call_indirect, "(iii)i", NULL }, + { "host_consume_stack", host_consume_stack, "(i)i", NULL }, +}; + +void * +canary_addr() +{ + uint8_t *p = os_thread_get_stack_boundary(); +#if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + uint32_t page_size = os_getpagesize(); + uint32_t guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; + return p + page_size * guard_page_count; +#else + return p; +#endif +} + +void +canary_init(void) +{ + uint32_t *canary = canary_addr(); + *canary = 0xaabbccdd; +} + +bool +canary_check(void) +{ + /* assume an overflow if the first uint32_t on the stack was modified */ + const uint32_t *canary = (void *)canary_addr(); + return *canary == 0xaabbccdd; +} + +struct record { + bool failed; + bool leaked; + char exception[128]; /* EXCEPTION_BUF_LEN */ +}; + +void +print_record(unsigned int start, unsigned int end, const struct record *rec) +{ + printf("%5u - %5u | %6s | %6s | %s\n", start, end, + rec->failed ? "failed" : "ok", rec->leaked ? "leaked" : "ok", + rec->exception); +} + +int +main(int argc, char **argv) +{ + char *buffer; + char error_buf[128]; + + if (argc != 3) { + return 2; + } + const char *module_path = argv[1]; + const char *funcname = argv[2]; + + wasm_module_t module = NULL; + uint32 buf_size; + uint32 stack_size = 4096; + /* + * disable app heap. + * - we use wasi + * - https://github.com/bytecodealliance/wasm-micro-runtime/issues/2275 + */ + uint32 heap_size = 0; + + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + init_args.mem_alloc_type = Alloc_With_System_Allocator; + init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); + init_args.native_module_name = "env"; + init_args.native_symbols = native_symbols; + if (!wasm_runtime_full_init(&init_args)) { + printf("wasm_runtime_full_init failed.\n"); + return -1; + } + + buffer = bh_read_file_to_buffer(module_path, &buf_size); + if (!buffer) { + printf("bh_read_file_to_buffer failed\n"); + goto fail; + } + + module = wasm_runtime_load((uint8 *)buffer, buf_size, error_buf, + sizeof(error_buf)); + if (!module) { + printf("wasm_runtime_load failed: %s\n", error_buf); + goto fail; + } + + /* header */ + printf(" stack size | fail? | leak? | exception\n"); + printf("-------------------------------------------------------------------" + "--------\n"); + + uint32_t page_size = os_getpagesize(); + unsigned int stack; + unsigned int prevstack = 0; /* appease GCC -Wmaybe-uninitialized */ + unsigned int stack_range_start = 0; + unsigned int stack_range_end = page_size * 6; + unsigned int step = 16; + struct record rec0; + struct record rec1; + struct record *rec = &rec0; + struct record *prevrec = &rec1; + bool have_prevrec = false; + for (stack = stack_range_start; stack < stack_range_end; stack += step) { + wasm_module_inst_t module_inst = NULL; + wasm_exec_env_t exec_env = NULL; + bool failed = true; + const char *exception = NULL; + nest = 0; + + canary_init(); + module_inst = wasm_runtime_instantiate(module, stack_size, heap_size, + error_buf, sizeof(error_buf)); + if (!module_inst) { + printf("wasm_runtime_instantiate failed: %s\n", error_buf); + goto fail2; + } + + exec_env = wasm_runtime_create_exec_env(module_inst, stack_size); + if (!exec_env) { + printf("wasm_runtime_create_exec_env failed\n"); + goto fail2; + } + + wasm_function_inst_t func = + wasm_runtime_lookup_function(module_inst, funcname); + if (!func) { + printf("wasm_runtime_lookup_function failed for %s\n", funcname); + goto fail2; + } + + /* note: the function type is (ii)i */ + uint32_t wasm_argv[] = { + stack, /* native_stack */ + 30, /* recurse_count */ + }; + uint32_t wasm_argc = 2; + if (!wasm_runtime_call_wasm(exec_env, func, wasm_argc, wasm_argv)) { + exception = wasm_runtime_get_exception(module_inst); + goto fail2; + } + failed = false; + fail2: + if (!canary_check()) { + printf("stack overurn detected for stack=%u\n", stack); + abort(); + } + + /* + * note: non-zero "nest" here demonstrates resource leak on longjmp + * from signal handler. + * cf. + * https://github.com/bytecodealliance/wasm-micro-runtime/issues/3320 + */ + memset(rec, 0, sizeof(*rec)); + rec->failed = failed; + rec->leaked = nest != 0; + strncpy(rec->exception, exception ? exception : "", + sizeof(rec->exception)); + if (have_prevrec && memcmp(prevrec, rec, sizeof(*rec))) { + print_record(prevstack, stack, prevrec); + have_prevrec = false; + } + if (!have_prevrec) { + prevstack = stack; + struct record *tmp = prevrec; + prevrec = rec; + rec = tmp; + have_prevrec = true; + } + if (exec_env) { + wasm_runtime_destroy_exec_env(exec_env); + } + if (module_inst) { + wasm_runtime_deinstantiate(module_inst); + } + } + if (have_prevrec) { + print_record(prevstack, stack, prevrec); + } + +fail: + if (module) { + wasm_runtime_unload(module); + } + if (buffer) { + BH_FREE(buffer); + } + wasm_runtime_destroy(); +} diff --git a/samples/native-stack-overflow/src/native_impl.c b/samples/native-stack-overflow/src/native_impl.c new file mode 100644 index 000000000..7037e1029 --- /dev/null +++ b/samples/native-stack-overflow/src/native_impl.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#define __STDC_WANT_LIB_EXT1__ 1 + +#include +#include +#include + +#include "wasm_export.h" +#include "bh_platform.h" + +/* + * this "nest" var has two purposes: + * - prevent tail-call optimization + * - detect possible resource leak + */ +unsigned int nest = 0; +ptrdiff_t prev_diff = 0; + +uint32_t +call_indirect(wasm_exec_env_t exec_env, uint32_t funcidx, uint32_t x) +{ + uint32_t argv[1] = { + x, + }; + uint32_t argc = 1; + if (!wasm_runtime_call_indirect(exec_env, funcidx, argc, argv)) { + /* failed */ + return 0; + } + return argv[0]; +} + +uint32_t +host_consume_stack_and_call_indirect(wasm_exec_env_t exec_env, uint32_t funcidx, + uint32_t x, uint32_t stack) +{ + void *boundary = os_thread_get_stack_boundary(); + void *fp = __builtin_frame_address(0); + ptrdiff_t diff = fp - boundary; + /* + * because this function performs recursive calls depending on + * the user input, we don't have an apriori knowledge how much stack + * we need. perform the overflow check on each iteration. + */ + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { + return 0; + } + if (diff > stack) { + prev_diff = diff; + nest++; + uint32_t ret = + host_consume_stack_and_call_indirect(exec_env, funcidx, x, stack); + nest--; + return ret; + } + return call_indirect(exec_env, funcidx, x); +} + +__attribute__((noinline)) static uint32_t +consume_stack1(wasm_exec_env_t exec_env, void *base, uint32_t stack) +#if defined(__clang__) + __attribute__((disable_tail_calls)) +#endif +{ + void *fp = __builtin_frame_address(0); + ptrdiff_t diff = (unsigned char *)base - (unsigned char *)fp; + assert(diff > 0); + if (diff > stack) { + return diff; + } + return consume_stack1(exec_env, base, stack); +} + +uint32_t +host_consume_stack(wasm_exec_env_t exec_env, uint32_t stack) +{ + /* + * this function consumes a bit more than "stack" bytes. + */ + if (!wasm_runtime_detect_native_stack_overflow_size(exec_env, 64 + stack)) { + return 0; + } + void *base = __builtin_frame_address(0); + return consume_stack1(exec_env, base, stack); +} diff --git a/samples/native-stack-overflow/wasm-apps/testapp.c b/samples/native-stack-overflow/wasm-apps/testapp.c new file mode 100644 index 000000000..23ea32efc --- /dev/null +++ b/samples/native-stack-overflow/wasm-apps/testapp.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2024 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +uint32_t +host_consume_stack_and_call_indirect(int (*)(int), uint32_t, uint32_t); +uint32_t host_consume_stack(uint32_t); + +int +cb(int x) +{ + return x * x; +} + +int +consume_stack_cb(int x) __attribute__((disable_tail_calls)) +{ + /* + * intentions: + * + * - consume native stack by making recursive calls + * + * - avoid tail-call optimization (either by the C compiler or + * aot-compiler) + */ + if (x == 0) { + return 0; + } + return consume_stack_cb(x - 1) + 1; +} + +int +host_consume_stack_cb(int x) +{ + return host_consume_stack(x); +} + +__attribute__((export_name("test1"))) uint32_t +test1(uint32_t native_stack, uint32_t recurse_count) +{ + /* + * ------ os_thread_get_stack_boundary + * ^ + * | + * | "native_stack" bytes of stack left by + * | host_consume_stack_and_call_indirect + * | + * | ^ + * | | + * | | consume_stack_cb (interpreter or aot) + * v | + * ^ + * | + * | host_consume_stack_and_call_indirect + * | + * + * + */ + uint32_t ret = host_consume_stack_and_call_indirect( + consume_stack_cb, recurse_count, native_stack); + return 42; +} + +__attribute__((export_name("test2"))) uint32_t +test2(uint32_t native_stack, uint32_t recurse_count) +{ + uint32_t ret = host_consume_stack_and_call_indirect(host_consume_stack_cb, + 6000, native_stack); + return 42; +} diff --git a/test-tools/addr2line/addr2line.py b/test-tools/addr2line/addr2line.py index 4502d5fec..421b0bdb2 100644 --- a/test-tools/addr2line/addr2line.py +++ b/test-tools/addr2line/addr2line.py @@ -43,6 +43,28 @@ For example, there is a call-stack dump: """ +def locate_sourceMappingURL_section(wasm_objdump: Path, wasm_file: Path) -> bool: + """ + Figure out if the wasm file has a sourceMappingURL section. + """ + cmd = f"{wasm_objdump} -h {wasm_file}" + p = subprocess.run( + shlex.split(cmd), + check=True, + capture_output=True, + text=True, + universal_newlines=True, + ) + outputs = p.stdout.split(os.linesep) + + for line in outputs: + line = line.strip() + if "sourceMappingURL" in line: + return True + + return False + + def get_code_section_start(wasm_objdump: Path, wasm_file: Path) -> int: """ Find the start offset of Code section in a wasm file. @@ -62,15 +84,6 @@ def get_code_section_start(wasm_objdump: Path, wasm_file: Path) -> int: ) outputs = p.stdout.split(os.linesep) - # if there is no .debug section, return -1 - for line in outputs: - line = line.strip() - if ".debug_info" in line: - break - else: - print(f"No .debug_info section found {wasm_file}") - return -1 - for line in outputs: line = line.strip() if "Code" in line: @@ -79,7 +92,7 @@ def get_code_section_start(wasm_objdump: Path, wasm_file: Path) -> int: return -1 -def get_line_info_from_function_addr( +def get_line_info_from_function_addr_dwarf( dwarf_dump: Path, wasm_file: Path, offset: int ) -> tuple[str, str, str, str]: """ @@ -126,7 +139,7 @@ def get_dwarf_tag_value(tag: str, line: str) -> str: return m.groups()[0] -def get_line_info_from_function_name( +def get_line_info_from_function_name_dwarf( dwarf_dump: Path, wasm_file: Path, function_name: str ) -> tuple[str, str, str]: """ @@ -160,6 +173,51 @@ def get_line_info_from_function_name( return (function_name, function_file, function_line) +def get_line_info_from_function_addr_sourcemapping( + emsymbolizer: Path, wasm_file: Path, offset: int +) -> tuple[str, str, str, str]: + """ + Find the location info of a given offset in a wasm file which is compiled with emcc. + + {emsymbolizer} {wasm_file} {offset of file} + + there usually are two lines: + ?? + relative path to source file:line:column + """ + debug_info_source = wasm_file.with_name(f"{wasm_file.name}.map") + cmd = f"{emsymbolizer} -t code -f {debug_info_source} {wasm_file} {offset}" + p = subprocess.run( + shlex.split(cmd), + check=False, + capture_output=True, + text=True, + universal_newlines=True, + cwd=Path.cwd(), + ) + outputs = p.stdout.split(os.linesep) + + function_name, function_file = "", "unknown" + function_line, function_column = "?", "?" + + for line in outputs: + line = line.strip() + + if not line: + continue + + m = re.match("(.*):(\d+):(\d+)", line) + if m: + function_file, function_line, function_column = m.groups() + continue + else: + # it's always ??, not sure about that + if "??" != line: + function_name = line + + return (function_name, function_file, function_line, function_column) + + def parse_line_info(line_info: str) -> tuple[str, str, str]: """ line_info -> [file, line, column] @@ -178,9 +236,13 @@ def parse_call_stack_line(line: str) -> tuple[str, str, str]: #00: 0x0a04 - $f18 => (00, 0x0a04, $f18) Old format: #00 $f18 => (00, _, $f18) + Text format (-DWAMR_BUILD_LOAD_CUSTOM_SECTION=1 -DWAMR_BUILD_CUSTOM_NAME_SECTION=1): + #02: 0x0200 - a => (02, 0x0200, a) + _start (always): + #05: 0x011f - _start => (05, 0x011f, _start) """ - # New format + # New format and Text format and _start PATTERN = r"#([0-9]+): 0x([0-9a-f]+) - (\S+)" m = re.match(PATTERN, line) if m is not None: @@ -246,6 +308,7 @@ def main(): action="store_true", help="use call stack without addresses or from fast interpreter mode", ) + parser.add_argument("--emsdk", type=Path, help="path to emsdk") args = parser.parse_args() wasm_objdump = args.wabt.joinpath("bin/wasm-objdump") @@ -257,12 +320,20 @@ def main(): llvm_cxxfilt = args.wasi_sdk.joinpath("bin/llvm-cxxfilt") assert llvm_cxxfilt.exists() + emcc_production = locate_sourceMappingURL_section(wasm_objdump, args.wasm_file) + if emcc_production: + if args.emsdk is None: + print("Please provide the path to emsdk via --emsdk") + return -1 + + emsymbolizer = args.emsdk.joinpath("upstream/emscripten/emsymbolizer") + assert emsymbolizer.exists() + code_section_start = get_code_section_start(wasm_objdump, args.wasm_file) if code_section_start == -1: return -1 - if args.no_addr: - function_index_to_name = parse_module_functions(wasm_objdump, args.wasm_file) + function_index_to_name = parse_module_functions(wasm_objdump, args.wasm_file) assert args.call_stack_file.exists() with open(args.call_stack_file, "rt", encoding="ascii") as f: @@ -272,38 +343,68 @@ def main(): continue splitted = parse_call_stack_line(line) - assert splitted is not None + if splitted is None: + print(f"{line}") + continue _, offset, index = splitted - if not index.startswith("$f"): # E.g. _start - print(f"{i}: {index}") - continue - index = index[2:] - if args.no_addr: + # FIXME: w/ emcc production + if not index.startswith("$f"): # E.g. _start or Text format + print(f"{i}: {index}") + continue + index = index[2:] + if index not in function_index_to_name: print(f"{i}: {line}") continue - line_info = get_line_info_from_function_name( - llvm_dwarf_dump, args.wasm_file, function_index_to_name[index] - ) + if not emcc_production: + _, function_file, function_line = ( + get_line_info_from_function_name_dwarf( + llvm_dwarf_dump, + args.wasm_file, + function_index_to_name[index], + ) + ) + else: + _, function_file, function_line = _, "unknown", "?" - _, funciton_file, function_line = line_info function_name = demangle(llvm_cxxfilt, function_index_to_name[index]) print(f"{i}: {function_name}") - print(f"\tat {funciton_file}:{function_line}") + print(f"\tat {function_file}:{function_line}") else: offset = int(offset, 16) + # match the algorithm in wasm_interp_create_call_stack() + # either a *offset* to *code* section start + # or a *offset* in a file + assert offset > code_section_start offset = offset - code_section_start - line_info = get_line_info_from_function_addr( - llvm_dwarf_dump, args.wasm_file, offset - ) - function_name, funciton_file, function_line, function_column = line_info + if emcc_production: + function_name, function_file, function_line, function_column = ( + get_line_info_from_function_addr_sourcemapping( + emsymbolizer, args.wasm_file, offset + ) + ) + else: + function_name, function_file, function_line, function_column = ( + get_line_info_from_function_addr_dwarf( + llvm_dwarf_dump, args.wasm_file, offset + ) + ) + + # if can't parse function_name, use name section or + if function_name == "": + if index.startswith("$f"): + function_name = function_index_to_name.get(index[2:], index) + else: + function_name = index + function_name = demangle(llvm_cxxfilt, function_name) + print(f"{i}: {function_name}") - print(f"\tat {funciton_file}:{function_line}:{function_column}") + print(f"\tat {function_file}:{function_line}:{function_column}") return 0 diff --git a/test-tools/flame-graph-helper/.gitignore b/test-tools/flame-graph-helper/.gitignore new file mode 100644 index 000000000..b5ddc69ff --- /dev/null +++ b/test-tools/flame-graph-helper/.gitignore @@ -0,0 +1,2 @@ +*.* +!*.py \ No newline at end of file diff --git a/test-tools/flame-graph-helper/process_folded_data.py b/test-tools/flame-graph-helper/process_folded_data.py new file mode 100644 index 000000000..e4650fe25 --- /dev/null +++ b/test-tools/flame-graph-helper/process_folded_data.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +""" +It is used to process *out.folded* file generated by [FlameGraph](https://github.com/brendangregg/FlameGraph). + +- translate jitted function names, which are in a form like `aot_func#N` or `[module name]#aot_func#N`, into corresponding names in a name section in .wasm +- divide the translated functions into different modules if the module name is specified in the symbol + +Usage: + +After +``` bash +# collect profiling data in perf.data + +$ perf script -i perf.data > out.perf + +$ ./FlameGraph/stackcollapse-perf.pl out.perf > out.folded +``` + +Use this script to translate the function names in out.folded + +``` +$ python translate_wasm_function_name.py --wabt_home --folded out.folded <.wasm> +# out.folded -> out.folded.translated +``` + +""" + +import argparse +import os +from pathlib import Path +import re +import shlex +import subprocess +from typing import Dict, List + + +# parse arguments like "foo=bar,fiz=biz" into a dictatory {foo:bar,fiz=biz} +class ParseKVArgs(argparse.Action): + def __call__(self, parser, namespace, values, option_string=None): + setattr(namespace, self.dest, dict()) + for value in values.split(","): + k, v = value.split("=") + getattr(namespace, self.dest)[k] = v + + +def calculate_import_function_count( + wasm_objdump_bin: Path, module_names: Dict[str, Path] +) -> Dict[str, int]: + """ + for every wasm file in , calculate the number of functions in the import section. + + using " -j Import -x " + """ + + assert wasm_objdump_bin.exists() + + import_function_counts = {} + for module_name, wasm_path in module_names.items(): + assert wasm_path.exists() + command = f"{wasm_objdump_bin} -j Import -x {wasm_path}" + p = subprocess.run( + shlex.split(command), + capture_output=True, + check=False, + text=True, + universal_newlines=True, + ) + + if p.stderr: + print("No content in import section") + import_function_counts[module_name] = 0 + continue + + import_function_count = 0 + for line in p.stdout.split(os.linesep): + line = line.strip() + + if not line: + continue + + if not " func" in line: + continue + + m = re.search(r"^-\s+func", line) + assert m + + import_function_count += 1 + + # print(f"! there are {import_function_count} import function in {module_name}") + import_function_counts[module_name] = import_function_count + + return import_function_counts + + +def collect_name_section_content( + wasm_objdump_bin: Path, module_names: Dict[str, Path] +) -> Dict[str, Dict[int, str]]: + """ + for every wasm file in , get the content of name section. + + execute "wasm_objdump_bin -j name -x wasm_file" + """ + assert wasm_objdump_bin.exists() + + name_sections = {} + for module_name, wasm_path in module_names.items(): + assert wasm_path.exists() + command = f"{wasm_objdump_bin} -j name -x {wasm_path}" + p = subprocess.run( + shlex.split(command), + capture_output=True, + check=False, + text=True, + universal_newlines=True, + ) + + if p.stderr: + print("No content in name section") + name_sections[module_name] = {} + continue + + name_section = {} + for line in p.stdout.split(os.linesep): + line = line.strip() + + if not line: + continue + + if not " func" in line: + continue + + # - func[N] <__imported_wasi_snapshot_preview1_fd_close> + m = re.match(r"- func\[(\d+)\] <(.+)>", line) + assert m + + func_index, func_name = m.groups() + name_section.update({int(func_index): func_name}) + + name_sections[module_name] = name_section + + return name_sections + + +def is_stack_check_mode(folded: Path) -> bool: + """ + check if there is a function name looks like "aot_func_internal#N", it means that WAMR adds a stack check function before the original function. + """ + with open(folded, "rt", encoding="utf-8") as f: + for line in f: + line = line.strip() + if "aot_func_internal" in line: + return True + return False + + +def replace_function_name( + import_function_counts: Dict[str, int], + name_sections: Dict[str, Dict[int, str]], + folded_in: Path, + module_names: Dict[str, Path], +) -> None: + """ + read content in . every line contains symbols which are separated by ";". + + Usually, all jitted functions are in the form of "aot_func#N". N is its function index. Use the index to find the corresponding function name in the name section. + + if there is a function name looks like "aot_func_internal#N", it means that WAMR adds a stack check function before the original function. + In this case, "aot_func#N" should be translated with "_precheck" as a suffix and "aot_func_internal#N" should be treated as the original one + """ + + assert folded_in.exists(), f"{folded_in} doesn't exist" + + stack_check_mode = is_stack_check_mode(folded_in) + + # every wasm has a translated out.folded, like out..folded.translated + folded_out_files = {} + for module_name in module_names.keys(): + wasm_folded_out_path = folded_in.with_suffix(f".{module_name}.translated") + print(f"-> write into {wasm_folded_out_path}") + folded_out_files[module_name] = wasm_folded_out_path.open( + "wt", encoding="utf-8" + ) + # Plus a default translated out.folded + default_folded_out_path = folded_in.with_suffix(".translated") + print(f"-> write into {default_folded_out_path}") + default_folded_out = default_folded_out_path.open("wt", encoding="utf-8") + + with folded_in.open("rt", encoding="utf-8") as f_in: + for line in f_in: + line = line.strip() + + m = re.match(r"(.*) (\d+)", line) + assert m + syms, samples = m.groups() + + new_line = [] + last_function_module_name = "" + for sym in syms.split(";"): + if not "aot_func" in sym: + new_line.append(sym) + continue + + # [module_name]#aot_func#N or aot_func#N + splitted = sym.split("#") + module_name = "" if splitted[0] == "aot_func" else splitted[0] + # remove [ and ] + module_name = module_name[1:-1] + + if len(module_name) == 0 and len(module_names) > 1: + raise RuntimeError( + f"❌ {sym} doesn't have a module name, but there are multiple wasm files" + ) + + if not module_name in module_names: + raise RuntimeError( + f"❌ can't find corresponds wasm file for {module_name}" + ) + + last_function_module_name = module_name + + func_idx = int(splitted[-1]) + # adjust index + func_idx = func_idx + import_function_counts[module_name] + + # print(f"🔍 {module_name} {splitted[1]} {func_idx}") + + if func_idx in name_sections[module_name]: + if len(module_name) > 0: + wasm_func_name = f"[Wasm] [{module_name}] {name_sections[module_name][func_idx]}" + else: + wasm_func_name = ( + f"[Wasm] {name_sections[module_name][func_idx]}" + ) + else: + if len(module_name) > 0: + wasm_func_name = f"[Wasm] [{module_name}] func[{func_idx}]" + else: + wasm_func_name = f"[Wasm] func[{func_idx}]" + + if stack_check_mode: + # aot_func_internal -> xxx + # aot_func --> xxx_precheck + if "aot_func" == splitted[1]: + wasm_func_name += "_precheck" + + new_line.append(wasm_func_name) + + line = ";".join(new_line) + line += f" {samples}" + + # always write into the default output + default_folded_out.write(line + os.linesep) + # based on the module name of last function, write into the corresponding output + if len(last_function_module_name) > 0: + folded_out_files[last_function_module_name].write(line + os.linesep) + + default_folded_out.close() + for f in folded_out_files.values(): + f.close() + + +def main(wabt_home: str, folded: str, module_names: Dict[str, Path]) -> None: + wabt_home = Path(wabt_home) + assert wabt_home.exists() + + folded = Path(folded) + assert folded.exists() + + wasm_objdump_bin = wabt_home.joinpath("bin", "wasm-objdump") + import_function_counts = calculate_import_function_count( + wasm_objdump_bin, module_names + ) + + name_sections = collect_name_section_content(wasm_objdump_bin, module_names) + + replace_function_name(import_function_counts, name_sections, folded, module_names) + + +if __name__ == "__main__": + argparse = argparse.ArgumentParser() + argparse.add_argument( + "--wabt_home", required=True, help="wabt home, like /opt/wabt-1.0.33" + ) + argparse.add_argument( + "--wasm", + action="append", + default=[], + help="wasm files for profiling before. like --wasm apple.wasm --wasm banana.wasm", + ) + argparse.add_argument( + "--wasm_names", + action=ParseKVArgs, + default={}, + metavar="module_name=wasm_file, ...", + help="multiple wasm files and their module names, like a=apple.wasm,b=banana.wasm,c=cake.wasm", + ) + argparse.add_argument( + "folded_file", + help="a out.folded generated by flamegraph/stackcollapse-perf.pl", + ) + + args = argparse.parse_args() + + if not args.wasm and not args.wasm_names: + print("Please specify wasm files with either --wasm or --wasm_names") + exit(1) + + # - only one wasm file. And there is no [module name] in out.folded + # - multiple wasm files. via `--wasm X --wasm Y --wasm Z`. And there is [module name] in out.folded. use the basename of wasm as the module name + # - multiple wasm files. via `--wasm_names X=x,Y=y,Z=z`. And there is [module name] in out.folded. use the specified module name + module_names = {} + if args.wasm_names: + for name, wasm_path in args.wasm_names.items(): + module_names[name] = Path(wasm_path) + else: + # use the basename of wasm as the module name + for wasm in args.wasm: + wasm_path = Path(wasm) + module_names[wasm_path.stem] = wasm_path + + main(args.wabt_home, args.folded_file, module_names) diff --git a/test-tools/trans-jitted-func-name/trans_wasm_func_name.py b/test-tools/trans-jitted-func-name/trans_wasm_func_name.py deleted file mode 100644 index 0206fc287..000000000 --- a/test-tools/trans-jitted-func-name/trans_wasm_func_name.py +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -# -""" -It is used to translate jitted functions' names(in out.folded) to coorespond name in name section in .wasm - -Usage: - -After -``` -$ perf script -i perf.data > out.perf - -# fold call stacks -$ ./FlameGraph/stackcollapse-perf.pl out.perf > out.folded -``` - -Add a step: -``` -# translate jitted functions' names -$ python translate_wasm_function_name.py --wabt_home --folded out.folded <.wasm> -# out.folded -> out.folded.translated -$ ls out.folded.translated -``` - -Then -``` -# generate flamegraph -$ ./FlameGraph/flamegraph.pl out.folded.translated > perf.wasm.svg -``` - -""" - -import argparse -import os -from pathlib import Path -import re -import shlex -import subprocess - - -def preflight_check(wabt_home: Path) -> Path: - """ - if wasm-objdump exists in wabt_home - """ - wasm_objdump_bin = wabt_home.joinpath("bin", "wasm-objdump") - if not wasm_objdump_bin.exists(): - raise RuntimeError(f"wasm-objdump not found in {wabt_home}") - - return wasm_objdump_bin - - -def collect_import_section_content(wasm_objdump_bin: Path, wasm_file: Path) -> dict: - """ - execute "wasm_objdump_bin -j Import -x " and return a dict like {function: X, global: Y, memory: Z, table: N} - """ - assert wasm_objdump_bin.exists() - assert wasm_file.exists() - - command = f"{wasm_objdump_bin} -j Import -x {wasm_file}" - p = subprocess.run( - shlex.split(command), - capture_output=True, - check=False, - text=True, - universal_newlines=True, - ) - - if p.stderr: - print("No content in import section") - return {} - - import_section = {} - for line in p.stdout.split(os.linesep): - line = line.strip() - - if not line: - continue - - if re.search(r"^-\s+func", line): - import_section.update(function=import_section.get("function", 0) + 1) - else: - pass - - assert len(import_section) > 0, "failed to retrive content of import section" - return import_section - - -def collect_name_section_content(wasm_objdump_bin: Path, wasm_file: Path) -> dict: - """ - execute "wasm_objdump_bin -j name -x wasm_file" and store the output in a dict - {1: xxxx, 2: yyyy, 3: zzzz} - """ - assert wasm_objdump_bin.exists() - assert wasm_file.exists() - - command = f"{wasm_objdump_bin} -j name -x {wasm_file}" - p = subprocess.run( - shlex.split(command), - capture_output=True, - check=False, - text=True, - universal_newlines=True, - ) - - if p.stderr: - raise RuntimeError(f"not found name section in {wasm_file}") - - name_section = {} - for line in p.stdout.split(os.linesep): - line = line.strip() - - if not line: - continue - - # - func[0] <__imported_wasi_snapshot_preview1_fd_close> - if line.startswith("- func"): - m = re.match(r"- func\[(\d+)\] <(.+)>", line) - assert m - - func_index, func_name = m.groups() - name_section.update({int(func_index): func_name}) - - assert name_section - return name_section - - -def replace_function_name( - import_section: dict, name_section: dict, folded_in: str, folded_out: str -) -> None: - """ - read content in . each line will be like: - - quiche::BalsaFrame::ProcessHeaders;non-virtual thunk to Envoy::Http::Http1::BalsaParser::MessageDone;Envoy::Http::Http1::ConnectionImpl::onMessageComplete;Envoy::Http::Http1::ConnectionImpl::onMessageCompleteImpl;Envoy::Http::Http1::ServerConnectionImpl::onMessageCompleteBase;Envoy::Http::ConnectionManagerImpl::ActiveStream::decodeHeaders;Envoy::Http::FilterManager::decodeHeaders;virtual thunk to Envoy::Extensions::Common::Wasm::Context::decodeHeaders;proxy_wasm::ContextBase::onRequestHeaders;proxy_wasm::wamr::Wamr::getModuleFunctionImpl;wasm_func_call;wasm_runtime_call_wasm;wasm_call_function;call_wasm_with_hw_bound_check;wasm_interp_call_wasm;llvm_jit_call_func_bytecode;wasm_runtime_invoke_native;push_args_end;aot_func_internal#3302;aot_func_internal#3308;asm_sysvec_apic_timer_interrupt;sysvec_apic_timer_interrupt;__sysvec_apic_timer_interrupt;hrtimer_interrupt;__hrtimer_run_queues;__remove_hrtimer;rb_next 1110899 - - symbol names are spearated by ";" - - if there is a symbol named like "aot_func#XXX" or "aot_func_internal#XXX", it will be replaced with the function name in name section by index - """ - folded_in = Path(folded_in) - assert folded_in.exists() - folded_out = Path(folded_out) - - import_function_count = import_section.get("function", 0) - with folded_in.open("rt", encoding="utf-8") as f_in, folded_out.open( - "wt", encoding="utf-8" - ) as f_out: - precheck_mode = False - for line in f_in: - line = line.strip() - if "aot_func_internal" in line: - precheck_mode = True - - f_in.seek(0) - for line in f_in: - new_line = [] - line = line.strip() - - m = re.match(r"(.*) (\d+)", line) - syms, samples = m.groups() - for sym in syms.split(";"): - m = re.match(r"aot_func(_internal)?#(\d+)", sym) - if not m: - new_line.append(sym) - continue - - func_idx = int(m.groups()[-1]) + import_function_count - if func_idx in name_section: - wasm_func_name = f"[Wasm] {name_section[func_idx]}" - else: - wasm_func_name = ( - f"[Wasm] function[{func_idx + import_function_count}]" - ) - - if precheck_mode: - # aot_func_internal -> xxx - # aot_func --> xxx_precheck - wasm_func_name += "_precheck" if not m.groups()[0] else "" - else: - # aot_func --> xxx - pass - - new_line.append(wasm_func_name) - - line = ";".join(new_line) - line += f" {samples}" - f_out.write(line + os.linesep) - - print(f"⚙️ {folded_in} -> {folded_out}") - - -def main(wabt_home: str, wasm_file: str, folded: str) -> None: - wabt_home = Path(wabt_home) - wasm_file = Path(wasm_file) - - wasm_objdump_bin = preflight_check(wabt_home) - import_section = collect_import_section_content(wasm_objdump_bin, wasm_file) - name_section = collect_name_section_content(wasm_objdump_bin, wasm_file) - - replace_function_name(import_section, name_section, folded, folded + ".translated") - - -if __name__ == "__main__": - argparse = argparse.ArgumentParser() - argparse.add_argument( - "--folded", help="stackcollapse-perf.pl generated, like out.folded" - ) - argparse.add_argument("wasm_file", help="wasm file") - argparse.add_argument("--wabt_home", help="wabt home, like /opt/wabt-1.0.33") - - args = argparse.parse_args() - main(args.wabt_home, args.wasm_file, args.folded) diff --git a/tests/wamr-test-suites/spec-test-script/all.py b/tests/wamr-test-suites/spec-test-script/all.py index 98f5c1e63..13db47203 100644 --- a/tests/wamr-test-suites/spec-test-script/all.py +++ b/tests/wamr-test-suites/spec-test-script/all.py @@ -95,7 +95,7 @@ def ignore_the_case( return True if gc_flag: - if case_name in ["type-equivalence", "type-rec", "array_init_elem", "array_init_data"]: + if case_name in ["array_init_elem", "array_init_data"]: return True if sgx_flag: diff --git a/tests/wamr-test-suites/spec-test-script/gc_ignore_cases.patch b/tests/wamr-test-suites/spec-test-script/gc_ignore_cases.patch index a627a38f6..bc91d6b06 100644 --- a/tests/wamr-test-suites/spec-test-script/gc_ignore_cases.patch +++ b/tests/wamr-test-suites/spec-test-script/gc_ignore_cases.patch @@ -9,78 +9,6 @@ index 335496f0..5b975028 100644 - "integer representation too long" + "invalid type flag" ;; In GC extension, the first byte in rectype define is just one byte, not LEB128 encoded. ) -diff --git a/test/core/binary.wast b/test/core/binary.wast -index 1661a1c6..84c716b9 100644 ---- a/test/core/binary.wast -+++ b/test/core/binary.wast -@@ -1082,7 +1082,7 @@ - ) - - ;; 1 br_table target declared, 2 given --(assert_malformed -+(;assert_malformed - (module binary - "\00asm" "\01\00\00\00" - "\01\04\01" ;; type section -@@ -1132,3 +1132,4 @@ - ) - "unexpected content after last section" - ) -+;) -diff --git a/test/core/data.wast b/test/core/data.wast -index a5c87fbb..6f948bae 100644 ---- a/test/core/data.wast -+++ b/test/core/data.wast -@@ -306,9 +306,10 @@ - "\02\01\41\00\0b" ;; active data segment 0 for memory 1 - "\00" ;; empty vec(byte) - ) -- "unknown memory 1" -+ "unknown memory" - ) - -+(; not supported by wat2wasm - ;; Data segment with memory index 0 (no memory section) - (assert_invalid - (module binary -@@ -317,7 +318,7 @@ - "\00\41\00\0b" ;; active data segment 0 for memory 0 - "\00" ;; empty vec(byte) - ) -- "unknown memory 0" -+ "unknown memory" - ) - - ;; Data segment with memory index 1 (no memory section) -@@ -328,7 +329,7 @@ - "\02\01\41\00\0b" ;; active data segment 0 for memory 1 - "\00" ;; empty vec(byte) - ) -- "unknown memory 1" -+ "unknown memory" - ) - - ;; Data segment with memory index 1 and vec(byte) as above, -@@ -348,7 +349,7 @@ - "\20\21\22\23\24\25\26\27\28\29\2a\2b\2c\2d\2e\2f" - "\30\31\32\33\34\35\36\37\38\39\3a\3b\3c\3d" - ) -- "unknown memory 1" -+ "unknown memory" - ) - - ;; Data segment with memory index 1 and specially crafted vec(byte) after. -@@ -368,8 +369,9 @@ - "\20\21\22\23\24\25\26\27\28\29\2a\2b\2c\2d\2e\2f" - "\30\31\32\33\34\35\36\37\38\39\3a\3b\3c\3d" - ) -- "unknown memory 1" -+ "unknown memory" - ) -+;) - - - ;; Invalid offsets diff --git a/test/core/elem.wast b/test/core/elem.wast index df1610f6..32c1d8b3 100644 --- a/test/core/elem.wast @@ -268,196 +196,11 @@ index 00000000..32650644 + ) + "unsupported initializer expression for table" +) -diff --git a/test/core/gc/ref_test.wast b/test/core/gc/ref_test.wast -index 590b81b8..e0aa49ed 100644 ---- a/test/core/gc/ref_test.wast -+++ b/test/core/gc/ref_test.wast -@@ -310,15 +310,16 @@ - (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 11))))) - (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 12))))) - -- (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 1))))) -- (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 2))))) -+ ;; Must have explicit sub relationship -+ ;; (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 1))))) -+ ;; (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 2))))) - -- (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 11))))) -- (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 12))))) -+ ;; (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 11))))) -+ ;; (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 12))))) - -- (br_if $l (i32.eqz (ref.test (ref $t2') (table.get (i32.const 2))))) -+ ;; (br_if $l (i32.eqz (ref.test (ref $t2') (table.get (i32.const 2))))) - -- (br_if $l (i32.eqz (ref.test (ref $t2) (table.get (i32.const 12))))) -+ ;; (br_if $l (i32.eqz (ref.test (ref $t2) (table.get (i32.const 12))))) - - (return) - ) diff --git a/test/core/gc/type-subtyping.wast b/test/core/gc/type-subtyping.wast -index a9022fc3..4e22e91b 100644 +index a9022fc3..4aa36e2a 100644 --- a/test/core/gc/type-subtyping.wast +++ b/test/core/gc/type-subtyping.wast -@@ -112,6 +112,8 @@ - ) - ) - -+;; don't support recursive type equality and subtype check -+(; - (module - (rec (type $f1 (sub (func))) (type (struct (field (ref $f1))))) - (rec (type $f2 (sub (func))) (type (struct (field (ref $f2))))) -@@ -135,6 +137,7 @@ - (func $g (type $g2)) - (global (ref $g1) (ref.func $g)) - ) -+;) - - (assert_invalid - (module -@@ -156,6 +159,8 @@ - (global (ref $f1) (ref.func $g)) - ) - -+;; don't support recursive type equality and subtype check -+(; - (module - (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) - (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) -@@ -201,6 +206,7 @@ - (global (ref $g12) (ref.func $g12)) - (global (ref $g22) (ref.func $g12)) - ) -+;) - - (assert_invalid - (module -@@ -226,6 +232,8 @@ - - ;; Runtime types - -+;; don't support recursive type equality and subtype check -+(; - (module - (type $t0 (sub (func (result (ref null func))))) - (rec (type $t1 (sub $t0 (func (result (ref null $t1)))))) -@@ -286,6 +294,7 @@ - (assert_trap (invoke "fail4") "cast") - (assert_trap (invoke "fail5") "cast") - (assert_trap (invoke "fail6") "cast") -+;) - - (module - (type $t1 (sub (func))) -@@ -316,7 +325,8 @@ - (assert_trap (invoke "fail3") "cast") - (assert_trap (invoke "fail4") "cast") - -- -+;; don't support recursive type equality and subtype check -+(; - (module - (rec (type $f1 (sub (func))) (type (struct (field (ref $f1))))) - (rec (type $f2 (sub (func))) (type (struct (field (ref $f2))))) -@@ -346,6 +356,7 @@ - ) - ) - (assert_return (invoke "run") (i32.const 1)) -+;) - - (module - (rec (type $f1 (sub (func))) (type (struct (field (ref $f1))))) -@@ -370,6 +381,8 @@ - ) - (assert_return (invoke "run") (i32.const 1)) - -+;; don't support recursive type equality and subtype check -+(; - (module - (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) - (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) -@@ -390,7 +403,6 @@ - ) - (assert_return (invoke "run") (i32.const 1) (i32.const 1)) - -- - (module - (rec (type $f11 (sub (func (result (ref func))))) (type $f12 (sub $f11 (func (result (ref $f11)))))) - (rec (type $f21 (sub (func (result (ref func))))) (type $f22 (sub $f21 (func (result (ref $f21)))))) -@@ -429,7 +441,9 @@ - (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1) - (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1) - ) -+;) - -+(; we use normalized function type index - (module - (rec (type $f11 (sub (func))) (type $f12 (sub $f11 (func)))) - (rec (type $f21 (sub (func))) (type $f22 (sub $f11 (func)))) -@@ -439,6 +453,7 @@ - ) - ) - (assert_return (invoke "run") (i32.const 0)) -+;) - - (module - (rec (type $f01 (sub (func))) (type $f02 (sub $f01 (func)))) -@@ -547,15 +562,15 @@ - (func (import "M3" "g") (type $g1)) - ) - --(module -- (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) -- (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) -- (rec -- (type $g2 (sub $f2 (func))) -- (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2))))) -- ) -- (func (export "g") (type $g2)) --) -+;; (module -+;; (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) -+;; (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) -+;; (rec -+;; (type $g2 (sub $f2 (func))) -+;; (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2))))) -+;; ) -+;; (func (export "g") (type $g2)) -+;; ) - (register "M4") - (module - (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) -@@ -597,17 +612,17 @@ - (func (import "M6" "g") (type $f1)) - ) - --(module -- (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) -- (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) -- (rec -- (type $g2 (sub $f2 (func))) -- (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2))))) -- ) -- (rec (type $h (sub $g2 (func))) (type (struct))) -- (func (export "h") (type $h)) --) --(register "M7") -+;; (module -+;; (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) -+;; (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) -+;; (rec -+;; (type $g2 (sub $f2 (func))) -+;; (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2))))) -+;; ) -+;; (rec (type $h (sub $g2 (func))) (type (struct))) -+;; (func (export "h") (type $h)) -+;; ) -+;; (register "M7") - (module - (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) - (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) -@@ -740,7 +755,7 @@ +@@ -740,7 +740,7 @@ "sub type" ) @@ -466,7 +209,7 @@ index a9022fc3..4e22e91b 100644 (module (type $f0 (sub (func (param i32) (result i32)))) (type $s0 (sub $f0 (struct))) -@@ -764,7 +779,7 @@ +@@ -764,7 +764,7 @@ "sub type" ) @@ -475,7 +218,7 @@ index a9022fc3..4e22e91b 100644 (module (type $s0 (sub (struct))) (type $f0 (sub $s0 (func (param i32) (result i32)))) -@@ -772,7 +787,7 @@ +@@ -772,7 +772,7 @@ "sub type" ) @@ -1262,29 +1005,3 @@ index 0b2d26f7..bdab6a01 100644 (table $t0 30 30 funcref) (table $t1 30 30 funcref) (elem (table $t1) (i32.const 2) func 3 1 4 1) -diff --git a/test/core/unreached-valid.wast b/test/core/unreached-valid.wast -index f3feb0f3..d8ef8743 100644 ---- a/test/core/unreached-valid.wast -+++ b/test/core/unreached-valid.wast -@@ -60,7 +60,7 @@ - - ;; Validation after unreachable - --(module -+(;module - (func (export "meet-bottom") - (block (result f64) - (block (result f32) -@@ -76,7 +76,6 @@ - - (assert_trap (invoke "meet-bottom") "unreachable") - -- - ;; Bottom heap type - - (module -@@ -106,3 +105,4 @@ - (unreachable) - ) - ) -+;) diff --git a/wamr-compiler/main.c b/wamr-compiler/main.c index 909752523..1044014c0 100644 --- a/wamr-compiler/main.c +++ b/wamr-compiler/main.c @@ -176,7 +176,7 @@ print_help() printf(" Enable the specified LLVM passes, using comma to separate\n"); printf(" --use-prof-file= Use profile file collected by LLVM PGO (Profile-Guided Optimization)\n"); printf(" --enable-segue[=] Enable using segment register GS as the base address of linear memory,\n"); - printf(" only available on linux/linux-sgx x86-64, which may improve performance,\n"); + printf(" only available on linux x86-64, which may improve performance,\n"); printf(" flags can be: i32.load, i64.load, f32.load, f64.load, v128.load,\n"); printf(" i32.store, i64.store, f32.store, f64.store, v128.store\n"); printf(" Use comma to separate, e.g. --enable-segue=i32.load,i64.store\n");