diff options
186 files changed, 14162 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bdcf7af --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.cache/ +*build*/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..37a4912 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,67 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +cmake_minimum_required(VERSION 3.19) +project(nanowasm LANGUAGES C VERSION 0.0.0) +option(NW_EXAMPLES "Build examples") +option(NW_LOG "Enables logging to stderr") +option(NW_LOG_CUSTOM "Allows user code to define nwp_log. Enables NW_LOG") +option(NW_CHECK_CODE "Check opcodes inside code section (slow).") +add_library(${PROJECT_NAME}) +target_include_directories(${PROJECT_NAME} PUBLIC include + PRIVATE private_include) + +set(compilers + "GNU" + "Clang" + "TinyCC" +) + +if(NW_LOG_CUSTOM) + set(NW_LOG ON) +endif() + +foreach(c ${compilers}) + if(CMAKE_C_COMPILER_ID STREQUAL ${c}) + set(cflags_np + -pedantic + -Wall + ) + + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(cflags_np ${cflags_np} -Og -g) + else() + set(cflags_np ${cflags_np} -Os) + endif() + + break() + endif() +endforeach() + +include(CheckCompilerFlag) + +foreach(f ${cflags_np}) + string(REPLACE "-" "_" var supported_${f}) + check_compiler_flag(C ${f} ${var}) + + if(${var}) + set(sup_cflags ${sup_cflags} ${f}) + endif() +endforeach() + +if(NW_LOG) + target_compile_definitions(${PROJECT_NAME} PRIVATE NW_LOG) +endif() + +if(NW_EXAMPLES) + add_subdirectory(examples) +endif() + +target_compile_options(${PROJECT_NAME} PRIVATE ${sup_cflags}) +set_target_properties(${PROJECT_NAME} PROPERTIES C_STANDARD 90 C_EXTENSIONS OFF) +add_subdirectory(src) +install(TARGETS ${PROJECT_NAME}) diff --git a/COMPARE.md b/COMPARE.md new file mode 100644 index 0000000..635f6ff --- /dev/null +++ b/COMPARE.md @@ -0,0 +1,261 @@ +# Comparing WebAssembly/Wasm interpreters + +This document provides a more detailed explanation behind the +[comparison chart](README.md#comparison-chart). + +- **Note:** `N/A` means no measurements have been made yet. + +## Asynchronous interface + +As of the time of this writing, there is no known asynchronous WebAssembly +interpreter other than `nanowasm`. All other interpreters implement a +function that will run the WebAssembly application until it either +finishes or traps (e.g.: `wasm_application_execute_main` in +`wasm-micro-runtime`). + +In the scope of `nanowasm`, it was deemed interesting to design it as an +asynchronous library for several reasons: + +- Running several module instances synchronously requires OS-level threading, +which might not be desirable or even feasible to implement under some +resource-constrained environments. +- An asynchronous interface allows hosts to stop the execution for a module +instance as easily as stop calling `nw_run`. Otherwise, interpreters must +provide an `terminate`-like interface that must be called from a separate +context (e.g.: `wasm_runtime_terminate` in `wasm-micro-runtime`). +- The reasons above require a synchronous interpreter to be aware of the +underlying platform, as it must be aware of primitives such as locks in order +to remain thread-safe. However, this restricts portability towards new, unknown +platforms. + +Despite its advantages, an asynchronous interface requires a more careful +design and, more importantly, it incurs a larger memory footprint. However, +`nanowasm` strives to remain smaller compared to its synchronous counterparts. + +## I/O-agnostic + +### Module bytecode + +As of the time of this writing, all interpreters other than `nanowasm` require +the module bytecode be memory-mapped. Typically, this requires to either: + +- Dump the bytecode into memory. +- Allocate a memory-mapped file. + +Whereas the former is inefficient memory-wise and probably unacceptable on +resource-constrained environments, the latter is just not possible unless +a hardware [MMU](https://en.wikipedia.org/wiki/Memory_management_unit) is +present. + +In the context of `nanowasm`, it was considered interesting not to assume +_where_ the module bytecode comes from, and instead access it via file-like +semantics. For example, this would allow MMU-less systems to store module +bytecode on non-volatile memory, which is often larger and less expensive, +albeit slower. + +### Memories + +WebAssembly defines four different memory areas: + +- Table memory. +- Linear memory. +- Global memory. +- Stack. + +All interpreters other than `nanowasm` allocate these memory areas internally +via the system heap, or a custom heap defined by the user. This raises the +following concerns: + +- Some resource-constrained environments might prefer to avoid the use of +a heap, or maybe no heap implementation is even available. +- It forces each of these memory areas to remain contiguous. On environments +with segmented memory, this might limit the amount of contiguous memory that +can be allocated. + +In the context of `nanowasm`, MMU-less systems were considered a priority for +its design, and therefore it was conceived so that these memory areas are +never allocated by `nanowasm` itself. Instead, `nanowasm` provides to the +host a series of interfaces (i.e., callbacks) to implement in order to define +how these areas are accessed. Therefore, whether accessing those areas +requires the use of a heap, and how it is used, is entirely up to the host +implementation. + +While possibly a bit cumbersome from a first glance, this flexible design +brings in many new possibilities. For example, it allows MMU-less systems to +store memory pages into non-contiguous memory areas, or even store them into +larger, non-volatile memory, similarly to how fully-fledged operating systems +implement virtual memory. + +## No heap required + +All interpreters other than `nanowasm` would allocate many internal data +structures, as well as arbitrarily large chunks of data in order to accomodate +the different memory areas defined by the WebAssembly standard. Aside from the +limitations [explained above](#memories), this means the memory required by a +module or a module instance cannot be known at compile-time, since it depends +on how the heap is implemented, and even the module bytecode itself. + +On the other hand, `nanowasm` was designed with resource-constrained +environments in mind, where a heap implementation might be either undesired +or just unavailable. Therefore, it had to be implemented so that the memory +required by modules and module instances remained static. This is achieved +efficiently by storing all data structures for all possible states into a +`union`. + +This design allows hosts to allocate modules and module instances in any way, +be it: + +- Automatically i.e., from the stack. +- Statically i.e., via the `static` qualifier. +- Dynamically i.e., from the heap. + +## Big-endian support + +Even if little-endian architectures, such as `amd64`, are arguably more popular +as of the time of this writing, big-endian counterparts are still being +produced and are therefore considered equally relevant by `nanowasm`. + +### [`wasm-micro-runtime`] + +Despite the fact that `wasm-micro-runtime` seems to byte-swap integers +according to the platform endianness, no big-endian platforms are listed so far +on its `README.md`. Also, due to its big code base, it is difficult to ensure +whether all integer reads and write are done in an endianness-agnostic way. + +### [`wac`] + +`wac` naively compares the `\0asm` magic string as a little-endian integer. + +## No compiler-specific extensions + +All interpreters other than `nanowasm` rely extensively on system-specific +macros and/or extensions to the C language. These might restrict their use on +less popular compilers and/or new environments. + +On the other hand, `nanowasm` is written in standard ANSI C (C89/C90) +i.e., without any language extensions, as well as no system-specific macros. +On a broader sense, the use of macros and/or other preprocessor directives is +restricted to a minimum on `nanowasm`, as opposed to other interpreters such +as `wasm3`. + +Such reduced use of the preprocessor is considered to enhance readability, +even if it might incur some extra boilerplate code. + +## Public functions + +### `nanowasm` + +The numbers were extracted from [`nw.h`](include/nanowasm/nw.h). + +### [`wasm-micro-runtime`] + +- Commit: `4e50d2191ca8f177ad03a9d80eebc44b59a932db` + +The numbers were extracted from `wasm_export.h`. + +### [`wasm3`] + +- Commit: `35b5e2fb53c5cbc1ff3d7e42c381cd7cfa14f308` + +The numbers were extracted from `wasm3.h`. + +## Minimal memory footprint + +### `nanowasm` + +The numbers were extracted from the `test` application built by the project +by default, which links the `nanowasm` library. + +The project was built with: + +``` +cmake -B build +cmake --build build +``` + +Then, the size for the `test` executable was obtained via: + +``` +$ size build/test/test + text data bss dec hex filename + 50590 3576 16 54182 d3a6 build/test/test +``` + +Of course, these numbers are subject to change since many opcodes are still +not implemented in `nanowasm`. + +### [`wasm-micro-runtime`] + +- Commit: `4e50d2191ca8f177ad03a9d80eebc44b59a932db` + +A minimal application, namely `wamr-ex`, was written with +`wasm-micro-runtime`'s `iwasm` library. This application: + +1. Dumps a `.wasm` file into memory, since `wasm-micro-runtime` requires +module code to either reside in memory or belong to a memory-mapped file. +2. Calls the following functions: + - `wasm_runtime_init` + - `wasm_runtime_load` + - `wasm_runtime_instantiate` + - `wasm_application_execute_main` + - `wasm_runtime_unload` + - `wasm_runtime_deinstantiate` + +The example was built with the default CMake flags i.e.: + +``` +cmake -B build +cmake --build build +``` + +The executable size was the obtained via: + +``` +$ size build/wamr-ex + text data bss dec hex filename + 463165 9224 932 473321 738e9 build/wamr-ex +``` + +### [`wasm3`] + +- Commit: `35b5e2fb53c5cbc1ff3d7e42c381cd7cfa14f308` + +`wasm3` provides a sample application, also called `wasm3`, in its source tree. +This application allows to run any `.wasm` file, along with some extra command +line options. + +The project was built with the default CMake flags i.e.: + +``` +cmake -B build +cmake --build build +``` + +The executable size was the obtained via: + +``` +$ size build/wasm3 + text data bss dec hex filename + 531667 20332 6720 558719 8867f build/wasm3 +``` + +## Per-module memory usage + +### `nanowasm` + +The numbers were extracted by looking up `sizeof (struct nw_mod)` via `gdb(1)`, +from an `x86_64-linux-gnu` machine. Results might vary depending on the +target platform. + +## Per-instance memory usage + +### `nanowasm` + +The numbers were extracted by looking up `sizeof (struct nw_inst)` via `gdb(1)`, +from an `x86_64-linux-gnu` machine. Results might vary depending on the +target platform. + +[`wasm-micro-runtime`]: https://github.com/bytecodealliance/wasm-micro-runtime +[`wasm3`]: https://github.com/wasm3/wasm3 +[`wac`]: https://github.com/kanaka/wac +[`toywasm`]: https://github.com/yamt/toywasm @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/README.md b/README.md new file mode 100644 index 0000000..44065a4 --- /dev/null +++ b/README.md @@ -0,0 +1,90 @@ +# `nanowasm`, the tiny Wasm interpreter that does not block you + +**This project is still unfinished and is not meant for production use.** + +This project aims to provide the most minimal Wasm interpreter possible in +strictly portable ANSI C (C89/C90). It can run one or more Wasm applications +concurrently, without threading. This project requires the +[Nanowasm extensions](https://gitea.privatedns.org/xavi/nanowasm-design/) +to the WebAssembly language. A NanoWasm-compatible program can be built from +a MVP WebAssembly program with [`nwc`](https://gitea.privatedns.org/xavi/nwc/). + +## Features + +- Portable **ANSI C** implementation. **Runs on bare metal environments**. +- **No dependencies** other than an ANSI C (C89/C90) compiler. +- Asynchronous interface that allows to **run multiple applications** +**concurrently, without threads**. +- **I/O-agnostic** implementation for maximum flexibility: read modules +from a network, write the stack into a file, implement a MMU in software... +Anything is possible! +- Suitable for **resource-constrained devices**, such as microcontrollers. +- **No memory allocator required**. +- Supports both **big-endian and little-endian architectures**. + +### Comparison chart + +| Feature | `nanowasm` | [`wasm-micro-runtime`] | [`wasm3`] | [`wac`] | [`toywasm`] | +| ------- | ------- | ------- | ------- | ------- | ------- | +| **Asynchronous interface** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | +| **I/O-agnostic** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | +| **No heap required** | ✅ | ❌ | ❌ | ❌ | ❌ | +| **Big-endian support** | ✅ | ⚠️ | ✅ | ❌ | ✅ | +| **No compiler-specific extensions** | ✅ | ❌ | ❌ | ❌ | ❌ | +| **C dialect** | **`c89`** | `gnu99` | `c99` | `gnu99` | `c11` | +| **Public functions** | **7** | 156 | 44 | N/A | N/A | +| **Minimal `.text` footprint** | **49 KiB** | 452 KiB | 519 KiB | N/A | N/A | +| **Per-module memory usage** | ~1016 bytes | N/A | N/A | N/A | N/A | +| **Per-instance memory usage** | ~728 bytes | N/A | N/A | N/A | N/A | + +Also, see a [detailed explanation](COMPARE.md) behind the numbers above. + +## What `nanowasm` is not + +As opposed to other interpreters, `nanowasm` prefers lower memory usage +rather than run-time performance. Therefore, it should not be unfairly +compared _performance_-wise against other interpreters. + +## How to build + +Use the conventional process in CMake projects: + +``` +cmake -B <dir> +cmake --build <dir> +``` + +## Examples + +- [`example/minimal.c`](examples/minimal.c) provides the essential minimum code +required to work with `nanowasm` i.e., without any imports. This is only meant +as a starting point for implementations, and not as a standalone program. + +- [`example/proc_exit.c`](examples/proc_exit.c) defines the code required to +support the following minimal C program built with +[`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk): + +```c +int main() +{ + return 0; +} +``` + +## License + +``` +nanowasm, a tiny WebAssembly/Wasm interpreter +Copyright (C) 2023-2025 Xavier Del Campo Romero + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at https://mozilla.org/MPL/2.0/. +``` + +Also, see [LICENSE](LICENSE). + +[`wasm-micro-runtime`]: https://github.com/bytecodealliance/wasm-micro-runtime +[`wasm3`]: https://github.com/wasm3/wasm3 +[`wac`]: https://github.com/kanaka/wac +[`toywasm`]: https://github.com/yamt/toywasm diff --git a/doc/frame b/doc/frame new file mode 100644 index 0000000..62f387c --- /dev/null +++ b/doc/frame @@ -0,0 +1,30 @@ +[ previous frame ] +[ meta local 0 ] +[ entry 0 local 0 ] <- local_start +[ entry 1 local 0 ] +[ ... ] +[ entry n local 0 ] +[ meta local 1 ] +[ entry 0 local 1 ] +[ entry 1 local 1 ] +[ ... ] +[ entry m local 1 ] +[ meta local j ] +[ ... ] +[ entry i local j ] +[ frame 0 ] <- addr +[ return value 0 ] + +[ previous frame ] +[ entry 0 local 0 ] <- local_start +[ entry 1 local 0 ] +[ ... ] +[ entry n local 0 ] +[ entry 0 local 1 ] +[ entry 1 local 1 ] +[ ... ] +[ entry m local 1 ] +[ ... ] +[ entry i local j ] +[ frame 1 ] <- addr +[ return value 0 ] diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..c182e37 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,10 @@ +function(nw_example example) + add_executable(nw_${example} ${example}.c) + target_link_libraries(nw_${example} PRIVATE ${PROJECT_NAME}) + install(TARGETS nw_${example}) + target_compile_options(nw_${example} PRIVATE ${sup_cflags}) +endfunction() + +nw_example(minimal) +nw_example(proc_exit) +nw_example(file) diff --git a/examples/example.wasm b/examples/example.wasm Binary files differnew file mode 100644 index 0000000..25871e0 --- /dev/null +++ b/examples/example.wasm diff --git a/examples/file.c b/examples/file.c new file mode 100644 index 0000000..c913830 --- /dev/null +++ b/examples/file.c @@ -0,0 +1,659 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <errno.h> +#include <stddef.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +struct inst +{ + long retval; + size_t n_files; + + struct file + { + FILE *f; + int fd; + } *files; + + struct mem + { + void *p; + size_t n; + } stack, linear, global; +}; + +static int io_read(void *const buf, const size_t n, void *const user) +{ + FILE *const f = user; + const size_t r = fread(buf, 1, 1, f); + + if (!r && ferror(f)) + { + fprintf(stderr, "ferror\n"); + return -1; + } + + return r; +} + +static enum nw_state io_seek(const long offset, void *const user) +{ + if (fseek(user, offset, SEEK_SET)) + { + fprintf(stderr, "fseek(3): %s\n", strerror(errno)); + return NW_FATAL; + } + + return NW_OK; +} + +static enum nw_state io_tell(long *const out, void *const user) +{ + const long offset = ftell(user); + + if (offset < 0) + { + fprintf(stderr, "ftell(3): %s\n", strerror(errno)); + return NW_FATAL; + } + + *out = offset; + return NW_OK; +} + +static int io_eof(void *const user) +{ + FILE *const f = user; + + return feof(f); +} + +static int push(const void *const src, const size_t n, void *const user) +{ + struct inst *const inst = user; + struct mem *const s = &inst->stack; + char *const p = realloc(s->p, s->n + n); + + if (!p) + { + fprintf(stderr, "realloc(3): %s\n", strerror(errno)); + return -1; + } + + fprintf(stderr, "pushed %zu bytes: {", n); + + for (size_t i = 0; i < n; i++) + { + fprintf(stderr, "%hhu", ((const char *)src)[i]); + + if (i + 1 < n) + fputs(", ", stderr); + } + + memcpy(&p[s->n], src, n); + s->p = p; + s->n += n; + fprintf(stderr, "}, stack size=%zu\n", s->n); + return n; +} + +static int pop(void *const dst, const size_t n, void *const user) +{ + struct inst *const inst = user; + struct mem *const s = &inst->stack; + + if (s->n < n) + { + fprintf(stderr, "stack underflow\n"); + return -1; + } + + const char *const src = (const char *)s->p + (s->n - n); + + fprintf(stderr, "popped %zu bytes: {", n); + + for (size_t i = 0; i < n; i++) + { + fprintf(stderr, "%hhu", ((const char *)src)[i]); + + if (i + 1 < n) + fputs(", ", stderr); + } + + if (dst) + memcpy(dst, src, n); + + if (s->n == n) + { + free(s->p); + s->p = NULL; + } + else + { + char *const p = realloc(s->p, s->n - n); + + if (!p) + { + fprintf(stderr, "realloc(3): %s\n", strerror(errno)); + return -1; + } + + s->p = p; + } + + s->n -= n; + fprintf(stderr, "}, stack size=%zu\n", s->n); + return n; +} + +static size_t ptr(void *const user) +{ + return ((const struct inst *)user)->stack.n; +} + +static int load(const nw_varuint32 offset, void *const dst, const size_t n, + const struct mem *const m) +{ + if (n > m->n || offset > m->n - n) + { + fprintf(stderr, "out-of-bounds access, offset=%lu, n=%zu\n", + (unsigned long)offset, (unsigned long)n); + return -1; + } + + fprintf(stderr, "loaded %zu bytes from offset %lu: {", (unsigned long)n, + (unsigned long)offset); + + for (size_t i = 0; i < n; i++) + { + fprintf(stderr, "%hhu", ((const char *)m->p)[offset + i]); + + if (i + 1 < n) + fputs(", ", stderr); + } + + fputs("}\n", stderr); + memcpy(dst, (const char *)m->p + offset, n); + return n; +} + +static int store(const nw_varuint32 offset, const void *const src, + const size_t n, const struct mem *const m) +{ + if (n > m->n || offset > m->n - n) + { + fprintf(stderr, "out-of-bounds access, offset=%lu, n=%lu\n", + (unsigned long)offset, (unsigned long)n); + return -1; + } + + fprintf(stderr, "stored %lu bytes into offset %lu: {", (unsigned long)n, + (unsigned long)offset); + + for (size_t i = 0; i < n; i++) + { + fprintf(stderr, "%hhu", ((const char *)src)[i]); + + if (i + 1 < n) + fputs(", ", stderr); + } + + fputs("}\n", stderr); + memcpy((char *)m->p + offset, src, n); + return n; +} + +static int stread(const size_t offset, void *const dst, const size_t n, + void *const user) +{ + const struct inst *const inst = user; + + return load(offset, dst, n, &inst->stack); +} + +static int stwrite(const size_t offset, const void *const src, const size_t n, + void *const user) +{ + const struct inst *const inst = user; + + return store(offset, src, n, &inst->stack); +} + +static int ensure_linear(struct mem *const m, const unsigned long offset, + const size_t n) +{ + void *const p = realloc(m->p, offset + n); + + if (!p) + { + fprintf(stderr, "realloc(3): %s\n", strerror(errno)); + return -1; + } + + m->p = p; + m->n = offset + n; + memset((char *)m->p + offset, 0, n); + return 0; +} + +static int lnload(const unsigned long offset, void *const dst, const size_t n, + void *const user) +{ + struct inst *const i = user; + struct mem *const m = &i->linear; + + if (offset >= m->n && ensure_linear(m, offset, n)) + { + fprintf(stderr, "ensure_linear failed\n"); + return -1; + } + + fprintf(stderr, "loaded %lu bytes from offset %lu: {", (unsigned long)n, + (unsigned long)offset); + + for (size_t i = 0; i < n; i++) + { + fprintf(stderr, "%hhu", ((const char *)m->p)[offset + i]); + + if (i + 1 < n) + fputs(", ", stderr); + } + + fputs("}\n", stderr); + memcpy(dst, (const char *)m->p + offset, n); + return n; +} + +static int lnstore(const unsigned long offset, const void *const src, + const size_t n, void *const user) +{ + struct inst *const i = user; + struct mem *const m = &i->linear; + + if (offset >= m->n && ensure_linear(m, offset, n)) + { + fprintf(stderr, "ensure_linear failed\n"); + return -1; + } + + fprintf(stderr, "stored %lu bytes into offset %lu: {", (unsigned long)n, + (unsigned long)offset); + + for (size_t i = 0; i < n; i++) + { + fprintf(stderr, "%hhu", ((const char *)src)[i]); + + if (i + 1 < n) + fputs(", ", stderr); + } + + fputs("}\n", stderr); + memcpy((char *)m->p + offset, src, n); + return n; +} + +static int glload(const unsigned long offset, void *const dst, + const size_t n, void *const user) +{ + const struct inst *const i = user; + + return load(offset, dst, n, &i->global); +} + +static int glstore(const unsigned long offset, const void *const src, + const size_t n, void *const user) +{ + const struct inst *const i = user; + + return store(offset, src, n, &i->global); +} + +static enum nw_state wasi_exit(const union nw_value *const params, + union nw_value *const ret, void *user) +{ + struct inst *const i = user; + + i->retval = params->i32; + return NW_OK; +} + +static enum nw_state wasi_fd_close(const union nw_value *const params, + union nw_value *const ret, void *user) +{ + return NW_FATAL; +} + +static enum nw_state wasi_fd_fdstat_get(const union nw_value *const params, + union nw_value *const ret, void *user) +{ + return NW_FATAL; +} + +static enum nw_state wasi_fd_prestat_get(const union nw_value *const params, + union nw_value *const ret, void *user) +{ + /* + * From wasi/phases/old/snapshot_0/docs.md: + * + * fd_prestat_get(fd: fd) -> (errno, prestat) + * Return a description of the given preopened file descriptor. + * Params + * fd: fd + * Results + * error: errno + * buf: prestat The buffer where the description is stored. + * + * Offset: 0 + * prestat: Union + * Information about a pre-opened capability. + * Size: 8 + * Alignment: 4 + * Union Layout + * tag_size: 1 + * tag_align: 1 + * contents_offset: 4 + * contents_size: 4 + * contents_align: 4 + */ + + const long fd = params[0].i32; + const unsigned long prestat = params[1].i32; + const struct file *fl = NULL; + struct inst *const inst = user; + + fprintf(stderr, "fd=%lu, prestat=%#lx\n", fd, prestat); + + for (size_t i = 0; i < inst->n_files; i++) + { + const struct file *const f = &inst->files[i]; + + if (fd == (unsigned long)f->fd) + { + fl = f; + break; + } + } + + if (!fl) + { + /* __WASI_EBADF */ + ret->i32 = 8; + return NW_OK; + } + + return NW_OK; +} + +static enum nw_state wasi_fd_prestat_dir_name( + const union nw_value *const params, union nw_value *const ret, void *user) +{ + return NW_FATAL; +} + +static enum nw_state wasi_fd_read( const union nw_value *const params, + union nw_value *const ret, void *user) +{ + const long fd = params[0].i32; + const unsigned long iovec = params[1].i32, n = params[2].i32, + nread = params[3].i32; + + fprintf(stderr, "fd=%ld, iovec=%#lx, n=%lu, nread=%#lx\n", + fd, iovec, n, nread); + return NW_FATAL; +} + +static enum nw_state wasi_path_open( const union nw_value *const params, + union nw_value *const ret, void *user) +{ + return NW_FATAL; +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + FILE *f = NULL; + struct inst ins = {0}; + + if (argc != 2) + { + fprintf(stderr, "%s <wasm>\n", *argv); + goto end; + } + + const char *const path = argv[1]; + + if (!(f = fopen(path, "rb"))) + { + fprintf(stderr, "fopen(3) %s: %s\n", path, strerror(errno)); + goto end; + } + + static const struct nw_import imports[] = + { + { + .kind = NW_KIND_FUNCTION, + .module = "wasi_snapshot_preview1", + .field = "proc_exit", + .u.function = + { + .fn = wasi_exit, + .signature = "(i)" + } + }, + + { + .kind = NW_KIND_FUNCTION, + .module = "wasi_snapshot_preview1", + .field = "fd_close", + .u.function = + { + .fn = wasi_fd_close, + .signature = "i(i)" + } + }, + + { + .kind = NW_KIND_FUNCTION, + .module = "wasi_snapshot_preview1", + .field = "fd_fdstat_get", + .u.function = + { + .fn = wasi_fd_fdstat_get, + .signature = "i(ii)" + } + }, + + { + .kind = NW_KIND_FUNCTION, + .module = "wasi_snapshot_preview1", + .field = "fd_prestat_get", + .u.function = + { + .fn = wasi_fd_prestat_get, + .signature = "i(ii)" + } + }, + + { + .kind = NW_KIND_FUNCTION, + .module = "wasi_snapshot_preview1", + .field = "fd_prestat_dir_name", + .u.function = + { + .fn = wasi_fd_prestat_dir_name, + .signature = "i(ii)" + } + }, + + { + .kind = NW_KIND_FUNCTION, + .module = "wasi_snapshot_preview1", + .field = "fd_read", + .u.function = + { + .fn = wasi_fd_read, + .signature = "i(iiii)" + } + }, + + { + .kind = NW_KIND_FUNCTION, + .module = "wasi_snapshot_preview1", + .field = "path_open", + .u.function = + { + .fn = wasi_path_open, + .signature = "i(iiiiiIIii)" + } + }, + }; + + const struct nw_io_cfg io = + { + .read = io_read, + .seek = io_seek, + .tell = io_tell, + .eof = io_eof, + .user = f + }; + + enum {N_IMPORTS = sizeof imports / sizeof *imports}; + + const struct nw_mod_cfg cfg = + { + .imports = imports, + .imp_indexes = (struct nw_import_index[N_IMPORTS]){{0}}, + .n_imports = N_IMPORTS, + .io = io + }; + + struct nw_mod m; + struct nw_mod_out mout; + + nw_init(&m, &cfg); + +again: + + switch (nw_load(&m, &mout)) + { + case NW_OK: + break; + + case NW_AGAIN: + goto again; + + case NW_FATAL: + fprintf(stderr, "nw_load failed\n"); + goto end; + } + + if (!(ins.global.p = malloc(ins.global.n = mout.global))) + { + fprintf(stderr, "malloc(3): %s\n", strerror(errno)); + goto end; + } + + enum {ARGS = 10}; + const struct nw_inst_cfg icfg = + { + .entry = "_start", + .interp_cfg = + { + .io = io, + .m = &m, + .user = &ins, + .args = (union nw_value[ARGS]){{0}}, + .n_args = ARGS, + .stack = + { + .push = push, + .pop = pop, + .ptr = ptr, + .read = stread, + .write = stwrite + }, + + .linear = + { + .load = lnload, + .store = lnstore + }, + + .global = + { + .load = glload, + .store = glstore + } + } + }; + + struct nw_inst inst; + + if (nw_start(&inst, &icfg)) + { + fprintf(stderr, "nw_start failed\n"); + goto end; + } + +again2: + + switch (nw_run(&inst)) + { + case NW_OK: + break; + + case NW_AGAIN: + + if (ins.retval) + { + ret = ins.retval; + fprintf(stderr, "instance exited with status %d\n", ret); + goto end; + } + + goto again2; + + case NW_FATAL: + fprintf(stderr, "nw_run failed: %s\n", nw_rexc(&inst)); + goto end; + } + + ret = EXIT_SUCCESS; + +end: + + for (size_t i = 0; i < ins.n_files; i++) + { + const struct file *const f = &ins.files[i]; + + if (fclose(f->f)) + { + fprintf(stderr, "fclose(3) [%lu]: %s\n", (unsigned long)i, + strerror(errno)); + ret = EXIT_FAILURE; + } + } + + if (f && fclose(f)) + { + fprintf(stderr, "fclose(3) %s: %s\n", path, strerror(errno)); + goto end; + } + + free(ins.files); + free(ins.stack.p); + free(ins.linear.p); + free(ins.global.p); + return ret; +} diff --git a/examples/minimal.c b/examples/minimal.c new file mode 100644 index 0000000..8812ac5 --- /dev/null +++ b/examples/minimal.c @@ -0,0 +1,136 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <stddef.h> +#include <stdlib.h> + +static int io_read(void *const buf, const size_t n, void *const user) +{ + return -1; +} + +static enum nw_state io_seek(const long offset, void *const user) +{ + return -1; +} + +static enum nw_state io_tell(long *const out, void *const user) +{ + return -1; +} + +static int io_eof(void *const user) +{ + return -1; +} + +static int push(const void *const src, const size_t n, void *const user) +{ + return -1; +} + +static int pop(void *const dst, const size_t n, void *const user) +{ + return -1; +} + +static size_t ptr(void *const user) +{ + return 0; +} + +static int load(const nw_varuint32 offset, void *const dst, const size_t n, + void *const user) +{ + return -1; +} + +static int store(const nw_varuint32 offset, const void *const src, + const size_t n, void *const user) +{ + return -1; +} + +int main(int argc, char *argv[]) +{ + const struct nw_io_cfg io = + { + .read = io_read, + .seek = io_seek, + .tell = io_tell, + .eof = io_eof + }; + + const struct nw_mod_cfg cfg = + { + .io = io + }; + + struct nw_mod m; + struct nw_mod_out mout; + + nw_init(&m, &cfg); + +again: + + switch (nw_load(&m, &mout)) + { + case NW_OK: + break; + + case NW_AGAIN: + goto again; + + case NW_FATAL: + return EXIT_FAILURE; + } + + const struct nw_inst_cfg icfg = + { + .interp_cfg = + { + .io = io, + .m = &m, + .stack = + { + .push = push, + .pop = pop, + .ptr = ptr + }, + + .linear = + { + .load = load, + .store = store + } + } + }; + + struct nw_inst inst; + + if (nw_start(&inst, &icfg)) + return EXIT_FAILURE; + +again2: + + switch (nw_run(&inst)) + { + case NW_OK: + break; + + case NW_AGAIN: + goto again2; + + case NW_FATAL: + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/examples/proc_exit.c b/examples/proc_exit.c new file mode 100644 index 0000000..d4a580e --- /dev/null +++ b/examples/proc_exit.c @@ -0,0 +1,484 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <errno.h> +#include <stddef.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +struct inst +{ + long retval; + + struct mem + { + void *p; + size_t n; + } stack, linear, global; +}; + +static int io_read(void *const buf, const size_t n, void *const user) +{ + FILE *const f = user; + const size_t r = fread(buf, 1, 1, f); + + if (!r && ferror(f)) + { + fprintf(stderr, "%s: ferror\n", __func__); + return -1; + } + + return r; +} + +static enum nw_state io_seek(const long offset, void *const user) +{ + if (fseek(user, offset, SEEK_SET)) + { + fprintf(stderr, "%s: fseek(3): %s\n", __func__, strerror(errno)); + return NW_FATAL; + } + + return NW_OK; +} + +static enum nw_state io_tell(long *const out, void *const user) +{ + const long offset = ftell(user); + + if (offset < 0) + { + fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno)); + return NW_FATAL; + } + + *out = offset; + return NW_OK; +} + +static int io_eof(void *const user) +{ + FILE *const f = user; + + return feof(f); +} + +static int push(const void *const src, const size_t n, void *const user) +{ + struct inst *const inst = user; + struct mem *const s = &inst->stack; + char *const p = realloc(s->p, s->n + n); + + if (!p) + { + fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno)); + return -1; + } + + fprintf(stderr, "pushed %zu bytes: {", n); + + for (size_t i = 0; i < n; i++) + { + fprintf(stderr, "%hhu", ((const char *)src)[i]); + + if (i + 1 < n) + fputs(", ", stderr); + } + + memcpy(&p[s->n], src, n); + s->p = p; + s->n += n; + fprintf(stderr, "}, stack size=%zu\n", s->n); + return n; +} + +static int pop(void *const dst, const size_t n, void *const user) +{ + struct inst *const inst = user; + struct mem *const s = &inst->stack; + + if (s->n < n) + { + fprintf(stderr, "%s: stack underflow\n", __func__); + return -1; + } + + const char *const src = (const char *)s->p + (s->n - n); + + fprintf(stderr, "popped %zu bytes: {", n); + + for (size_t i = 0; i < n; i++) + { + fprintf(stderr, "%hhu", ((const char *)src)[i]); + + if (i + 1 < n) + fputs(", ", stderr); + } + + memcpy(dst, src, n); + + if (s->n == n) + { + free(s->p); + s->p = NULL; + } + else + { + char *const p = realloc(s->p, s->n - n); + + if (!p) + { + fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno)); + return -1; + } + + s->p = p; + } + + s->n -= n; + fprintf(stderr, "}, stack size=%zu\n", s->n); + return n; +} + +static size_t ptr(void *const user) +{ + return ((const struct inst *)user)->stack.n; +} + +static int load(const nw_varuint32 offset, void *const dst, const size_t n, + const struct mem *const m) +{ + if (n > m->n || offset > m->n - n) + { + fprintf(stderr, "%s: out-of-bounds access, offset=%lu, n=%zu\n", + __func__, (unsigned long)offset, n); + return -1; + } + + fprintf(stderr, "%s: loaded %zu bytes from offset %lu: {", __func__, n, + (unsigned long)offset); + + for (size_t i = 0; i < n; i++) + { + fprintf(stderr, "%hhu", ((const char *)m->p)[offset + i]); + + if (i + 1 < n) + fputs(", ", stderr); + } + + fputs("}\n", stderr); + memcpy(dst, (const char *)m->p + offset, n); + return n; +} + +static int store(const nw_varuint32 offset, const void *const src, + const size_t n, const struct mem *const m) +{ + if (n > m->n || offset > m->n - n) + { + fprintf(stderr, "%s: out-of-bounds access, offset=%lu, n=%zu\n", + __func__, (unsigned long)offset, n); + return -1; + } + + fprintf(stderr, "%s: stored %zu bytes into offset %lu: {", __func__, n, + (unsigned long)offset); + + for (size_t i = 0; i < n; i++) + { + fprintf(stderr, "%hhu", ((const char *)src)[i]); + + if (i + 1 < n) + fputs(", ", stderr); + } + + fputs("}\n", stderr); + memcpy((char *)m->p + offset, src, n); + return n; +} + +static int stread(const size_t offset, void *const dst, const size_t n, + void *const user) +{ + const struct inst *const inst = user; + + return load(offset, dst, n, &inst->stack); +} + +static int stwrite(const size_t offset, const void *const src, const size_t n, + void *const user) +{ + const struct inst *const inst = user; + + return store(offset, src, n, &inst->stack); +} + +static int ensure_linear(struct mem *const m, const unsigned long offset, + const size_t n) +{ + void *const p = realloc(m->p, offset + n); + + if (!p) + { + fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno)); + return -1; + } + + m->p = p; + m->n = offset + n; + memset((char *)m->p + offset, 0, n); + return 0; +} + +static int lnload(const unsigned long offset, void *const dst, const size_t n, + void *const user) +{ + struct inst *const i = user; + struct mem *const m = &i->linear; + + if (offset >= m->n && ensure_linear(m, offset, n)) + { + fprintf(stderr, "%s: ensure_linear failed\n", __func__); + return -1; + } + + fprintf(stderr, "%s: loaded %zu bytes from offset %lu: {", __func__, n, + (unsigned long)offset); + + for (size_t i = 0; i < n; i++) + { + fprintf(stderr, "%hhu", ((const char *)m->p)[offset + i]); + + if (i + 1 < n) + fputs(", ", stderr); + } + + fputs("}\n", stderr); + memcpy(dst, (const char *)m->p + offset, n); + return n; +} + +static int lnstore(const unsigned long offset, const void *const src, + const size_t n, void *const user) +{ + struct inst *const i = user; + struct mem *const m = &i->linear; + + if (offset >= m->n && ensure_linear(m, offset, n)) + { + fprintf(stderr, "%s: ensure_linear failed\n", __func__); + return -1; + } + + fprintf(stderr, "%s: stored %zu bytes into offset %lu: {", __func__, n, + (unsigned long)offset); + + for (size_t i = 0; i < n; i++) + { + fprintf(stderr, "%hhu", ((const char *)src)[i]); + + if (i + 1 < n) + fputs(", ", stderr); + } + + fputs("}\n", stderr); + memcpy((char *)m->p + offset, src, n); + return n; +} + +static int glload(const unsigned long offset, void *const dst, + const size_t n, void *const user) +{ + const struct inst *const i = user; + + return load(offset, dst, n, &i->global); +} + +static int glstore(const unsigned long offset, const void *const src, + const size_t n, void *const user) +{ + const struct inst *const i = user; + + return store(offset, src, n, &i->global); +} + +static enum nw_state wasi_exit(const union nw_value *const params, + union nw_value *const ret, void *user, struct nw_next *const next) +{ + struct inst *const i = user; + + i->retval = params->i32; + return NW_OK; +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + FILE *f = NULL; + struct inst ins = {0}; + + if (argc != 2) + { + fprintf(stderr, "%s <wasm>\n", *argv); + goto end; + } + + const char *const path = argv[1]; + + if (!(f = fopen(path, "rb"))) + { + fprintf(stderr, "%s: fopen(3) %s: %s\n", __func__, path, + strerror(errno)); + goto end; + } + + static const struct nw_import imports[] = + { + { + .kind = NW_KIND_FUNCTION, + .module = "wasi_snapshot_preview1", + .field = "proc_exit", + .u.function = + { + .fn = wasi_exit, + .signature = "(i)" + } + } + }; + + const struct nw_io_cfg io = + { + .read = io_read, + .seek = io_seek, + .tell = io_tell, + .eof = io_eof, + .user = f + }; + + enum {N_IMPORTS = sizeof imports / sizeof *imports}; + + const struct nw_mod_cfg cfg = + { + .imports = imports, + .imp_indexes = (struct nw_import_index[N_IMPORTS]){{0}}, + .n_imports = N_IMPORTS, + .io = io + }; + + struct nw_mod m; + struct nw_mod_out mout; + + nw_init(&m, &cfg); + +again: + + switch (nw_load(&m, &mout)) + { + case NW_OK: + break; + + case NW_AGAIN: + goto again; + + case NW_FATAL: + fprintf(stderr, "%s: nw_load failed\n", __func__); + goto end; + } + + if (!(ins.global.p = malloc(ins.global.n = mout.global))) + { + fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); + goto end; + } + + enum {ARGS = 1}; + const struct nw_inst_cfg icfg = + { + .entry = "_start", + .interp_cfg = + { + .io = io, + .m = &m, + .user = &ins, + .args = (union nw_value[ARGS]){{0}}, + .n_args = ARGS, + .stack = + { + .push = push, + .pop = pop, + .ptr = ptr, + .read = stread, + .write = stwrite + }, + + .linear = + { + .load = lnload, + .store = lnstore + }, + + .global = + { + .load = glload, + .store = glstore + } + } + }; + + struct nw_inst inst; + + if (nw_start(&inst, &icfg)) + { + fprintf(stderr, "%s: nw_start failed\n", __func__); + goto end; + } + +again2: + + switch (nw_run(&inst)) + { + case NW_OK: + break; + + case NW_AGAIN: + + if (ins.retval) + { + ret = ins.retval; + fprintf(stderr, "instance exited with status %d\n", ret); + goto end; + } + + goto again2; + + case NW_FATAL: + fprintf(stderr, "%s: nw_run failed: %s\n", __func__, + nw_rexc(&inst)); + goto end; + } + + ret = EXIT_SUCCESS; + +end: + + if (f && fclose(f)) + { + fprintf(stderr, "%s: fclose(3) %s: %s\n", __func__, path, + strerror(errno)); + goto end; + } + + free(ins.stack.p); + free(ins.linear.p); + free(ins.global.p); + return ret; +} diff --git a/include/nanowasm/dbg.h b/include/nanowasm/dbg.h new file mode 100644 index 0000000..010a5c8 --- /dev/null +++ b/include/nanowasm/dbg.h @@ -0,0 +1,33 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef NW_DBG_H +#define NW_DBG_H + +#include <nanowasm/types.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +void nw_dbg_init(struct nw_dbg *d, const struct nw_dbg_cfg *cfg); +void nw_dbg_local(struct nw_dbg *d, unsigned long index); +void nw_dbg_param(struct nw_dbg *d, unsigned long index); +void nw_dbg_global(struct nw_dbg *d, unsigned long index); +void nw_dbg_mem_load(struct nw_dbg *d, enum nw_type t, unsigned long offset); +void nw_dbg_bt(struct nw_dbg *d, unsigned depth); +int nw_dbg_value(const struct nw_dbg *d, struct nw_dbg_value *v); +int nw_dbg_pc(const struct nw_dbg *d, long *pc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/nanowasm/linear.h b/include/nanowasm/linear.h new file mode 100644 index 0000000..71abf1c --- /dev/null +++ b/include/nanowasm/linear.h @@ -0,0 +1,20 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef NW_LINEAR_H +#define NW_LINEAR_H + +#include <nanowasm/types.h> + +enum nw_state nw_linear_load(struct nw_inst *i, struct nw_sm_io *io, + unsigned long offset); +enum nw_state nw_linear_store(struct nw_inst *i, struct nw_sm_io *io, + unsigned long offset); + +#endif diff --git a/include/nanowasm/nw.h b/include/nanowasm/nw.h new file mode 100644 index 0000000..093fa21 --- /dev/null +++ b/include/nanowasm/nw.h @@ -0,0 +1,32 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef NW_H +#define NW_H + +#include <nanowasm/types.h> +#include <nanowasm/linear.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +void nw_init(struct nw_mod *m, const struct nw_mod_cfg *cfg); +enum nw_state nw_load(struct nw_mod *m, struct nw_mod_out *out); +int nw_start(struct nw_inst *i, const struct nw_inst_cfg *icfg); +enum nw_state nw_run(struct nw_inst *i); +const char *nw_lexc(const struct nw_mod *m); +const char *nw_rexc(const struct nw_inst *i); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/nanowasm/private.h b/include/nanowasm/private.h new file mode 100644 index 0000000..60fe383 --- /dev/null +++ b/include/nanowasm/private.h @@ -0,0 +1,675 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef NANOWASM_PRIVATE_H +#define NANOWASM_PRIVATE_H + +#if !defined(NW_H) && !defined(NW_TYPES_H) +#error Do not #include <nanowasm/private.h> directly. \ + Please either #include <nanowasm/nw.h> or <nanowasm/types.h> +#endif + +#include <stddef.h> + +typedef signed char nw_varint1; +typedef unsigned char nw_varuint1; +typedef signed char nw_varint7; +typedef unsigned char nw_varuint7; +typedef unsigned long nw_varuint32; +typedef long nw_varint32; +typedef struct nw_ull nw_varuint64; +typedef struct nw_ll nw_varint64; + +struct nw_interp; + +enum +{ + NW_SECTION_CUSTOM, + NW_SECTION_TYPE, + NW_SECTION_IMPORT, + NW_SECTION_FUNCTION, + NW_SECTION_TABLE, + NW_SECTION_MEMORY, + NW_SECTION_GLOBAL, + NW_SECTION_EXPORT, + NW_SECTION_START, + NW_SECTION_ELEMENT, + NW_SECTION_CODE, + NW_SECTION_DATA, + NW_SECTION_DATA_COUNT, + + NW_SECTIONS +}; + +enum +{ + NW_CUSTOM_TO, + NW_CUSTOM_FTI, + NW_CUSTOM_FBO, + NW_CUSTOM_LO, + NW_CUSTOM_ITI, + + NW_CUSTOM_SECTIONS +}; + +struct nw_sm_leb128 +{ + unsigned shift, bcnt; + struct nw_ull result; +}; + +struct nw_leuint32 +{ + unsigned char v[4]; +}; + +struct nw_leuint64 +{ + unsigned char v[8]; +}; + +struct nw_global +{ + nw_varuint1 mutability; + enum nw_type type; + union nw_value value; +}; + +struct nw_table +{ + unsigned long initial, max; + unsigned char data; +}; + +struct nw_return +{ + nw_varuint1 count; + enum nw_type type; +}; + +struct nw_fn +{ + nw_varuint32 index, param_count; + long param_types; + struct nw_return ret; +}; + +struct nw_frame +{ + nw_varuint32 local_count, body_size, block_i; + struct nw_fn fn; + struct nw_return prev_ret; + int child; + unsigned char prev_op; + size_t fr_start, local_start, local_end; + long start, pc; +}; + +struct nw_local_meta +{ + nw_varuint32 entry_count; + enum nw_type type; +}; + +struct nw_find_local +{ + nw_varuint32 index, entry_i; + struct nw_sm_io io; + struct nw_local_meta meta; + size_t addr; + enum nw_state (*next)(struct nw_interp *); +}; + +struct nw_find_param +{ + const struct nw_fn *fn; + struct nw_sm_leb128 leb128; + struct nw_sm_io io; + long pc; + nw_varuint32 index, param_i; + size_t sz; + enum nw_state (*next)(struct nw_interp *); + + struct nw_find_param_out + { + enum nw_type type; + size_t addr; + } out; +}; + +struct nw_get_import_type +{ + nw_varuint32 index; + struct nw_sm_io io; + struct nw_leuint32 value; + unsigned char kind; + enum nw_state (*next)(struct nw_interp *); + + struct nw_get_import_type_out + { + enum nw_kind kind; + } out; +}; + +union nw_i_sm +{ + struct nw_i_sm_b + { + unsigned char op; + struct nw_sm_io io; + struct nw_next next; + long pc; + void (*f)(struct nw_interp *); + } bytecode; + + struct nw_i_sm_unreachable + { + long offset; + } unreachable; + + struct nw_i_sm_i32_const + { + nw_varint32 value; + struct nw_sm_leb128 leb128; + struct nw_sm_io io; + } i32_const; + + struct nw_i_sm_i64_const + { + nw_varuint64 value; + struct nw_sm_leb128 leb128; + struct nw_sm_io io; + } i64_const; + + struct nw_i_sm_exp + { + enum nw_kind kind; + struct nw_sm_leb128 leb128; + nw_varuint32 count, entry_i, len, len_i; + const char *sym; + nw_varuint32 index; + long offset; + enum nw_state (*next)(struct nw_interp *); + } export; + + struct nw_i_sm_type + { + long pc; + struct nw_sm_io io; + struct nw_sm_leb128 leb128; + nw_varuint32 param_i, fn_index; + struct nw_leuint32 fti, to; + struct nw_get_import_type git; + enum nw_state (*next)(struct nw_interp *); + struct nw_fn out; + } type; + + struct nw_i_sm_ffn + { + void (*next)(struct nw_interp *); + struct nw_sm_io io; + struct nw_fn fn; + struct nw_leuint32 fbo; + } ffn; + + struct nw_i_sm_pl + { + long body_start; + struct nw_sm_leb128 leb128; + struct nw_sm_io io; + struct nw_frame fr; + nw_varuint32 param_i, local_i, local_count, entry_i; + union nw_value value; + enum nw_type type; + struct nw_local_meta meta; + } pl; + + struct nw_i_sm_sb + { + void (*next)(struct nw_interp *); + struct nw_sm_leb128 leb128; + struct nw_sm_io io; + } start_block; + + struct nw_i_sm_imm + { + void (*next)(struct nw_interp *); + struct nw_sm_leb128 leb128; + + struct nw_i_sm_imm_out + { + nw_varuint32 flags, offset; + } out; + } imm; + + struct nw_i_sm_load + { + unsigned long addr; + struct nw_sm_io io; + struct nw_i_sm_imm_out imm; + + union + { + char i8; + long i32; + struct nw_ll i64; + float f32; + double f64; + struct nw_leuint32 v32; + } value; + } load; + + struct nw_i_sm_store + { + unsigned long addr; + struct nw_sm_io io; + struct nw_i_sm_imm_out imm; + + union + { + long i32; + struct nw_ull i64; + float f32; + double f64; + struct nw_leuint32 v32; + struct nw_leuint64 v64; + } value; + } store; + + struct nw_i_sm_br + { + nw_varuint32 relative_depth; + struct nw_sm_leb128 leb128; + struct nw_sm_io io; + } br; + + struct nw_i_sm_br_if + { + nw_varuint32 relative_depth; + struct nw_sm_leb128 leb128; + struct nw_sm_io io; + union nw_value condition; + } br_if; + + struct nw_i_sm_call + { + nw_varuint32 index; + struct nw_sm_leb128 leb128; + } call; + + struct nw_i_sm_call_indirect + { + nw_varuint32 index, value; + struct nw_sm_leb128 leb128; + } call_indirect; + + struct nw_i_sm_end + { + long offset; + struct nw_sm_leb128 leb128; + } end; + + struct nw_i_sm_unwind + { + nw_varuint32 pending, entry_i; + size_t sz; + struct nw_sm_io io; + struct nw_sm_leb128 leb128; + struct nw_frame fr; + union nw_value retval, value; + struct nw_find_local fl; + } unwind; + + struct nw_i_sm_set_global + { + nw_varuint32 index; + struct nw_sm_leb128 leb128; + struct nw_sm_io io; + struct nw_global gl; + } set_global; + + struct nw_i_sm_get_global + { + nw_varuint32 index; + struct nw_sm_leb128 leb128; + struct nw_sm_io io; + struct nw_global gl; + } get_global; + + struct nw_i_sm_set_local + { + nw_varuint32 index; + struct nw_sm_leb128 leb128; + struct nw_sm_io io; + enum nw_state (*next)(struct nw_interp *); + + union + { + struct nw_find_local l; + struct nw_find_param p; + } f; + + struct nw_i_sm_set_local_out + { + union nw_value value; + enum nw_type type; + } out; + } set_local; + + struct nw_i_sm_tee_local + { + struct nw_sm_io io; + struct nw_i_sm_set_local_out in; + } tee_local; + + struct nw_i_sm_get_local + { + nw_varuint32 index; + struct nw_sm_leb128 leb128; + struct nw_sm_io io; + + union + { + struct nw_find_local l; + struct nw_find_param p; + } f; + + union nw_value value; + enum nw_type type; + } get_local; + + struct nw_i_sm_break + { + nw_varuint32 relative_depth, label_i; + struct nw_sm_io io; + long pc, lo; + struct nw_leuint32 offset, lpc, dst, n; + } brk; + + struct nw_i_sm_arithm + { + enum nw_type type; + struct nw_sm_io io; + union nw_value value; + + struct nw_i_sm_arithm_out + { + union nw_value left, right; + } out; + + int (*op)(const struct nw_i_sm_arithm_out *, union nw_value *); + } arithm; + + struct nw_i_sm_unary + { + enum nw_type type; + struct nw_sm_io io; + union nw_value in, out; + int (*op)(const union nw_value *, union nw_value *); + } unary; + + struct nw_i_sm_drop + { + struct nw_sm_io io; + union nw_value value; + } drop; + + struct nw_i_sm_call_import + { + enum nw_type type; + nw_varuint32 type_i, param_i; + struct nw_sm_leb128 leb128; + struct nw_sm_io io; + struct nw_fn fn; + struct nw_next next; + union nw_value value; + long pc; + size_t addr, sz; + const struct nw_import *imp; + } call_import; +}; + +struct nw_interp +{ + const void *set; + const char *exception; + int exit; + int retval; + struct nw_interp_cfg cfg; + enum nw_state (*next)(struct nw_interp *); + struct nw_frame fr; + union nw_i_sm sm; + void *state, *args; + enum nw_type push_type; + + struct + { + nw_varuint32 n_pages; + } linear, table; +}; + +union nw_sm +{ + struct nw_sm_cm + { + unsigned char buf[sizeof "\0asm" - 1]; + struct nw_sm_io io; + } check_magic; + + struct nw_sm_cv + { + unsigned char version[sizeof (struct nw_leuint32)]; + struct nw_sm_io io; + } check_version; + + struct nw_sm_st + { + nw_varuint32 entry_i, param_count, p_i; + struct nw_sm_leb128 leb128; + } type; + + struct nw_sm_imp + { + long mod_off, field_off; + nw_varuint32 entry_i, mod_len, field_len, len_i, imp_i; + struct nw_sm_leb128 leb128; + } import; + + struct nw_sm_fn + { + nw_varuint32 entry_i; + struct nw_sm_leb128 leb128; + } function; + + struct nw_sm_tb + { + nw_varuint32 count, entry_i; + nw_varint7 elem_type; + nw_varuint1 flags; + size_t out_sz; + struct nw_sm_leb128 leb128; + } table; + + struct nw_sm_mem + { + nw_varuint32 count; + nw_varuint1 flags; + struct nw_sm_leb128 leb128; + } memory; + + struct nw_sm_gl + { + nw_varuint32 entry_i; + struct nw_sm_leb128 leb128; + struct nw_interp interp; + union nw_value value; + } global; + + struct nw_sm_d + { + nw_varuint32 entry_i, index, size; + struct nw_sm_leb128 leb128; + struct nw_interp interp; + long value, offset; + } data; + + struct nw_sm_dc + { + struct nw_sm_leb128 leb128; + } data_count; + + struct nw_sm_exp + { + nw_varuint32 count, entry_i, field_len, len_i; + struct nw_sm_io io; + struct nw_sm_leb128 leb128; + } export; + + struct nw_sm_start + { + struct nw_sm_leb128 leb128; + } start; + + struct nw_sm_c + { + nw_varuint32 count, entry_i, body_size; + struct nw_sm_leb128 leb128; + struct nw_sm_io io; + long start, body_start, op_off; + unsigned long rem; + enum nw_state (*next)(struct nw_mod *); + unsigned char op; + + union + { + unsigned long f32; + struct nw_ull f64; + + struct nw_sm_c_t + { + nw_varuint32 count, i; + } target; + } u; + + struct nw_sm_c_fn + { + nw_varuint32 local_count, local_i, local_total; + unsigned blocks; + } fn; + } code; + + struct nw_sm_custom + { + nw_varuint32 name_len, len_i; + struct nw_sm_leb128 leb128; + unsigned char byte; + int candidate[NW_CUSTOM_SECTIONS]; + long start; + } custom; + + struct nw_sm_lo + { + struct nw_sm_io io; + } lo; +}; + +struct nw_mod +{ + nw_varuint32 type_count, data_count, global_count, import_count, + function_count; + long sections[NW_SECTIONS], c_sections[NW_CUSTOM_SECTIONS]; + enum nw_state (*next)(struct nw_mod *); + struct nw_mod_cfg cfg; + union nw_sm sm; + struct nw_mod_out out; + const char *exception; + + struct nw_mod_section + { + nw_varuint7 section; + nw_varuint32 len; + long offset, cur; + struct nw_sm_leb128 leb128; + } section; +}; + +struct nw_inst; + +union nw_inst_sm +{ + struct nw_inst_sm_gl + { + nw_varuint32 count, entry_i; + struct nw_sm_leb128 leb128; + struct nw_sm_io io; + struct nw_global out; + enum nw_state (*next)(struct nw_inst *); + } global; + + struct nw_inst_sm_d + { + char b; + nw_varuint32 count, index, size, entry_i, bytes_i; + struct nw_sm_leb128 leb128; + struct nw_sm_io io; + struct nw_global out; + unsigned long offset; + enum nw_state (*next)(struct nw_inst *); + } data; +}; + +struct nw_inst +{ + const char *entry; + struct nw_interp interp; + union nw_inst_sm sm; + enum nw_state (*next)(struct nw_inst *); +}; + +union nw_dbg_sm +{ + struct nw_dbg_sm_local + { + struct nw_sm_io io; + struct nw_find_local fl; + enum nw_state (*inext)(struct nw_interp *); + } local; + + struct nw_dbg_sm_param + { + struct nw_sm_io io; + struct nw_find_param fp; + enum nw_state (*inext)(struct nw_interp *); + } param; + + struct nw_dbg_sm_global + { + struct nw_sm_io io; + unsigned long index; + struct nw_global gl; + enum nw_state (*inext)(struct nw_interp *); + } global; + + struct nw_dbg_sm_mem_load + { + struct nw_sm_io io; + unsigned long offset; + enum nw_state (*inext)(struct nw_interp *); + } mem_load; +}; + +struct nw_dbg +{ + long pc; + struct nw_dbg_cfg cfg; + struct nw_dbg_value v; + union nw_dbg_sm sm; +}; + +#endif diff --git a/include/nanowasm/types.h b/include/nanowasm/types.h new file mode 100644 index 0000000..23cd4fa --- /dev/null +++ b/include/nanowasm/types.h @@ -0,0 +1,180 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef NW_TYPES_H +#define NW_TYPES_H + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct nw_inst; + +enum nw_state +{ + NW_OK, + NW_AGAIN, + NW_FATAL +}; + +enum nw_kind +{ + NW_KIND_FUNCTION, + NW_KIND_TABLE, + NW_KIND_MEMORY, + NW_KIND_GLOBAL, + + NW_KINDS +}; + +struct nw_ull +{ + unsigned long low, hi; +}; + +struct nw_ll +{ + long low, hi; +}; + +union nw_value +{ + long i32; + struct nw_ll i64; + float f32; + double f64; +}; + +enum nw_type +{ + NW_TYPE_I32, + NW_TYPE_I64, + NW_TYPE_F32, + NW_TYPE_F64 +}; + +struct nw_args +{ + const union nw_value *args; + size_t n; +}; + +struct nw_next +{ + enum nw_state (*fn)(void *args, struct nw_next *next); + void *user; +}; + +struct nw_io_cfg +{ + int (*read)(void *buf, size_t n, void *user); + int (*eof)(void *user); + enum nw_state (*pc)(long offset, struct nw_next * next, void *user); + enum nw_state (*seek)(long offset, void *user); + enum nw_state (*tell)(long *offset, void *user); + void *user; +}; + +struct nw_sm_io +{ + void *buf; + size_t n, read; +}; + +struct nw_import +{ + enum nw_kind kind; + const char *module, *field; + + union + { + struct nw_import_fn + { + const char *signature; + enum nw_state (*fn)(const union nw_value *params, + union nw_value *ret, void *user, struct nw_next *next); + } function; + } u; +}; + +struct nw_import_index +{ + unsigned long index; +}; + +struct nw_mod_cfg +{ + const struct nw_import *imports; + struct nw_import_index *imp_indexes; + struct nw_io_cfg io; + size_t n_imports; +}; + +struct nw_interp_cfg +{ + size_t n_args; + void *user; + + struct nw_mem_cfg + { + int (*load)(unsigned long pos, void *src, size_t n, void *user); + int (*store)(unsigned long pos, const void *src, size_t n, void *user); + } global, linear, table; + + struct nw_fifo_cfg + { + int (*push)(const void *src, size_t n, void *user); + int (*pop)(void *dst, size_t n, void *user); + int (*read)(size_t offset, void *dst, size_t n, void *user); + int (*write)(size_t offset, const void *dst, size_t n, void *user); + size_t (*ptr)(void *user); + } stack; + + struct nw_io_cfg io; + const struct nw_mod *m; + union nw_value *args; +}; + +struct nw_inst_cfg +{ + const char *entry; + struct nw_interp_cfg interp_cfg; +}; + +struct nw_mod_out +{ + struct nw_mod_out_mem + { + unsigned long initial, max; + } linear, table; + + size_t global; +}; + +struct nw_dbg_cfg +{ + struct nw_inst *inst; +}; + +struct nw_dbg_value +{ + enum nw_type type; + union nw_value value; +}; + +#include <nanowasm/private.h> + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/private_include/nw/global.h b/private_include/nw/global.h new file mode 100644 index 0000000..7f289d2 --- /dev/null +++ b/private_include/nw/global.h @@ -0,0 +1,20 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef GLOBAL_H +#define GLOBAL_H + +#include <nanowasm/nw.h> + +enum nw_state nwp_global_load(struct nw_interp *i, struct nw_sm_io *io, + nw_varuint32 index); +enum nw_state nwp_global_store(struct nw_interp *i, struct nw_sm_io *io, + nw_varuint32 index); + +#endif diff --git a/private_include/nw/inst.h b/private_include/nw/inst.h new file mode 100644 index 0000000..3a68440 --- /dev/null +++ b/private_include/nw/inst.h @@ -0,0 +1,17 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef INST_H +#define INST_H + +#include <nanowasm/nw.h> + +enum nw_state nwp_inst_run(struct nw_inst *i); + +#endif diff --git a/private_include/nw/interp.h b/private_include/nw/interp.h new file mode 100644 index 0000000..a6f0d74 --- /dev/null +++ b/private_include/nw/interp.h @@ -0,0 +1,39 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef INTERP_H +#define INTERP_H + +#include <nanowasm/nw.h> +#include <nw/opcodes.h> +#include <stddef.h> + +struct nwp_interp_set +{ + const enum opcode *opcodes; + size_t n; +}; + +struct nwp_ops +{ + void (*const *ops)(struct nw_interp *); + size_t n; +}; + +extern const struct nwp_interp_set nwp_interp_initexpr_set, nwp_interp_data_set; +extern const struct nwp_ops nwp_ops; + +int nwp_interp_start(struct nw_interp *i, + const struct nw_interp_cfg *cfg, const struct nwp_interp_set *set); +void nwp_interp_resume(struct nw_interp *i); +void nwp_interp_limited(struct nw_interp *i); +void nwp_interp_full(struct nw_interp *i); +enum nw_state nwp_interp_run(struct nw_interp *i); + +#endif diff --git a/private_include/nw/io.h b/private_include/nw/io.h new file mode 100644 index 0000000..2b28847 --- /dev/null +++ b/private_include/nw/io.h @@ -0,0 +1,40 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef IO_H +#define IO_H + +#include <nanowasm/nw.h> + +enum nw_state nwp_io_read(const struct nw_io_cfg *cfg, struct nw_sm_io *io, + void *user); +enum nw_state nwp_varint1(const struct nw_io_cfg *cfg, struct nw_sm_leb128 *l, + nw_varint1 *out, void *user); +enum nw_state nwp_varint7(const struct nw_io_cfg *cfg, struct nw_sm_leb128 *l, + nw_varint7 *out, void *user); +enum nw_state nwp_varint32(const struct nw_io_cfg *cfg, struct nw_sm_leb128 *l, + nw_varint32 *out, void *user); +enum nw_state nwp_varint64(const struct nw_io_cfg *cfg, struct nw_sm_leb128 *l, + nw_varint64 *out, void *user); +enum nw_state nwp_varuint1(const struct nw_io_cfg *cfg, struct nw_sm_leb128 *l, + nw_varuint1 *out, void *user); +enum nw_state nwp_varuint7(const struct nw_io_cfg *cfg, struct nw_sm_leb128 *l, + nw_varuint7 *out, void *user); +enum nw_state nwp_varuint32(const struct nw_io_cfg *cfg, struct nw_sm_leb128 *l, + nw_varuint32 *out, void *user); +enum nw_state nwp_varuint64(const struct nw_io_cfg *cfg, struct nw_sm_leb128 *l, + nw_varuint64 *out, void *user); +enum nw_state nwp_leb128(const struct nw_io_cfg *cfg, struct nw_sm_leb128 *l, + unsigned maxbits, int sign, void *user); +unsigned long nwp_leuint32(const struct nw_leuint32 *v); +void nwp_toleuint32(unsigned long v, struct nw_leuint32 *out); +void nwp_leuint64(const struct nw_leuint64 *v, struct nw_ull *out); +void nwp_toleuint64(const struct nw_ull *v, struct nw_leuint64 *out); + +#endif diff --git a/private_include/nw/linear.h b/private_include/nw/linear.h new file mode 100644 index 0000000..a316e77 --- /dev/null +++ b/private_include/nw/linear.h @@ -0,0 +1,21 @@ + +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef LINEAR_H +#define LINEAR_H + +#include <nanowasm/types.h> + +enum nw_state nwp_linear_load(struct nw_interp *i, struct nw_sm_io *io, + unsigned long offset); +enum nw_state nwp_linear_store(struct nw_interp *i, struct nw_sm_io *io, + unsigned long offset); + +#endif diff --git a/private_include/nw/log.h b/private_include/nw/log.h new file mode 100644 index 0000000..c5e2870 --- /dev/null +++ b/private_include/nw/log.h @@ -0,0 +1,17 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef LOG_H +#define LOG_H + +#ifdef NW_LOG +int nwp_log(const char *fmt, ...); +#endif + +#endif diff --git a/private_include/nw/mem.h b/private_include/nw/mem.h new file mode 100644 index 0000000..b1aa7ce --- /dev/null +++ b/private_include/nw/mem.h @@ -0,0 +1,20 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef MEM_H +#define MEM_H + +#include <nanowasm/nw.h> + +enum nw_state nwp_mem_store(const struct nw_mem_cfg *const cfg, + struct nw_sm_io *io, nw_varuint32 offset, void *user); +enum nw_state nwp_mem_load(const struct nw_mem_cfg *const cfg, + struct nw_sm_io *io, nw_varuint32 offset, void *user); + +#endif diff --git a/private_include/nw/opcodes.h b/private_include/nw/opcodes.h new file mode 100644 index 0000000..410bd97 --- /dev/null +++ b/private_include/nw/opcodes.h @@ -0,0 +1,192 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef OPCODES_H +#define OPCODES_H + +enum opcode +{ + OP_UNREACHABLE, + OP_NOP, + OP_BLOCK, + OP_LOOP, + OP_IF, + OP_ELSE, + OP_END = 0xb, + OP_BR, + OP_BR_IF, + OP_BR_TABLE, + OP_RETURN, + OP_CALL, + OP_CALL_INDIRECT, + OP_DROP = 0x1a, + OP_SELECT, + OP_GET_LOCAL = 0x20, + OP_SET_LOCAL, + OP_TEE_LOCAL, + OP_GET_GLOBAL, + OP_SET_GLOBAL, + OP_I32_LOAD = 0x28, + OP_I64_LOAD, + OP_F32_LOAD, + OP_F64_LOAD, + OP_I32_LOAD8_S, + OP_I32_LOAD8_U, + OP_I32_LOAD16_S, + OP_I32_LOAD16_U, + OP_I64_LOAD8_S, + OP_I64_LOAD8_U, + OP_I64_LOAD16_S, + OP_I64_LOAD16_U, + OP_I64_LOAD32_S, + OP_I64_LOAD32_U, + OP_I32_STORE, + OP_I64_STORE, + OP_F32_STORE, + OP_F64_STORE, + OP_I32_STORE8, + OP_I32_STORE16, + OP_I64_STORE8, + OP_I64_STORE16, + OP_I64_STORE32, + OP_CURRENT_MEMORY, + OP_GROW_MEMORY, + OP_I32_CONST, + OP_I64_CONST, + OP_F32_CONST, + OP_F64_CONST, + OP_I32_EQZ, + OP_I32_EQ, + OP_I32_NE, + OP_I32_LT_S, + OP_I32_LT_U, + OP_I32_GT_S, + OP_I32_GT_U, + OP_I32_LE_S, + OP_I32_LE_U, + OP_I32_GE_S, + OP_I32_GE_U, + OP_I64_EQZ, + OP_I64_EQ, + OP_I64_NE, + OP_I64_LT_S, + OP_I64_LT_U, + OP_I64_GT_S, + OP_I64_GT_U, + OP_I64_LE_S, + OP_I64_LE_U, + OP_I64_GE_S, + OP_I64_GE_U, + OP_F32_EQ, + OP_F32_NE, + OP_F32_LT, + OP_F32_GT, + OP_F32_LE, + OP_F32_GE, + OP_F64_EQ, + OP_F64_NE, + OP_F64_LT, + OP_F64_GT, + OP_F64_LE, + OP_F64_GE, + OP_I32_CLZ, + OP_I32_CTZ, + OP_I32_POPCNT, + OP_I32_ADD, + OP_I32_SUB, + OP_I32_MUL, + OP_I32_DIV_S, + OP_I32_DIV_U, + OP_I32_REM_S, + OP_I32_REM_U, + OP_I32_AND, + OP_I32_OR, + OP_I32_XOR, + OP_I32_SHL, + OP_I32_SHR_S, + OP_I32_SHR_U, + OP_I32_ROTL, + OP_I32_ROTR, + OP_I64_CLZ, + OP_I64_CTZ, + OP_I64_POPCNT, + OP_I64_ADD, + OP_I64_SUB, + OP_I64_MUL, + OP_I64_DIV_S, + OP_I64_DIV_U, + OP_I64_REM_S, + OP_I64_REM_U, + OP_I64_AND, + OP_I64_OR, + OP_I64_XOR, + OP_I64_SHL, + OP_I64_SHR_S, + OP_I64_SHR_U, + OP_I64_ROTL, + OP_I64_ROTR, + OP_F32_ABS, + OP_F32_NEG, + OP_F32_CEIL, + OP_F32_FLOOR, + OP_F32_TRUNC, + OP_F32_NEAREST, + OP_F32_SQRT, + OP_F32_ADD, + OP_F32_SUB, + OP_F32_MUL, + OP_F32_DIV, + OP_F32_MIN, + OP_F32_MAX, + OP_F32_COPYSIGN, + OP_F64_ABS, + OP_F64_NEG, + OP_F64_CEIL, + OP_F64_FLOOR, + OP_F64_TRUNC, + OP_F64_NEAREST, + OP_F64_SQRT, + OP_F64_ADD, + OP_F64_SUB, + OP_F64_MUL, + OP_F64_DIV, + OP_F64_MIN, + OP_F64_MAX, + OP_F64_COPYSIGN, + OP_I32_WRAP_I64, + OP_I32_TRUNC_S_F32, + OP_I32_TRUNC_U_F32, + OP_I32_TRUNC_S_F64, + OP_I32_TRUNC_U_F64, + OP_I64_EXTEND_S_I32, + OP_I64_EXTEND_U_I32, + OP_I64_TRUNC_S_F32, + OP_I64_TRUNC_U_F32, + OP_I64_TRUNC_S_F64, + OP_I64_TRUNC_U_F64, + OP_F32_CONVERT_S_I32, + OP_F32_CONVERT_U_I32, + OP_F32_CONVERT_S_I64, + OP_F32_CONVERT_U_I64, + OP_F32_DEMOTE_F64, + OP_F64_CONVERT_S_I32, + OP_F64_CONVERT_U_I32, + OP_F64_CONVERT_S_I64, + OP_F64_CONVERT_U_I64, + OP_F64_PROMOTE_F32, + OP_I32_REINTERPRET_F32, + OP_I64_REINTERPRET_F64, + OP_F32_REINTERPRET_I32, + OP_F64_REINTERPRET_I64, + OP_MISC = 0xfc +}; + +const char *nwp_op_tostr(enum opcode op); + +#endif diff --git a/private_include/nw/ops.h b/private_include/nw/ops.h new file mode 100644 index 0000000..dce95a6 --- /dev/null +++ b/private_include/nw/ops.h @@ -0,0 +1,90 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef OPS_H +#define OPS_H + +#include <nanowasm/nw.h> +#include <nw/opcodes.h> +#include <nw/types.h> + +void nwp_op_unreachable(struct nw_interp *i); +void nwp_op_nop(struct nw_interp *i); +void nwp_op_block(struct nw_interp *i); +void nwp_op_loop(struct nw_interp *i); +void nwp_op_if(struct nw_interp *i); +void nwp_op_else(struct nw_interp *i); +void nwp_op_end(struct nw_interp *i); +void nwp_op_br(struct nw_interp *i); +void nwp_op_br_if(struct nw_interp *i); +void nwp_op_br_table(struct nw_interp *i); +void nwp_op_return(struct nw_interp *i); +void nwp_op_call(struct nw_interp *i); +void nwp_op_call_indirect(struct nw_interp *i); +void nwp_op_drop(struct nw_interp *i); +void nwp_op_get_local(struct nw_interp *i); +void nwp_op_set_local(struct nw_interp *i); +void nwp_op_tee_local(struct nw_interp *i); +void nwp_op_set_global(struct nw_interp *i); +void nwp_op_get_global(struct nw_interp *i); +void nwp_op_set_global(struct nw_interp *i); +void nwp_op_i32_load(struct nw_interp *i); +void nwp_op_i32_load8_u(struct nw_interp *i); +void nwp_op_i32_store(struct nw_interp *i); +void nwp_op_i64_store(struct nw_interp *i); +void nwp_op_current_memory(struct nw_interp *i); +void nwp_op_i32_const(struct nw_interp *i); +void nwp_op_i64_const(struct nw_interp *i); +void nwp_op_f32_const(struct nw_interp *i); +void nwp_op_f64_const(struct nw_interp *i); +void nwp_op_i32_add(struct nw_interp *i); +void nwp_op_i32_sub(struct nw_interp *i); +void nwp_op_i32_mul(struct nw_interp *i); +void nwp_op_i32_and(struct nw_interp *i); +void nwp_op_i32_or(struct nw_interp *i); +void nwp_op_i32_eqz(struct nw_interp *i); +void nwp_op_i32_eq(struct nw_interp *i); +void nwp_op_i32_ne(struct nw_interp *i); +void nwp_op_i32_lt_s(struct nw_interp *i); +void nwp_op_i32_ge_s(struct nw_interp *i); +void nwp_op_i32_ge_u(struct nw_interp *i); + +void nwp_op_check_no_immediate(struct nw_mod *m); +void nwp_op_check_block(struct nw_mod *m); +void nwp_op_check_loop(struct nw_mod *m); +void nwp_op_check_end(struct nw_mod *m); +void nwp_op_check_relative_depth(struct nw_mod *m); +void nwp_op_check_br_table(struct nw_mod *m); +void nwp_op_check_call(struct nw_mod *m); +void nwp_op_check_call_indirect(struct nw_mod *m); +void nwp_op_check_local_index(struct nw_mod *m); +void nwp_op_check_global_index(struct nw_mod *m); +void nwp_op_check_memory_immediate(struct nw_mod *m); +void nwp_op_check_varuint1(struct nw_mod *m); +void nwp_op_check_varint32(struct nw_mod *m); +void nwp_op_check_varint64(struct nw_mod *m); +void nwp_op_check_uint32(struct nw_mod *m); +void nwp_op_check_uint64(struct nw_mod *m); +void nwp_op_check_misc(struct nw_mod *m); + +struct nwp_check_op +{ + enum opcode start, end; + void (*f)(struct nw_mod *); +}; + +struct nwp_check_ops +{ + const struct nwp_check_op *ops; + size_t n; +}; + +extern const struct nwp_check_ops nwp_check_ops; + +#endif diff --git a/private_include/nw/routines.h b/private_include/nw/routines.h new file mode 100644 index 0000000..974f6da --- /dev/null +++ b/private_include/nw/routines.h @@ -0,0 +1,71 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef ROUTINES_H +#define ROUTINES_H + +#include <nanowasm/nw.h> + +void nwp_check_magic(struct nw_mod *m); +void nwp_check_version(struct nw_mod *m); +void nwp_section(struct nw_mod *m); +void nwp_section_custom(struct nw_mod *m); +void nwp_section_type(struct nw_mod *m); +void nwp_section_import(struct nw_mod *m); +void nwp_section_function(struct nw_mod *m); +void nwp_section_table(struct nw_mod *m); +void nwp_section_memory(struct nw_mod *m); +void nwp_section_global(struct nw_mod *m); +void nwp_section_export(struct nw_mod *m); +void nwp_section_start(struct nw_mod *m); +void nwp_section_element(struct nw_mod *m); +void nwp_section_code(struct nw_mod *m); +void nwp_section_data(struct nw_mod *m); +void nwp_section_data_count(struct nw_mod *m); +void nwp_section_to(struct nw_mod *m); +void nwp_section_fti(struct nw_mod *m); +void nwp_section_fbo(struct nw_mod *m); +void nwp_section_lo(struct nw_mod *m); +void nwp_section_iti(struct nw_mod *m); +void nwp_section_skip(struct nw_mod *m); +enum nw_state nwp_section_exit(struct nw_mod *m); + +void nwp_init_globals(struct nw_inst *i, + enum nw_state (*next)(struct nw_inst *)); +void nwp_init_data(struct nw_inst *i, + enum nw_state (*next)(struct nw_inst *)); + +enum nw_state nwp_execute(struct nw_interp *i); +void nwp_find_export(struct nw_interp *i, const char *sym, + enum nw_state (*next)(struct nw_interp *)); +void nwp_call(struct nw_interp *i, nw_varuint32 index); +void nwp_call_function(struct nw_interp *i, nw_varuint32 index); +void nwp_call_import(struct nw_interp *i, nw_varuint32 index); +void nwp_find_function(struct nw_interp *i, const struct nw_fn *fn, + void (*next)(struct nw_interp *)); +void nwp_get_function_type(struct nw_interp *i, nw_varuint32 index, + enum nw_state (*next)(struct nw_interp *)); +void nwp_get_import_type(struct nw_interp *i, struct nw_get_import_type *t, + nw_varuint32 index, enum nw_state (*next)(struct nw_interp *)); +void nwp_mem_imm(struct nw_interp *i, void (*next)(struct nw_interp *)); +void nwp_unwind(struct nw_interp *i); +void nwp_break(struct nw_interp *i, nw_varuint32 relative_depth); +void nwp_find_param(struct nw_interp *i, struct nw_find_param *f, + nw_varuint32 index, enum nw_state (*next)(struct nw_interp *), void *args); +void nwp_find_local(struct nw_interp *i, struct nw_find_local *f, + nw_varuint32 index, enum nw_state (*next)(struct nw_interp *), void *args); +void nwp_set_local(struct nw_interp *i, + enum nw_state (*next)(struct nw_interp *)); +void nwp_arithm(struct nw_interp *i, enum nw_type t, + int (*op)(const struct nw_i_sm_arithm_out *, union nw_value *)); +void nwp_unary(struct nw_interp *i, enum nw_type t, + int (*op)(const union nw_value *, union nw_value *)); +void nwp_start_block(struct nw_interp *i); + +#endif diff --git a/private_include/nw/stack.h b/private_include/nw/stack.h new file mode 100644 index 0000000..332b4f5 --- /dev/null +++ b/private_include/nw/stack.h @@ -0,0 +1,24 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef STACK_H +#define STACK_H + +#include <nanowasm/nw.h> +#include <stddef.h> + +size_t nwp_stack_ptr(const struct nw_interp *i); +enum nw_state nwp_stack_push(struct nw_interp *i, struct nw_sm_io *io); +enum nw_state nwp_stack_pop(struct nw_interp *i, struct nw_sm_io *io); +enum nw_state nwp_stack_read(struct nw_interp *i, struct nw_sm_io *io, + size_t offset); +enum nw_state nwp_stack_write(struct nw_interp *i, struct nw_sm_io *io, + size_t offset); + +#endif diff --git a/private_include/nw/types.h b/private_include/nw/types.h new file mode 100644 index 0000000..99e6f55 --- /dev/null +++ b/private_include/nw/types.h @@ -0,0 +1,19 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef TYPES_H +#define TYPES_H + +#include <nanowasm/nw.h> +#include <stddef.h> + +int nwp_get_type(nw_varint7 type, enum nw_type *vtype); +int nwp_type_sz(enum nw_type type, size_t *sz); + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..4151987 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,23 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + init.c + load.c + run.c + rexc.c + start.c +) + +add_subdirectory(dbg) +add_subdirectory(inst) +add_subdirectory(interp) +add_subdirectory(io) +add_subdirectory(log) +add_subdirectory(op) +add_subdirectory(routines) +add_subdirectory(types) diff --git a/src/dbg/CMakeLists.txt b/src/dbg/CMakeLists.txt new file mode 100644 index 0000000..c68a3c8 --- /dev/null +++ b/src/dbg/CMakeLists.txt @@ -0,0 +1,15 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + global.c + init.c + local.c + mem_read.c + param.c + value.c +) diff --git a/src/dbg/global.c b/src/dbg/global.c new file mode 100644 index 0000000..cad17ae --- /dev/null +++ b/src/dbg/global.c @@ -0,0 +1,47 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/dbg.h> +#include <nw/global.h> +#include <nw/log.h> +#include <nw/types.h> + +static enum nw_state read_global(struct nw_interp *const i) +{ + struct nw_dbg *const d = i->args; + struct nw_dbg_sm_global *const pg = &d->sm.global; + struct nw_dbg_value *const v = &d->v; + const struct nw_global *const gl = &pg->gl; + const enum nw_state n = nwp_global_load(i, &pg->io, pg->index); + + if (n) + return n; + + v->type = gl->type; + v->value = gl->value; + i->next = pg->inext; + return NW_AGAIN; +} + +void nw_dbg_global(struct nw_dbg *const d, const unsigned long index) +{ + const struct nw_dbg_sm_global gl = {0}; + struct nw_dbg_sm_global *const pg = &d->sm.global; + struct nw_sm_io *const io = &pg->io; + struct nw_interp *const i = &d->cfg.inst->interp; + + *pg = gl; + pg->index = index; + pg->inext = i->next; + io->buf = &pg->gl; + io->n = sizeof pg->gl; + i->args = d; + i->next = read_global; +} diff --git a/src/dbg/init.c b/src/dbg/init.c new file mode 100644 index 0000000..6169413 --- /dev/null +++ b/src/dbg/init.c @@ -0,0 +1,19 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/dbg.h> + +void nw_dbg_init(struct nw_dbg *const pd, const struct nw_dbg_cfg *const cfg) +{ + const struct nw_dbg d = {0}; + + *pd = d; + pd->cfg = *cfg; +} diff --git a/src/dbg/local.c b/src/dbg/local.c new file mode 100644 index 0000000..b48ac92 --- /dev/null +++ b/src/dbg/local.c @@ -0,0 +1,71 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/dbg.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state read_local(struct nw_interp *const i) +{ + struct nw_dbg *const d = i->args; + struct nw_dbg_sm_local *const pl = &d->sm.local; + const struct nw_find_local *const fl = &pl->fl; + const enum nw_state n = nwp_stack_read(i, &pl->io, fl->addr); + + if (n) + return n; + + i->next = pl->inext; + return NW_AGAIN; +} + +static enum nw_state done(struct nw_interp *const i) +{ + struct nw_dbg *const d = i->args; + struct nw_dbg_sm_local *const pl = &d->sm.local; + const struct nw_local_meta *const m = &pl->fl.meta; + const enum nw_type t = m->type; + size_t sz; + + if (nwp_type_sz(t, &sz)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)t); +#endif + return -1; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &d->v.value; + io.n = sz; + pl->io = io; + } + + i->next = read_local; + return NW_AGAIN; +} + +void nw_dbg_local(struct nw_dbg *const d, const unsigned long index) +{ + const struct nw_dbg_sm_local l = {0}; + struct nw_dbg_sm_local *const pl = &d->sm.local; + struct nw_interp *const i = &d->cfg.inst->interp; + + *pl = l; + pl->inext = i->next; + nwp_find_local(i, &pl->fl, index, done, d); +} diff --git a/src/dbg/mem_read.c b/src/dbg/mem_read.c new file mode 100644 index 0000000..d1f419c --- /dev/null +++ b/src/dbg/mem_read.c @@ -0,0 +1,47 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/dbg.h> +#include <nw/linear.h> +#include <nw/types.h> + +static enum nw_state load_mem(struct nw_interp *const i) +{ + struct nw_dbg *const d = i->args; + struct nw_dbg_sm_mem_load *const pm = &d->sm.mem_load; + const enum nw_state n = nwp_linear_load(i, &pm->io, pm->offset); + + if (n) + return n; + + i->next = pm->inext; + return NW_AGAIN; +} + +void nw_dbg_mem_load(struct nw_dbg *const d, const enum nw_type t, + const unsigned long offset) +{ + const struct nw_dbg_sm_mem_load ml = {0}; + struct nw_dbg_sm_mem_load *const pm = &d->sm.mem_load; + struct nw_sm_io *const io = &pm->io; + struct nw_interp *const i = &d->cfg.inst->interp; + struct nw_dbg_value *const v = &d->v; + size_t sz; + + nwp_type_sz(t, &sz); + *pm = ml; + pm->inext = i->next; + pm->offset = offset; + v->type = t; + io->buf = &v->value; + io->n = sz; + i->args = d; + i->next = load_mem; +} diff --git a/src/dbg/param.c b/src/dbg/param.c new file mode 100644 index 0000000..db0a599 --- /dev/null +++ b/src/dbg/param.c @@ -0,0 +1,71 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/dbg.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state read_param(struct nw_interp *const i) +{ + struct nw_dbg *const d = i->args; + struct nw_dbg_sm_param *const p = &d->sm.param; + const struct nw_find_param_out *const out = &p->fp.out; + const enum nw_state n = nwp_stack_read(i, &p->io, out->addr); + + if (n) + return n; + + i->next = p->inext; + return NW_AGAIN; +} + +static enum nw_state done(struct nw_interp *const i) +{ + struct nw_dbg *const d = i->args; + struct nw_dbg_sm_param *const p = &d->sm.param; + const struct nw_find_param_out *const po = &p->fp.out; + const enum nw_type t = po->type; + size_t sz; + + if (nwp_type_sz(t, &sz)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)t); +#endif + return -1; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &d->v.value; + io.n = sz; + p->io = io; + } + + i->next = read_param; + return NW_AGAIN; +} + +void nw_dbg_param(struct nw_dbg *const d, const unsigned long index) +{ + const struct nw_dbg_sm_param p = {0}; + struct nw_dbg_sm_param *const pp = &d->sm.param; + struct nw_interp *const i = &d->cfg.inst->interp; + + *pp = p; + pp->inext = i->next; + nwp_find_param(i, &pp->fp, index, done, d); +} diff --git a/src/dbg/value.c b/src/dbg/value.c new file mode 100644 index 0000000..633c5f7 --- /dev/null +++ b/src/dbg/value.c @@ -0,0 +1,17 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/dbg.h> + +int nw_dbg_value(const struct nw_dbg *const d, struct nw_dbg_value *const v) +{ + *v = d->v; + return 0; +} diff --git a/src/init.c b/src/init.c new file mode 100644 index 0000000..d0358e9 --- /dev/null +++ b/src/init.c @@ -0,0 +1,28 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <stddef.h> + +void nw_init(struct nw_mod *const m, const struct nw_mod_cfg *const cfg) +{ + const struct nw_mod mm = {0}; + size_t i; + + *m = mm; + m->cfg = *cfg; + + for (i = 0; i < cfg->n_imports; i++) + cfg->imp_indexes[i].index = -1; + + nwp_check_magic(m); +} diff --git a/src/inst/CMakeLists.txt b/src/inst/CMakeLists.txt new file mode 100644 index 0000000..a082191 --- /dev/null +++ b/src/inst/CMakeLists.txt @@ -0,0 +1,10 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + run.c +) diff --git a/src/inst/run.c b/src/inst/run.c new file mode 100644 index 0000000..5a8e411 --- /dev/null +++ b/src/inst/run.c @@ -0,0 +1,17 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/inst.h> +#include <nw/interp.h> + +enum nw_state nwp_inst_run(struct nw_inst *const i) +{ + return nwp_interp_run(&i->interp); +} diff --git a/src/interp/CMakeLists.txt b/src/interp/CMakeLists.txt new file mode 100644 index 0000000..c38da8c --- /dev/null +++ b/src/interp/CMakeLists.txt @@ -0,0 +1,21 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + data_set.c + initexpr_set.c + ops.c + run.c + resume.c + start.c +) + +add_subdirectory(global) +add_subdirectory(linear) +add_subdirectory(mem) +add_subdirectory(routines) +add_subdirectory(stack) diff --git a/src/interp/data_set.c b/src/interp/data_set.c new file mode 100644 index 0000000..266852b --- /dev/null +++ b/src/interp/data_set.c @@ -0,0 +1,24 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/opcodes.h> + +static const enum opcode opcodes[] = +{ + OP_END, + OP_I32_CONST, +}; + +const struct nwp_interp_set nwp_interp_data_set = +{ + opcodes, + sizeof opcodes / sizeof *opcodes +}; diff --git a/src/interp/global/CMakeLists.txt b/src/interp/global/CMakeLists.txt new file mode 100644 index 0000000..0f0e056 --- /dev/null +++ b/src/interp/global/CMakeLists.txt @@ -0,0 +1,11 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + load.c + store.c +) diff --git a/src/interp/global/load.c b/src/interp/global/load.c new file mode 100644 index 0000000..26d8c8d --- /dev/null +++ b/src/interp/global/load.c @@ -0,0 +1,34 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/global.h> +#include <nw/mem.h> +#include <nw/log.h> + +enum nw_state nwp_global_load(struct nw_interp *const i, + struct nw_sm_io *const io, const nw_varuint32 index) +{ + const struct nw_interp_cfg *const cfg = &i->cfg; + const size_t offset = index * sizeof (struct nw_global); + const enum nw_state n = nwp_mem_load(&cfg->global, io, offset, cfg->user); + + if (n == NW_FATAL) + { + static const char *const exc = "failed to load from global memory"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + } + + return n; +} diff --git a/src/interp/global/store.c b/src/interp/global/store.c new file mode 100644 index 0000000..91550d4 --- /dev/null +++ b/src/interp/global/store.c @@ -0,0 +1,34 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/global.h> +#include <nw/interp.h> +#include <nw/mem.h> +#include <nw/log.h> + +enum nw_state nwp_global_store(struct nw_interp *const i, + struct nw_sm_io *const io, const nw_varuint32 index) +{ + const struct nw_interp_cfg *const cfg = &i->cfg; + const size_t offset = index * sizeof (struct nw_global); + const enum nw_state n = nwp_mem_store(&cfg->global, io, offset, cfg->user); + + if (n == NW_FATAL) + { + static const char *const exc = "failed to store into global memory"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + } + + return n; +} diff --git a/src/interp/initexpr_set.c b/src/interp/initexpr_set.c new file mode 100644 index 0000000..1e200f6 --- /dev/null +++ b/src/interp/initexpr_set.c @@ -0,0 +1,28 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/opcodes.h> + +static const enum opcode opcodes[] = +{ + OP_NOP, + OP_END, + OP_I32_CONST, + OP_I64_CONST, + OP_F32_CONST, + OP_F64_CONST +}; + +const struct nwp_interp_set nwp_interp_initexpr_set = +{ + opcodes, + sizeof opcodes / sizeof *opcodes +}; diff --git a/src/interp/linear/CMakeLists.txt b/src/interp/linear/CMakeLists.txt new file mode 100644 index 0000000..0f0e056 --- /dev/null +++ b/src/interp/linear/CMakeLists.txt @@ -0,0 +1,11 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + load.c + store.c +) diff --git a/src/interp/linear/load.c b/src/interp/linear/load.c new file mode 100644 index 0000000..afc6154 --- /dev/null +++ b/src/interp/linear/load.c @@ -0,0 +1,39 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/linear.h> +#include <nw/interp.h> +#include <nw/mem.h> +#include <nw/log.h> + +enum nw_state nwp_linear_load(struct nw_interp *const i, + struct nw_sm_io *const io, const unsigned long offset) +{ + const struct nw_interp_cfg *const cfg = &i->cfg; + const enum nw_state n = nwp_mem_load(&cfg->linear, io, offset, cfg->user); + + if (n == NW_FATAL) + { + static const char *const exc = "failed to load from linear memory"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + } + + return n; +} + +enum nw_state nw_linear_load(struct nw_inst *const i, + struct nw_sm_io *const io, const unsigned long offset) +{ + return nwp_linear_load(&i->interp, io, offset); +} diff --git a/src/interp/linear/store.c b/src/interp/linear/store.c new file mode 100644 index 0000000..6edffd9 --- /dev/null +++ b/src/interp/linear/store.c @@ -0,0 +1,40 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/linear.h> +#include <nw/linear.h> +#include <nw/interp.h> +#include <nw/mem.h> +#include <nw/log.h> + +enum nw_state nwp_linear_store(struct nw_interp *const i, + struct nw_sm_io *const io, const unsigned long offset) +{ + const struct nw_interp_cfg *const cfg = &i->cfg; + const enum nw_state n = nwp_mem_store(&cfg->linear, io, offset, cfg->user); + + if (n == NW_FATAL) + { + static const char *const exc = "failed to store into linear memory"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + } + + return n; +} + +enum nw_state nw_linear_store(struct nw_inst *const i, + struct nw_sm_io *const io, const unsigned long offset) +{ + return nwp_linear_store(&i->interp, io, offset); +} diff --git a/src/interp/mem/CMakeLists.txt b/src/interp/mem/CMakeLists.txt new file mode 100644 index 0000000..0f0e056 --- /dev/null +++ b/src/interp/mem/CMakeLists.txt @@ -0,0 +1,11 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + load.c + store.c +) diff --git a/src/interp/mem/load.c b/src/interp/mem/load.c new file mode 100644 index 0000000..0ccf29b --- /dev/null +++ b/src/interp/mem/load.c @@ -0,0 +1,27 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/mem.h> +#include <nw/log.h> + +enum nw_state nwp_mem_load(const struct nw_mem_cfg *const cfg, + struct nw_sm_io *const io, const nw_varuint32 offset, void *const user) +{ + void *const dst = (unsigned char *)io->buf + io->read; + const int n = cfg->load(offset + io->read, dst, io->n - io->read, user); + + if (n < 0) + return NW_FATAL; + else if ((io->read += n) >= io->n) + return NW_OK; + + return NW_AGAIN; +} diff --git a/src/interp/mem/store.c b/src/interp/mem/store.c new file mode 100644 index 0000000..cc58169 --- /dev/null +++ b/src/interp/mem/store.c @@ -0,0 +1,27 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/mem.h> +#include <nw/log.h> + +enum nw_state nwp_mem_store(const struct nw_mem_cfg *const cfg, + struct nw_sm_io *const io, const nw_varuint32 offset, void *const user) +{ + const void *const src = (const unsigned char *)io->buf + io->read; + const int n = cfg->store(offset + io->read, src, io->n - io->read, user); + + if (n < 0) + return NW_FATAL; + else if ((io->read += n) >= io->n) + return NW_OK; + + return NW_AGAIN; +} diff --git a/src/interp/ops.c b/src/interp/ops.c new file mode 100644 index 0000000..6135f96 --- /dev/null +++ b/src/interp/ops.c @@ -0,0 +1,278 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/opcodes.h> +#include <nw/ops.h> +#include <nw/interp.h> + +static void (*const ops[])(struct nw_interp *) = +{ + nwp_op_unreachable, /* OP_UNREACHABLE */ + nwp_op_nop, /* OP_NOP */ + nwp_op_block, /* OP_BLOCK */ + nwp_op_loop, /* OP_LOOP */ + NULL, /* OP_IF */ + NULL, /* OP_ELSE */ + NULL, + NULL, + NULL, + NULL, + NULL, + nwp_op_end, /* OP_END */ + nwp_op_br, /* OP_BR */ + nwp_op_br_if, /* OP_BR_IF */ + NULL, /* OP_BR_TABLE */ + nwp_op_return, /* OP_RETURN */ + nwp_op_call, /* OP_CALL */ + nwp_op_call_indirect, /* OP_CALL_INDIRECT */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + nwp_op_drop, /* OP_DROP */ + NULL, /* OP_SELECT */ + NULL, + NULL, + NULL, + NULL, + nwp_op_get_local, /* OP_GET_LOCAL */ + nwp_op_set_local, /* OP_SET_LOCAL */ + nwp_op_tee_local, /* OP_TEE_LOCAL */ + nwp_op_get_global, /* OP_GET_GLOBAL */ + nwp_op_set_global, /* OP_SET_GLOBAL */ + NULL, + NULL, + NULL, + nwp_op_i32_load, /* OP_I32_LOAD */ + NULL, /* OP_I64_LOAD */ + NULL, /* OP_F32_LOAD */ + NULL, /* OP_F64_LOAD */ + NULL, /* OP_I32_LOAD8_S */ + nwp_op_i32_load8_u, /* OP_I32_LOAD8_U */ + NULL, /* OP_I32_LOAD16_S */ + NULL, /* OP_I32_LOAD16_U */ + NULL, /* OP_I64_LOAD8_S */ + NULL, /* OP_I64_LOAD8_U */ + NULL, /* OP_I64_LOAD16_S */ + NULL, /* OP_I64_LOAD16_U */ + NULL, /* OP_I64_LOAD32_S */ + NULL, /* OP_I64_LOAD32_U */ + nwp_op_i32_store, /* OP_I32_STORE */ + nwp_op_i64_store, /* OP_I64_STORE */ + NULL, /* OP_F32_STORE */ + NULL, /* OP_F64_STORE */ + NULL, /* OP_I32_STORE8 */ + NULL, /* OP_I32_STORE16 */ + NULL, /* OP_I64_STORE8 */ + NULL, /* OP_I64_STORE16 */ + NULL, /* OP_I64_STORE32 */ + NULL, /* OP_CURRENT_MEMORY */ + NULL, /* OP_GROW_MEMORY */ + nwp_op_i32_const, /* OP_I32_CONST */ + nwp_op_i64_const, /* OP_I64_CONST */ + NULL, /* OP_F32_CONST */ + NULL, /* OP_F64_CONST */ + nwp_op_i32_eqz, /* OP_I32_EQZ */ + nwp_op_i32_eq, /* OP_I32_EQ */ + nwp_op_i32_ne, /* OP_I32_NE */ + nwp_op_i32_lt_s, /* OP_I32_LT_S */ + NULL, /* OP_I32_LT_U */ + NULL, /* OP_I32_GT_S */ + NULL, /* OP_I32_GT_U */ + NULL, /* OP_I32_LE_S */ + NULL, /* OP_I32_LE_U */ + nwp_op_i32_ge_s, /* OP_I32_GE_S */ + nwp_op_i32_ge_u, /* OP_I32_GE_U */ + NULL, /* OP_I64_EQZ */ + NULL, /* OP_I64_EQ */ + NULL, /* OP_I64_NE */ + NULL, /* OP_I64_LT_S */ + NULL, /* OP_I64_LT_U */ + NULL, /* OP_I64_GT_S */ + NULL, /* OP_I64_GT_U */ + NULL, /* OP_I64_LE_S */ + NULL, /* OP_I64_LE_U */ + NULL, /* OP_I64_GE_S */ + NULL, /* OP_I64_GE_U */ + NULL, /* OP_F32_EQ */ + NULL, /* OP_F32_NE */ + NULL, /* OP_F32_LT */ + NULL, /* OP_F32_GT */ + NULL, /* OP_F32_LE */ + NULL, /* OP_F32_GE */ + NULL, /* OP_F64_EQ */ + NULL, /* OP_F64_NE */ + NULL, /* OP_F64_LT */ + NULL, /* OP_F64_GT */ + NULL, /* OP_F64_LE */ + NULL, /* OP_F64_GE */ + NULL, /* OP_I32_CLZ */ + NULL, /* OP_I32_CTZ */ + NULL, /* OP_I32_POPCNT */ + nwp_op_i32_add, /* OP_I32_ADD */ + nwp_op_i32_sub, /* OP_I32_SUB */ + nwp_op_i32_mul, /* OP_I32_MUL */ + NULL, /* OP_I32_DIV_S */ + NULL, /* OP_I32_DIV_U */ + NULL, /* OP_I32_REM_S */ + NULL, /* OP_I32_REM_U */ + nwp_op_i32_and, /* OP_I32_AND */ + nwp_op_i32_or, /* OP_I32_OR */ + NULL, /* OP_I32_XOR */ + NULL, /* OP_I32_SHL */ + NULL, /* OP_I32_SHR_S */ + NULL, /* OP_I32_SHR_U */ + NULL, /* OP_I32_ROTL */ + NULL, /* OP_I32_ROTR */ + NULL, /* OP_I64_CLZ */ + NULL, /* OP_I64_CTZ */ + NULL, /* OP_I64_POPCNT */ + NULL, /* OP_I64_ADD */ + NULL, /* OP_I64_SUB */ + NULL, /* OP_I64_MUL */ + NULL, /* OP_I64_DIV_S */ + NULL, /* OP_I64_DIV_U */ + NULL, /* OP_I64_REM_S */ + NULL, /* OP_I64_REM_U */ + NULL, /* OP_I64_AND */ + NULL, /* OP_I64_OR */ + NULL, /* OP_I64_XOR */ + NULL, /* OP_I64_SHL */ + NULL, /* OP_I64_SHR_S */ + NULL, /* OP_I64_SHR_U */ + NULL, /* OP_I64_ROTL */ + NULL, /* OP_I64_ROTR */ + NULL, /* OP_F32_ABS */ + NULL, /* OP_F32_NEG */ + NULL, /* OP_F32_CEIL */ + NULL, /* OP_F32_FLOOR */ + NULL, /* OP_F32_TRUNC */ + NULL, /* OP_F32_NEAREST */ + NULL, /* OP_F32_SQRT */ + NULL, /* OP_F32_ADD */ + NULL, /* OP_F32_SUB */ + NULL, /* OP_F32_MUL */ + NULL, /* OP_F32_DIV */ + NULL, /* OP_F32_MIN */ + NULL, /* OP_F32_MAX */ + NULL, /* OP_F32_COPYSIGN */ + NULL, /* OP_F64_ABS */ + NULL, /* OP_F64_NEG */ + NULL, /* OP_F64_CEIL */ + NULL, /* OP_F64_FLOOR */ + NULL, /* OP_F64_TRUNC */ + NULL, /* OP_F64_NEAREST */ + NULL, /* OP_F64_SQRT */ + NULL, /* OP_F64_ADD */ + NULL, /* OP_F64_SUB */ + NULL, /* OP_F64_MUL */ + NULL, /* OP_F64_DIV */ + NULL, /* OP_F64_MIN */ + NULL, /* OP_F64_MAX */ + NULL, /* OP_F64_COPYSIGN */ + NULL, /* OP_I32_WRAP_I64 */ + NULL, /* OP_I32_TRUNC_S_F32 */ + NULL, /* OP_I32_TRUNC_U_F32 */ + NULL, /* OP_I32_TRUNC_S_F64 */ + NULL, /* OP_I32_TRUNC_U_F64 */ + NULL, /* OP_I64_EXTEND_S_I32 */ + NULL, /* OP_I64_EXTEND_U_I32 */ + NULL, /* OP_I64_TRUNC_S_F32 */ + NULL, /* OP_I64_TRUNC_U_F32 */ + NULL, /* OP_I64_TRUNC_S_F64 */ + NULL, /* OP_I64_TRUNC_U_F64 */ + NULL, /* OP_F32_CONVERT_S_I32 */ + NULL, /* OP_F32_CONVERT_U_I32 */ + NULL, /* OP_F32_CONVERT_S_I64 */ + NULL, /* OP_F32_CONVERT_U_I64 */ + NULL, /* OP_F32_DEMOTE_F64 */ + NULL, /* OP_F64_CONVERT_S_I32 */ + NULL, /* OP_F64_CONVERT_U_I32 */ + NULL, /* OP_F64_CONVERT_S_I64 */ + NULL, /* OP_F64_CONVERT_U_I64 */ + NULL, /* OP_F64_PROMOTE_F32 */ + NULL, /* OP_I32_REINTERPRET_F32 */ + NULL, /* OP_I64_REINTERPRET_F64 */ + NULL, /* OP_F32_REINTERPRET_I32 */ + NULL, /* OP_F64_REINTERPRET_I64 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL /* OP_MISC */ +}; + +const struct nwp_ops nwp_ops = +{ + ops, + sizeof ops / sizeof *ops +}; diff --git a/src/interp/resume.c b/src/interp/resume.c new file mode 100644 index 0000000..cf9ece2 --- /dev/null +++ b/src/interp/resume.c @@ -0,0 +1,20 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/interp.h> + +void nwp_interp_resume(struct nw_interp *const i) +{ + if (i->set) + nwp_interp_limited(i); + else + nwp_interp_full(i); +} diff --git a/src/interp/routines/CMakeLists.txt b/src/interp/routines/CMakeLists.txt new file mode 100644 index 0000000..b27172b --- /dev/null +++ b/src/interp/routines/CMakeLists.txt @@ -0,0 +1,13 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + execute.c + find_export.c + full.c + limited.c +) diff --git a/src/interp/routines/execute.c b/src/interp/routines/execute.c new file mode 100644 index 0000000..49711ae --- /dev/null +++ b/src/interp/routines/execute.c @@ -0,0 +1,83 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/opcodes.h> +#include <nw/ops.h> +#include <nw/routines.h> + +static enum nw_state execute(struct nw_interp *const i) +{ + struct nw_i_sm_b *const b = &i->sm.bytecode; + +#ifdef NW_LOG + nwp_log("opcode: %s, pc=%#lx\n", nwp_op_tostr(b->op), b->pc); +#endif + b->f(i); + i->fr.prev_op = b->op; + return NW_AGAIN; +} + +static enum nw_state repeat(struct nw_interp *const i) +{ + struct nw_i_sm_b *const b = &i->sm.bytecode; + struct nw_next *const next = &b->next; + const enum nw_state n = next->fn(next->user, next); + + if (n) + return n; + + return execute(i); +} + +static enum nw_state exec_pc(struct nw_interp *const i) +{ + struct nw_i_sm_b *const b = &i->sm.bytecode; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const long pc = b->pc; + + if (cfg->pc) + { + const enum nw_state n = cfg->pc(pc, &b->next, cfg->user); + + if (n == NW_FATAL) + { + static const char *const exc = "pc callback failed"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s, pc: %ld\n", exc, pc); +#endif + return NW_FATAL; + } + else if (n) + { + i->next = repeat; + return n; + } + } + + return execute(i); +} + +enum nw_state nwp_execute(struct nw_interp *const i) +{ + struct nw_i_sm_b *const b = &i->sm.bytecode; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&b->pc, cfg->user); + + if (n) + return n; + + b->pc -= sizeof b->op; + i->next = exec_pc; + return NW_AGAIN; +} diff --git a/src/interp/routines/find_export.c b/src/interp/routines/find_export.c new file mode 100644 index 0000000..effa8d1 --- /dev/null +++ b/src/interp/routines/find_export.c @@ -0,0 +1,229 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <string.h> + +static enum nw_state entry_loop(struct nw_interp *i); + +static enum nw_state get_index(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_exp *const e = &i->sm.export; + struct nw_sm_leb128 *const l = &e->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &e->index, cfg->user); + + if (n) + return n; + + i->next = e->next; + return NW_AGAIN; +} + +static enum nw_state get_kind(struct nw_interp *const i) +{ + unsigned char kind; + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_exp *const e = &i->sm.export; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &kind; + io.n = sizeof kind; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else if (kind >= NW_KINDS) + { + static const char *const exc = "invalid export kind"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)kind); +#endif + return NW_FATAL; + } + + e->kind = kind; + i->next = get_index; + return NW_AGAIN; +} + +static enum nw_state skip_index(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_exp *const e = &i->sm.export; + struct nw_sm_leb128 *const l = &e->leb128; + nw_varuint32 index; + const enum nw_state n = nwp_varuint32(cfg, l, &index, cfg->user); + + if (n) + return n; + + e->entry_i++; + i->next = entry_loop; + return NW_AGAIN; +} + +static enum nw_state skip_kind(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + unsigned char b; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &b; + io.n = sizeof b; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + + i->next = skip_index; + return NW_AGAIN; +} + +static enum nw_state skip_field_str(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + const struct nw_i_sm_exp *const e = &i->sm.export; + const long offset = e->offset + (e->len - e->len_i); + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + i->next = skip_kind; + return NW_AGAIN; +} + +static enum nw_state tell(struct nw_interp *const i) +{ + struct nw_i_sm_exp *const e = &i->sm.export; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&e->offset, cfg->user); + + if (n) + return n; + + i->next = skip_field_str; + return NW_AGAIN; +} + +static enum nw_state compare(struct nw_interp *const i) +{ + unsigned char b; + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_exp *const e = &i->sm.export; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &b; + io.n = sizeof b; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else if (b != e->sym[e->len_i++]) + i->next = tell; + else if (e->len_i >= e->len) + i->next = get_kind; + + return NW_AGAIN; +} + +static enum nw_state get_len(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_exp *const e = &i->sm.export; + struct nw_sm_leb128 *const l = &e->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &e->len, cfg->user); + + if (n) + return n; + else if (e->len != strlen(e->sym)) + i->next = skip_field_str; + else + i->next = compare; + + return NW_AGAIN; +} + +static enum nw_state entry_loop(struct nw_interp *const i) +{ + struct nw_i_sm_exp *const e = &i->sm.export; + + if (e->entry_i >= e->count) + { + static const char *const exc = "failed to find symbol"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %s\n", exc, e->sym); +#endif + return NW_FATAL; + } + + e->len_i = 0; + i->next = get_len; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_exp *const e = &i->sm.export; + struct nw_sm_leb128 *const l = &e->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &e->count, cfg->user); + + if (n) + return n; + + i->next = entry_loop; + return NW_AGAIN; +} + +static enum nw_state seek(struct nw_interp *const i) +{ + const struct nw_mod *const m = i->cfg.m; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const long offset = m->sections[NW_SECTION_EXPORT]; + enum nw_state n; + + if (!offset) + { + static const char *const exc = "section not found"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %s\n", exc, "export"); +#endif + return NW_FATAL; + } + else if ((n = cfg->seek(offset, cfg->user))) + return n; + + i->next = get_count; + return NW_AGAIN; +} + +void nwp_find_export(struct nw_interp *const i, const char *const sym, + enum nw_state (*const next)(struct nw_interp *)) +{ + const struct nw_i_sm_exp e = {0}; + struct nw_i_sm_exp *const pe = &i->sm.export; + + *pe = e; + pe->sym = sym; + pe->next = next; + i->next = seek; +} diff --git a/src/interp/routines/full.c b/src/interp/routines/full.c new file mode 100644 index 0000000..c00f11f --- /dev/null +++ b/src/interp/routines/full.c @@ -0,0 +1,63 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/opcodes.h> +#include <nw/ops.h> +#include <nw/routines.h> + +static enum nw_state tell(struct nw_interp *const i) +{ + long offset; + struct nw_i_sm_b *const b = &i->sm.bytecode; + static const char *const exc = "invalid opcode"; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&offset, cfg->user); + + if (n) + return n; + + offset -= sizeof b->op; + +#ifdef NW_LOG + nwp_log("%s: %#x, offset=%#lx\n", exc, (unsigned)b->op, offset); +#endif + i->exception = exc; + return NW_FATAL; +} + +static enum nw_state get_bytecode(struct nw_interp *const i) +{ + struct nw_i_sm_b *const b = &i->sm.bytecode; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &b->io, cfg->user); + + if (n) + return n; + else if (b->op >= nwp_ops.n || !(b->f = nwp_ops.ops[b->op])) + { + i->next = tell; + return NW_AGAIN; + } + + i->next = nwp_execute; + return NW_AGAIN; +} + +void nwp_interp_full(struct nw_interp *const i) +{ + struct nw_i_sm_b *const pb = &i->sm.bytecode, b = {0}; + + b.io.buf = &pb->op; + b.io.n = sizeof pb->op; + *pb = b; + i->next = get_bytecode; +} diff --git a/src/interp/routines/limited.c b/src/interp/routines/limited.c new file mode 100644 index 0000000..dc18ded --- /dev/null +++ b/src/interp/routines/limited.c @@ -0,0 +1,54 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <stddef.h> + +static enum nw_state get_bytecode(struct nw_interp *const in) +{ + const struct nw_io_cfg *const cfg = &in->cfg.io; + struct nw_i_sm_b *const b = &in->sm.bytecode; + const struct nwp_interp_set *const set = in->set; + static const char *const exc = "invalid opcode"; + const enum nw_state n = nwp_io_read(cfg, &b->io, cfg->user); + size_t i; + + if (n) + return n; + + for (i = 0; i < set->n; i++) + if (b->op == set->opcodes[i]) + { + b->f = nwp_ops.ops[b->op]; + in->next = nwp_execute; + return NW_AGAIN; + } + +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)b->op); +#endif + in->exception = exc; + return NW_FATAL; +} + +void nwp_interp_limited(struct nw_interp *const i) +{ + struct nw_i_sm_b *const b = &i->sm.bytecode; + struct nw_sm_io io = {0}; + + io.buf = &b->op; + io.n = sizeof b->op; + b->io = io; + i->next = get_bytecode; +} diff --git a/src/interp/run.c b/src/interp/run.c new file mode 100644 index 0000000..f5e80e6 --- /dev/null +++ b/src/interp/run.c @@ -0,0 +1,16 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> + +enum nw_state nwp_interp_run(struct nw_interp *const i) +{ + return i->next(i); +} diff --git a/src/interp/stack/CMakeLists.txt b/src/interp/stack/CMakeLists.txt new file mode 100644 index 0000000..51c7cb5 --- /dev/null +++ b/src/interp/stack/CMakeLists.txt @@ -0,0 +1,14 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + pop.c + push.c + ptr.c + read.c + write.c +) diff --git a/src/interp/stack/pop.c b/src/interp/stack/pop.c new file mode 100644 index 0000000..03ed689 --- /dev/null +++ b/src/interp/stack/pop.c @@ -0,0 +1,37 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/stack.h> + +enum nw_state nwp_stack_pop(struct nw_interp *const i, + struct nw_sm_io *const io) +{ + void *const dst = (unsigned char *)io->buf + io->read; + const struct nw_interp_cfg *const cfg = &i->cfg; + const struct nw_fifo_cfg *const fc = &cfg->stack; + const int n = fc->pop(dst, io->n - io->read, cfg->user); + + if (n < 0) + { + static const char *const exc = "failed to pop from stack"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + return NW_FATAL; + } + else if ((io->read += n) >= io->n) + return NW_OK; + + return NW_AGAIN; +} diff --git a/src/interp/stack/ptr.c b/src/interp/stack/ptr.c new file mode 100644 index 0000000..d5fa455 --- /dev/null +++ b/src/interp/stack/ptr.c @@ -0,0 +1,21 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/stack.h> +#include <stddef.h> + +size_t nwp_stack_ptr(const struct nw_interp *const i) +{ + const struct nw_interp_cfg *const cfg = &i->cfg; + const struct nw_fifo_cfg *const fc = &cfg->stack; + + return fc->ptr(cfg->user); +} diff --git a/src/interp/stack/push.c b/src/interp/stack/push.c new file mode 100644 index 0000000..b8b48ce --- /dev/null +++ b/src/interp/stack/push.c @@ -0,0 +1,37 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/stack.h> + +enum nw_state nwp_stack_push(struct nw_interp *const i, + struct nw_sm_io *const io) +{ + const void *const src = (const unsigned char *)io->buf + io->read; + const struct nw_interp_cfg *const cfg = &i->cfg; + const struct nw_fifo_cfg *const fc = &cfg->stack; + const int n = fc->push(src, io->n - io->read, cfg->user); + + if (n < 0) + { + static const char *const exc = "failed to push to stack"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + return NW_FATAL; + } + else if ((io->read += n) >= io->n) + return NW_OK; + + return NW_AGAIN; +} diff --git a/src/interp/stack/read.c b/src/interp/stack/read.c new file mode 100644 index 0000000..8db6bcc --- /dev/null +++ b/src/interp/stack/read.c @@ -0,0 +1,37 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/stack.h> + +enum nw_state nwp_stack_read(struct nw_interp *const i, + struct nw_sm_io *const io, const size_t offset) +{ + void *const dst = (unsigned char *)io->buf + io->read; + const struct nw_interp_cfg *const cfg = &i->cfg; + const struct nw_fifo_cfg *const fc = &cfg->stack; + const int n = fc->read(offset + io->read, dst, io->n - io->read, cfg->user); + + if (n < 0) + { + static const char *const exc = "failed to read from stack"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + return NW_FATAL; + } + else if ((io->read += n) >= io->n) + return NW_OK; + + return NW_AGAIN; +} diff --git a/src/interp/stack/write.c b/src/interp/stack/write.c new file mode 100644 index 0000000..73e749e --- /dev/null +++ b/src/interp/stack/write.c @@ -0,0 +1,38 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/stack.h> + +enum nw_state nwp_stack_write(struct nw_interp *const i, + struct nw_sm_io *const io, const size_t offset) +{ + const void *const src = (const unsigned char *)io->buf + io->read; + const struct nw_interp_cfg *const cfg = &i->cfg; + const struct nw_fifo_cfg *const fc = &cfg->stack; + const int n = fc->write(offset + io->read, src, io->n - io->read, + cfg->user); + + if (n < 0) + { + static const char *const exc = "failed to write to stack"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + return NW_FATAL; + } + else if ((io->read += n) >= io->n) + return NW_OK; + + return NW_AGAIN; +} diff --git a/src/interp/start.c b/src/interp/start.c new file mode 100644 index 0000000..2bade41 --- /dev/null +++ b/src/interp/start.c @@ -0,0 +1,28 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/interp.h> + +int nwp_interp_start(struct nw_interp *const i, + const struct nw_interp_cfg *const cfg, + const struct nwp_interp_set *const set) +{ + const struct nw_interp in = {0}; + const struct nw_mod_out *const mout = &cfg->m->out; + + *i = in; + i->cfg = *cfg; + i->set = set; + i->table.n_pages = mout->table.initial; + i->linear.n_pages = mout->linear.initial; + nwp_interp_resume(i); + return 0; +} diff --git a/src/io/CMakeLists.txt b/src/io/CMakeLists.txt new file mode 100644 index 0000000..b7c19cb --- /dev/null +++ b/src/io/CMakeLists.txt @@ -0,0 +1,23 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + leb128.c + leuint32.c + leuint64.c + read.c + toleuint32.c + toleuint64.c + varint1.c + varint7.c + varint32.c + varint64.c + varuint1.c + varuint7.c + varuint32.c + varuint64.c +) diff --git a/src/io/leb128.c b/src/io/leb128.c new file mode 100644 index 0000000..efb599b --- /dev/null +++ b/src/io/leb128.c @@ -0,0 +1,75 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + + +/* The functions below is a (somewhat heavily) modified version of that + * provided by the wac project: https://github.com/kanaka/wac + * + * Copyright (C) Joel Martin <github@martintribe.org> + * The wac project is licensed under the MPL 2.0 (Mozilla Public License + * 2.0). The text of the MPL 2.0 license is included below and can be + * found at https://www.mozilla.org/MPL/2.0/ + */ + +#include <nanowasm/types.h> +#include <nw/io.h> +#include <nw/log.h> + +enum nw_state nwp_leb128(const struct nw_io_cfg *const cfg, + struct nw_sm_leb128 *const l, const unsigned maxbits, const int sign, + void *const user) +{ + unsigned char byte; + + for (;;) + { + const int n = cfg->read(&byte, sizeof byte, user); + unsigned long v; + + if (n < 0) + return NW_FATAL; + else if (!n) + return NW_AGAIN; + + v = (unsigned long)(byte & 0x7f) << l->shift;; + + if (l->shift < 32) + l->result.low |= v; + else + l->result.hi |= v; + + l->shift += 7; + + if (!(byte & 0x80)) + break; + else if (++l->bcnt > (maxbits + 7u - 1u) / 7u) + { +#ifdef NW_LOG + long offset; + const enum nw_state n = cfg->tell(&offset, cfg->user); + + if (n) + return n; + + nwp_log("leb128 overflow, offset=%#lx\n", (unsigned long)offset); +#endif + return NW_FATAL; + } + } + + if (sign && (l->shift < maxbits) && (byte & 0x40)) + { + if (l->shift < 32) + l->result.low |= -1l << (l->shift); + else + l->result.hi |= -1l << (l->shift); + } + + return NW_OK; +} diff --git a/src/io/leuint32.c b/src/io/leuint32.c new file mode 100644 index 0000000..41a2e3b --- /dev/null +++ b/src/io/leuint32.c @@ -0,0 +1,17 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> + +unsigned long nwp_leuint32(const struct nw_leuint32 *const v) +{ + return v->v[0] | (v->v[1] << 8) | ((unsigned long)v->v[2] << 16) + | ((unsigned long)v->v[3] << 24); +} diff --git a/src/io/leuint64.c b/src/io/leuint64.c new file mode 100644 index 0000000..6d3deb0 --- /dev/null +++ b/src/io/leuint64.c @@ -0,0 +1,24 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> + +void nwp_leuint64(const struct nw_leuint64 *const v, + struct nw_ull *const ull) +{ + ull->low = v->v[0] + | (v->v[1] << 8) + | ((unsigned long)v->v[2] << 16ul) + | ((unsigned long)v->v[3] << 24ul); + ull->hi = v->v[4] + | (v->v[5] << 8) + | ((unsigned long) v->v[6] << 16ul) + | ((unsigned long)v->v[7] << 24ul); +} diff --git a/src/io/read.c b/src/io/read.c new file mode 100644 index 0000000..d4222b5 --- /dev/null +++ b/src/io/read.c @@ -0,0 +1,31 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <stddef.h> + +enum nw_state nwp_io_read(const struct nw_io_cfg *const cfg, + struct nw_sm_io *const io, void *const user) +{ + void *const buf = (unsigned char *)io->buf + io->read; + const size_t rem = io->n - io->read; + const int n = cfg->read(buf, rem, user); + + if (n < 0 || (!n && cfg->eof(user))) + return NW_FATAL; + + if ((io->read += n) >= io->n) + { + io->read = 0; + return NW_OK; + } + + return NW_AGAIN; +} diff --git a/src/io/toleuint32.c b/src/io/toleuint32.c new file mode 100644 index 0000000..c1c96c7 --- /dev/null +++ b/src/io/toleuint32.c @@ -0,0 +1,19 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> + +void nwp_toleuint32(const unsigned long v, struct nw_leuint32 *const r) +{ + r->v[0] = v; + r->v[1] = v >> 8; + r->v[2] = v >> 16; + r->v[3] = v >> 24; +} diff --git a/src/io/toleuint64.c b/src/io/toleuint64.c new file mode 100644 index 0000000..57856c2 --- /dev/null +++ b/src/io/toleuint64.c @@ -0,0 +1,23 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> + +void nwp_toleuint64(const struct nw_ull *const v, struct nw_leuint64 *const r) +{ + r->v[0] = v->low; + r->v[1] = v->low >> 8; + r->v[2] = v->low >> 16; + r->v[3] = v->low >> 24; + r->v[4] = v->hi; + r->v[5] = v->hi >> 8; + r->v[6] = v->hi >> 16; + r->v[7] = v->hi >> 24; +} diff --git a/src/io/varint1.c b/src/io/varint1.c new file mode 100644 index 0000000..502765b --- /dev/null +++ b/src/io/varint1.c @@ -0,0 +1,27 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/types.h> +#include <nw/io.h> + +enum nw_state nwp_varint1(const struct nw_io_cfg *const cfg, + struct nw_sm_leb128 *const l, nw_varint1 *const out, void *const user) +{ + const enum nw_state ret = nwp_leb128(cfg, l, 1, 1, user); + + if (!ret) + { + static const struct nw_sm_leb128 ll; + + *out = l->result.low; + *l = ll; + } + + return ret; +} diff --git a/src/io/varint32.c b/src/io/varint32.c new file mode 100644 index 0000000..f1c58cd --- /dev/null +++ b/src/io/varint32.c @@ -0,0 +1,27 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/types.h> +#include <nw/io.h> + +enum nw_state nwp_varint32(const struct nw_io_cfg *const cfg, + struct nw_sm_leb128 *const l, nw_varint32 *const out, void *const user) +{ + const enum nw_state ret = nwp_leb128(cfg, l, 32, 1, user); + + if (!ret) + { + static const struct nw_sm_leb128 ll; + + *out = l->result.low; + *l = ll; + } + + return ret; +} diff --git a/src/io/varint64.c b/src/io/varint64.c new file mode 100644 index 0000000..5d0a3fd --- /dev/null +++ b/src/io/varint64.c @@ -0,0 +1,28 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/types.h> +#include <nw/io.h> + +enum nw_state nwp_varint64(const struct nw_io_cfg *const cfg, + struct nw_sm_leb128 *const l, nw_varint64 *const out, void *const user) +{ + const enum nw_state ret = nwp_leb128(cfg, l, 64, 1, user); + + if (!ret) + { + static const struct nw_sm_leb128 ll; + + out->low = l->result.low; + out->hi = l->result.hi; + *l = ll; + } + + return ret; +} diff --git a/src/io/varint7.c b/src/io/varint7.c new file mode 100644 index 0000000..d22d25c --- /dev/null +++ b/src/io/varint7.c @@ -0,0 +1,27 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/types.h> +#include <nw/io.h> + +enum nw_state nwp_varint7(const struct nw_io_cfg *const cfg, + struct nw_sm_leb128 *const l, nw_varint7 *const out, void *const user) +{ + const enum nw_state ret = nwp_leb128(cfg, l, 7, 1, user); + + if (!ret) + { + static const struct nw_sm_leb128 ll; + + *out = l->result.low; + *l = ll; + } + + return ret; +} diff --git a/src/io/varuint1.c b/src/io/varuint1.c new file mode 100644 index 0000000..2f9587c --- /dev/null +++ b/src/io/varuint1.c @@ -0,0 +1,27 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/types.h> +#include <nw/io.h> + +enum nw_state nwp_varuint1(const struct nw_io_cfg *const cfg, + struct nw_sm_leb128 *const l, nw_varuint1 *const out, void *const user) +{ + const enum nw_state ret = nwp_leb128(cfg, l, 1, 0, user); + + if (!ret) + { + static const struct nw_sm_leb128 ll; + + *out = l->result.low; + *l = ll; + } + + return ret; +} diff --git a/src/io/varuint32.c b/src/io/varuint32.c new file mode 100644 index 0000000..1ad2c35 --- /dev/null +++ b/src/io/varuint32.c @@ -0,0 +1,27 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/types.h> +#include <nw/io.h> + +enum nw_state nwp_varuint32(const struct nw_io_cfg *const cfg, + struct nw_sm_leb128 *const l, nw_varuint32 *const out, void *const user) +{ + const enum nw_state ret = nwp_leb128(cfg, l, 32, 0, user); + + if (!ret) + { + static const struct nw_sm_leb128 ll; + + *out = l->result.low; + *l = ll; + } + + return ret; +} diff --git a/src/io/varuint64.c b/src/io/varuint64.c new file mode 100644 index 0000000..5538bb8 --- /dev/null +++ b/src/io/varuint64.c @@ -0,0 +1,27 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/types.h> +#include <nw/io.h> + +enum nw_state nwp_varuint64(const struct nw_io_cfg *const cfg, + struct nw_sm_leb128 *const l, nw_varuint64 *const out, void *const user) +{ + const enum nw_state ret = nwp_leb128(cfg, l, 64, 0, user); + + if (!ret) + { + static const struct nw_sm_leb128 ll; + + *out = l->result; + *l = ll; + } + + return ret; +} diff --git a/src/io/varuint7.c b/src/io/varuint7.c new file mode 100644 index 0000000..e3c2e91 --- /dev/null +++ b/src/io/varuint7.c @@ -0,0 +1,27 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/types.h> +#include <nw/io.h> + +enum nw_state nwp_varuint7(const struct nw_io_cfg *const cfg, + struct nw_sm_leb128 *const l, nw_varuint7 *const out, void *const user) +{ + const enum nw_state ret = nwp_leb128(cfg, l, 7, 0, user); + + if (!ret) + { + static const struct nw_sm_leb128 ll; + + *out = l->result.low; + *l = ll; + } + + return ret; +} diff --git a/src/load.c b/src/load.c new file mode 100644 index 0000000..7b08be4 --- /dev/null +++ b/src/load.c @@ -0,0 +1,21 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/types.h> + +enum nw_state nw_load(struct nw_mod *const m, struct nw_mod_out *const out) +{ + const enum nw_state n = m->next(m); + + if (!n && out) + *out = m->out; + + return n; +} diff --git a/src/log/CMakeLists.txt b/src/log/CMakeLists.txt new file mode 100644 index 0000000..0678c22 --- /dev/null +++ b/src/log/CMakeLists.txt @@ -0,0 +1,10 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +if(NW_LOG AND NOT NW_LOG_CUSTOM) + target_sources(${PROJECT_NAME} PRIVATE log.c) +endif() diff --git a/src/log/log.c b/src/log/log.c new file mode 100644 index 0000000..a0408a9 --- /dev/null +++ b/src/log/log.c @@ -0,0 +1,23 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nw/log.h> +#include <stdarg.h> +#include <stdio.h> + +int nwp_log(const char *const fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vfprintf(stderr, fmt, ap); + va_end(ap); + return ret; +} diff --git a/src/op/CMakeLists.txt b/src/op/CMakeLists.txt new file mode 100644 index 0000000..53036f0 --- /dev/null +++ b/src/op/CMakeLists.txt @@ -0,0 +1,45 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + block.c + br.c + br_if.c + call.c + call_indirect.c + drop.c + end.c + get_local.c + get_global.c + i32_add.c + i32_and.c + i32_const.c + i32_eq.c + i32_eqz.c + i32_ge_s.c + i32_ge_u.c + i32_load.c + i32_load8_u.c + i32_lt_s.c + i32_mul.c + i32_ne.c + i32_or.c + i32_store.c + i32_sub.c + i64_const.c + i64_store.c + loop.c + nop.c + return.c + set_global.c + set_local.c + tee_local.c + tostr.c + unreachable.c +) + +add_subdirectory(check) diff --git a/src/op/block.c b/src/op/block.c new file mode 100644 index 0000000..e35a722 --- /dev/null +++ b/src/op/block.c @@ -0,0 +1,17 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/ops.h> +#include <nw/routines.h> + +void nwp_op_block(struct nw_interp *const i) +{ + nwp_start_block(i); +} diff --git a/src/op/br.c b/src/op/br.c new file mode 100644 index 0000000..83ceca7 --- /dev/null +++ b/src/op/br.c @@ -0,0 +1,36 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/ops.h> +#include <nw/routines.h> + +static enum nw_state get_relative_depth(struct nw_interp *const i) +{ + struct nw_i_sm_br *const b = &i->sm.br; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, &b->leb128, + &b->relative_depth, cfg->user); + + if (n) + return n; + + nwp_break(i, b->relative_depth); + return NW_AGAIN; +} + +void nwp_op_br(struct nw_interp *const i) +{ + static const struct nw_i_sm_br b = {0}; + + i->next = get_relative_depth; + i->sm.br = b; +} diff --git a/src/op/br_if.c b/src/op/br_if.c new file mode 100644 index 0000000..661c959 --- /dev/null +++ b/src/op/br_if.c @@ -0,0 +1,95 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static int condition(struct nw_interp *const i) +{ + const struct nw_i_sm_br_if *const b = &i->sm.br_if; + + switch (i->push_type) + { + case NW_TYPE_I32: + return b->condition.i32; + + case NW_TYPE_I64: + return b->condition.i64.low; + + case NW_TYPE_F32: + return b->condition.f32; + + case NW_TYPE_F64: + return b->condition.f64; + } + + return 0; +} + +static enum nw_state pop(struct nw_interp *const i) +{ + struct nw_i_sm_br_if *const b = &i->sm.br_if; + const enum nw_state n = nwp_stack_pop(i, &b->io); + + if (n) + return n; + else if (condition(i)) + nwp_break(i, b->relative_depth); + else + nwp_interp_resume(i); + + return NW_AGAIN; +} + +static enum nw_state get_relative_depth(struct nw_interp *const i) +{ + struct nw_i_sm_br_if *const b = &i->sm.br_if; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, &b->leb128, + &b->relative_depth, cfg->user); + size_t sz; + + if (n) + return n; + else if (nwp_type_sz(i->push_type, &sz)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)i->push_type); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &b->condition; + io.n = sz; + b->io = io; + i->next = pop; + } + + return NW_AGAIN; +} + +void nwp_op_br_if(struct nw_interp *const i) +{ + static const struct nw_i_sm_br_if b = {0}; + + i->next = get_relative_depth; + i->sm.br_if = b; +} diff --git a/src/op/call.c b/src/op/call.c new file mode 100644 index 0000000..11a9183 --- /dev/null +++ b/src/op/call.c @@ -0,0 +1,50 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/ops.h> +#include <nw/routines.h> +#include <nw/types.h> + +static enum nw_state get_pc(struct nw_interp *const i) +{ + struct nw_i_sm_call *const c = &i->sm.call; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&i->fr.pc, cfg->user); + + if (n) + return n; + + nwp_call(i, c->index); + return NW_AGAIN; +} + +static enum nw_state get_index(struct nw_interp *const i) +{ + struct nw_i_sm_call *const c = &i->sm.call; + struct nw_sm_leb128 *const l = &c->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &c->index, cfg->user); + + if (n) + return n; + + i->next = get_pc; + return NW_AGAIN; +} + +void nwp_op_call(struct nw_interp *const i) +{ + static const struct nw_i_sm_call c = {0}; + + i->sm.call = c; + i->next = get_index; +} diff --git a/src/op/call_indirect.c b/src/op/call_indirect.c new file mode 100644 index 0000000..f41a14d --- /dev/null +++ b/src/op/call_indirect.c @@ -0,0 +1,64 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/ops.h> +#include <nw/routines.h> +#include <nw/types.h> + +static enum nw_state get_pc(struct nw_interp *const i) +{ + struct nw_i_sm_call *const c = &i->sm.call; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&i->fr.pc, cfg->user); + + if (n) + return n; + + nwp_call(i, c->index); + return NW_AGAIN; +} + +static enum nw_state get_value(struct nw_interp *const i) +{ + struct nw_i_sm_call_indirect *const ci = &i->sm.call_indirect; + struct nw_sm_leb128 *const l = &ci->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &ci->value, cfg->user); + + if (n) + return n; + + i->next = get_pc; + return NW_AGAIN; +} + +static enum nw_state get_index(struct nw_interp *const i) +{ + struct nw_i_sm_call_indirect *const ci = &i->sm.call_indirect; + struct nw_sm_leb128 *const l = &ci->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &ci->index, cfg->user); + + if (n) + return n; + + i->next = get_value; + return NW_AGAIN; +} + +void nwp_op_call_indirect(struct nw_interp *const i) +{ + static const struct nw_i_sm_call_indirect ci = {0}; + + i->sm.call_indirect = ci; + i->next = get_index; +} diff --git a/src/op/check/CMakeLists.txt b/src/op/check/CMakeLists.txt new file mode 100644 index 0000000..0745eb5 --- /dev/null +++ b/src/op/check/CMakeLists.txt @@ -0,0 +1,24 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + block.c + br_table.c + call.c + end.c + global_index.c + i64_const.c + local_index.c + memory_immediate.c + misc.c + no_immediate.c + relative_depth.c + varint32.c + varuint1.c + uint32.c + uint64.c +) diff --git a/src/op/check/block.c b/src/op/check/block.c new file mode 100644 index 0000000..7d5176e --- /dev/null +++ b/src/op/check/block.c @@ -0,0 +1,43 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/types.h> + +static enum nw_state get_type(struct nw_mod *const m) +{ + nw_varint7 type; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varint7(cfg, l, &type, cfg->user); + enum nw_type t; + + if (n) + return n; + else if (nwp_get_type(type, &t) && type != 0x40) + { +#ifdef NW_LOG + nwp_log("invalid block_type %#x\n", (unsigned)type); +#endif + return NW_FATAL; + } + + c->fn.blocks++; + m->next = m->sm.code.next; + return NW_AGAIN; +} + +void nwp_op_check_block(struct nw_mod *const m) +{ + m->next = get_type; +} diff --git a/src/op/check/br_table.c b/src/op/check/br_table.c new file mode 100644 index 0000000..d104669 --- /dev/null +++ b/src/op/check/br_table.c @@ -0,0 +1,78 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/ops.h> + +static enum nw_state loop(struct nw_mod *m); + +static enum nw_state get_default_target(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + nw_varuint32 target; + const enum nw_state n = nwp_varuint32(cfg, l, &target, cfg->user); + + if (n) + return n; + + m->next = c->next; + return NW_AGAIN; +} + +static enum nw_state get_entry(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_c_t *const t = &c->u.target; + struct nw_sm_leb128 *const l = &c->leb128; + nw_varuint32 entry; + const enum nw_state n = nwp_varuint32(cfg, l, &entry, cfg->user); + + if (n) + return n; + + t->i++; + m->next = loop; + return NW_AGAIN; +} + +static enum nw_state loop(struct nw_mod *const m) +{ + const struct nw_sm_c *const c = &m->sm.code; + const struct nw_sm_c_t *const t = &c->u.target; + + m->next = t->i >= t->count ? get_default_target : get_entry; + return NW_AGAIN; +} + +static enum nw_state get_target_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + struct nw_sm_c_t *const t = &c->u.target; + const enum nw_state n = nwp_varuint32(cfg, l, &t->count, cfg->user); + + if (n) + return n; + + m->next = loop; + return NW_AGAIN; +} + +void nwp_op_check_br_table(struct nw_mod *const m) +{ + static const struct nw_sm_c_t t = {0}; + + m->sm.code.u.target = t; + m->next = get_target_count; +} diff --git a/src/op/check/call.c b/src/op/check/call.c new file mode 100644 index 0000000..7b02032 --- /dev/null +++ b/src/op/check/call.c @@ -0,0 +1,40 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/ops.h> + +static enum nw_state get_index(struct nw_mod *const m) +{ + nw_varuint32 index; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &index, cfg->user); + + if (n) + return n; + else if (index >= c->count + m->cfg.n_imports) + { +#ifdef NW_LOG + nwp_log("invalid function index %lu\n", (unsigned long)index); +#endif + return NW_FATAL; + } + + m->next = m->sm.code.next; + return NW_AGAIN; +} + +void nwp_op_check_call(struct nw_mod *const m) +{ + m->next = get_index; +} diff --git a/src/op/check/end.c b/src/op/check/end.c new file mode 100644 index 0000000..8ec6bf7 --- /dev/null +++ b/src/op/check/end.c @@ -0,0 +1,36 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/ops.h> + +static enum nw_state run(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_c_fn *const fn = &c->fn; + + if (!fn->blocks) + { +#ifdef NW_LOG + nwp_log("unexpected end in function %lu\n", (unsigned long)c->entry_i); +#endif + return NW_FATAL; + } + + fn->blocks--; + m->next = c->next; + return NW_AGAIN; +} + +void nwp_op_check_end(struct nw_mod *const m) +{ + m->next = run; +} diff --git a/src/op/check/global_index.c b/src/op/check/global_index.c new file mode 100644 index 0000000..b3a18bb --- /dev/null +++ b/src/op/check/global_index.c @@ -0,0 +1,40 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/ops.h> + +static enum nw_state get_index(struct nw_mod *const m) +{ + nw_varuint32 index; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &index, cfg->user); + + if (n) + return n; + else if (index >= m->global_count) + { +#ifdef NW_LOG + nwp_log("invalid global index %lu\n", (unsigned long)index); +#endif + return NW_FATAL; + } + + m->next = c->next; + return NW_AGAIN; +} + +void nwp_op_check_global_index(struct nw_mod *const m) +{ + m->next = get_index; +} diff --git a/src/op/check/i64_const.c b/src/op/check/i64_const.c new file mode 100644 index 0000000..e283642 --- /dev/null +++ b/src/op/check/i64_const.c @@ -0,0 +1,32 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/ops.h> + +static enum nw_state get_value(struct nw_mod *const m) +{ + nw_varint64 value; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varint64(cfg, l, &value, cfg->user); + + if (n) + return n; + + m->next = c->next; + return NW_AGAIN; +} + +void nwp_op_check_varint64(struct nw_mod *const m) +{ + m->next = get_value; +} diff --git a/src/op/check/local_index.c b/src/op/check/local_index.c new file mode 100644 index 0000000..a6df9ad --- /dev/null +++ b/src/op/check/local_index.c @@ -0,0 +1,43 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/ops.h> + +static enum nw_state get_index(struct nw_mod *const m) +{ + nw_varuint32 index; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &index, cfg->user); + + if (n) + return n; +#if 0 + else if (index >= c->fn.local_total) + { + LOG("%s: invalid local index %lu in function %lu\n", __func__, + (unsigned long)index, (unsigned long)c->entry_i); + return NW_FATAL; + } +#else + /* TODO: take function parameters into account for local_total*/ +#endif + + m->next = c->next; + return NW_AGAIN; +} + +void nwp_op_check_local_index(struct nw_mod *const m) +{ + m->next = get_index; +} diff --git a/src/op/check/memory_immediate.c b/src/op/check/memory_immediate.c new file mode 100644 index 0000000..98aae52 --- /dev/null +++ b/src/op/check/memory_immediate.c @@ -0,0 +1,47 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/ops.h> + +static enum nw_state get_offset(struct nw_mod *const m) +{ + nw_varuint32 offset; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &offset, cfg->user); + + if (n) + return n; + + m->next = m->sm.code.next; + return NW_AGAIN; +} + +static enum nw_state get_flags(struct nw_mod *const m) +{ + nw_varuint32 flags; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &flags, cfg->user); + + if (n) + return n; + + m->next = get_offset; + return NW_AGAIN; +} + +void nwp_op_check_memory_immediate(struct nw_mod *const m) +{ + m->next = get_flags; +} diff --git a/src/op/check/misc.c b/src/op/check/misc.c new file mode 100644 index 0000000..fc89d21 --- /dev/null +++ b/src/op/check/misc.c @@ -0,0 +1,34 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/ops.h> + +static enum nw_state run(struct nw_mod *const m) +{ + unsigned char op; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &op; + io.n = sizeof op; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + + m->next = m->sm.code.next; + return NW_AGAIN; +} + +void nwp_op_check_misc(struct nw_mod *const m) +{ + m->next = run; +} diff --git a/src/op/check/no_immediate.c b/src/op/check/no_immediate.c new file mode 100644 index 0000000..86cd22c --- /dev/null +++ b/src/op/check/no_immediate.c @@ -0,0 +1,23 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/ops.h> + +static enum nw_state run(struct nw_mod *const m) +{ + m->next = m->sm.code.next; + return NW_AGAIN; +} + +void nwp_op_check_no_immediate(struct nw_mod *const m) +{ + m->next = run; +} diff --git a/src/op/check/relative_depth.c b/src/op/check/relative_depth.c new file mode 100644 index 0000000..41011b8 --- /dev/null +++ b/src/op/check/relative_depth.c @@ -0,0 +1,40 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/ops.h> + +static enum nw_state get_relative_depth(struct nw_mod *const m) +{ + nw_varuint32 relative_depth; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &relative_depth, cfg->user); + + if (n) + return n; + else if (relative_depth >= c->fn.blocks) + { +#ifdef NW_LOG + nwp_log("invalid relative depth %lu\n", (unsigned long)relative_depth); +#endif + return NW_FATAL; + } + + m->next = c->next; + return NW_AGAIN; +} + +void nwp_op_check_relative_depth(struct nw_mod *const m) +{ + m->next = get_relative_depth; +} diff --git a/src/op/check/uint32.c b/src/op/check/uint32.c new file mode 100644 index 0000000..28aa852 --- /dev/null +++ b/src/op/check/uint32.c @@ -0,0 +1,36 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/ops.h> + +static enum nw_state get_value(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + const enum nw_state n = nwp_io_read(cfg, &c->io, cfg->user); + + if (n) + return n; + + m->next = c->next; + return NW_AGAIN; +} + +void nwp_op_check_uint32(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_io io = {0}; + + io.buf = &c->u; + io.n = sizeof (struct nw_leuint32); + c->io = io; + m->next = get_value; +} diff --git a/src/op/check/uint64.c b/src/op/check/uint64.c new file mode 100644 index 0000000..a925999 --- /dev/null +++ b/src/op/check/uint64.c @@ -0,0 +1,36 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/ops.h> + +static enum nw_state get_value(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + const enum nw_state n = nwp_io_read(cfg, &c->io, cfg->user); + + if (n) + return n; + + m->next = c->next; + return NW_AGAIN; +} + +void nwp_op_check_uint64(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_io io = {0}; + + io.buf = &c->u; + io.n = sizeof (struct nw_leuint64); + c->io = io; + m->next = get_value; +} diff --git a/src/op/check/varint32.c b/src/op/check/varint32.c new file mode 100644 index 0000000..aff3904 --- /dev/null +++ b/src/op/check/varint32.c @@ -0,0 +1,32 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/ops.h> + +static enum nw_state get_value(struct nw_mod *const m) +{ + nw_varint32 value; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varint32(cfg, l, &value, cfg->user); + + if (n) + return n; + + m->next = c->next; + return NW_AGAIN; +} + +void nwp_op_check_varint32(struct nw_mod *const m) +{ + m->next = get_value; +} diff --git a/src/op/check/varuint1.c b/src/op/check/varuint1.c new file mode 100644 index 0000000..87ef6c4 --- /dev/null +++ b/src/op/check/varuint1.c @@ -0,0 +1,32 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/ops.h> + +static enum nw_state get_value(struct nw_mod *const m) +{ + nw_varuint1 value; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint1(cfg, l, &value, cfg->user); + + if (n) + return n; + + m->next = c->next; + return NW_AGAIN; +} + +void nwp_op_check_varuint1(struct nw_mod *const m) +{ + m->next = get_value; +} diff --git a/src/op/drop.c b/src/op/drop.c new file mode 100644 index 0000000..3b5dffd --- /dev/null +++ b/src/op/drop.c @@ -0,0 +1,101 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/opcodes.h> +#include <nw/ops.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state pop(struct nw_interp *const i) +{ + struct nw_i_sm_drop *const d = &i->sm.drop; + const enum nw_state n = nwp_stack_pop(i, &d->io); + + if (n) + return n; + + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state check(struct nw_interp *const i) +{ + struct nw_i_sm_drop *const d = &i->sm.drop; + const struct nw_frame *const fr = &i->fr; + const struct nw_return *const r = &fr->prev_ret; + const unsigned char prev = fr->prev_op; + size_t sz; + + if (prev != OP_CALL && prev != OP_CALL_INDIRECT) + { + static const char *const exc = + "expected call or call_indirect before drop"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s, got: %#x\n", exc, (unsigned)prev); +#endif + return NW_FATAL; + } + else if (!r->count) + { + static const char *const exc = + "called drop from function without return value"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + return NW_FATAL; + } + else if (r->type != i->push_type) + { + static const char *const exc = + "mismatch between return type and last push type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s, expected: %#hhx, got: %#hhx\n", exc, + (unsigned char)i->push_type, (unsigned char)r->type); +#endif + return NW_FATAL; + } + else if (nwp_type_sz(r->type, &sz)) + { + static const char *const exc = "invalid return type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)r->type); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &d->value; + io.n = sz; + d->io = io; + i->next = pop; + } + + return NW_AGAIN; +} + +void nwp_op_drop(struct nw_interp *const i) +{ + const struct nw_i_sm_drop d = {{0}}; + + i->sm.drop = d; + i->next = check; +} diff --git a/src/op/end.c b/src/op/end.c new file mode 100644 index 0000000..9eb575d --- /dev/null +++ b/src/op/end.c @@ -0,0 +1,67 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/routines.h> + +static enum nw_state run(struct nw_interp *const i) +{ + const struct nw_i_sm_end *const e = &i->sm.end; + struct nw_frame *const f = &i->fr; + + if (i->set) + return NW_OK; + else if (f->block_i) + { + f->block_i--; + nwp_interp_resume(i); + } + else + { + const long expected = f->start + f->body_size; + + if (e->offset != expected) + { + static const char *const exc = "unexpected end"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + return NW_FATAL; + } + else if (f->child) + nwp_unwind(i); + } + + return NW_AGAIN; +} + +static enum nw_state get_offset(struct nw_interp *const i) +{ + struct nw_i_sm_end *const e = &i->sm.end; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&e->offset, cfg->user); + + if (n) + return n; + + return run(i); +} + +void nwp_op_end(struct nw_interp *const i) +{ + static const struct nw_i_sm_end e = {0}; + + i->sm.end = e; + i->next = get_offset; +} diff --git a/src/op/get_global.c b/src/op/get_global.c new file mode 100644 index 0000000..eb701b5 --- /dev/null +++ b/src/op/get_global.c @@ -0,0 +1,93 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/global.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/stack.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/routines.h> + +static enum nw_state push(struct nw_interp *const i) +{ + struct nw_i_sm_get_global *const g = &i->sm.get_global; + const enum nw_state n = nwp_stack_push(i, &g->io); + + if (n) + return n; + + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state read_global(struct nw_interp *const i) +{ + struct nw_i_sm_get_global *const g = &i->sm.get_global; + struct nw_sm_io *const io = &g->io; + struct nw_global *const gl = &g->gl; + const enum nw_state n = nwp_global_load(i, io, g->index); + size_t sz; + + if (n) + return n; + else if (nwp_type_sz(gl->type, &sz)) + { + static const char *const exc = "invalid global type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)gl->type); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &gl->value; + io.n = sz; + g->io = io; + i->next = push; + i->push_type = gl->type; + } + + return NW_AGAIN; +} + +static enum nw_state get_index(struct nw_interp *const i) +{ + struct nw_i_sm_get_global *const g = &i->sm.get_global; + struct nw_sm_leb128 *const l = &g->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &g->index, cfg->user); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &g->gl; + io.n = sizeof g->gl; + g->io = io; + i->next = read_global; + } + + return NW_AGAIN; +} + +void nwp_op_get_global(struct nw_interp *const i) +{ + static const struct nw_i_sm_get_global g = {0}; + + i->sm.get_global = g; + i->next = get_index; +} diff --git a/src/op/get_local.c b/src/op/get_local.c new file mode 100644 index 0000000..6a9a42a --- /dev/null +++ b/src/op/get_local.c @@ -0,0 +1,150 @@ + +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static int init_io(struct nw_interp *const i, const enum nw_type t) +{ + struct nw_i_sm_get_local *const gl = &i->sm.get_local; + size_t sz; + + if (nwp_type_sz(t, &sz)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)t); +#endif + return -1; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &gl->value; + io.n = sz; + gl->io = io; + } + + return 0; +} + +static enum nw_state push(struct nw_interp *const i) +{ + struct nw_i_sm_get_local *const gl = &i->sm.get_local; + const enum nw_state n = nwp_stack_push(i, &gl->io); + + if (n) + return n; + + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state get_local(struct nw_interp *const i) +{ + struct nw_i_sm_get_local *const gl = &i->sm.get_local; + const struct nw_local_meta *const m = &gl->f.l.meta; + const enum nw_state n = nwp_stack_read(i, &gl->io, gl->f.l.addr); + + if (n) + return n; + else if (init_io(i, m->type)) + return NW_FATAL; + + i->next = push; + i->push_type = m->type; + return NW_AGAIN; +} + +static enum nw_state prepare_local(struct nw_interp *const i) +{ + const struct nw_local_meta *const m = &i->sm.get_local.f.l.meta; + + if (init_io(i, m->type)) + return NW_FATAL; + + i->next = get_local; + return NW_AGAIN; +} + +static enum nw_state get_param(struct nw_interp *const i) +{ + struct nw_i_sm_get_local *const gl = &i->sm.get_local; + const struct nw_find_param_out *const o = &gl->f.p.out; + const enum nw_state n = nwp_stack_read(i, &gl->io, o->addr); + + if (n) + return n; + else if (init_io(i, o->type)) + return NW_FATAL; + + i->next = push; + i->push_type = o->type; + return NW_AGAIN; +} + +static enum nw_state prepare_param(struct nw_interp *const i) +{ + struct nw_i_sm_get_local *const gl = &i->sm.get_local; + const struct nw_find_param_out *const o = &gl->f.p.out; + + if (init_io(i, o->type)) + return NW_FATAL; + + i->next = get_param; + return NW_AGAIN; +} + +static enum nw_state get_index(struct nw_interp *const i) +{ + struct nw_i_sm_get_local *const gl = &i->sm.get_local; + struct nw_sm_leb128 *const l = &gl->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const struct nw_frame *const fr = &i->fr; + const struct nw_fn *const fn = &fr->fn; + const enum nw_state n = nwp_varuint32(cfg, l, &gl->index, cfg->user); + nw_varuint32 index; + + if (n) + return n; + else if (gl->index < fn->param_count) + nwp_find_param(i, &gl->f.p, gl->index, prepare_param, NULL); + else if ((index = gl->index - fn->param_count) >= fr->local_count) + { + static const char *const exc = "invalid local index"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu\n", exc, (unsigned long)gl->index); +#endif + return NW_FATAL; + } + else + nwp_find_local(i, &gl->f.l, index, prepare_local, NULL); + + return NW_AGAIN; +} + +void nwp_op_get_local(struct nw_interp *const i) +{ + static const struct nw_i_sm_get_local gl = {0}; + + i->sm.get_local = gl; + i->next = get_index; +} diff --git a/src/op/i32_add.c b/src/op/i32_add.c new file mode 100644 index 0000000..fe2ec69 --- /dev/null +++ b/src/op/i32_add.c @@ -0,0 +1,23 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> + +static int add(const struct nw_i_sm_arithm_out *const o, + union nw_value *const res) +{ + res->i32 = (unsigned long)o->left.i32 + (unsigned long)o->right.i32; + return 0; +} + +void nwp_op_i32_add(struct nw_interp *const i) +{ + nwp_arithm(i, NW_TYPE_I32, add); +} diff --git a/src/op/i32_and.c b/src/op/i32_and.c new file mode 100644 index 0000000..d52bead --- /dev/null +++ b/src/op/i32_and.c @@ -0,0 +1,23 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> + +static int and(const struct nw_i_sm_arithm_out *const o, + union nw_value *const res) +{ + res->i32 = o->left.i32 & o->right.i32; + return 0; +} + +void nwp_op_i32_and(struct nw_interp *const i) +{ + nwp_arithm(i, NW_TYPE_I32, and); +} diff --git a/src/op/i32_const.c b/src/op/i32_const.c new file mode 100644 index 0000000..80dc9dd --- /dev/null +++ b/src/op/i32_const.c @@ -0,0 +1,58 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/stack.h> + +static enum nw_state push(struct nw_interp *const i) +{ + struct nw_i_sm_i32_const *const c = &i->sm.i32_const; + const enum nw_state n = nwp_stack_push(i, &c->io); + + if (n) + return n; + + i->push_type = NW_TYPE_I32; + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state get_value(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_i32_const *const c = &i->sm.i32_const; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varint32(cfg, l, &c->value, cfg->user); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &c->value; + io.n = sizeof c->value; + c->io = io; + i->next = push; + } + + return NW_AGAIN; +} + +void nwp_op_i32_const(struct nw_interp *const i) +{ + static const struct nw_i_sm_i32_const c = {0}; + + i->next = get_value; + i->sm.i32_const = c; +} diff --git a/src/op/i32_eq.c b/src/op/i32_eq.c new file mode 100644 index 0000000..3f2cc42 --- /dev/null +++ b/src/op/i32_eq.c @@ -0,0 +1,23 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> + +static int eq(const struct nw_i_sm_arithm_out *const o, + union nw_value *const res) +{ + res->i32 = o->left.i32 == o->right.i32; + return 0; +} + +void nwp_op_i32_eq(struct nw_interp *const i) +{ + nwp_arithm(i, NW_TYPE_I32, eq); +} diff --git a/src/op/i32_eqz.c b/src/op/i32_eqz.c new file mode 100644 index 0000000..cd533fd --- /dev/null +++ b/src/op/i32_eqz.c @@ -0,0 +1,22 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> + +static int eqz(const union nw_value *const in, union nw_value *const out) +{ + out->i32 = !in->i32; + return 0; +} + +void nwp_op_i32_eqz(struct nw_interp *const i) +{ + nwp_unary(i, NW_TYPE_I32, eqz); +} diff --git a/src/op/i32_ge_s.c b/src/op/i32_ge_s.c new file mode 100644 index 0000000..137b80d --- /dev/null +++ b/src/op/i32_ge_s.c @@ -0,0 +1,23 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> + +static int ge_s(const struct nw_i_sm_arithm_out *const o, + union nw_value *const res) +{ + res->i32 = o->left.i32 >= o->right.i32; + return 0; +} + +void nwp_op_i32_ge_s(struct nw_interp *const i) +{ + nwp_arithm(i, NW_TYPE_I32, ge_s); +} diff --git a/src/op/i32_ge_u.c b/src/op/i32_ge_u.c new file mode 100644 index 0000000..c280dec --- /dev/null +++ b/src/op/i32_ge_u.c @@ -0,0 +1,23 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> + +static int ge_u(const struct nw_i_sm_arithm_out *const o, + union nw_value *const res) +{ + res->i32 = (unsigned long)o->left.i32 >= (unsigned long)o->right.i32; + return 0; +} + +void nwp_op_i32_ge_u(struct nw_interp *const i) +{ + nwp_arithm(i, NW_TYPE_I32, ge_u); +} diff --git a/src/op/i32_load.c b/src/op/i32_load.c new file mode 100644 index 0000000..e2d883b --- /dev/null +++ b/src/op/i32_load.c @@ -0,0 +1,89 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/linear.h> +#include <nw/stack.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/routines.h> + +static enum nw_state push(struct nw_interp *const i) +{ + struct nw_i_sm_load *const l = &i->sm.load; + const enum nw_state n = nwp_stack_push(i, &l->io); + + if (n) + return n; + + i->push_type = NW_TYPE_I32; + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state load(struct nw_interp *const i) +{ + struct nw_i_sm_load *const l = &i->sm.load; + const enum nw_state n = nwp_linear_load(i, &l->io, l->addr + l->imm.offset); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + l->value.i32 = nwp_leuint32(&l->value.v32); + io.buf = &l->value.i32; + io.n = sizeof l->value.i32; + l->io = io; + i->next = push; + } + + i->next = push; + return NW_AGAIN; +} + +static enum nw_state pop_addr(struct nw_interp *const i) +{ + struct nw_i_sm_load *const l = &i->sm.load; + const enum nw_state n = nwp_stack_pop(i, &l->io); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &l->value.i32; + io.n = sizeof l->value.i32; + l->io = io; + i->next = load; + } + + return NW_AGAIN; +} + +static void prepare(struct nw_interp *const i) +{ + struct nw_i_sm_imm_out imm = i->sm.imm.out; + struct nw_i_sm_load *const pl = &i->sm.load, l = {0}; + + l.imm = imm; + l.io.buf = &pl->addr; + l.io.n = sizeof pl->addr; + *pl = l; + i->next = pop_addr; +} + +void nwp_op_i32_load(struct nw_interp *const i) +{ + nwp_mem_imm(i, prepare); +} diff --git a/src/op/i32_load8_u.c b/src/op/i32_load8_u.c new file mode 100644 index 0000000..da4282b --- /dev/null +++ b/src/op/i32_load8_u.c @@ -0,0 +1,89 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/linear.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/stack.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/routines.h> + +static enum nw_state push(struct nw_interp *const i) +{ + struct nw_i_sm_load *const l = &i->sm.load; + const enum nw_state n = nwp_stack_push(i, &l->io); + + if (n) + return n; + + i->push_type = NW_TYPE_I32; + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state load(struct nw_interp *const i) +{ + struct nw_i_sm_load *const l = &i->sm.load; + const enum nw_state n = nwp_linear_load(i, &l->io, l->addr + l->imm.offset); + + if (n) + return n; + else + { + const unsigned char b = l->value.i8; + struct nw_sm_io io = {0}; + + io.buf = &l->value.i32; + io.n = sizeof l->value.i32; + l->value.i32 = b; + l->io = io; + i->next = push; + } + + return NW_AGAIN; +} + +static enum nw_state pop_addr(struct nw_interp *const i) +{ + struct nw_i_sm_load *const l = &i->sm.load; + const enum nw_state n = nwp_stack_pop(i, &l->io); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &l->value.i8; + io.n = sizeof l->value.i8; + l->io = io; + i->next = load; + } + + return NW_AGAIN; +} + +static void prepare(struct nw_interp *const i) +{ + struct nw_i_sm_imm_out imm = i->sm.imm.out; + struct nw_i_sm_load *const pl = &i->sm.load, l = {0}; + + l.imm = imm; + l.io.buf = &pl->addr; + l.io.n = sizeof pl->addr; + *pl = l; + i->next = pop_addr; +} + +void nwp_op_i32_load8_u(struct nw_interp *const i) +{ + nwp_mem_imm(i, prepare); +} diff --git a/src/op/i32_lt_s.c b/src/op/i32_lt_s.c new file mode 100644 index 0000000..3e583c3 --- /dev/null +++ b/src/op/i32_lt_s.c @@ -0,0 +1,23 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> + +static int lt_s(const struct nw_i_sm_arithm_out *const o, + union nw_value *const res) +{ + res->i32 = o->left.i32 < o->right.i32; + return 0; +} + +void nwp_op_i32_lt_s(struct nw_interp *const i) +{ + nwp_arithm(i, NW_TYPE_I32, lt_s); +} diff --git a/src/op/i32_mul.c b/src/op/i32_mul.c new file mode 100644 index 0000000..8af64de --- /dev/null +++ b/src/op/i32_mul.c @@ -0,0 +1,23 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> + +static int mul(const struct nw_i_sm_arithm_out *const o, + union nw_value *const res) +{ + res->i32 = (unsigned long)o->left.i32 * (unsigned long)o->right.i32; + return 0; +} + +void nwp_op_i32_mul(struct nw_interp *const i) +{ + nwp_arithm(i, NW_TYPE_I32, mul); +} diff --git a/src/op/i32_ne.c b/src/op/i32_ne.c new file mode 100644 index 0000000..c62a2a7 --- /dev/null +++ b/src/op/i32_ne.c @@ -0,0 +1,23 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> + +static int ne(const struct nw_i_sm_arithm_out *const o, + union nw_value *const res) +{ + res->i32 = o->left.i32 != o->right.i32; + return 0; +} + +void nwp_op_i32_ne(struct nw_interp *const i) +{ + nwp_arithm(i, NW_TYPE_I32, ne); +} diff --git a/src/op/i32_or.c b/src/op/i32_or.c new file mode 100644 index 0000000..774dbed --- /dev/null +++ b/src/op/i32_or.c @@ -0,0 +1,23 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> + +static int or(const struct nw_i_sm_arithm_out *const o, + union nw_value *const res) +{ + res->i32 = o->left.i32 | o->right.i32; + return 0; +} + +void nwp_op_i32_or(struct nw_interp *const i) +{ + nwp_arithm(i, NW_TYPE_I32, or); +} diff --git a/src/op/i32_store.c b/src/op/i32_store.c new file mode 100644 index 0000000..caff6b9 --- /dev/null +++ b/src/op/i32_store.c @@ -0,0 +1,90 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/linear.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/stack.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/routines.h> + +static enum nw_state store(struct nw_interp *const i) +{ + struct nw_i_sm_store *const s = &i->sm.store; + const enum nw_state n = nwp_linear_store(i, &s->io, s->addr + s->imm.offset); + + if (n) + return n; + + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state pop_addr(struct nw_interp *const i) +{ + struct nw_i_sm_store *const s = &i->sm.store; + const enum nw_state n = nwp_stack_pop(i, &s->io); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &s->value.i32; + io.n = sizeof s->value.i32; + s->io = io; + i->next = store; + } + + return NW_AGAIN; +} + +static enum nw_state pop_value(struct nw_interp *const i) +{ + struct nw_i_sm_store *const s = &i->sm.store; + const enum nw_state n = nwp_stack_pop(i, &s->io); + + if (n) + return n; + else + { + const struct nw_sm_io io = {0}; + + s->value.i32 = nwp_leuint32(&s->value.v32); + s->io = io; + s->io.buf = &s->addr; + s->io.n = sizeof s->addr; + i->next = pop_addr; + } + + return NW_AGAIN; +} + +static void prepare(struct nw_interp *const i) +{ + const struct nw_i_sm_store s = {0}; + struct nw_i_sm_store *const ps = &i->sm.store; + struct nw_sm_io *const io = &ps->io; + struct nw_i_sm_imm_out out; + + out = i->sm.imm.out; + *ps = s; + ps->imm = out; + io->buf = &ps->value.i32; + io->n = sizeof ps->value.i32; + i->next = pop_value; +} + +void nwp_op_i32_store(struct nw_interp *const i) +{ + nwp_mem_imm(i, prepare); +} diff --git a/src/op/i32_sub.c b/src/op/i32_sub.c new file mode 100644 index 0000000..46c4f66 --- /dev/null +++ b/src/op/i32_sub.c @@ -0,0 +1,23 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> + +static int sub(const struct nw_i_sm_arithm_out *const o, + union nw_value *const res) +{ + res->i32 = (unsigned long)o->left.i32 - (unsigned long)o->right.i32; + return 0; +} + +void nwp_op_i32_sub(struct nw_interp *const i) +{ + nwp_arithm(i, NW_TYPE_I32, sub); +} diff --git a/src/op/i64_const.c b/src/op/i64_const.c new file mode 100644 index 0000000..20d539b --- /dev/null +++ b/src/op/i64_const.c @@ -0,0 +1,58 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/stack.h> + +static enum nw_state push(struct nw_interp *const i) +{ + struct nw_i_sm_i64_const *const c = &i->sm.i64_const; + const enum nw_state n = nwp_stack_push(i, &c->io); + + if (n) + return n; + + i->push_type = NW_TYPE_I64; + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state get_value(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_i64_const *const c = &i->sm.i64_const; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint64(cfg, l, &c->value, cfg->user); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &c->value; + io.n = sizeof c->value; + c->io = io; + i->next = push; + } + + return NW_AGAIN; +} + +void nwp_op_i64_const(struct nw_interp *const i) +{ + const struct nw_i_sm_i64_const c = {{0}}; + + i->next = get_value; + i->sm.i64_const = c; +} diff --git a/src/op/i64_store.c b/src/op/i64_store.c new file mode 100644 index 0000000..fbae8b4 --- /dev/null +++ b/src/op/i64_store.c @@ -0,0 +1,87 @@ +/* +* nanowasm, a tiny WebAssembly/Wasm interpreter +* Copyright (C) 2023-2025 Xavier Del Campo Romero +* +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at https://mozilla.org/MPL/2.0/. +*/ + +#include <nanowasm/nw.h> +#include <nw/linear.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/stack.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/routines.h> + +static enum nw_state store(struct nw_interp *const i) +{ + struct nw_i_sm_store *const s = &i->sm.store; + const enum nw_state n = nwp_linear_store(i, &s->io, + s->addr + s->imm.offset); + + if (n) + return n; + + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state pop_addr(struct nw_interp *const i) +{ + struct nw_i_sm_store *const s = &i->sm.store; + const enum nw_state n = nwp_stack_pop(i, &s->io); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &s->value.i64; + io.n = sizeof s->value.i64; + s->io = io; + i->next = store; + } + + return NW_AGAIN; +} + +static enum nw_state pop_value(struct nw_interp *const i) +{ + struct nw_i_sm_store *const s = &i->sm.store; + const enum nw_state n = nwp_stack_pop(i, &s->io); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + nwp_leuint64(&s->value.v64, &s->value.i64); + io.buf = &s->addr; + io.n = sizeof s->addr; + s->io = io; + i->next = pop_addr; + } + + return NW_AGAIN; +} + +static void prepare(struct nw_interp *const i) +{ + struct nw_i_sm_store *const ps = &i->sm.store, s = {0}; + + s.imm = i->sm.imm.out; + s.io.buf = &ps->value.v64; + s.io.n = sizeof ps->value.v64; + *ps =s; + i->next = pop_value; +} + +void nwp_op_i64_store(struct nw_interp *const i) +{ + nwp_mem_imm(i, prepare); +} diff --git a/src/op/loop.c b/src/op/loop.c new file mode 100644 index 0000000..e129610 --- /dev/null +++ b/src/op/loop.c @@ -0,0 +1,16 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> + +void nwp_op_loop(struct nw_interp *const i) +{ + nwp_start_block(i); +} diff --git a/src/op/nop.c b/src/op/nop.c new file mode 100644 index 0000000..b6ef7c2 --- /dev/null +++ b/src/op/nop.c @@ -0,0 +1,17 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/ops.h> + +void nwp_op_nop(struct nw_interp *const i) +{ + nwp_interp_resume(i); +} diff --git a/src/op/return.c b/src/op/return.c new file mode 100644 index 0000000..0b2a129 --- /dev/null +++ b/src/op/return.c @@ -0,0 +1,19 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/routines.h> + +void nwp_op_return(struct nw_interp *const i) +{ + nwp_unwind(i); +} diff --git a/src/op/set_global.c b/src/op/set_global.c new file mode 100644 index 0000000..9448c32 --- /dev/null +++ b/src/op/set_global.c @@ -0,0 +1,122 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/global.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/stack.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/routines.h> + +static enum nw_state set_global(struct nw_interp *const i) +{ + struct nw_i_sm_get_global *const g = &i->sm.get_global; + const enum nw_state n = nwp_global_store(i, &g->io, g->index); + + if (n) + return n; + + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state pop(struct nw_interp *const i) +{ + struct nw_i_sm_get_global *const g = &i->sm.get_global; + const enum nw_state n = nwp_stack_pop(i, &g->io); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &g->gl; + io.n = sizeof g->gl; + g->io = io; + i->next = set_global; + } + + return NW_AGAIN; +} + +static enum nw_state read_global(struct nw_interp *const i) +{ + struct nw_i_sm_get_global *const g = &i->sm.get_global; + struct nw_global *const gl = &g->gl; + const enum nw_state n = nwp_global_load(i, &g->io, g->index); + size_t sz; + + if (n) + return n; + else if (!gl->mutability) + { + static const char *const exc = "cannot set non-mutable global"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s, index: %lu\n", exc, (unsigned long)g->index); +#endif + return NW_FATAL; + } + else if (nwp_type_sz(gl->type, &sz)) + { + static const char *const exc = "invalid global type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)gl->type); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &gl->value; + io.n = sz; + g->io = io; + i->next = pop; + i->push_type = gl->type; + } + + return NW_AGAIN; +} + +static enum nw_state get_index(struct nw_interp *const i) +{ + struct nw_i_sm_get_global *const g = &i->sm.get_global; + struct nw_sm_leb128 *const l = &g->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &g->index, cfg->user); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &g->gl; + io.n = sizeof g->gl; + g->io = io; + i->next = read_global; + } + + return NW_AGAIN; +} + +void nwp_op_set_global(struct nw_interp *const i) +{ + static const struct nw_i_sm_set_global g = {0}; + + i->sm.set_global = g; + i->next = get_index; +} diff --git a/src/op/set_local.c b/src/op/set_local.c new file mode 100644 index 0000000..0488e16 --- /dev/null +++ b/src/op/set_local.c @@ -0,0 +1,24 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/ops.h> +#include <nw/routines.h> + +static enum nw_state resume(struct nw_interp *const i) +{ + nwp_interp_resume(i); + return NW_AGAIN; +} + +void nwp_op_set_local(struct nw_interp *const i) +{ + nwp_set_local(i, resume); +} diff --git a/src/op/tee_local.c b/src/op/tee_local.c new file mode 100644 index 0000000..b95603c --- /dev/null +++ b/src/op/tee_local.c @@ -0,0 +1,64 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/routines.h> +#include <nw/stack.h> + +static enum nw_state push(struct nw_interp *const i) +{ + struct nw_i_sm_tee_local *const t = &i->sm.tee_local; + const enum nw_state n = nwp_stack_push(i, &t->io); + + if (n) + return n; + + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state prepare(struct nw_interp *const i) +{ + struct nw_i_sm_tee_local *const t = &i->sm.tee_local; + struct nw_i_sm_set_local_out o; + size_t sz; + + o = i->sm.set_local.out; + + if (nwp_type_sz(o.type, &sz)) + { + static const char *const exc = "invalid local type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)o.type); +#endif + return NW_FATAL; + } + else + { + struct nw_i_sm_tee_local tl = {{0}}; + + tl.in = o; + tl.io.buf = &t->in.value; + tl.io.n = sz; + *t = tl; + i->next = push; + } + + return NW_AGAIN; +} + +void nwp_op_tee_local(struct nw_interp *const i) +{ + nwp_set_local(i, prepare); +} diff --git a/src/op/tostr.c b/src/op/tostr.c new file mode 100644 index 0000000..b2ac6dd --- /dev/null +++ b/src/op/tostr.c @@ -0,0 +1,278 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/opcodes.h> + +const char *nwp_op_tostr(const enum opcode op) +{ + static const char *const s[] = + { + "OP_UNREACHABLE", + "OP_NOP", + "OP_BLOCK", + "OP_LOOP", + "OP_IF", + "OP_ELSE", + NULL, + NULL, + NULL, + NULL, + NULL, + "OP_END", + "OP_BR", + "OP_BR_IF", + "OP_BR_TABLE", + "OP_RETURN", + "OP_CALL", + "OP_CALL_INDIRECT", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "OP_DROP", + "OP_SELECT", + NULL, + NULL, + NULL, + NULL, + "OP_GET_LOCAL", + "OP_SET_LOCAL", + "OP_TEE_LOCAL", + "OP_GET_GLOBAL", + "OP_SET_GLOBAL", + NULL, + NULL, + NULL, + "OP_I32_LOAD", + "OP_I64_LOAD", + "OP_F32_LOAD", + "OP_F64_LOAD", + "OP_I32_LOAD8_S", + "OP_I32_LOAD8_U", + "OP_I32_LOAD16_S", + "OP_I32_LOAD16_U", + "OP_I64_LOAD8_S", + "OP_I64_LOAD8_U", + "OP_I64_LOAD16_S", + "OP_I64_LOAD16_U", + "OP_I64_LOAD32_S", + "OP_I64_LOAD32_U", + "OP_I32_STORE", + "OP_I64_STORE", + "OP_F32_STORE", + "OP_F64_STORE", + "OP_I32_STORE8", + "OP_I32_STORE16", + "OP_I64_STORE8", + "OP_I64_STORE16", + "OP_I64_STORE32", + "OP_CURRENT_MEMORY", + "OP_GROW_MEMORY", + "OP_I32_CONST", + "OP_I64_CONST", + "OP_F32_CONST", + "OP_F64_CONST", + "OP_I32_EQZ", + "OP_I32_EQ", + "OP_I32_NE", + "OP_I32_LT_S", + "OP_I32_LT_U", + "OP_I32_GT_S", + "OP_I32_GT_U", + "OP_I32_LE_S", + "OP_I32_LE_U", + "OP_I32_GE_S", + "OP_I32_GE_U", + "OP_I64_EQZ", + "OP_I64_EQ", + "OP_I64_NE", + "OP_I64_LT_S", + "OP_I64_LT_U", + "OP_I64_GT_S", + "OP_I64_GT_U", + "OP_I64_LE_S", + "OP_I64_LE_U", + "OP_I64_GE_S", + "OP_I64_GE_U", + "OP_F32_EQ", + "OP_F32_NE", + "OP_F32_LT", + "OP_F32_GT", + "OP_F32_LE", + "OP_F32_GE", + "OP_F64_EQ", + "OP_F64_NE", + "OP_F64_LT", + "OP_F64_GT", + "OP_F64_LE", + "OP_F64_GE", + "OP_I32_CLZ", + "OP_I32_CTZ", + "OP_I32_POPCNT", + "OP_I32_ADD", + "OP_I32_SUB", + "OP_I32_MUL", + "OP_I32_DIV_S", + "OP_I32_DIV_U", + "OP_I32_REM_S", + "OP_I32_REM_U", + "OP_I32_AND", + "OP_I32_OR", + "OP_I32_XOR", + "OP_I32_SHL", + "OP_I32_SHR_S", + "OP_I32_SHR_U", + "OP_I32_ROTL", + "OP_I32_ROTR", + "OP_I64_CLZ", + "OP_I64_CTZ", + "OP_I64_POPCNT", + "OP_I64_ADD", + "OP_I64_SUB", + "OP_I64_MUL", + "OP_I64_DIV_S", + "OP_I64_DIV_U", + "OP_I64_REM_S", + "OP_I64_REM_U", + "OP_I64_AND", + "OP_I64_OR", + "OP_I64_XOR", + "OP_I64_SHL", + "OP_I64_SHR_S", + "OP_I64_SHR_U", + "OP_I64_ROTL", + "OP_I64_ROTR", + "OP_F32_ABS", + "OP_F32_NEG", + "OP_F32_CEIL", + "OP_F32_FLOOR", + "OP_F32_TRUNC", + "OP_F32_NEAREST", + "OP_F32_SQRT", + "OP_F32_ADD", + "OP_F32_SUB", + "OP_F32_MUL", + "OP_F32_DIV", + "OP_F32_MIN", + "OP_F32_MAX", + "OP_F32_COPYSIGN", + "OP_F64_ABS", + "OP_F64_NEG", + "OP_F64_CEIL", + "OP_F64_FLOOR", + "OP_F64_TRUNC", + "OP_F64_NEAREST", + "OP_F64_SQRT", + "OP_F64_ADD", + "OP_F64_SUB", + "OP_F64_MUL", + "OP_F64_DIV", + "OP_F64_MIN", + "OP_F64_MAX", + "OP_F64_COPYSIGN", + "OP_I32_WRAP_I64", + "OP_I32_TRUNC_S_F32", + "OP_I32_TRUNC_U_F32", + "OP_I32_TRUNC_S_F64", + "OP_I32_TRUNC_U_F64", + "OP_I64_EXTEND_S_I32", + "OP_I64_EXTEND_U_I32", + "OP_I64_TRUNC_S_F32", + "OP_I64_TRUNC_U_F32", + "OP_I64_TRUNC_S_F64", + "OP_I64_TRUNC_U_F64", + "OP_F32_CONVERT_S_I32", + "OP_F32_CONVERT_U_I32", + "OP_F32_CONVERT_S_I64", + "OP_F32_CONVERT_U_I64", + "OP_F32_DEMOTE_F64", + "OP_F64_CONVERT_S_I32", + "OP_F64_CONVERT_U_I32", + "OP_F64_CONVERT_S_I64", + "OP_F64_CONVERT_U_I64", + "OP_F64_PROMOTE_F32", + "OP_I32_REINTERPRET_F32", + "OP_I64_REINTERPRET_F64", + "OP_F32_REINTERPRET_I32", + "OP_F64_REINTERPRET_I64", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "OP_MISC", + }; + + if (op < 0 || op >= sizeof s / sizeof *s) + return "invalid opcode"; + + return s[op]; +} diff --git a/src/op/unreachable.c b/src/op/unreachable.c new file mode 100644 index 0000000..6ecd374 --- /dev/null +++ b/src/op/unreachable.c @@ -0,0 +1,38 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/ops.h> + +static enum nw_state tell(struct nw_interp *const i) +{ + struct nw_i_sm_unreachable *const u = &i->sm.unreachable; + const struct nw_io_cfg *const cfg = &i->cfg.io; + static const char *const exc = "unreachable"; + const enum nw_state n = cfg->tell(&u->offset, cfg->user); + + if (n) + return n; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s, offset: %ld\n", exc, u->offset); +#endif + return NW_FATAL; +} + +void nwp_op_unreachable(struct nw_interp *const i) +{ + static const struct nw_i_sm_unreachable u = {0}; + + i->sm.unreachable = u; + i->next = tell; +} diff --git a/src/rexc.c b/src/rexc.c new file mode 100644 index 0000000..b2c73a4 --- /dev/null +++ b/src/rexc.c @@ -0,0 +1,15 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> + +const char *nw_rexc(const struct nw_inst *const i) +{ + return i->interp.exception; +} diff --git a/src/routines/CMakeLists.txt b/src/routines/CMakeLists.txt new file mode 100644 index 0000000..05c055b --- /dev/null +++ b/src/routines/CMakeLists.txt @@ -0,0 +1,32 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + arithm.c + break.c + call.c + call_function.c + call_import.c + call_indirect.c + check_magic.c + check_version.c + find_function.c + find_local.c + find_param.c + get_function_type.c + get_import_type.c + init_data.c + init_globals.c + mem_imm.c + section.c + set_local.c + start_block.c + unary.c + unwind.c +) + +add_subdirectory(section) diff --git a/src/routines/arithm.c b/src/routines/arithm.c new file mode 100644 index 0000000..6ab4799 --- /dev/null +++ b/src/routines/arithm.c @@ -0,0 +1,119 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state push(struct nw_interp *const i) +{ + struct nw_i_sm_arithm *const s = &i->sm.arithm; + const enum nw_state n = nwp_stack_push(i, &s->io); + + if (n) + return n; + + i->push_type = s->type; + nwp_interp_resume(i); + return NW_AGAIN; +} + +static int init_io(struct nw_interp *const i) +{ + struct nw_i_sm_arithm *const s = &i->sm.arithm; + size_t sz; + + if (nwp_type_sz(s->type, &sz)) + { + static const char *const exc = "invalid operand type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)s->type); +#endif + return -1; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &s->value; + io.n = sz; + s->io = io; + } + + return 0; +} + +static enum nw_state pop_left(struct nw_interp *const i) +{ + struct nw_i_sm_arithm *const s = &i->sm.arithm; + const enum nw_state n = nwp_stack_pop(i, &s->io); + + if (n) + return n; + + s->out.left = s->value; + + if (s->op(&s->out, &s->value)) + { + static const char *const exc = "arithmetic operation failed"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + return NW_FATAL; + } + else if (init_io(i)) + return NW_FATAL; + + i->next = push; + return NW_AGAIN; +} + +static enum nw_state pop_right(struct nw_interp *const i) +{ + struct nw_i_sm_arithm *const s = &i->sm.arithm; + const enum nw_state n = nwp_stack_pop(i, &s->io); + + if (n) + return n; + else if (init_io(i)) + return NW_FATAL; + + s->out.right = s->value; + i->next = pop_left; + return NW_AGAIN; +} + +static enum nw_state prepare_right(struct nw_interp *const i) +{ + if (init_io(i)) + return NW_FATAL; + + i->next = pop_right; + return NW_AGAIN; +} + +void nwp_arithm(struct nw_interp *const i, const enum nw_type t, + int (*const op)(const struct nw_i_sm_arithm_out *, union nw_value *)) +{ + const struct nw_i_sm_arithm s = {0}; + struct nw_i_sm_arithm *const ps = &i->sm.arithm; + + *ps = s; + ps->type = t; + ps->op = op; + i->next = prepare_right; +} diff --git a/src/routines/break.c b/src/routines/break.c new file mode 100644 index 0000000..3ee94a1 --- /dev/null +++ b/src/routines/break.c @@ -0,0 +1,243 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state get_pc(struct nw_interp *); + +static enum nw_state seek_dst(struct nw_interp *const i) +{ + const struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const unsigned long dst = nwp_leuint32(&b->dst); + const enum nw_state n = cfg->seek(dst, cfg->user); + + if (n) + return n; + + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state skip(struct nw_interp *const i) +{ + struct nw_i_sm_break *const b = &i->sm.brk; + const unsigned long n_labels = nwp_leuint32(&b->n); + + if (++b->label_i >= n_labels) + { + static const char *const exc = "no label found"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s from offset: %ld\n", exc, b->pc); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &b->lpc; + io.n = sizeof b->lpc; + b->io = io; + i->next = get_pc; + } + + return NW_AGAIN; +} + +static enum nw_state get_dst(struct nw_interp *const i) +{ + struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const unsigned long pc = nwp_leuint32(&b->lpc); + const enum nw_state n = nwp_io_read(cfg, &b->io, cfg->user); + + if (n) + return n; + else if (pc == b->pc) + i->next = seek_dst; + else + return skip(i); + + return NW_AGAIN; +} + +static enum nw_state get_pc(struct nw_interp *const i) +{ + struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &b->io, cfg->user); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &b->dst; + io.n = sizeof b->dst; + b->io = io; + i->next = get_dst; + } + + return NW_AGAIN; +} + +static enum nw_state get_n(struct nw_interp *const i) +{ + struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &b->io, cfg->user); + + if (n) + return n; + else if (!nwp_leuint32(&b->n)) + { + static const char *const exc = "function has no labels"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &b->lpc; + io.n = sizeof b->lpc; + b->io = io; + } + + i->next = get_pc; + return NW_AGAIN; +} + +static enum nw_state seek_start(struct nw_interp *const i) +{ + struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const unsigned long offset = nwp_leuint32(&b->offset); + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &b->n; + io.n = sizeof b->n; + b->io = io; + i->next = get_n; + } + + return NW_AGAIN; +} + +static enum nw_state get_start(struct nw_interp *const i) +{ + struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &b->io, cfg->user); + + if (n) + return n; + + i->next = seek_start; + return NW_AGAIN; +} + +static enum nw_state seek_lo(struct nw_interp *const i) +{ + const struct nw_mod *const m = i->cfg.m; + struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + long offset = m->c_sections[NW_CUSTOM_LO]; + enum nw_state n; + + if (!offset) + { + static const char *const exc = "nw_lo section not found"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + return NW_FATAL; + } + else if (i->fr.fn.index < m->import_count) + { + static const char *const exc = "invalid function index"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu\n", exc, (unsigned long)i->fr.fn.index); +#endif + return NW_FATAL; + } + + offset += sizeof b->offset * (i->fr.fn.index - m->import_count); + + if ((n = cfg->seek(offset, cfg->user))) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &b->offset; + io.n = sizeof b->offset; + b->io = io; + i->next = get_start; + } + + return NW_AGAIN; +} + +static enum nw_state tell(struct nw_interp *const i) +{ + struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&b->pc, cfg->user); + + if (n) + return n; + else if (i->fr.block_i < b->relative_depth) + { + static const char *const exc = "relative depth exceeds block level"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s, rdepth: %lu, blvl: %lu\n", exc, + (unsigned long)i->fr.block_i, (unsigned long)b->relative_depth); +#endif + return NW_FATAL; + } + + i->fr.block_i -= b->relative_depth; + i->next = seek_lo; + return NW_AGAIN; +} + +void nwp_break(struct nw_interp *const i, const nw_varuint32 relative_depth) +{ + const struct nw_i_sm_break b = {0}; + struct nw_i_sm_break *const pb = &i->sm.brk; + + *pb = b; + pb->relative_depth = relative_depth; + i->next = tell; +} diff --git a/src/routines/call.c b/src/routines/call.c new file mode 100644 index 0000000..d564344 --- /dev/null +++ b/src/routines/call.c @@ -0,0 +1,17 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> + +void nwp_call(struct nw_interp *const i, const nw_varuint32 index) +{ + index >= i->cfg.m->import_count ? + nwp_call_function(i, index) : nwp_call_import(i, index); +} diff --git a/src/routines/call_function.c b/src/routines/call_function.c new file mode 100644 index 0000000..eeee2fc --- /dev/null +++ b/src/routines/call_function.c @@ -0,0 +1,242 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state get_entry_count(struct nw_interp *); + +static enum nw_state push_frame(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + const enum nw_state n = nwp_stack_push(i, &pl->io); + + if (n) + return n; + + i->fr = pl->fr; +#ifdef NW_LOG + nwp_log("entering function %lu\n", (unsigned long)i->fr.fn.index); +#endif + nwp_interp_resume(i); + return NW_AGAIN; +} + +static void init(struct nw_i_sm_pl *const pl) +{ + union nw_value *const v = &pl->value; + struct nw_sm_io io = {0}; + + io.buf = v; + + switch (pl->meta.type) + { + case NW_TYPE_I32: + v->i32 = 0; + io.n = sizeof v->i32; + break; + + case NW_TYPE_I64: + v->i64.low = 0; + v->i64.hi = 0; + io.n = sizeof v->i64; + break; + + case NW_TYPE_F32: + v->f32 = 0; + io.n = sizeof v->f32; + break; + + case NW_TYPE_F64: + v->f64 = 0; + io.n = sizeof v->f64; + break; + } + + pl->io = io; +} + +static enum nw_state get_code_start(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + struct nw_frame *const fr = &pl->fr; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&fr->start, cfg->user); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &i->fr; + io.n = sizeof i->fr; + pl->io = io; + fr->local_end = nwp_stack_ptr(i); + fr->body_size -= fr->start - pl->body_start; + i->next = push_frame; + } + + return NW_AGAIN; +} + +static enum nw_state push_local(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + const enum nw_state n = nwp_stack_push(i, &pl->io); + + if (n) + return n; + else if (++pl->entry_i >= pl->meta.entry_count) + i->next = ++pl->local_i >= pl->local_count ? + get_code_start : get_entry_count; + else + init(pl); + + return NW_AGAIN; +} + +static enum nw_state push_meta(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + const enum nw_state n = nwp_stack_push(i, &pl->io); + + if (n) + return n; + + init(pl); + i->next = push_local; + return NW_AGAIN; +} + +static enum nw_state get_type(struct nw_interp *const i) +{ + nw_varint7 type; + struct nw_i_sm_pl *const pl = &i->sm.pl; + struct nw_sm_leb128 *const l = &pl->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varint7(cfg, l, &type, cfg->user); + + if (n) + return n; + else if (nwp_get_type(type, &pl->meta.type)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %x\n", exc, (unsigned)type); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &pl->meta; + io.n = sizeof pl->meta; + pl->io = io; + i->next = push_meta; + } + + return NW_AGAIN; +} + +static enum nw_state get_entry_count(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + struct nw_sm_leb128 *const l = &pl->leb128; + nw_varuint32 *const count = &pl->meta.entry_count; + struct nw_frame *const fr = &pl->fr; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, count, cfg->user); + + if (n) + return n; + + pl->entry_i = 0; + fr->local_count += *count; + i->next = get_type; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + struct nw_sm_leb128 *const l = &pl->leb128; + struct nw_frame *const fr = &pl->fr; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &pl->local_count, cfg->user); + + if (n) + return n; + + fr->local_start = nwp_stack_ptr(i); + i->next = pl->local_count ? get_entry_count : get_code_start; + return NW_AGAIN; +} + +static enum nw_state get_body_start(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&pl->body_start, cfg->user); + + if (n) + return n; + + i->next = get_count; + return NW_AGAIN; +} + +static enum nw_state get_body_len(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + struct nw_sm_leb128 *const l = &pl->leb128; + struct nw_frame *const fr = &pl->fr; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &fr->body_size, cfg->user); + + if (n) + return n; + + i->next = get_body_start; + return NW_AGAIN; +} + +static void begin(struct nw_interp *const i) +{ + const struct nw_i_sm_pl l = {0}; + const struct nw_fn fn = i->sm.ffn.fn; + struct nw_i_sm_pl *const pl = &i->sm.pl; + struct nw_frame *const fr = &pl->fr; + + *pl = l; + fr->child = 1; + fr->fn = fn; + fr->fr_start = nwp_stack_ptr(i); + i->next = get_body_len; +} + +static enum nw_state find(struct nw_interp *const i) +{ + nwp_find_function(i, &i->sm.type.out, begin); + return NW_AGAIN; +} + +void nwp_call_function(struct nw_interp *const i, const nw_varuint32 index) +{ + nwp_get_function_type(i, index, find); +} diff --git a/src/routines/call_import.c b/src/routines/call_import.c new file mode 100644 index 0000000..63a736e --- /dev/null +++ b/src/routines/call_import.c @@ -0,0 +1,477 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> +#include <stddef.h> +#include <string.h> + +static enum nw_state seek_param_types(struct nw_interp *); + +static enum nw_state seek_pc(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->seek(ci->pc, cfg->user); + + if (n) + return n; + + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state push_return(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const enum nw_state n = nwp_stack_push(i, &ci->io); + + if (n) + return n; + + i->next = seek_pc; + return NW_AGAIN; +} + +static int parse_signature(const struct nw_import *const imp, + size_t *const nargs, int *const ret) +{ + enum + { + RETURN, + PARAM_BEGIN, + PARAM, + END + } state = 0; + + const char *s; + + if (nargs) + *nargs = 0; + + if (ret) + *ret = 0; + + for (s = imp->u.function.signature; *s; s++) + { + switch (state) + { + case RETURN: + if (strchr("iIfF", *s)) + { + if (ret) + *ret = 1; + + state++; + } + else if (*s == '(') + state = PARAM; + + break; + + case PARAM_BEGIN: + if (*s != '(') + return -1; + + state++; + break; + + case PARAM: + if (strchr("iIfF", *s)) + { + if (nargs) + (*nargs)++; + } + else if (*s == ')') + state++; + else + return -1; + + break; + + case END: + goto end; + } + } + +end: + return state != END || *s; +} + +static int check_ret(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const struct nw_import *const imp = ci->imp; + const struct nw_fn *const fn = &ci->fn; + const nw_varuint1 retcnt = fn->ret.count; + int hasret; + + if (parse_signature(imp, NULL, &hasret)) + { + static const char *const exc = "invalid imported function signature"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s::%s: %s: %s\n", imp->module, imp->field, exc, + imp->u.function.signature); +#endif + return NW_FATAL; + } + else if (hasret != retcnt) + { + static const char *const exc = "return count mismatch"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s::%s: %s: expected: %d, got: %d\n", imp->module, imp->field, + exc, hasret, retcnt); +#endif + return -1; + } + + return 0; +} + +static enum nw_state call_done(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const struct nw_fn *const fn = &ci->fn; + const nw_varuint32 pcnt = fn->param_count; + const struct nw_interp_cfg *const icfg = &i->cfg; + union nw_value *const ret = fn->ret.count ? icfg->args + pcnt : NULL; + + if (ret) + { + size_t sz; + + if (nwp_type_sz(fn->ret.type, &sz)) + { + static const char *const exc = "invalid return type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)fn->ret.type); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = ret; + io.n = sz; + ci->io = io; + i->next = push_return; + } + } + else + nwp_interp_resume(i); + + return NW_AGAIN; +} + +static enum nw_state repeat(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + struct nw_next *const next = &ci->next; + const struct nw_import *const imp = ci->imp; + const enum nw_state n = next->fn(next->user, next); + + if (n == NW_FATAL) + { + static const char *const exc = "import function failed"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %s::%s\n", exc, imp->module, imp->field); +#endif + return NW_FATAL; + } + else if (n) + return n; + + return call_done(i); +} + +static enum nw_state call(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const struct nw_fn *const fn = &ci->fn; + const nw_varuint32 pcnt = fn->param_count; + const struct nw_interp_cfg *const icfg = &i->cfg; + const union nw_value *const args = pcnt ? icfg->args : NULL; + union nw_value *const ret = fn->ret.count ? icfg->args + pcnt : NULL; + const struct nw_import *const imp = ci->imp; + enum nw_state n; + + if (fn->ret.count && check_ret(i)) + return NW_FATAL; + else if ((n = imp->u.function.fn(args, ret, icfg->user, &ci->next))) + { + if (n == NW_FATAL) + { + static const char *const exc = "import function failed"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %s::%s\n", exc, imp->module, imp->field); +#endif + } + else if (ci->next.fn) + i->next = repeat; + + return n; + } + + return call_done(i); +} + +static enum nw_state pop_args(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const enum nw_state n = nwp_stack_pop(i, &ci->io); + + if (n) + return n; + + i->next = call; + return NW_AGAIN; +} + +static enum nw_state get_value(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const enum nw_state n = nwp_stack_read(i, &ci->io, ci->addr); + const struct nw_import *const imp = ci->imp; + const size_t max = i->cfg.n_args; + const nw_varuint32 pcnt = ci->fn.param_count; + size_t nargs; + + if (n) + return n; + else if (parse_signature(imp, &nargs, NULL)) + { + static const char *const exc = "invalid imported function signature"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s::%s: %s: %s\n", imp->module, imp->field, exc, + imp->u.function.signature); +#endif + return NW_FATAL; + } + else if (nargs > max) + { + static const char *const exc = "exceeded maximum args"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s, %s::%s requests %lu, max: %lu\n", exc, imp->module, + imp->field, (unsigned long)nargs, (unsigned long)max); +#endif + return NW_FATAL; + } + else if (pcnt != nargs) + { + static const char *const exc = "arg number mismatch"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s, %s::%s expected %lu args, got %lu\n", exc, imp->module, + imp->field, (unsigned long)nargs, (unsigned long)pcnt); +#endif + return NW_FATAL; + } + + i->cfg.args[ci->param_i++] = ci->value; + + if (ci->param_i >= ci->fn.param_count) + { + struct nw_sm_io io = {0}; + + io.n = ci->sz; + ci->io = io; + i->next = pop_args; + } + else + i->next = seek_param_types; + + return NW_AGAIN; +} + +static enum nw_state get_param_type(struct nw_interp *const i) +{ + nw_varint7 type; + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + struct nw_sm_leb128 *const l = &ci->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varint7(cfg, l, &type, cfg->user); + enum nw_type vtype; + size_t sz; + + if (n) + return n; + else if (nwp_get_type(type, &vtype) || nwp_type_sz(vtype, &sz)) + { + static const char *const exc = "invalid param type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)type); +#endif + return NW_FATAL; + } + else if (ci->type_i >= ci->param_i) + { + if (ci->addr < sz) + { + static const char *const exc = "stack underflow"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)type); +#endif + return NW_FATAL; + } + + if (ci->type_i == ci->param_i) + ci->type = vtype; + + ci->addr -= sz; + } + + ci->sz += sz; + + if (++ci->type_i >= ci->fn.param_count) + { + struct nw_sm_io io = {0}; + + if (nwp_type_sz(ci->type, &sz)) + { + static const char *const exc = "invalid param type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)type); +#endif + return NW_FATAL; + } + + io.buf = &ci->value; + io.n = sz; + ci->io = io; + i->next = get_value; + } + + return NW_AGAIN; +} + +static enum nw_state seek_param_types(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->seek(ci->fn.param_types, cfg->user); + + if (n) + return n; + + ci->type_i = 0; + ci->sz = 0; + ci->addr = nwp_stack_ptr(i); + i->next = get_param_type; + return NW_AGAIN; +} + +static enum nw_state tell(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&ci->pc, cfg->user); + + if (n) + return n; + + i->next = seek_param_types; + return NW_AGAIN; +} + +static const struct nw_import *find(const struct nw_interp *const i, + const nw_varuint32 index) +{ + const struct nw_mod_cfg *const cfg = &i->cfg.m->cfg; + size_t j; + + for (j = 0; j < cfg->n_imports; j++) + { + const struct nw_import_index *const ii = &cfg->imp_indexes[j]; + + if (ii->index == index) + { + const struct nw_import *const imp = &cfg->imports[j]; + +#ifdef NW_LOG + nwp_log("import function index %lu matches %s::%s (import index %lu)\n", + (unsigned long)index, imp->module, imp->field, (unsigned long)j); +#endif + return &cfg->imports[j]; + } + } + + return NULL; +} + +static enum nw_state prepare(struct nw_interp *const i) +{ + const struct nw_fn fn = i->sm.type.out; + const struct nw_import *const imp = find(i, fn.index); + + if (!imp) + { + static const char *const exc = "function import not found"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu\n", exc, (unsigned long)fn.index); +#endif + return NW_FATAL; + } + else if (imp->kind != NW_KIND_FUNCTION) + { + static const char *const exc = "import not a function"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %s::%s\n", exc, imp->module, imp->field); +#endif + return NW_FATAL; + } + else + { + const struct nw_i_sm_call_import ci = {0}; + struct nw_i_sm_call_import *const pci = &i->sm.call_import; + + *pci = ci; + pci->fn = fn; + pci->imp = imp; + i->next = pci->fn.param_count ? tell : call; + } + + return NW_AGAIN; +} + +void nwp_call_import(struct nw_interp *const i, const nw_varuint32 index) +{ + nwp_get_function_type(i, index, prepare); +} diff --git a/src/routines/call_indirect.c b/src/routines/call_indirect.c new file mode 100644 index 0000000..9500969 --- /dev/null +++ b/src/routines/call_indirect.c @@ -0,0 +1,48 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/ops.h> + +static enum nw_state get_value(struct nw_mod *const m) +{ + nw_varuint32 index; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &index, cfg->user); + + if (n) + return n; + + m->next = m->sm.code.next; + return NW_AGAIN; +} + +static enum nw_state get_index(struct nw_mod *const m) +{ + nw_varuint32 index; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &index, cfg->user); + + if (n) + return n; + + m->next = get_value; + return NW_AGAIN; +} + +void nwp_op_check_call_indirect(struct nw_mod *const m) +{ + m->next = get_index; +} diff --git a/src/routines/check_magic.c b/src/routines/check_magic.c new file mode 100644 index 0000000..868d669 --- /dev/null +++ b/src/routines/check_magic.c @@ -0,0 +1,48 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <string.h> + +static enum nw_state run(struct nw_mod *const m) +{ + static const unsigned char magic[] = {'\0', 'a', 's', 'm'}; + struct nw_sm_cm *const cm = &m->sm.check_magic; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &cm->io, cfg->user); + + if (n) + return n; + + if (memcmp(cm->buf, magic, sizeof cm->buf)) + { +#ifdef NW_LOG + nwp_log("wrong magic bytes\n"); +#endif + return NW_FATAL; + } + + nwp_check_version(m); + return NW_AGAIN; +} + +void nwp_check_magic(struct nw_mod *const m) +{ + const struct nw_sm_cm cm = {0}; + struct nw_sm_cm *const pcm = &m->sm.check_magic; + struct nw_sm_io *const io = &pcm->io; + + *pcm = cm; + io->buf = &pcm->buf; + io->n = sizeof pcm->buf; + m->next = run; +} diff --git a/src/routines/check_version.c b/src/routines/check_version.c new file mode 100644 index 0000000..4f6ee05 --- /dev/null +++ b/src/routines/check_version.c @@ -0,0 +1,48 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <string.h> + +static enum nw_state run(struct nw_mod *const m) +{ + static const unsigned char version[] = {1, 0, 0, 0}; + struct nw_sm_cv *const cv = &m->sm.check_version; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &cv->io, cfg->user); + + if (n) + return n; + + if (memcmp(&cv->version, version, sizeof cv->version)) + { +#ifdef NW_LOG + nwp_log("wrong version bytes\n"); +#endif + return NW_FATAL; + } + + nwp_section(m); + return NW_AGAIN; +} + +void nwp_check_version(struct nw_mod *const m) +{ + const struct nw_sm_cv cv = {0}; + struct nw_sm_cv *const pcv = &m->sm.check_version; + struct nw_sm_io *const io = &pcv->io; + + *pcv = cv; + io->buf = &pcv->version; + io->n = sizeof pcv->version; + m->next = run; +} diff --git a/src/routines/find_function.c b/src/routines/find_function.c new file mode 100644 index 0000000..c0273ec --- /dev/null +++ b/src/routines/find_function.c @@ -0,0 +1,100 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state seek_code(struct nw_interp *const i) +{ + const struct nw_i_sm_ffn *const f = &i->sm.ffn; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const unsigned long fbo = nwp_leuint32(&f->fbo); + const enum nw_state n = cfg->seek(fbo, cfg->user); + + if (n) + return n; + + f->next(i); + return NW_AGAIN; +} + +static enum nw_state get_fbo(struct nw_interp *const i) +{ + struct nw_i_sm_ffn *const f = &i->sm.ffn; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &f->io, cfg->user); + + if (n) + return n; + + i->next = seek_code; + return NW_AGAIN; +} + +static enum nw_state seek_fbo(struct nw_interp *const i) +{ + struct nw_i_sm_ffn *const f = &i->sm.ffn; + const struct nw_mod *const m = i->cfg.m; + long offset = m->c_sections[NW_CUSTOM_FBO]; + const struct nw_io_cfg *const cfg = &i->cfg.io; + enum nw_state n; + + if (!offset) + { + static const char *const exc = "nw_fbo section not found"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + return NW_FATAL; + } + else if (f->fn.index < m->import_count) + { + static const char *const exc = "invalid function index"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu\n", exc, (unsigned long)f->fn.index); +#endif + return NW_FATAL; + } + + offset += sizeof f->fbo * (f->fn.index - m->import_count); + + if ((n = cfg->seek(offset, cfg->user))) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &f->fbo; + io.n = sizeof f->fbo; + f->io = io; + i->next = get_fbo; + } + + return NW_AGAIN; +} + +void nwp_find_function(struct nw_interp *const i, const struct nw_fn *const fn, + void (*const next)(struct nw_interp *)) +{ + const struct nw_i_sm_ffn f = {0}; + struct nw_i_sm_ffn *const pf = &i->sm.ffn; + + *pf = f; + pf->fn = *fn; + pf->next = next; + i->next = seek_fbo; +} diff --git a/src/routines/find_local.c b/src/routines/find_local.c new file mode 100644 index 0000000..e5ab9d2 --- /dev/null +++ b/src/routines/find_local.c @@ -0,0 +1,125 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> +#include <limits.h> + +static enum nw_state read_meta(struct nw_interp *const i) +{ + struct nw_find_local *const f = i->state; + const struct nw_local_meta *const m = &f->meta; + const nw_varuint32 index = f->index - f->entry_i; + const enum nw_state n = nwp_stack_read(i, &f->io, f->addr); + size_t sz; + nw_varuint32 count; + + if (n) + return n; + + f->addr += sizeof f->meta; + count = m->entry_count; + + if (nwp_type_sz(m->type, &sz)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)m->type); +#endif + return NW_FATAL; + } + else if (index < count) + { + const size_t offset = sz * index; + + if (f->addr > ULONG_MAX - offset) + { + static const char *const exc = "out-of-bounds access to local"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: addr: %lu, offset: %lu\n", exc, + (unsigned long)f->addr, (unsigned long)offset); +#endif + return NW_FATAL; + } + else if ((f->addr += offset) >= i->fr.local_end) + { + static const char *const exc = "invalid local index"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu\n", exc, (unsigned long)f->index); +#endif + return NW_FATAL; + } + + i->next = f->next; + } + else + { + const size_t offset = sz * count; + + f->entry_i += count; + + if (f->addr > ULONG_MAX - offset) + { + static const char *const exc = "out-of-bounds access to local"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: addr: %lu, offset: %lu\n", exc, + (unsigned long)f->addr, (unsigned long)offset); +#endif + return NW_FATAL; + } + else if ((f->addr += offset) >= i->fr.local_end) + { + static const char *const exc = "invalid local index"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu\n", exc, (unsigned long)f->index); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &f->meta; + io.n = sizeof f->meta; + f->io = io; + } + } + + return NW_AGAIN; +} + +void nwp_find_local(struct nw_interp *const i, struct nw_find_local *const f, + const nw_varuint32 index, enum nw_state (*const next)(struct nw_interp *), + void *const args) +{ + const struct nw_find_local fl = {0}; + + *f = fl; + f->index = index; + f->next = next; + f->addr = i->fr.local_start; + f->io.buf = &f->meta; + f->io.n = sizeof f->meta; + i->state = f; + i->args = args; + i->next = read_meta; +} diff --git a/src/routines/find_param.c b/src/routines/find_param.c new file mode 100644 index 0000000..eeae86d --- /dev/null +++ b/src/routines/find_param.c @@ -0,0 +1,104 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/types.h> + +static enum nw_state seek_pc(struct nw_interp *const i) +{ + struct nw_find_param *const fp = i->state; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->seek(fp->pc, cfg->user); + + if (n) + return n; + + fp->out.addr = i->fr.fr_start - fp->sz; + i->next = fp->next; + return NW_AGAIN; +} + +static enum nw_state get_param_type(struct nw_interp *const i) +{ + nw_varint7 type; + struct nw_find_param *const fp = i->state; + struct nw_sm_leb128 *const l = &fp->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varint7(cfg, l, &type, cfg->user); + enum nw_type param_type; + size_t sz; + + if (n) + return n; + else if (nwp_get_type(type, ¶m_type) + || nwp_type_sz(param_type, &sz)) + { + static const char *const exc = "invalid param type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)type); +#endif + return NW_FATAL; + } + + if (fp->param_i == fp->index) + fp->out.type = param_type; + + if (fp->param_i >= fp->index) + fp->sz += sz; + + if (++fp->param_i >= i->fr.fn.param_count) + i->next = seek_pc; + + return NW_AGAIN; +} + +static enum nw_state seek_param_types(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + const long offset = i->fr.fn.param_types; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + i->next = get_param_type; + return NW_AGAIN; +} + +static enum nw_state tell(struct nw_interp *const i) +{ + struct nw_find_param *const fp = i->state; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&fp->pc, cfg->user); + + if (n) + return n; + + i->next = seek_param_types; + return NW_AGAIN; +} + +void nwp_find_param(struct nw_interp *const i, struct nw_find_param *const f, + const nw_varuint32 index, enum nw_state (*const next)(struct nw_interp *), + void *const args) +{ + const struct nw_find_param fp = {0}; + + *f = fp; + f->index = index; + f->next = next; + i->state = f; + i->next = tell; + i->args = args; +} diff --git a/src/routines/get_function_type.c b/src/routines/get_function_type.c new file mode 100644 index 0000000..e30200f --- /dev/null +++ b/src/routines/get_function_type.c @@ -0,0 +1,331 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state get_param_type(struct nw_interp *); + +static enum nw_state seek_pc(struct nw_interp *const i) +{ + const struct nw_i_sm_type *const t = &i->sm.type; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->seek(t->pc, cfg->user); + + if (n) + return n; + + i->next = t->next; + return NW_AGAIN; +} + +static enum nw_state get_return_type(struct nw_interp *const i) +{ + nw_varint7 type; + struct nw_i_sm_type *const t = &i->sm.type; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varint7(cfg, &t->leb128, &type, cfg->user); + + if (n) + return n; + else if (nwp_get_type(type, &t->out.ret.type)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)type); +#endif + return NW_FATAL; + } + + i->next = seek_pc; + return NW_AGAIN; +} + +static enum nw_state get_return_count(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_type *const t = &i->sm.type; + struct nw_sm_leb128 *const l = &t->leb128; + nw_varuint1 *const return_count = &t->out.ret.count; + const enum nw_state n = nwp_varuint1(cfg, l, return_count, cfg->user); + + if (n) + return n; + else if (*return_count) + i->next = get_return_type; + else + t->next(i); + + return NW_AGAIN; +} + +static enum nw_state get_param_type(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_type *const t = &i->sm.type; + nw_varint7 type; + enum nw_type param_type; + const enum nw_state n = nwp_varint7(cfg, &t->leb128, &type, cfg->user); + + if (n) + return n; + else if (nwp_get_type(type, ¶m_type)) + { + i->exception = "invalid type"; + return NW_FATAL; + } + else if (++t->param_i >= t->out.param_count) + i->next = get_return_count; + + return NW_AGAIN; +} + +static enum nw_state tell_param_types(struct nw_interp *const i) +{ + struct nw_i_sm_type *const t = &i->sm.type; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&t->out.param_types, cfg->user); + + if (n) + return n; + + i->next = get_param_type; + return NW_AGAIN; +} + +static enum nw_state get_param_count(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_type *const t = &i->sm.type; + struct nw_sm_leb128 *const l = &t->leb128; + nw_varuint32 *const count = &t->out.param_count; + const enum nw_state n = nwp_varuint32(cfg, l, count, cfg->user); + + if (n) + return n; + + i->next = *count ? tell_param_types : get_return_count; + return NW_AGAIN; +} + +static enum nw_state get_form(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_type *const t = &i->sm.type; + struct nw_sm_leb128 *const l = &t->leb128; + nw_varint7 form; + const enum nw_state n = nwp_varint7(cfg, l, &form, cfg->user); + + if (n) + return n; + else if (form != 0x60) + { + static const char *const exc = "type index not a function"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)form); +#endif + return NW_FATAL; + } + + i->next = get_param_count; + return NW_AGAIN; +} + +static enum nw_state seek_type(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + const struct nw_i_sm_type *const t = &i->sm.type; + const unsigned long to = nwp_leuint32(&t->to); + const enum nw_state n = cfg->seek(to, cfg->user); + + if (n) + return n; + + i->next = get_form; + return NW_AGAIN; +} + +static enum nw_state get_to(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_type *const t = &i->sm.type; + const enum nw_state n = nwp_io_read(cfg, &t->io, cfg->user); + + if (n) + return n; + + i->next = seek_type; + return NW_AGAIN; +} + +static enum nw_state seek_to(struct nw_interp *const i) +{ + const struct nw_interp_cfg *const icfg = &i->cfg; + struct nw_i_sm_type *const t = &i->sm.type; + const struct nw_mod *const m = icfg->m; + long offset = m->c_sections[NW_CUSTOM_TO]; + const unsigned long fti = nwp_leuint32(&t->fti); + const struct nw_io_cfg *const cfg = &icfg->io; + enum nw_state n; + + if (fti >= m->type_count) + { + static const char *const exc = "invalid type index"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu\n", exc, fti); +#endif + return NW_FATAL; + } + else if (!offset) + { + static const char *const exc = "nw_to section not found"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + return NW_FATAL; + } + + offset += sizeof t->to * fti; + + if ((n = cfg->seek(offset, cfg->user))) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &t->to; + io.n = sizeof t->to; + t->io = io; + i->next = get_to; + } + + return NW_AGAIN; +} + +static enum nw_state get_fti(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_type *const t = &i->sm.type; + const enum nw_state n = nwp_io_read(cfg, &t->io, cfg->user); + + if (n) + return n; + + i->next = seek_to; + return NW_AGAIN; +} + +static enum nw_state seek_fti(struct nw_interp *const i) +{ + const struct nw_interp_cfg *const icfg = &i->cfg; + const struct nw_io_cfg *const cfg = &icfg->io; + struct nw_i_sm_type *const t = &i->sm.type; + long offset = icfg->m->c_sections[NW_CUSTOM_FTI]; + enum nw_state n; + + if (!offset) + { + static const char *const exc = "nw_fti section not found"; + +#ifdef NW_LOG + nwp_log("%s: %s\n", exc, "function type index"); +#endif + i->exception = exc; + return NW_FATAL; + } + + offset += sizeof t->fti * t->fn_index; + + if ((n = cfg->seek(offset, cfg->user))) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &t->fti; + io.n = sizeof t->fti; + t->io = io; + i->next = get_fti; + } + + return NW_AGAIN; +} + +static enum nw_state get_index(struct nw_interp *const i) +{ + struct nw_i_sm_type *const t = &i->sm.type; + const struct nw_get_import_type_out *const out = &t->git.out; + struct nw_sm_leb128 *const l = &t->leb128; + nw_varuint32 index; + const struct nw_io_cfg *const cfg = &i->cfg.io; + enum nw_state n; + + if (out->kind != NW_KIND_FUNCTION) + { + static const char *const exc = "import type not a function"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)out->kind); +#endif + return NW_FATAL; + } + else if ((n = nwp_varuint32(cfg, l, &index, cfg->user))) + return n; + + nwp_toleuint32(index, &t->fti); + i->next = seek_to; + return NW_AGAIN; +} + +static enum nw_state tell(struct nw_interp *const i) +{ + struct nw_i_sm_type *const t = &i->sm.type; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const nw_varuint32 icnt = i->cfg.m->import_count, index = t->out.index; + const enum nw_state n = cfg->tell(&t->pc, cfg->user); + + if (n) + return n; + else if (index >= icnt) + { + t->fn_index = index - icnt; + i->next = seek_fti; + } + else + nwp_get_import_type(i, &t->git, index, get_index); + + return NW_AGAIN; +} + +void nwp_get_function_type(struct nw_interp *const i, const nw_varuint32 index, + enum nw_state (*const next)(struct nw_interp *)) +{ + const struct nw_i_sm_type t = {0}; + struct nw_i_sm_type *const pt = &i->sm.type; + + *pt = t; + pt->out.index = index; + pt->next = next; + i->next = tell; +} diff --git a/src/routines/get_import_type.c b/src/routines/get_import_type.c new file mode 100644 index 0000000..cf5c371 --- /dev/null +++ b/src/routines/get_import_type.c @@ -0,0 +1,123 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/types.h> + +static enum nw_state get_kind(struct nw_interp *const i) +{ + struct nw_get_import_type *const t = i->state; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &t->io, cfg->user); + + if (n) + return n; + else if (t->kind >= NW_KINDS) + { + static const char *const exc = "invalid external_kind"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)t->kind); +#endif + return NW_FATAL; + } + + t->out.kind = t->kind; + i->next = t->next; + return NW_AGAIN; +} + +static enum nw_state seek_type(struct nw_interp *const i) +{ + struct nw_get_import_type *const t = i->state; + const unsigned long offset = nwp_leuint32(&t->value); + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &t->kind; + io.n = sizeof t->kind; + t->io = io; + i->next = get_kind; + } + + return NW_AGAIN; +} + +static enum nw_state get_value(struct nw_interp *const i) +{ + struct nw_get_import_type *const t = i->state; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &t->io, cfg->user); + + if (n) + return n; + + i->next = seek_type; + return NW_AGAIN; +} + +static enum nw_state seek_iti(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_get_import_type *const t = i->state; + long offset = i->cfg.m->c_sections[NW_CUSTOM_ITI]; + enum nw_state n; + + if (!offset) + { + static const char *const exc = "nw_iti section not found"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + return NW_FATAL; + } + + offset += t->index * sizeof t->value; + + if ((n = cfg->seek(offset, cfg->user))) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &t->value; + io.n = sizeof t->value; + t->io = io; + i->next = get_value; + } + + return NW_AGAIN; +} + +void nwp_get_import_type(struct nw_interp *const i, + struct nw_get_import_type *const it, + const nw_varuint32 index, + enum nw_state (*const next)(struct nw_interp *const i)) +{ + const struct nw_get_import_type t = {0}; + + *it = t; + it->index = index; + it->next = next; + i->state = it; + i->next = seek_iti; +} diff --git a/src/routines/i32_arithm.c b/src/routines/i32_arithm.c new file mode 100644 index 0000000..e833a11 --- /dev/null +++ b/src/routines/i32_arithm.c @@ -0,0 +1,39 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/stack.h> +#include <nw/routines.h> + +static enum nw_state push(struct nw_interp *const i) +{ + struct nw_i_sm_i32_arithm *const a = &i->sm.i32_arithm; + const enum nw_state n = nwp_stack_push(i, &a->io); + + if (n) + return n; + + i->push_type = NW_TYPE_I32; + nwp_interp_resume(i); + return NW_AGAIN; +} + +void nwp_op_i32_arithm(struct nw_interp *const i, const long result) +{ + const struct nw_i_sm_i32_arithm a = {0}; + struct nw_i_sm_i32_arithm *const s = &i->sm.i32_arithm; + + *s = a; + s->result = result; + s->io.buf = &s->result; + s->io.n = sizeof s->result; + i->next = push; +} diff --git a/src/routines/init_data.c b/src/routines/init_data.c new file mode 100644 index 0000000..c9482a0 --- /dev/null +++ b/src/routines/init_data.c @@ -0,0 +1,196 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/linear.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state get_index(struct nw_inst *); +static enum nw_state get_byte(struct nw_inst *); + +static enum nw_state store_byte(struct nw_inst *const inst) +{ + struct nw_inst_sm_d *const d = &inst->sm.data; + struct nw_interp *const i = &inst->interp; + const unsigned long offset = d->offset + d->bytes_i; + const enum nw_state n = nwp_linear_store(i, &d->io, offset); + + if (n) + return n; + else if (++d->bytes_i >= d->size) + inst->next = ++d->entry_i >= d->count ? d->next : get_index; + else + inst->next = get_byte; + + return NW_AGAIN; +} + +static enum nw_state get_byte(struct nw_inst *const inst) +{ + struct nw_inst_sm_d *const d = &inst->sm.data; + struct nw_interp *const i = &inst->interp; + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &d->b; + io.n = sizeof d->b; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &d->b; + io.n = sizeof d->b; + d->io = io; + } + + inst->next = store_byte; + return NW_AGAIN; +} + +static enum nw_state get_size(struct nw_inst *const inst) +{ + struct nw_inst_sm_d *const d = &inst->sm.data; + struct nw_sm_leb128 *const l = &d->leb128; + struct nw_interp *const i = &inst->interp; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &d->size, cfg->user); + + if (n) + return n; + + d->bytes_i = 0; + inst->next = get_byte; + return NW_AGAIN; +} + +static enum nw_state pop(struct nw_inst *const inst) +{ + struct nw_inst_sm_d *const d = &inst->sm.data; + const enum nw_state n = nwp_stack_pop(&inst->interp, &d->io); + + if (n) + return n; + + inst->next = get_size; + return NW_AGAIN; +} + +static enum nw_state loop(struct nw_inst *const inst) +{ + struct nw_inst_sm_d *const d = &inst->sm.data; + const enum nw_state n = nwp_interp_run(&inst->interp); + struct nw_sm_io io = {0}; + + if (n) + return n; + + io.buf = &d->offset; + io.n = sizeof d->offset; + d->io = io; + inst->next = pop; + return NW_AGAIN; +} + +static enum nw_state setup_initexpr(struct nw_inst *const inst) +{ + struct nw_interp *const i = &inst->interp; + const struct nw_interp_cfg icfg = i->cfg; + + if (nwp_interp_start(i, &icfg, &nwp_interp_data_set)) + { +#ifdef NW_LOG + nwp_log("nw_interp_start failed\n"); +#endif + return NW_FATAL; + } + + inst->next = loop; + return NW_AGAIN; +} + +static enum nw_state get_index(struct nw_inst *const inst) +{ + struct nw_inst_sm_d *const d = &inst->sm.data; + struct nw_sm_leb128 *const l = &d->leb128; + struct nw_interp *const i = &inst->interp; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &d->index, cfg->user); + + if (n) + return n; + + return setup_initexpr(inst); +} + +static enum nw_state get_count(struct nw_inst *const i) +{ + struct nw_inst_sm_d *const d = &i->sm.data; + struct nw_sm_leb128 *const l = &d->leb128; + const struct nw_interp_cfg *const icfg = &i->interp.cfg; + const struct nw_io_cfg *const cfg = &icfg->io; + const nw_varuint32 expected = icfg->m->data_count; + const enum nw_state n = nwp_varuint32(cfg, l, &d->count, cfg->user); + + if (n) + return n; + else if (d->count != expected) + { +#ifdef NW_LOG + static const char *const exc = "data count mismatch"; + + nwp_log("%s, expected %lu, got %lu\n", exc, + (unsigned long)expected, (unsigned long)d->count); +#endif + return NW_FATAL; + } + + i->next = d->count ? get_index : d->next; + return NW_AGAIN; +} + +static enum nw_state seek_data(struct nw_inst *const i) +{ + struct nw_inst_sm_gl *const d = &i->sm.global; + const struct nw_interp_cfg *const icfg = &i->interp.cfg; + const struct nw_io_cfg *const cfg = &icfg->io; + const long offset = icfg->m->sections[NW_SECTION_DATA]; + enum nw_state n; + + if (!offset) + { + i->next = d->next; + return NW_AGAIN; + } + else if ((n = cfg->seek(offset, cfg->user))) + return n; + + i->next = get_count; + return NW_AGAIN; +} + +void nwp_init_data(struct nw_inst *const i, + enum nw_state (*const next)(struct nw_inst *)) +{ + const struct nw_inst_sm_d d = {0}; + struct nw_inst_sm_d *const p = &i->sm.data; + + *p = d; + p->next = next; + i->next = seek_data; +} diff --git a/src/routines/init_globals.c b/src/routines/init_globals.c new file mode 100644 index 0000000..6d1e5e4 --- /dev/null +++ b/src/routines/init_globals.c @@ -0,0 +1,181 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/global.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state get_content_type(struct nw_inst *const i); + +static enum nw_state store(struct nw_inst *const inst) +{ + struct nw_inst_sm_gl *const g = &inst->sm.global; + struct nw_interp *const i = &inst->interp; + const enum nw_state n = nwp_global_store(i, &g->io, g->entry_i); + + if (n) + return n; + + inst->next = ++g->entry_i >= g->count ? g->next : get_content_type; + return NW_AGAIN; +} + +static enum nw_state pop(struct nw_inst *const inst) +{ + struct nw_inst_sm_gl *const g = &inst->sm.global; + struct nw_interp *const i = &inst->interp; + const enum nw_state n = nwp_stack_pop(i, &g->io); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &g->out; + io.n = sizeof g->out; + g->io = io; + inst->next = store; + } + + return NW_AGAIN; +} + +static enum nw_state loop(struct nw_inst *const i) +{ + struct nw_inst_sm_gl *const g = &i->sm.global; + const enum nw_state n = nwp_interp_run(&i->interp); + size_t sz; + + if (n) + return n; + else if (nwp_type_sz(g->out.type, &sz)) + { +#ifdef NW_LOG + static const char *const exc = "invalid type"; + + nwp_log("%s: %#x\n", exc, (unsigned)g->out.type); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &g->out.value; + io.n = sz; + g->io = io; + i->next = pop; + } + + return NW_AGAIN; +} + +static enum nw_state get_mutability(struct nw_inst *const inst) +{ + struct nw_inst_sm_gl *const g = &inst->sm.global; + struct nw_interp *const i = &inst->interp; + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_sm_leb128 *const l = &g->leb128; + const enum nw_state n = nwp_varuint1(cfg, l, &g->out.mutability, cfg->user); + + if (n) + return n; + + nwp_interp_resume(i); + inst->next = loop; + return NW_AGAIN; +} + +static enum nw_state get_content_type(struct nw_inst *const i) +{ + nw_varint7 content_type; + struct nw_inst_sm_gl *const g = &i->sm.global; + const struct nw_io_cfg *const cfg = &i->interp.cfg.io; + struct nw_sm_leb128 *const l = &g->leb128; + const enum nw_state n = nwp_varint7(cfg, l, &content_type, cfg->user); + + if (n) + return n; + else if (nwp_get_type(content_type, &g->out.type)) + { +#ifdef NW_LOG + static const char *const exc = "invalid type"; + + nwp_log("%s: %#x\n", exc, (unsigned)content_type); +#endif + return NW_FATAL; + } + + i->next = get_mutability; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_inst *const i) +{ + struct nw_inst_sm_gl *const g = &i->sm.global; + struct nw_sm_leb128 *const l = &g->leb128; + const struct nw_interp_cfg *const icfg = &i->interp.cfg; + const struct nw_io_cfg *const cfg = &icfg->io; + const nw_varuint32 expected = icfg->m->global_count; + const enum nw_state n = nwp_varuint32(cfg, l, &g->count, cfg->user); + + if (n) + return n; + else if (g->count != expected) + { +#ifdef NW_LOG + static const char *const exc = "global count mismatch"; + + nwp_log("%s, expected %lu, got %lu\n", exc, + (unsigned long)expected, (unsigned long)g->count); +#endif + return NW_FATAL; + } + + i->next = g->count ? get_content_type : g->next; + return NW_AGAIN; +} + +static enum nw_state seek_global(struct nw_inst *const i) +{ + struct nw_inst_sm_gl *const g = &i->sm.global; + const struct nw_interp_cfg *const icfg = &i->interp.cfg; + const struct nw_io_cfg *const cfg = &icfg->io; + const long offset = icfg->m->sections[NW_SECTION_GLOBAL]; + enum nw_state n; + + if (!offset) + { + i->next = g->next; + return NW_AGAIN; + } + else if ((n = cfg->seek(offset, cfg->user))) + return n; + + i->next = get_count; + return NW_AGAIN; +} + +void nwp_init_globals(struct nw_inst *const i, + enum nw_state (*const next)(struct nw_inst *)) +{ + const struct nw_inst_sm_gl gl = {0}; + struct nw_inst_sm_gl *const p = &i->sm.global; + + *p = gl; + p->next = next; + i->next = seek_global; +} diff --git a/src/routines/mem_imm.c b/src/routines/mem_imm.c new file mode 100644 index 0000000..c93ee1a --- /dev/null +++ b/src/routines/mem_imm.c @@ -0,0 +1,51 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> +#include <nw/io.h> + +static enum nw_state get_offset(struct nw_interp *const i) +{ + struct nw_i_sm_imm *const imm = &i->sm.imm; + struct nw_sm_leb128 *const l = &imm->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &imm->out.offset, cfg->user); + + if (n) + return n; + + imm->next(i); + return NW_AGAIN; +} + +static enum nw_state get_flags(struct nw_interp *const i) +{ + struct nw_i_sm_imm *const imm = &i->sm.imm; + struct nw_sm_leb128 *const l = &imm->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &imm->out.flags, cfg->user); + + if (n) + return n; + + i->next = get_offset; + return NW_AGAIN; +} + +void nwp_mem_imm(struct nw_interp *const i, + void (*const next)(struct nw_interp *)) +{ + const struct nw_i_sm_imm imm = {0}; + struct nw_i_sm_imm *const p = &i->sm.imm; + + *p = imm; + p->next = next; + i->next = get_flags; +} diff --git a/src/routines/section.c b/src/routines/section.c new file mode 100644 index 0000000..5198424 --- /dev/null +++ b/src/routines/section.c @@ -0,0 +1,105 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static void (*const fn[])(struct nw_mod *) = +{ + nwp_section_custom, + nwp_section_type, + nwp_section_import, + nwp_section_function, + nwp_section_table, + nwp_section_memory, + nwp_section_global, + nwp_section_export, + nwp_section_start, + nwp_section_element, + nwp_section_code, + nwp_section_data, + nwp_section_data_count +}; + +static enum nw_state get_offset(struct nw_mod *const m) +{ + struct nw_mod_section *const s = &m->section; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&s->offset, cfg->user); + long *const offset = &m->sections[s->section]; + + if (n) + return n; + /* Custom section can appear more than once. */ + else if (s->section != NW_SECTION_CUSTOM && *offset) + { +#ifdef NW_LOG + nwp_log("ignoring duplicate section %u\n", (unsigned)s->section); +#endif + nwp_section_skip(m); + } + else + { + nwp_log("processing section %u\n", (unsigned)s->section); + *offset = s->offset; + fn[s->section](m); + } + + return NW_AGAIN; +} + +static enum nw_state get_length(struct nw_mod *const m) +{ + struct nw_mod_section *const s = &m->section; + struct nw_sm_leb128 *const l = &s->leb128; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &s->len, cfg->user); + + if (n) + return n; + + m->next = get_offset; + return NW_AGAIN; +} + +static enum nw_state get_section(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_mod_section *const s = &m->section; + struct nw_sm_leb128 *const l = &s->leb128; + const enum nw_state n = nwp_varuint7(cfg, l, &s->section, cfg->user); + + if (n) + { + if (cfg->eof(cfg->user)) + return NW_OK; + + return n; + } + else if (s->section >= sizeof fn / sizeof *fn) + { +#ifdef NW_LOG + nwp_log("invalid section %u\n", (unsigned)s->section); +#endif + return NW_FATAL; + } + + m->next = get_length; + return NW_AGAIN; +} + +void nwp_section(struct nw_mod *const m) +{ + const struct nw_mod_section s = {0}; + + m->section = s; + m->next = get_section; +} diff --git a/src/routines/section/CMakeLists.txt b/src/routines/section/CMakeLists.txt new file mode 100644 index 0000000..ae78d27 --- /dev/null +++ b/src/routines/section/CMakeLists.txt @@ -0,0 +1,34 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + code.c + custom.c + data.c + data_count.c + element.c + exit.c + export.c + fbo.c + fti.c + function.c + global.c + import.c + iti.c + lo.c + memory.c + ops.c + skip.c + start.c + table.c + to.c + type.c +) + +if(NW_CHECK_CODE) + target_compile_definitions(${PROJECT_NAME} PRIVATE NW_CHECK_CODE) +endif() diff --git a/src/routines/section/code.c b/src/routines/section/code.c new file mode 100644 index 0000000..3ee0c5b --- /dev/null +++ b/src/routines/section/code.c @@ -0,0 +1,298 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/opcodes.h> +#include <nw/ops.h> +#include <nw/routines.h> + +static enum nw_state local_loop(struct nw_mod *); +static enum nw_state get_opcode(struct nw_mod *); +static enum nw_state entry_loop(struct nw_mod *); + +static enum nw_state get_post_offset(struct nw_mod *const m) +{ + long offset; + unsigned long consumed; + struct nw_sm_c *const c = &m->sm.code; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&offset, cfg->user); + + if (n) + return n; + else if ((consumed = offset - c->op_off) && consumed >= c->rem) + { +#ifdef NW_LOG + nwp_log("parameters exceed expected body length\n"); +#endif + return NW_FATAL; + } + + c->rem -= consumed; + m->next = get_opcode; + return NW_AGAIN; +} + +typedef void (*op_fn)(struct nw_mod *); + +static op_fn find(unsigned char op) +{ + size_t i; + + for (i = 0; i < nwp_check_ops.n; i++) + { + const struct nwp_check_op *const o = &nwp_check_ops.ops[i]; + + if (op >= o->start && op <= o->end) + return o->f; + } + + return NULL; +} + +static enum nw_state get_op_offset(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&c->op_off, cfg->user); + op_fn fn; + + if (n) + return n; + else if (!(fn = find(c->op))) + { +#ifdef NW_LOG + nwp_log("invalid opcode %#hhx\n", (unsigned char)c->op); +#endif + return NW_FATAL; + } + +#ifdef NW_LOG + nwp_log("opcode: %#hhx\n", (unsigned char)c->op); +#endif + c->next = get_post_offset; + fn(m); + return NW_AGAIN; +} + +static enum nw_state check_exit(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_c_fn *const fn = &c->fn; + + if (fn->blocks) + { +#ifdef NW_LOG + nwp_log("mismatched number of blocks in function %lu\n", + (unsigned long)c->entry_i); +#endif + return NW_FATAL; + } + + c->entry_i++; + m->next = entry_loop; + return NW_AGAIN; +} + +static enum nw_state get_opcode(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &c->op; + io.n = sizeof c->op; + + if (!c->rem) + return check_exit(m); + else if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else if (c->rem == 1 && c->op != OP_END) + { +#ifdef NW_LOG + nwp_log("%s: unexpected opcode %#x at body end\n", (unsigned)c->op); +#endif + return NW_FATAL; + } + + c->rem--; + m->next = get_op_offset; + return NW_AGAIN; +} + +#ifndef NW_CHECK_CODE +static enum nw_state skip(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->seek(c->body_start + c->rem, cfg->user); + + if (n) + return n; + + c->entry_i++; + m->next = entry_loop; + return NW_AGAIN; +} +#endif + +static enum nw_state get_rem(struct nw_mod *const m) +{ + unsigned long consumed; + struct nw_sm_c *const c = &m->sm.code; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&c->body_start, cfg->user); + + if (n) + return n; + else if ((consumed = c->body_start - c->start) >= c->body_size) + { +#ifdef NW_LOG + nwp_log("exceeded function body size\n"); +#endif + return NW_FATAL; + } + + c->rem = c->body_size - consumed; +#ifdef NW_CHECK_CODE + m->next = get_opcode; +#else + m->next = skip; +#endif + + return NW_AGAIN; +} + +static enum nw_state get_type(struct nw_mod *const m) +{ + nw_varint7 type; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varint7(cfg, l, &type, cfg->user); + + if (n) + return n; + + c->fn.local_i++; + m->next = local_loop; + return NW_AGAIN; +} + +static enum nw_state get_group_count(struct nw_mod *const m) +{ + nw_varuint32 group_count; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &group_count, cfg->user); + + if (n) + return n; + + c->fn.local_total += group_count; + m->next = get_type; + return NW_AGAIN; +} + +static enum nw_state local_loop(struct nw_mod *const m) +{ + const struct nw_sm_c_fn *const fn = &m->sm.code.fn; + + m->next = fn->local_i < fn->local_count ? get_group_count : get_rem; + return NW_AGAIN; +} + +static enum nw_state get_local_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_c_fn *const fn = &c->fn; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &fn->local_count, + cfg->user); + + if (n) + return n; + + m->next = fn->local_count ? local_loop : get_rem; + return NW_AGAIN; +} + +static enum nw_state get_start(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&c->start, cfg->user); + + if (n) + return n; + + m->next = get_local_count; + return NW_AGAIN; +} + +static enum nw_state get_body_size(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &c->body_size, cfg->user); + + if (n) + return n; + else if (!c->body_size) + { +#ifdef NW_LOG + nwp_log("unexpected zero size for function body %lu\n", + (unsigned long)c->entry_i); +#endif + return NW_FATAL; + } + + m->next = get_start; + return NW_AGAIN; +} + +static enum nw_state entry_loop(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_c_fn fn = {0}; + + fn.blocks = 1; + c->fn = fn; + m->next = c->entry_i < c->count ? get_body_size : nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &c->count, cfg->user); + + if (n) + return n; + + m->next = entry_loop; + return NW_AGAIN; +} + +void nwp_section_code(struct nw_mod *const m) +{ + const struct nw_sm_c c = {0}; + + m->sm.code = c; + m->next = get_count; +} diff --git a/src/routines/section/custom.c b/src/routines/section/custom.c new file mode 100644 index 0000000..926f46b --- /dev/null +++ b/src/routines/section/custom.c @@ -0,0 +1,145 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <string.h> + +static enum nw_state get_byte(struct nw_mod *); + +static enum nw_state skip(struct nw_mod *const m) +{ + const struct nw_sm_custom *const c = &m->sm.custom; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->seek(c->start, cfg->user); + + if (n) + return n; + + nwp_section_skip(m); + return NW_AGAIN; +} + +static enum nw_state compare(struct nw_mod *const m) +{ + struct nw_sm_custom *const c = &m->sm.custom; + const struct entry *out = NULL; + size_t i; + static const struct entry + { + const char *section; + void (*f)(struct nw_mod *); + } s[] = + { + {"nw_to", nwp_section_to}, + {"nw_fti", nwp_section_fti}, + {"nw_fbo", nwp_section_fbo}, + {"nw_lo", nwp_section_lo}, + {"nw_iti", nwp_section_iti} + }; + + for (i = 0; i < sizeof s / sizeof *s; i++) + { + int *const candidate = &c->candidate[i]; + const struct entry *const e = &s[i]; + + if (!*candidate) + continue; + else if (strlen(e->section) != c->name_len + || e->section[c->len_i] != c->byte) + { + *candidate = 0; + continue; + } + + out = e; + break; + } + + if (++c->len_i >= c->name_len) + { + if (out) + { + if (m->c_sections[out - s]) + { +#ifdef NW_LOG + nwp_log("duplicate section: %s\n", out->section); +#endif + return NW_FATAL; + } + + out->f(m); + } + else + m->next = skip; + } + else + m->next = get_byte; + + return NW_AGAIN; +} + +static enum nw_state get_byte(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_custom *const c = &m->sm.custom; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &c->byte; + io.n = sizeof c->byte; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + + m->next = compare; + return NW_AGAIN; +} + +static enum nw_state get_name_len(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_custom *const c = &m->sm.custom; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &c->name_len, cfg->user); + + if (n) + return n; + + m->next = get_byte; + return NW_AGAIN; +} + +static enum nw_state get_start(struct nw_mod *const m) +{ + struct nw_sm_custom *const c = &m->sm.custom; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&c->start, cfg->user); + + if (n) + return n; + + m->next = get_name_len; + return NW_AGAIN; +} + +void nwp_section_custom(struct nw_mod *const m) +{ + const struct nw_sm_custom c = {0}; + struct nw_sm_custom *const pc = &m->sm.custom; + size_t i; + + m->next = get_start; + *pc = c; + + for (i = 0; i < sizeof pc->candidate / sizeof *pc->candidate; i++) + pc->candidate[i] = 1; +} diff --git a/src/routines/section/data.c b/src/routines/section/data.c new file mode 100644 index 0000000..b189c9d --- /dev/null +++ b/src/routines/section/data.c @@ -0,0 +1,178 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <string.h> + +static enum nw_state get_index(struct nw_mod *); + +static enum nw_state skip_bytes(struct nw_mod *const m) +{ + struct nw_sm_d *const d = &m->sm.data; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const long offset = d->offset + d->size; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + m->next = ++d->entry_i >= m->data_count ? nwp_section_exit : get_index; + return NW_AGAIN; +} + +static enum nw_state tell(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_d *const d = &m->sm.data; + const enum nw_state n = cfg->tell(&d->offset, cfg->user); + + if (n) + return n; + + m->next = skip_bytes; + return NW_AGAIN; +} + +static enum nw_state get_size(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_d *const d = &m->sm.data; + struct nw_sm_leb128 *const l = &d->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &d->size, cfg->user); + + if (n) + return n; + else if (!d->size) + { +#ifdef NW_LOG + nwp_log("unexpected zero size for data entry %lu\n", + (unsigned long)d->entry_i); +#endif + return NW_FATAL; + } + + m->next = tell; + return NW_AGAIN; +} + +static int push(const void *const src, const size_t n, void *const user) +{ + struct nw_mod *const m = user; + struct nw_sm_d *const d = &m->sm.data; + + if (n > sizeof d->value) + { +#ifdef NW_LOG + nwp_log("stack overflow\n"); +#endif + return -1; + } + + memcpy(&d->value, src, n); + return n; +} + +static enum nw_state loop(struct nw_mod *const m) +{ + struct nw_sm_d *const d = &m->sm.data; + const enum nw_state n = nwp_interp_run(&d->interp); + + if (n) + return n; + + m->next = get_size; + return NW_AGAIN; +} + +static enum nw_state setup_initexpr(struct nw_mod *const m) +{ + struct nw_interp_cfg icfg = {0}; + struct nw_sm_d *const d = &m->sm.data; + + icfg.m = m; + icfg.io = m->cfg.io; + icfg.user = m; + icfg.stack.push = push; + + if (nwp_interp_start(&d->interp, &icfg, &nwp_interp_data_set)) + { +#ifdef NW_LOG + nwp_log("nw_interp_start failed\n"); +#endif + return NW_FATAL; + } + + m->next = loop; + return NW_AGAIN; +} + +static enum nw_state get_index(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_d *const d = &m->sm.data; + struct nw_sm_leb128 *const l = &d->leb128; + nw_varuint32 *const index = &d->index; + const enum nw_state n = nwp_varuint32(cfg, l, index, cfg->user); + + if (n) + return n; + else if (*index) + { +#ifdef NW_LOG + nwp_log("expected memory index 0 in data entry %lu, got %lu\n", + (unsigned long)d->entry_i, *index); +#endif + return NW_FATAL; + } + + return setup_initexpr(m); +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + const long dc = m->sections[NW_SECTION_DATA_COUNT]; + struct nw_sm_d *const d = &m->sm.data; + struct nw_sm_leb128 *const l = &d->leb128; + nw_varuint32 count, *const pcount = dc ? &count : &m->data_count; + enum nw_state n; + + if (!m->sections[NW_SECTION_MEMORY]) + { +#ifdef NW_LOG + nwp_log("data section found before memory section\n"); +#endif + return NW_FATAL; + } + else if ((n = nwp_varuint32(cfg, l, pcount, cfg->user))) + return n; + else if (dc && count != m->data_count) + { +#ifdef NW_LOG + nwp_log("data count mismatch, expected %lu, got %lu\n", + (unsigned long)m->data_count, (unsigned long)count); +#endif + return NW_FATAL; + } + + m->next = m->data_count ? get_index : nwp_section_exit; + return NW_AGAIN; +} + +void nwp_section_data(struct nw_mod *const m) +{ + const struct nw_sm_d d = {0}; + + m->sm.data = d; + m->next = get_count; +} diff --git a/src/routines/section/data_count.c b/src/routines/section/data_count.c new file mode 100644 index 0000000..9e7ea07 --- /dev/null +++ b/src/routines/section/data_count.c @@ -0,0 +1,35 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_dc *const dc = &m->sm.data_count; + struct nw_sm_leb128 *const l = &dc->leb128; + enum nw_state n; + + if ((n = nwp_varuint32(cfg, l, &m->data_count, cfg->user))) + return n; + + m->next = nwp_section_exit; + return NW_AGAIN; +} + +void nwp_section_data_count(struct nw_mod *const m) +{ + const struct nw_sm_dc dc = {0}; + + m->sm.data_count = dc; + m->next = get_count; +} diff --git a/src/routines/section/element.c b/src/routines/section/element.c new file mode 100644 index 0000000..7fd2f54 --- /dev/null +++ b/src/routines/section/element.c @@ -0,0 +1,20 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +void nwp_section_element(struct nw_mod *const m) +{ + /* TODO */ + nwp_section_skip(m); +} diff --git a/src/routines/section/exit.c b/src/routines/section/exit.c new file mode 100644 index 0000000..93a2446 --- /dev/null +++ b/src/routines/section/exit.c @@ -0,0 +1,36 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +enum nw_state nwp_section_exit(struct nw_mod *const m) +{ + long offset; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const struct nw_mod_section *const s = &m->section; + const enum nw_state n = cfg->tell(&offset, cfg->user); + unsigned long size; + + if (n) + return n; + else if ((size = offset - s->offset) != s->len) + { +#ifdef NW_LOG + nwp_log("size mismatch (%lu expected, got %lu)\n", + (unsigned long)s->len, size); +#endif + return NW_FATAL; + } + + nwp_section(m); + return NW_AGAIN; +} diff --git a/src/routines/section/export.c b/src/routines/section/export.c new file mode 100644 index 0000000..643b55f --- /dev/null +++ b/src/routines/section/export.c @@ -0,0 +1,123 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state entry_loop(struct nw_mod *m); + +static enum nw_state get_index(struct nw_mod *const m) +{ + nw_varuint32 index; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_exp *const e = &m->sm.export; + struct nw_sm_leb128 *const l = &e->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &index, cfg->user); + + if (n) + return n; + + e->entry_i++; + m->next = entry_loop; + return NW_AGAIN; +} + +static enum nw_state get_kind(struct nw_mod *const m) +{ + unsigned char kind; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &kind; + io.n = sizeof kind; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + + /* TODO: check kind */ + m->next = get_index; + return NW_AGAIN; +} + +static enum nw_state get_name(struct nw_mod *const m) +{ + unsigned char b; + struct nw_sm_exp *const e = &m->sm.export; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &b; + io.n = sizeof b; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else if (++e->len_i >= e->field_len) + { + e->len_i = 0; + m->next = get_kind; + } + + return NW_AGAIN; +} + +static enum nw_state get_len(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_exp *const e = &m->sm.export; + struct nw_sm_leb128 *const l = &e->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &e->field_len, cfg->user); + + if (n) + return n; + else if (!e->field_len) + { +#ifdef NW_LOG + nwp_log("invalid field length\n"); +#endif + return NW_FATAL; + } + + m->next = get_name; + return NW_AGAIN; +} + +static enum nw_state entry_loop(struct nw_mod *const m) +{ + const struct nw_sm_exp *const e = &m->sm.export; + + m->next = e->entry_i < e->count ? get_len : nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_exp *const e = &m->sm.export; + struct nw_sm_leb128 *const l = &e->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &e->count, cfg->user); + + if (n) + return n; + + m->next = entry_loop; + return NW_AGAIN; +} + +void nwp_section_export(struct nw_mod *const m) +{ + const struct nw_sm_exp e = {0}; + + m->sm.export = e; + m->next = get_count; +} diff --git a/src/routines/section/fbo.c b/src/routines/section/fbo.c new file mode 100644 index 0000000..82ebd14 --- /dev/null +++ b/src/routines/section/fbo.c @@ -0,0 +1,52 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state skip(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + const long offset = m->c_sections[NW_CUSTOM_FBO] + + sizeof (struct nw_leuint32) * m->function_count; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + nwp_section(m); + return NW_AGAIN; +} + +static enum nw_state check(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + enum nw_state n; + + if (!m->sections[NW_SECTION_CODE]) + { +#ifdef NW_LOG + nwp_log("code section must be defined first\n"); +#endif + return NW_FATAL; + } + else if ((n = cfg->tell(&m->c_sections[NW_CUSTOM_FBO], cfg->user))) + return n; + + m->next = skip; + return NW_AGAIN; +} + +void nwp_section_fbo(struct nw_mod *const m) +{ + m->next = check; +} diff --git a/src/routines/section/fti.c b/src/routines/section/fti.c new file mode 100644 index 0000000..ddc7762 --- /dev/null +++ b/src/routines/section/fti.c @@ -0,0 +1,59 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state skip(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + const long offset = m->c_sections[NW_CUSTOM_FTI] + + sizeof (struct nw_leuint32) * m->function_count; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + nwp_section(m); + return NW_AGAIN; +} + +static enum nw_state check(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + enum nw_state n; + + if (!m->sections[NW_SECTION_FUNCTION]) + { +#ifdef NW_LOG + nwp_log("function section must be defined first\n"); +#endif + return NW_FATAL; + } + else if (!m->sections[NW_SECTION_IMPORT]) + { +#ifdef NW_LOG + nwp_log("import section must be defined first\n"); +#endif + return NW_FATAL; + } + else if ((n = cfg->tell(&m->c_sections[NW_CUSTOM_FTI], cfg->user))) + return n; + + m->next = skip; + return NW_AGAIN; +} + +void nwp_section_fti(struct nw_mod *const m) +{ + m->next = check; +} diff --git a/src/routines/section/function.c b/src/routines/section/function.c new file mode 100644 index 0000000..7cbcaca --- /dev/null +++ b/src/routines/section/function.c @@ -0,0 +1,52 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state get_entry(struct nw_mod *const m) +{ + struct nw_sm_fn *const fn = &m->sm.function; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_leb128 *const l = &fn->leb128; + nw_varuint32 type; + const enum nw_state n = nwp_varuint32(cfg, l, &type, cfg->user); + + if (n) + return n; + else if (++fn->entry_i >= m->function_count) + m->next = nwp_section_exit; + + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_fn *const fn = &m->sm.function; + struct nw_sm_leb128 *const l = &fn->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &m->function_count, + cfg->user); + + if (n) + return n; + + m->next = get_entry; + return NW_AGAIN; +} + +void nwp_section_function(struct nw_mod *const m) +{ + const struct nw_sm_fn fn = {0}; + + m->next = get_count; + m->sm.function = fn; +} diff --git a/src/routines/section/global.c b/src/routines/section/global.c new file mode 100644 index 0000000..6223fc9 --- /dev/null +++ b/src/routines/section/global.c @@ -0,0 +1,148 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/types.h> +#include <limits.h> +#include <string.h> + +static enum nw_state get_content_type(struct nw_mod *); + +static int push(const void *const src, const size_t n, void *const user) +{ + struct nw_mod *const m = user; + struct nw_sm_gl *const gl = &m->sm.global; + + if (n > sizeof gl->value) + { +#ifdef NW_LOG + nwp_log("stack overflow\n"); +#endif + return -1; + } + + memcpy(&gl->value, src, n); + return n; +} + +static enum nw_state loop(struct nw_mod *const m) +{ + struct nw_sm_gl *const gl = &m->sm.global; + const enum nw_state n = nwp_interp_run(&gl->interp); + + if (n) + return n; + + m->next = ++gl->entry_i >= m->global_count ? + nwp_section_exit : get_content_type; + return NW_AGAIN; +} + +static enum nw_state get_global(struct nw_mod *const m) +{ + struct nw_sm_gl *const gl = &m->sm.global; + struct nw_interp_cfg cfg = {0}; + + cfg.m = m; + cfg.io = m->cfg.io; + cfg.user = m; + cfg.stack.push = push; + + if (nwp_interp_start(&gl->interp, &cfg, &nwp_interp_initexpr_set)) + { +#ifdef NW_LOG + nwp_log("nw_interp_start failed\n"); +#endif + return NW_FATAL; + } + + m->next = loop; + return NW_AGAIN; +} + +static enum nw_state get_mutability(struct nw_mod *const m) +{ + nw_varuint1 mutability; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_gl *const gl = &m->sm.global; + struct nw_sm_leb128 *const l = &gl->leb128; + const enum nw_state n = nwp_varuint1(cfg, l, &mutability, cfg->user); + + if (n) + return n; + + m->next = get_global; + return NW_AGAIN; +} + +static enum nw_state get_content_type(struct nw_mod *const m) +{ + nw_varint7 content_type; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_gl *const gl = &m->sm.global; + struct nw_sm_leb128 *const l = &gl->leb128; + const enum nw_state n = nwp_varint7(cfg, l, &content_type, cfg->user); + enum nw_type type; + + /* TODO: do not ignore type */ + if (n) + return n; + else if (nwp_get_type(content_type, &type)) + { +#ifdef NW_LOG + nwp_log("nwp_get_type failed\n"); +#endif + return NW_FATAL; + } + + m->next = get_mutability; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_gl *const gl = &m->sm.global; + struct nw_sm_leb128 *const l = &gl->leb128; + enum nw_state n; + + if (!m->sections[NW_SECTION_IMPORT]) + { +#ifdef NW_LOG + nwp_log("global section found before import section\n"); +#endif + return NW_FATAL; + } + else if ((n = nwp_varuint32(cfg, l, &m->global_count, cfg->user))) + return n; + else if (m->global_count > ULONG_MAX / sizeof (struct nw_global)) + { +#ifdef NW_LOG + nwp_log("global_count too large: %lu\n", + (unsigned long)m->global_count); +#endif + return NW_FATAL; + } + + m->out.global = m->global_count * sizeof (struct nw_global); + m->next = m->global_count ? get_content_type : nwp_section_exit; + return NW_AGAIN; +} + +void nwp_section_global(struct nw_mod *const m) +{ + const struct nw_sm_gl g = {0}; + + m->sm.global = g; + m->next = get_count; +} diff --git a/src/routines/section/import.c b/src/routines/section/import.c new file mode 100644 index 0000000..26ef49a --- /dev/null +++ b/src/routines/section/import.c @@ -0,0 +1,339 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <string.h> + +static enum nw_state entry_loop(struct nw_mod *); +static enum nw_state find_import(struct nw_mod *); + +static enum nw_state get_function_type(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + struct nw_sm_leb128 *const l = &imp->leb128; + struct nw_import_index *const ii = &m->cfg.imp_indexes[imp->imp_i]; + nw_varuint32 type; + const enum nw_state n = nwp_varuint32(cfg, l, &type, cfg->user); + + if (n) + return n; + else if (imp->entry_i >= m->cfg.n_imports) + { +#ifdef NW_LOG + nwp_log("too many import entries\n"); +#endif + return NW_FATAL; + } + + ii->index = imp->entry_i++; + m->next = entry_loop; + return NW_AGAIN; +} + +static enum nw_state skip_import(struct nw_mod *const m) +{ + struct nw_sm_imp *const imp = &m->sm.import; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->seek(imp->mod_off, cfg->user); + + if (n) + return n; + + imp->len_i = 0; + imp->imp_i++; + m->next = find_import; + return NW_AGAIN; +} + +static enum nw_state get_kind(struct nw_mod *const m) +{ + unsigned char kind; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + const struct nw_import *const i = &m->cfg.imports[imp->imp_i]; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &kind; + io.n = sizeof kind; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else if (kind != i->kind) + m->next = skip_import; + /* TODO: process every import type. */ + else if (kind != NW_KIND_FUNCTION) + m->next = nwp_section_exit; + else + m->next = get_function_type; + + return NW_AGAIN; +} + +static enum nw_state compare_field(struct nw_mod *const m) +{ + unsigned char byte; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + const struct nw_import *const i = &m->cfg.imports[imp->imp_i]; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &byte; + io.n = sizeof byte; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else if (byte != i->field[imp->len_i]) + m->next = skip_import; + else if (++imp->len_i >= imp->field_len) + { + m->next = get_kind; + imp->len_i = 0; + } + + return NW_AGAIN; +} + +static enum nw_state get_field_offset(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + const enum nw_state n = cfg->tell(&imp->field_off, cfg->user); + const struct nw_import *const i = &m->cfg.imports[imp->imp_i]; + + if (n) + return n; + + m->next = imp->field_len != strlen(i->field) ? skip_import : compare_field; + return NW_AGAIN; +} + +static enum nw_state get_field_len(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + struct nw_sm_leb128 *const l = &imp->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &imp->field_len, cfg->user); + + if (n) + return n; + + m->next = get_field_offset; + return NW_AGAIN; +} + +static enum nw_state compare_name(struct nw_mod *const m) +{ + unsigned char byte; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + const struct nw_import *const i = &m->cfg.imports[imp->imp_i]; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &byte; + io.n = sizeof byte; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else if (byte != i->module[imp->len_i]) + m->next = skip_import; + else if (++imp->len_i >= imp->mod_len) + { + m->next = get_field_len; + imp->len_i = 0; + } + + return NW_AGAIN; +} + +static enum nw_state dump_import_field_name(struct nw_mod *const m) +{ + unsigned char byte; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_io io = {0}; + struct nw_sm_imp *const imp = &m->sm.import; + enum nw_state n; + + io.buf = &byte; + io.n = sizeof byte; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + +#ifdef NW_LOG + nwp_log("%c", (char)byte); +#endif + + if (++imp->len_i >= imp->field_len) + { +#ifdef NW_LOG + nwp_log("\n"); +#endif + return NW_FATAL; + } + + return NW_AGAIN; +} + +static enum nw_state rewind_to_field_name(struct nw_mod *const m) +{ + struct nw_sm_imp *const imp = &m->sm.import; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->seek(imp->field_off, cfg->user); + + if (n) + return n; + + m->next = dump_import_field_name; + return NW_AGAIN; +} + +static enum nw_state dump_import_mod_name(struct nw_mod *const m) +{ + unsigned char byte; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &byte; + io.n = sizeof byte; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + +#ifdef NW_LOG + nwp_log("%c", (char)byte); +#endif + + if (++imp->len_i >= imp->mod_len) + { + imp->len_i = 0; + + if (imp->field_off) + { +#ifdef NW_LOG + nwp_log("::"); +#endif + m->next = rewind_to_field_name; + } + else + { +#ifdef NW_LOG + nwp_log("\n"); +#endif + return NW_FATAL; + } + } + + return NW_AGAIN; +} + +static enum nw_state rewind_to_mod_name(struct nw_mod *const m) +{ + struct nw_sm_imp *const imp = &m->sm.import; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->seek(imp->mod_off, cfg->user); + + if (n) + return n; + + m->next = dump_import_mod_name; + return NW_AGAIN; +} + +static enum nw_state find_import(struct nw_mod *const m) +{ + struct nw_sm_imp *const imp = &m->sm.import; + const struct nw_import *i; + + if (imp->imp_i >= m->cfg.n_imports) + { +#ifdef NW_LOG + nwp_log("required import: "); +#endif + m->next = rewind_to_mod_name; + return NW_AGAIN; + } + + i = &m->cfg.imports[imp->imp_i]; + + if (imp->mod_len == strlen(i->module)) + m->next = compare_name; + else + imp->imp_i++; + + return NW_AGAIN; +} + +static enum nw_state get_module_offset(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + const enum nw_state n = cfg->tell(&imp->mod_off, cfg->user); + + if (n) + return n; + + m->next = find_import; + return NW_AGAIN; +} + +static enum nw_state get_module_len(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + struct nw_sm_leb128 *const l = &imp->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &imp->mod_len, cfg->user); + + if (n) + return n; + + m->next = get_module_offset; + return NW_AGAIN; +} + +static enum nw_state entry_loop(struct nw_mod *const m) +{ + struct nw_sm_imp *const imp = &m->sm.import; + + imp->len_i = 0; + imp->imp_i = 0; + m->next = imp->entry_i < m->import_count ? + get_module_len : nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + struct nw_sm_leb128 *const l = &imp->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &m->import_count, cfg->user); + + if (n) + return n; + + m->next = entry_loop; + return NW_AGAIN; +} + +void nwp_section_import(struct nw_mod *const m) +{ + const struct nw_sm_imp imp = {0}; + + m->next = get_count; + m->sm.import = imp; +} diff --git a/src/routines/section/iti.c b/src/routines/section/iti.c new file mode 100644 index 0000000..6fc7abc --- /dev/null +++ b/src/routines/section/iti.c @@ -0,0 +1,52 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state skip(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + const long offset = m->c_sections[NW_CUSTOM_ITI] + + sizeof (struct nw_leuint32) * m->import_count; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + nwp_section(m); + return NW_AGAIN; +} + +static enum nw_state check(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + enum nw_state n; + + if (!m->sections[NW_SECTION_IMPORT]) + { +#ifdef NW_LOG + nwp_log("import section must be defined first\n"); +#endif + return NW_FATAL; + } + else if ((n = cfg->tell(&m->c_sections[NW_CUSTOM_ITI], cfg->user))) + return n; + + m->next = skip; + return NW_AGAIN; +} + +void nwp_section_iti(struct nw_mod *const m) +{ + m->next = check; +} diff --git a/src/routines/section/lo.c b/src/routines/section/lo.c new file mode 100644 index 0000000..aacfa67 --- /dev/null +++ b/src/routines/section/lo.c @@ -0,0 +1,58 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state skip(struct nw_mod *const m) +{ + const struct nw_sm_custom *const c = &m->sm.custom; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->seek(c->start, cfg->user); + + if (n) + return n; + + nwp_section_skip(m); + return NW_AGAIN; +} + +static enum nw_state get_offset(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&m->c_sections[NW_CUSTOM_LO], cfg->user); + + if (n) + return n; + + m->next = skip; + return NW_AGAIN; +} + +static enum nw_state check(struct nw_mod *const m) +{ + if (!m->sections[NW_SECTION_CODE]) + { +#ifdef NW_LOG + nwp_log("code section must be defined first\n"); +#endif + return NW_FATAL; + } + + m->next = get_offset; + return NW_AGAIN; +} + +void nwp_section_lo(struct nw_mod *const m) +{ + m->next = check; +} diff --git a/src/routines/section/memory.c b/src/routines/section/memory.c new file mode 100644 index 0000000..9277230 --- /dev/null +++ b/src/routines/section/memory.c @@ -0,0 +1,86 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state get_maximum(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_mem *const mem = &m->sm.memory; + struct nw_mod_out_mem *const lin = &m->out.linear; + struct nw_sm_leb128 *const l = &mem->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &lin->max, cfg->user); + + if (n) + return n; + + m->next = nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_initial(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_mem *const mem = &m->sm.memory; + struct nw_mod_out_mem *const lin = &m->out.linear; + struct nw_sm_leb128 *const l = &mem->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &lin->initial, cfg->user); + + if (n) + return n; + + m->next = mem->flags ? get_maximum : nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_flags(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_mem *const mem = &m->sm.memory; + struct nw_sm_leb128 *const l = &mem->leb128; + const enum nw_state n = nwp_varuint1(cfg, l, &mem->flags, cfg->user); + + if (n) + return n; + + m->next = get_initial; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_mem *const mem = &m->sm.memory; + struct nw_sm_leb128 *const l = &mem->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &mem->count, cfg->user); + + if (n) + return n; + else if (mem->count > 1) + { +#ifdef NW_LOG + nwp_log("MVP WebAssembly supports no more than 1 memory\n"); +#endif + return NW_FATAL; + } + + m->next = mem->count ? get_flags : nwp_section_exit; + return NW_AGAIN; +} + +void nwp_section_memory(struct nw_mod *const m) +{ + const struct nw_sm_mem mem = {0}; + + m->next = get_count; + m->sm.memory = mem; +} diff --git a/src/routines/section/ops.c b/src/routines/section/ops.c new file mode 100644 index 0000000..a81c8e7 --- /dev/null +++ b/src/routines/section/ops.c @@ -0,0 +1,141 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/ops.h> +#include <nw/opcodes.h> + +static const struct nwp_check_op ops[] = +{ + { + OP_UNREACHABLE, + OP_NOP, + nwp_op_check_no_immediate + }, + + { + OP_BLOCK, + OP_IF, + nwp_op_check_block + }, + + { + OP_ELSE, + OP_ELSE, + nwp_op_check_no_immediate + }, + + { + OP_END, + OP_END, + nwp_op_check_end + }, + + { + OP_BR, + OP_BR_IF, + nwp_op_check_relative_depth + }, + + { + OP_BR_TABLE, + OP_BR_TABLE, + nwp_op_check_br_table + }, + + { + OP_RETURN, + OP_RETURN, + nwp_op_check_no_immediate + }, + + { + OP_CALL, + OP_CALL, + nwp_op_check_call + }, + + { + OP_CALL_INDIRECT, + OP_CALL_INDIRECT, + nwp_op_check_call_indirect + }, + + { + OP_DROP, + OP_SELECT, + nwp_op_check_no_immediate + }, + + { + OP_GET_LOCAL, + OP_TEE_LOCAL, + nwp_op_check_local_index, + }, + + { + OP_GET_GLOBAL, + OP_SET_GLOBAL, + nwp_op_check_global_index + }, + + { + OP_I32_LOAD, + OP_I64_STORE32, + nwp_op_check_memory_immediate + }, + + { + OP_CURRENT_MEMORY, + OP_GROW_MEMORY, + nwp_op_check_varuint1 + }, + + { + OP_I32_CONST, + OP_I32_CONST, + nwp_op_check_varint32 + }, + + { + OP_I64_CONST, + OP_I64_CONST, + nwp_op_check_varint64 + }, + + { + OP_F32_CONST, + OP_F32_CONST, + nwp_op_check_uint32 + }, + + { + OP_F64_CONST, + OP_F64_CONST, + nwp_op_check_uint64 + }, + + { + OP_I32_EQZ, + OP_F64_PROMOTE_F32, + nwp_op_check_no_immediate + }, + + { + OP_MISC, + OP_MISC, + nwp_op_check_misc + } +}; + +const struct nwp_check_ops nwp_check_ops = +{ + ops, + sizeof ops / sizeof *ops +}; diff --git a/src/routines/section/skip.c b/src/routines/section/skip.c new file mode 100644 index 0000000..72296ce --- /dev/null +++ b/src/routines/section/skip.c @@ -0,0 +1,45 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state skip(struct nw_mod *const m) +{ + const struct nw_mod_section *const s = &m->section; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const long offset = s->cur + s->len; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + nwp_section(m); + return NW_AGAIN; +} + +static enum nw_state tell(struct nw_mod *const m) +{ + struct nw_mod_section *const s = &m->section; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&s->cur, cfg->user); + + if (n) + return n; + + m->next = skip; + return NW_AGAIN; +} + +void nwp_section_skip(struct nw_mod *const m) +{ + m->next = tell; +} diff --git a/src/routines/section/start.c b/src/routines/section/start.c new file mode 100644 index 0000000..8ff22f3 --- /dev/null +++ b/src/routines/section/start.c @@ -0,0 +1,37 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state get_index(struct nw_mod *const m) +{ + nw_varuint32 index; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_start *const st = &m->sm.start; + struct nw_sm_leb128 *const l = &st->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &index, cfg->user); + + if (n) + return n; + + m->next = nwp_section_exit; + return NW_AGAIN; +} + +void nwp_section_start(struct nw_mod *const m) +{ + const struct nw_sm_start s = {0}; + + m->sm.start = s; + m->next = get_index; +} diff --git a/src/routines/section/table.c b/src/routines/section/table.c new file mode 100644 index 0000000..56f62ae --- /dev/null +++ b/src/routines/section/table.c @@ -0,0 +1,117 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state get_maximum(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_tb *const tb = &m->sm.table; + struct nw_mod_out_mem *const ot = &m->out.table; + struct nw_sm_leb128 *const l = &tb->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &ot->max, cfg->user); + + if (n) + return n; + + m->next = nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_initial(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_tb *const tb = &m->sm.table; + struct nw_mod_out_mem *const ot = &m->out.table; + struct nw_sm_leb128 *const l = &tb->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &ot->initial, cfg->user); + + if (n) + return n; + + m->next = tb->flags ? get_maximum : nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_flags(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_tb *const tb = &m->sm.table; + struct nw_sm_leb128 *const l = &tb->leb128; + const enum nw_state n = nwp_varuint1(cfg, l, &tb->flags, cfg->user); + + if (n) + return n; + + m->next = get_initial; + return NW_AGAIN; +} + +static enum nw_state get_element_type(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_tb *const tb = &m->sm.table; + struct nw_sm_leb128 *const l = &tb->leb128; + const enum nw_state n = nwp_varint7(cfg, l, &tb->elem_type, cfg->user); + + if (n) + return n; + /* TODO: replace with enum? */ + else if (tb->elem_type != 0x70) + { +#ifdef NW_LOG + nwp_log("unexpected elem_type %hhx\n", (char)tb->elem_type); +#endif + return NW_FATAL; + } + + m->next = get_flags; + return NW_AGAIN; +} + +static enum nw_state entry_loop(struct nw_mod *const m) +{ + const struct nw_sm_tb *const tb = &m->sm.table; + + m->next = tb->entry_i < tb->count ? get_element_type : nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_tb *const tb = &m->sm.table; + struct nw_sm_leb128 *const l = &tb->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &tb->count, cfg->user); + + if (n) + return n; + else if (tb->count > 1u) + { +#ifdef NW_LOG + nwp_log("got %lu tables, but only 1 supported by the MVP\n", + (unsigned long)tb->count); +#endif + return NW_FATAL; + } + + m->next = entry_loop; + return NW_AGAIN; +} + +void nwp_section_table(struct nw_mod *const m) +{ + const struct nw_sm_tb t = {0}; + + m->sm.table = t; + m->next = get_count; +} diff --git a/src/routines/section/to.c b/src/routines/section/to.c new file mode 100644 index 0000000..cf459cd --- /dev/null +++ b/src/routines/section/to.c @@ -0,0 +1,52 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state skip(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + const long offset = m->c_sections[NW_CUSTOM_TO] + + sizeof (struct nw_leuint32) * m->type_count; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + nwp_section(m); + return NW_AGAIN; +} + +static enum nw_state check(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + enum nw_state n; + + if (!m->sections[NW_SECTION_TYPE]) + { +#ifdef NW_LOG + nwp_log("type section must be defined first\n"); +#endif + return NW_FATAL; + } + else if ((n = cfg->tell(&m->c_sections[NW_CUSTOM_TO], cfg->user))) + return n; + + m->next = skip; + return NW_AGAIN; +} + +void nwp_section_to(struct nw_mod *const m) +{ + m->next = check; +} diff --git a/src/routines/section/type.c b/src/routines/section/type.c new file mode 100644 index 0000000..c8e5e9c --- /dev/null +++ b/src/routines/section/type.c @@ -0,0 +1,164 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <stddef.h> + +static enum nw_state entry_loop(struct nw_mod *); + +static enum nw_state get_return_type(struct nw_mod *const m) +{ + struct nw_sm_st *const st = &m->sm.type; + struct nw_sm_leb128 *const l = &st->leb128; + const struct nw_io_cfg *const cfg = &m->cfg.io; + nw_varint7 return_type; + const enum nw_state n = nwp_varint7(cfg, l, &return_type, cfg->user); + + if (n) + return n; + + m->next = entry_loop; + st->entry_i++; + return NW_AGAIN; +} + +static enum nw_state get_return_count(struct nw_mod *const m) +{ + struct nw_sm_st *const st = &m->sm.type; + struct nw_sm_leb128 *const l = &st->leb128; + const struct nw_io_cfg *const cfg = &m->cfg.io; + nw_varuint1 return_count; + const enum nw_state n = nwp_varuint1(cfg, l, &return_count, cfg->user); + + if (n) + return n; + else if (return_count) + m->next = get_return_type; + else + { + m->next = entry_loop; + st->entry_i++; + } + + return NW_AGAIN; +} + +static enum nw_state param_loop(struct nw_mod *const m) +{ + struct nw_sm_st *const st = &m->sm.type; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_leb128 *const l = &st->leb128; + nw_varint7 type; + enum nw_state n; + + if (st->p_i >= st->param_count) + { + m->next = get_return_count; + return NW_AGAIN; + } + else if ((n = nwp_varint7(cfg, l, &type, cfg->user))) + return n; + + st->p_i++; + return NW_AGAIN; +} + +static enum nw_state get_param_count(struct nw_mod *const m) +{ + struct nw_sm_st *const st = &m->sm.type; + struct nw_sm_leb128 *const l = &st->leb128; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &st->param_count, + cfg->user); + + if (n) + return n; + + st->p_i = 0; + m->next = param_loop; + return NW_AGAIN; +} + +static int check_form(const nw_varint7 form) +{ + static const nw_varint7 v[] = + { + /* TODO: move values to enums? */ + 0x7f, /* i32 */ + 0x7e, /* i64 */ + 0x7d, /* f32 */ + 0x7c, /* f64 */ + 0x70, /* anyfunc */ + 0x60, /* func */ + 0x40 /* empty block_type */ + }; + + size_t i; + + for (i = 0; i < sizeof v / sizeof *v; i++) + if (form == v[i]) + return 0; + + return -1; +} + +static enum nw_state get_form(struct nw_mod *const m) +{ + struct nw_sm_st *const st = &m->sm.type; + struct nw_sm_leb128 *const l = &st->leb128; + const struct nw_io_cfg *const cfg = &m->cfg.io; + nw_varint7 form; + const enum nw_state n = nwp_varint7(cfg, l, &form, cfg->user); + + if (n) + return n; + else if (check_form(form)) + { +#ifdef NW_LOG + nwp_log("invalid form %#x\n", (unsigned)form); +#endif + return NW_FATAL; + } + + m->next = get_param_count; + return NW_AGAIN; +} + +static enum nw_state entry_loop(struct nw_mod *const m) +{ + struct nw_sm_st *const st = &m->sm.type; + + m->next = st->entry_i < m->type_count ? get_form : nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_st *const st = &m->sm.type; + struct nw_sm_leb128 *const l = &st->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &m->type_count, cfg->user); + + if (n) + return n; + + m->next = entry_loop; + return NW_AGAIN; +} + +void nwp_section_type(struct nw_mod *const m) +{ + const struct nw_sm_st st = {0}; + + m->next = get_count; + m->sm.type = st; +} diff --git a/src/routines/set_local.c b/src/routines/set_local.c new file mode 100644 index 0000000..84c7c26 --- /dev/null +++ b/src/routines/set_local.c @@ -0,0 +1,162 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state set_local(struct nw_interp *const i) +{ + struct nw_i_sm_set_local *const sl = &i->sm.set_local; + const enum nw_state n = nwp_stack_write(i, &sl->io, sl->f.l.addr); + + if (n) + return n; + + i->next = sl->next; + return NW_AGAIN; +} + +static int init_io(struct nw_interp *const i, const enum nw_type t) +{ + struct nw_i_sm_set_local *const sl = &i->sm.set_local; + size_t sz; + + if (nwp_type_sz(t, &sz)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)t); +#endif + return -1; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &sl->out.value; + io.n = sz; + sl->io = io; + } + + return 0; +} + +static enum nw_state pop(struct nw_interp *const i, const enum nw_type t, + enum nw_state (*const next)(struct nw_interp *)) +{ + struct nw_i_sm_set_local *const sl = &i->sm.set_local; + const enum nw_state n = nwp_stack_pop(i, &sl->io); + + if (n) + return n; + else if (init_io(i, t)) + return NW_FATAL; + + sl->out.type = t; + i->next = next; + return NW_AGAIN; +} + +static enum nw_state pop_local(struct nw_interp *const i) +{ + const struct nw_local_meta *const m = &i->sm.set_local.f.l.meta; + + return pop(i, m->type, set_local); +} + +static enum nw_state prepare_local(struct nw_interp *const i) +{ + const struct nw_local_meta *const m = &i->sm.set_local.f.l.meta; + + if (init_io(i, m->type)) + return NW_FATAL; + + i->next = pop_local; + return NW_AGAIN; +} + +static enum nw_state set_param(struct nw_interp *const i) +{ + struct nw_i_sm_set_local *const sl = &i->sm.set_local; + const enum nw_state n = nwp_stack_write(i, &sl->io, sl->f.p.out.addr); + + if (n) + return n; + + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state pop_param(struct nw_interp *const i) +{ + const struct nw_find_param_out *const o = &i->sm.set_local.f.p.out; + + return pop(i, o->type, set_param); +} + +static enum nw_state prepare_param(struct nw_interp *const i) +{ + struct nw_i_sm_set_local *const sl = &i->sm.set_local; + const struct nw_find_param_out *const o = &sl->f.p.out; + + if (init_io(i, o->type)) + return NW_FATAL; + + i->next = pop_param; + return NW_AGAIN; +} + +static enum nw_state get_index(struct nw_interp *const i) +{ + struct nw_i_sm_set_local *const sl = &i->sm.set_local; + struct nw_sm_leb128 *const l = &sl->leb128; + const struct nw_frame *const fr = &i->fr; + const struct nw_fn *const fn = &fr->fn; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &sl->index, cfg->user); + nw_varuint32 index; + + if (n) + return n; + else if (sl->index < fn->param_count) + nwp_find_param(i, &sl->f.p, sl->index, prepare_param, NULL); + else if ((index = sl->index - fn->param_count) >= fr->local_count) + { + static const char *const exc = "invalid local index"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu\n", exc, (unsigned long)sl->index); +#endif + return NW_FATAL; + } + else + nwp_find_local(i, &sl->f.l, index, prepare_local, NULL); + + return NW_AGAIN; +} + +void nwp_set_local(struct nw_interp *const i, + enum nw_state (*const next)(struct nw_interp *)) +{ + const struct nw_i_sm_set_local sl = {0}; + struct nw_i_sm_set_local *const p = &i->sm.set_local; + + *p = sl; + p->next = next; + i->next = get_index; +} diff --git a/src/routines/start_block.c b/src/routines/start_block.c new file mode 100644 index 0000000..83be44c --- /dev/null +++ b/src/routines/start_block.c @@ -0,0 +1,49 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/types.h> + +static enum nw_state get_block_type(struct nw_interp *const i) +{ + nw_varint7 type; + struct nw_i_sm_sb *const sb = &i->sm.start_block; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varint7(cfg, &sb->leb128, &type, cfg->user); + enum nw_type block_type; + + if (n) + return n; + else if (type != 0x40 && nwp_get_type(type, &block_type)) + { + static const char *const exc = "invalid block_type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#hhx\n", exc, (unsigned char)type); +#endif + return NW_FATAL; + } + + i->fr.block_i++; + nwp_interp_resume(i); + return NW_AGAIN; +} + +void nwp_start_block(struct nw_interp *const i) +{ + const struct nw_i_sm_sb sb = {0}; + + i->sm.start_block = sb; + i->next = get_block_type; +} diff --git a/src/routines/unary.c b/src/routines/unary.c new file mode 100644 index 0000000..d65f8aa --- /dev/null +++ b/src/routines/unary.c @@ -0,0 +1,86 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state push(struct nw_interp *const i) +{ + struct nw_i_sm_unary *const u = &i->sm.unary; + const enum nw_state n = nwp_stack_push(i, &u->io); + + if (n) + return n; + + i->push_type = u->type; + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state pop(struct nw_interp *const i) +{ + struct nw_i_sm_unary *const u = &i->sm.unary; + const enum nw_state n = nwp_stack_pop(i, &u->io); + size_t sz; + + if (n) + return n; + else if (u->op(&u->in, &u->out)) + { + static const char *const exc = "unary operation failed"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + return NW_FATAL; + } + else if (nwp_type_sz(u->type, &sz)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %d\n", exc, u->type); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &u->out; + io.n = sz; + u->io = io; + } + + i->next = push; + return NW_AGAIN; +} + +void nwp_unary(struct nw_interp *const i, const enum nw_type t, + int (*const op)(const union nw_value *, union nw_value *)) +{ + const struct nw_i_sm_unary u = {0}; + struct nw_i_sm_unary *const pu = &i->sm.unary; + size_t sz; + + *pu = u; + pu->type = t; + pu->op = op; + nwp_type_sz(t, &sz); + pu->io.buf = &pu->in; + pu->io.n = sz; + i->next = pop; +} diff --git a/src/routines/unwind.c b/src/routines/unwind.c new file mode 100644 index 0000000..e4200fd --- /dev/null +++ b/src/routines/unwind.c @@ -0,0 +1,391 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state prepare_pop(struct nw_interp *); + +static enum nw_state seek_pc(struct nw_interp *const i) +{ + const struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->seek(u->fr.pc, cfg->user); + struct nw_return r; + + if (n) + return n; + +#ifdef NW_LOG + nwp_log("returning to function %lu\n", u->fr.fn.index); +#endif + + r = i->fr.fn.ret; + i->fr = u->fr; + i->fr.prev_ret = r; + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state push_return_value(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const enum nw_state n = nwp_stack_push(i, &u->io); + + if (n) + return n; + else if (u->fr.child) + i->next = seek_pc; + else + return NW_OK; + + return NW_AGAIN; +} + +static int prepare_retval(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_fn *const fn = &i->fr.fn; + const struct nw_return *const r = &fn->ret; + const enum nw_type t = r->type; + size_t sz; + + if (nwp_type_sz(t, &sz)) + { + static const char *const exc = "invalid return type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)t); +#endif + return -1; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &u->retval; + io.n = sz; + u->io = io; + i->push_type = t; + i->next = push_return_value; + } + + return 0; +} + +static enum nw_state pop_params(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_fn *const fn = &i->fr.fn; + const struct nw_return *const r = &fn->ret; + const enum nw_state n = nwp_stack_pop(i, &u->io); + + if (n) + return n; + else if (r->count) + { + if (prepare_retval(i)) + return NW_FATAL; + } + else if (u->fr.child) + i->next = seek_pc; + else + return NW_OK; + + return NW_AGAIN; +} + +static enum nw_state get_param_type(struct nw_interp *const i) +{ + nw_varint7 type; + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varint7(cfg, &u->leb128, &type, cfg->user); + enum nw_type vtype; + size_t sz; + + if (n) + return n; + else if (nwp_get_type(type, &vtype) || nwp_type_sz(vtype, &sz)) + { + static const char *const exc = "invalid param type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)type); +#endif + return NW_FATAL; + } + + u->sz += sz; + + if (++u->entry_i >= i->fr.fn.param_count) + { + struct nw_sm_io io = {0}; + + io.n = u->sz; + u->io = io; + i->next = pop_params; + } + + return NW_AGAIN; +} + +static enum nw_state seek_param_types(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->seek(i->fr.fn.param_types, cfg->user); + + if (n) + return n; + + u->entry_i = 0; + i->next = get_param_type; + return NW_AGAIN; +} + +static enum nw_state check_retval(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_fn *const fn = &i->fr.fn; + const size_t addr = nwp_stack_ptr(i), start = i->fr.local_start; + const struct nw_return *const r = &fn->ret; + + if (addr != start) + { + static const char *const exc = "mismatched stack address"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: expected: %lu, got: %lu\n", exc, + (unsigned long)start, (unsigned long)addr); +#endif + return NW_FATAL; + } + else if (fn->param_count) + i->next = seek_param_types; + else if (r->count) + { + if (prepare_retval(i)) + return NW_FATAL; + } + else if (u->fr.child) + i->next = seek_pc; + else + return NW_OK; + + return NW_AGAIN; +} + +static enum nw_state pop_meta(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const enum nw_state n = nwp_stack_pop(i, &u->io); + + if (n) + return n; + else if (u->pending) + nwp_find_local(i, &u->fl, u->pending - 1, prepare_pop, NULL); + else + return check_retval(i); + + return NW_AGAIN; +} + +static enum nw_state pop_local(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + struct nw_local_meta *const m = &u->fl.meta; + const enum nw_state n = nwp_stack_pop(i, &u->io); + + if (n) + return n; + + u->pending--; + + if (++u->entry_i >= m->entry_count) + { + struct nw_sm_io io = {0}; + + io.buf = m; + io.n = sizeof *m; + u->io = io; + u->entry_i = 0; + i->next = pop_meta; + } + else if (u->pending) + return prepare_pop(i); + else + { + static const char *const exc = "entry and local count mismatch"; +#ifdef NW_LOG + const unsigned long pending = m->entry_count - u->entry_i; +#endif + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu locals pending\n", exc, pending); +#endif + return NW_FATAL; + } + + return NW_AGAIN; +} + +static enum nw_state prepare_pop(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_local_meta *const m = &u->fl.meta; + size_t sz; + + if (nwp_type_sz(m->type, &sz)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)m->type); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &u->value; + io.n = sz; + u->io = io; + i->next = pop_local; + } + + return NW_AGAIN; +} + +static enum nw_state pop_frame(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_frame *const fr = &i->fr; + const enum nw_state n = nwp_stack_pop(i, &u->io); + + if (n) + return n; + else if (fr->local_count) + { + u->pending = fr->local_count; + nwp_find_local(i, &u->fl, u->pending - 1, prepare_pop, NULL); + } + else + return check_retval(i); + + return NW_AGAIN; +} + +static enum nw_state pop_return_value(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const enum nw_state n = nwp_stack_pop(i, &u->io); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &u->fr; + io.n = sizeof u->fr; + u->io = io; + i->next = pop_frame; + } + + return NW_AGAIN; +} + +static enum nw_state run(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_frame *const fr = &i->fr; + const struct nw_fn *const fn = &fr->fn; + const struct nw_return *const r = &fn->ret; + + if (r->count) + { + size_t sz, end, addr; + + if (nwp_type_sz(r->type, &sz)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)r->type); +#endif + return NW_FATAL; + } + + end = fr->local_end; + addr = nwp_stack_ptr(i); + + if (addr < sz || addr < sizeof *fr) + { + static const char *const exc = "stack underflow"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + return NW_FATAL; + } + else if ((addr -= sizeof *fr + sz) != end) + { + static const char *const exc = "stack memory leak"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: expected: %lu, got: %lu\n", exc, (unsigned long)end, + (unsigned long)addr); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &u->retval; + io.n = sz; + u->io = io; + i->next = pop_return_value; + } + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &u->fr; + io.n = sizeof u->fr; + u->io = io; + i->next = pop_frame; + } + + return NW_AGAIN; +} + +void nwp_unwind(struct nw_interp *const i) +{ + const struct nw_i_sm_unwind u = {0}; + + i->sm.unwind = u; + i->next = run; +} diff --git a/src/run.c b/src/run.c new file mode 100644 index 0000000..2995ece --- /dev/null +++ b/src/run.c @@ -0,0 +1,16 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> + +enum nw_state nw_run(struct nw_inst *const i) +{ + return i->next(i); +} diff --git a/src/start.c b/src/start.c new file mode 100644 index 0000000..788a5ad --- /dev/null +++ b/src/start.c @@ -0,0 +1,81 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/inst.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <stddef.h> + +static enum nw_state push(struct nw_interp *const i) +{ + const struct nw_i_sm_exp *const e = &i->sm.export; + + if (e->kind != NW_KIND_FUNCTION) + { + static const char *const exc = "unexpected import kind"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %d\n", exc, e->kind); +#endif + return NW_FATAL; + } + + nwp_call(i, e->index); + return NW_AGAIN; +} + +static enum nw_state find_start(struct nw_inst *const inst) +{ + struct nw_interp *const i = &inst->interp; + struct nw_interp_cfg cfg; + + cfg = i->cfg; + + if (nwp_interp_start(i, &cfg, NULL)) + { +#ifdef NW_LOG + nwp_log("nwp_interp_start failed\n"); +#endif + return NW_FATAL; + } + + nwp_find_export(i, inst->entry, push); + inst->next = nwp_inst_run; + return NW_AGAIN; +} + +static enum nw_state init_data(struct nw_inst *const i) +{ + nwp_init_data(i, find_start); + return NW_AGAIN; +} + +int nw_start(struct nw_inst *const i, const struct nw_inst_cfg *const icfg) +{ + const struct nw_inst inst = {0}; + + *i = inst; + i->entry = icfg->entry; + + if (nwp_interp_start(&i->interp, &icfg->interp_cfg, + &nwp_interp_initexpr_set)) + { +#ifdef NW_LOG + nwp_log("nwp_interp_start failed\n"); +#endif + return -1; + } + + nwp_init_globals(i, init_data); + return 0; +} diff --git a/src/types/CMakeLists.txt b/src/types/CMakeLists.txt new file mode 100644 index 0000000..f3f0cb3 --- /dev/null +++ b/src/types/CMakeLists.txt @@ -0,0 +1,11 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + get.c + sz.c +) diff --git a/src/types/get.c b/src/types/get.c new file mode 100644 index 0000000..40ce472 --- /dev/null +++ b/src/types/get.c @@ -0,0 +1,42 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <nw/log.h> + +int nwp_get_type(const nw_varint7 type, enum nw_type *const out) +{ + static const struct type + { + nw_varint7 key; + enum nw_type value; + } list[] = + { + {0x7f, NW_TYPE_I32}, + {0x7e, NW_TYPE_I64}, + {0x7d, NW_TYPE_F32}, + {0x7c, NW_TYPE_F64} + }; + + size_t i; + + for (i = 0; i < sizeof list / sizeof *list; i++) + { + const struct type *const t = &list[i]; + + if (type == t->key) + { + *out = t->value; + return 0; + } + } + + return -1; +} diff --git a/src/types/sz.c b/src/types/sz.c new file mode 100644 index 0000000..e6660a9 --- /dev/null +++ b/src/types/sz.c @@ -0,0 +1,29 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <stddef.h> + +int nwp_type_sz(const enum nw_type t, size_t *const out) +{ + static const size_t sz[] = + { + sizeof (long), + sizeof (struct nw_ll), + sizeof (float), + sizeof (double) + }; + + if (t < 0 || t >= sizeof sz / sizeof *sz) + return -1; + + *out = sz[t]; + return 0; +} |
