aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-11-26 22:43:30 +0100
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2024-04-21 01:51:24 +0200
commitf25b015e5b668028c34974bbb22faa4105c26690 (patch)
tree28f2b08c17b3585d06694ad74004d0617eadb785
downloadnanowasm-sync-f25b015e5b668028c34974bbb22faa4105c26690.tar.gz
First commit
-rw-r--r--CMakeLists.txt46
-rw-r--r--LICENSE373
-rw-r--r--README.md29
-rw-r--r--include/nanowasm/nw.h29
-rw-r--r--include/nanowasm/types.h144
-rw-r--r--private_include/nw/fstring.h18
-rw-r--r--private_include/nw/interp.h39
-rw-r--r--private_include/nw/leb128.h18
-rw-r--r--private_include/nw/log.h21
-rw-r--r--private_include/nw/opcodes.h67
-rw-r--r--private_include/nw/ops.h69
-rw-r--r--private_include/nw/search.h29
-rw-r--r--private_include/nw/sections.h65
-rw-r--r--private_include/nw/types.h81
-rw-r--r--src/CMakeLists.txt20
-rw-r--r--src/fstrcmp.c41
-rw-r--r--src/interp.c516
-rw-r--r--src/leb128.c71
-rw-r--r--src/load.c178
-rw-r--r--src/op/CMakeLists.txt13
-rw-r--r--src/op/call/CMakeLists.txt11
-rw-r--r--src/op/call/call.c79
-rw-r--r--src/op/call/call_indirect.c49
-rw-r--r--src/op/constants/CMakeLists.txt13
-rw-r--r--src/op/constants/f32_const.c38
-rw-r--r--src/op/constants/f64_const.c38
-rw-r--r--src/op/constants/i32_const.c51
-rw-r--r--src/op/constants/i64_const.c38
-rw-r--r--src/op/control_flow/CMakeLists.txt20
-rw-r--r--src/op/control_flow/block.c75
-rw-r--r--src/op/control_flow/br.c38
-rw-r--r--src/op/control_flow/br_if.c95
-rw-r--r--src/op/control_flow/br_table.c56
-rw-r--r--src/op/control_flow/else.c21
-rw-r--r--src/op/control_flow/end.c24
-rw-r--r--src/op/control_flow/if.c36
-rw-r--r--src/op/control_flow/loop.c36
-rw-r--r--src/op/control_flow/nop.c22
-rw-r--r--src/op/control_flow/return.c30
-rw-r--r--src/op/control_flow/unreachable.c25
-rw-r--r--src/op/memory/CMakeLists.txt12
-rw-r--r--src/op/memory/current_memory.c44
-rw-r--r--src/op/memory/i32_load.c76
-rw-r--r--src/op/memory/i32_store.c125
-rw-r--r--src/op/numeric/CMakeLists.txt10
-rw-r--r--src/op/numeric/i32_sub.c66
-rw-r--r--src/op/variable_access/CMakeLists.txt14
-rw-r--r--src/op/variable_access/get_global.c100
-rw-r--r--src/op/variable_access/get_local.c112
-rw-r--r--src/op/variable_access/set_global.c38
-rw-r--r--src/op/variable_access/set_local.c124
-rw-r--r--src/op/variable_access/tee_local.c38
-rw-r--r--src/run.c42
-rw-r--r--src/search.c181
-rw-r--r--src/section/CMakeLists.txt22
-rw-r--r--src/section/code.c429
-rw-r--r--src/section/common.c150
-rw-r--r--src/section/custom.c30
-rw-r--r--src/section/data.c18
-rw-r--r--src/section/element.c17
-rw-r--r--src/section/export.c161
-rw-r--r--src/section/function.c127
-rw-r--r--src/section/global.c346
-rw-r--r--src/section/import.c274
-rw-r--r--src/section/memory.c102
-rw-r--r--src/section/start.c18
-rw-r--r--src/section/table.c112
-rw-r--r--src/section/type.c180
-rw-r--r--src/start.c81
-rw-r--r--src/stop.c27
-rw-r--r--src/types.c89
-rw-r--r--src/unload.c19
-rw-r--r--test/CMakeLists.txt24
-rwxr-xr-xtest/example.wasmbin0 -> 829 bytes
-rw-r--r--test/main.c105
75 files changed, 5975 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..b4713dc
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,46 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2024 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 C)
+option(NW_LOG "Enables logging to stderr" ON)
+add_library(${PROJECT_NAME})
+target_include_directories(${PROJECT_NAME} PUBLIC include
+ PRIVATE private_include)
+
+set(cflags_np
+ -ffunction-sections
+ -fdata-sections
+ -pedantic
+ -Wall
+ -std=c99
+)
+
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ set(cflags_np ${cflags_np} -Og)
+elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
+ set(cflags_np ${cflags_np} -Os)
+endif()
+
+include(CheckCompilerFlag)
+
+foreach(f ${cflags_np})
+ string(REPLACE "-" "_" var supported_${f})
+ check_compiler_flag(C ${f} ${var})
+
+ if(${var})
+ target_compile_options(${PROJECT_NAME} PRIVATE ${f})
+ endif()
+endforeach()
+
+if(NW_LOG)
+ target_compile_definitions(${PROJECT_NAME} PRIVATE ENABLE_LOG)
+endif()
+
+set_target_properties(${PROJECT_NAME} PROPERTIES C_STANDARD 99 C_EXTENSIONS OFF)
+add_subdirectory(src)
+add_subdirectory(test)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d0a1fa1
--- /dev/null
+++ b/LICENSE
@@ -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..a18be97
--- /dev/null
+++ b/README.md
@@ -0,0 +1,29 @@
+# `nanowasm`, a tiny WebAssembly/Wasm interpreter
+
+**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 ISO C99.
+
+## Features
+
+- Portable, ISO C99 implementation
+- Suitable for resource-constrained devices.
+- Does not require dynamic memory allocation.
+
+## 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.
+
+## License
+
+```
+nanowasm, a tiny WebAssembly/Wasm interpreter
+Copyright (C) 2023-2024 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/.
+```
diff --git a/include/nanowasm/nw.h b/include/nanowasm/nw.h
new file mode 100644
index 0000000..2cdb8a4
--- /dev/null
+++ b/include/nanowasm/nw.h
@@ -0,0 +1,29 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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_H
+#define NANOWASM_H
+
+#include <nanowasm/types.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+int nw_load(const struct nw_mod_cfg *cfg, struct nw_mod *m);
+int nw_start(const struct nw_inst_cfg *icfg, struct nw_inst *i);
+enum nw_state nw_run(struct nw_inst *i);
+int nw_stop(struct nw_inst *i);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/nanowasm/types.h b/include/nanowasm/types.h
new file mode 100644
index 0000000..637dd31
--- /dev/null
+++ b/include/nanowasm/types.h
@@ -0,0 +1,144 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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_TYPES_H
+#define NANOWASM_TYPES_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+union nw_value
+{
+ long i32;
+ long long i64;
+ float f32;
+ double f64;
+ /* TODO: define {de}serialization between Wasm and host. */
+ void *p;
+};
+
+enum nw_kind
+{
+ NW_KIND_FUNCTION,
+ NW_KIND_TABLE,
+ NW_KIND_MEMORY,
+ NW_KIND_GLOBAL
+};
+
+struct nw_args
+{
+ const union nw_value *args;
+ size_t n;
+};
+
+struct nw_buf
+{
+ void *buf;
+ size_t n;
+};
+
+enum nw_state
+{
+ NW_STATE_AGAIN,
+ NW_STATE_RETURNED,
+ NW_STATE_EXCEPTION,
+ NW_STATE_FATAL
+};
+
+struct nw_mod_cfg
+{
+ const struct nw_import
+ {
+ enum nw_kind kind;
+ const char *module, *field;
+
+ union
+ {
+ struct
+ {
+ /* This must follow the format below, whitespace ignored:
+ * [return type]([param type][, ...])
+ * For convenience, the interpreter can do automatic checks on
+ * pointer arguments and/or return values if using '*' as type.
+ * Examples:
+ * "()"
+ * "i32(f32)"
+ * "*(f64, i32, *, i64)"
+ * As per WebAssembly MVP spec, multiple return types are not
+ * supported yet. */
+ const char *signature;
+ int (*fn)(const struct nw_args *params,
+ union nw_value *ret, void *user);
+ } function;
+ } u;
+ } *imports;
+
+ size_t n_imports;
+ const char *path;
+ void *user;
+};
+
+struct nw_interp_cfg
+{
+ struct nw_buf stack, heap, global;
+ const struct nw_mod *m;
+};
+
+struct nw_inst_cfg
+{
+ struct nw_interp_cfg interp;
+};
+
+struct nw_mod
+{
+ struct nw_sections
+ {
+ long type, import, function, table, memory, global, export,
+ start, element, code, data;
+ } sections;
+
+ size_t data_len, heap_len;
+ struct nw_mod_cfg cfg;
+};
+
+struct nw_inst
+{
+ struct nw_interp
+ {
+ const struct nw_mod *m;
+ FILE *f;
+ const char *exception;
+ bool exit;
+ int retval;
+ struct nw_interp_cfg cfg;
+ size_t stack_i, global_i;
+ struct nw_frame *fp;
+ struct nw_gframe *gfp;
+ } interp;
+
+ union nw_inst_state
+ {
+ long retval;
+ const char *exception;
+ } state;
+};
+
+#define NW_BUF(sz) {.buf = (char [sz]){0}, .n = sz}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private_include/nw/fstring.h b/private_include/nw/fstring.h
new file mode 100644
index 0000000..a8feda4
--- /dev/null
+++ b/private_include/nw/fstring.h
@@ -0,0 +1,18 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 FSTRING_H
+#define FSTRING_H
+
+#include <stdbool.h>
+#include <stdio.h>
+
+int fstrcmp(const char *str, FILE *f, bool abort);
+
+#endif
diff --git a/private_include/nw/interp.h b/private_include/nw/interp.h
new file mode 100644
index 0000000..5fa37bf
--- /dev/null
+++ b/private_include/nw/interp.h
@@ -0,0 +1,39 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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>
+#include <stdint.h>
+#include <stdio.h>
+
+struct interp_set
+{
+ const enum opcode *opcodes;
+ size_t n;
+};
+
+extern const struct interp_set interp_initexpr_set;
+
+int interp_start(const struct nw_interp_cfg *cfg, FILE *f, struct nw_interp *i);
+int interp_run(struct nw_interp *i);
+int interp_run_limited(struct nw_interp *i, const struct interp_set *ops);
+int interp_check_opcode(uint8_t op, FILE *f);
+int interp_push(struct nw_interp *i, const struct nw_frame *f);
+int interp_pop(struct nw_interp *i);
+void *interp_stackptr(const struct nw_interp *i);
+int interp_stack_push(struct nw_interp *i, const void *src, size_t n);
+int interp_stack_pop(struct nw_interp *i, void *dst, size_t n);
+int interp_heap_store(struct nw_interp *i, size_t addr, const void *src, size_t n);
+int interp_heap_load(struct nw_interp *i, size_t addr, void *dst, size_t n);
+
+#endif
diff --git a/private_include/nw/leb128.h b/private_include/nw/leb128.h
new file mode 100644
index 0000000..4338255
--- /dev/null
+++ b/private_include/nw/leb128.h
@@ -0,0 +1,18 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 LEB128_H
+#define LEB128_H
+
+#include <stdio.h>
+
+int leb128_read_unsigned(FILE *f, unsigned maxbits, unsigned long long *out);
+int leb128_read_signed(FILE *f, unsigned maxbits, long long *out);
+
+#endif
diff --git a/private_include/nw/log.h b/private_include/nw/log.h
new file mode 100644
index 0000000..3eafc88
--- /dev/null
+++ b/private_include/nw/log.h
@@ -0,0 +1,21 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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
+
+#include <stdio.h>
+
+#ifdef ENABLE_LOG
+#define LOG(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define LOG(...)
+#endif
+
+#endif
diff --git a/private_include/nw/opcodes.h b/private_include/nw/opcodes.h
new file mode 100644
index 0000000..2822b0d
--- /dev/null
+++ b/private_include/nw/opcodes.h
@@ -0,0 +1,67 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 = 0xf,
+ 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_SUB = 0x6b
+};
+
+#endif
diff --git a/private_include/nw/ops.h b/private_include/nw/ops.h
new file mode 100644
index 0000000..02f191e
--- /dev/null
+++ b/private_include/nw/ops.h
@@ -0,0 +1,69 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <stdio.h>
+
+int op_unreachable(struct nw_interp *i);
+int op_nop(struct nw_interp *i);
+int op_block(struct nw_interp *i);
+int op_loop(struct nw_interp *i);
+int op_if(struct nw_interp *i);
+int op_else(struct nw_interp *i);
+int op_end(struct nw_interp *i);
+int op_br(struct nw_interp *i);
+int op_br_if(struct nw_interp *i);
+int op_br_table(struct nw_interp *i);
+int op_return(struct nw_interp *i);
+int op_call(struct nw_interp *i);
+int op_call_indirect(struct nw_interp *i);
+int op_get_local(struct nw_interp *i);
+int op_set_local(struct nw_interp *i);
+int op_tee_local(struct nw_interp *i);
+int op_get_global(struct nw_interp *i);
+int op_set_global(struct nw_interp *i);
+int op_i32_load(struct nw_interp *i);
+int op_i32_store(struct nw_interp *i);
+int op_current_memory(struct nw_interp *i);
+int op_i32_const(struct nw_interp *i);
+int op_i64_const(struct nw_interp *i);
+int op_f32_const(struct nw_interp *i);
+int op_f64_const(struct nw_interp *i);
+int op_i32_sub(struct nw_interp *i);
+
+int check_unreachable(FILE *f);
+int check_nop(FILE *f);
+int check_block(FILE *f);
+int check_loop(FILE *f);
+int check_if(FILE *f);
+int check_else(FILE *f);
+int check_end(FILE *f);
+int check_br(FILE *f);
+int check_br_if(FILE *f);
+int check_br_table(FILE *f);
+int check_return(FILE *f);
+int check_call(FILE *f);
+int check_call_indirect(FILE *f);
+int check_get_local(FILE *f);
+int check_set_local(FILE *f);
+int check_tee_local(FILE *f);
+int check_get_global(FILE *f);
+int check_set_global(FILE *f);
+int check_i32_load(FILE *f);
+int check_i32_store(FILE *f);
+int check_i32_const(FILE *f);
+int check_i64_const(FILE *f);
+int check_f32_const(FILE *f);
+int check_f64_const(FILE *f);
+int check_i32_sub(FILE *f);
+
+#endif
diff --git a/private_include/nw/search.h b/private_include/nw/search.h
new file mode 100644
index 0000000..937e6fd
--- /dev/null
+++ b/private_include/nw/search.h
@@ -0,0 +1,29 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 SEARCH_H
+#define SEARCH_H
+
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stdio.h>
+
+struct search_fn
+{
+ long start;
+ bool returns;
+ varuint32 index;
+};
+
+int search_exported_fn(const char *fn, const struct nw_mod *m, FILE *f,
+ struct search_fn *out);
+int search_fn(varuint32 index, const struct nw_mod *m, FILE *f,
+ struct search_fn *out);
+
+#endif
diff --git a/private_include/nw/sections.h b/private_include/nw/sections.h
new file mode 100644
index 0000000..bef9cd8
--- /dev/null
+++ b/private_include/nw/sections.h
@@ -0,0 +1,65 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 SECTIONS_H
+#define SECTIONS_H
+
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+struct section
+{
+ const struct nw_mod_cfg *cfg;
+ FILE *f;
+};
+
+int section_custom_check(const struct section *s, struct nw_mod *m, unsigned long len);
+int section_type_check(const struct section *s, struct nw_mod *m, unsigned long len);
+int section_import_check(const struct section *s, struct nw_mod *m, unsigned long len);
+int section_function_check(const struct section *s, struct nw_mod *m, unsigned long len);
+int section_table_check(const struct section *s, struct nw_mod *m, unsigned long len);
+int section_memory_check(const struct section *s, struct nw_mod *m, unsigned long len);
+int section_global_check(const struct section *s, struct nw_mod *m, unsigned long len);
+int section_export_check(const struct section *s, struct nw_mod *m, unsigned long len);
+int section_start_check(const struct section *s, struct nw_mod *m, unsigned long len);
+int section_element_check(const struct section *s, struct nw_mod *m, unsigned long len);
+int section_code_check(const struct section *s, struct nw_mod *m, unsigned long len);
+int section_data_check(const struct section *s, struct nw_mod *m, unsigned long len);
+
+struct section_function
+{
+ varuint32 type, index, nreturn;
+};
+
+struct section_code
+{
+ long start;
+};
+
+int section_function(const struct section *s, const struct nw_mod *m,
+ varuint32 idx, struct section_function *f);
+int section_code(const struct section *s, const struct nw_mod *m,
+ varuint32 idx, struct section_code *c);
+
+int section_code_push(FILE *f, long pc, struct nw_frame *fr);
+int section_type_push(FILE *f, const struct nw_mod *m, varuint32 idx,
+ struct nw_frame *fr);
+int section_global_push(FILE *f, const struct nw_mod *m, struct nw_interp *i);
+
+struct resizable_limits
+{
+ bool max_available;
+ size_t sz, max;
+};
+
+int check_resizable_limits(FILE *f, struct resizable_limits *r);
+
+#endif
diff --git a/private_include/nw/types.h b/private_include/nw/types.h
new file mode 100644
index 0000000..23a2306
--- /dev/null
+++ b/private_include/nw/types.h
@@ -0,0 +1,81 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 WASM_TYPES_H
+#define WASM_TYPES_H
+
+#include <nanowasm/nw.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+typedef bool varuint1;
+typedef signed char varint7;
+typedef unsigned char varuint7;
+typedef unsigned long varuint32;
+typedef long varint32;
+typedef unsigned long long varuint64;
+typedef long long varint64;
+
+#define VALUE_TYPES \
+ X(VALUE_TYPE_I32) \
+ X(VALUE_TYPE_I64) \
+ X(VALUE_TYPE_F32) \
+ X(VALUE_TYPE_F64)
+
+enum value_type
+{
+#define X(x) x,
+ VALUE_TYPES
+#undef X
+};
+
+struct retval
+{
+ bool returns;
+ enum value_type type;
+};
+
+struct nw_block
+{
+ long pc;
+ struct nw_block *prev;
+};
+
+struct nw_frame
+{
+ enum value_type local_type;
+ unsigned long n_locals;
+ struct retval retval;
+ struct nw_block *last_block;
+ struct nw_frame *prev, *next;
+};
+
+struct nw_gframe
+{
+ enum value_type type;
+ bool mutable;
+ struct nw_gframe *next;
+};
+
+int varuint1_read(FILE *f, varuint1 *out);
+int varint7_read(FILE *f, varint7 *out);
+int varuint7_read(FILE *f, varuint7 *out);
+int varuint32_read(FILE *f, varuint32 *out);
+int varint32_read(FILE *f, varint32 *out);
+int varuint64_read(FILE *f, varuint64 *out);
+int varint64_read(FILE *f, varint64 *out);
+
+int get_value_type(varint7 type, enum value_type *vtype);
+size_t get_type_size(enum value_type type);
+
+int32_t htoni32(int32_t in);
+const char *value_type_tostr(enum value_type v);
+
+#endif
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..77be1e5
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,20 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2024 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
+ fstrcmp.c
+ interp.c
+ leb128.c
+ load.c
+ run.c
+ search.c
+ start.c
+ stop.c
+ types.c
+)
+add_subdirectory(op)
+add_subdirectory(section)
diff --git a/src/fstrcmp.c b/src/fstrcmp.c
new file mode 100644
index 0000000..b26baa8
--- /dev/null
+++ b/src/fstrcmp.c
@@ -0,0 +1,41 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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/fstring.h>
+#include <nw/log.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+int fstrcmp(const char *str, FILE *const f, const bool abort)
+{
+ int ret = 0;
+
+ for (size_t i = 0; i < strlen(str); i++)
+ {
+ uint8_t byte;
+
+ if (!fread(&byte, sizeof byte, 1, f))
+ {
+ LOG("%s: fread(3) failed, feof=%d, ferror=%d\n",
+ __func__, feof(f), ferror(f));
+ return -1;
+ }
+ else if (byte != str[i])
+ {
+ if (abort)
+ return -1;
+ else
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
diff --git a/src/interp.c b/src/interp.c
new file mode 100644
index 0000000..3a32df0
--- /dev/null
+++ b/src/interp.c
@@ -0,0 +1,516 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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/interp.h>
+#include <nw/opcodes.h>
+#include <nw/ops.h>
+#include <nw/sections.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <limits.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const enum opcode initexpr_opcodes[] =
+{
+ OP_NOP,
+ OP_END,
+ OP_I32_CONST,
+ OP_I64_CONST,
+ OP_F32_CONST,
+ OP_F64_CONST
+};
+
+const struct interp_set interp_initexpr_set =
+{
+ .opcodes = initexpr_opcodes,
+ .n = sizeof initexpr_opcodes / sizeof *initexpr_opcodes
+};
+
+static int (*const ops[])(struct nw_interp *) =
+{
+ [OP_UNREACHABLE] = op_unreachable,
+ [OP_NOP] = op_nop,
+ [OP_BLOCK] = op_block,
+ [OP_LOOP] = op_loop,
+ [OP_IF] = op_if,
+ [OP_ELSE] = op_else,
+ [OP_END] = op_end,
+ [OP_BR] = op_br,
+ [OP_BR_IF] = op_br_if,
+ [OP_BR_TABLE] = op_br_table,
+ [OP_RETURN] = op_return,
+ [OP_CALL] = op_call,
+ [OP_CALL_INDIRECT] = op_call_indirect,
+ [OP_GET_LOCAL] = op_get_local,
+ [OP_SET_LOCAL] = op_set_local,
+ [OP_TEE_LOCAL] = op_tee_local,
+ [OP_GET_GLOBAL] = op_get_global,
+ [OP_SET_GLOBAL] = op_set_global,
+ [OP_I32_LOAD] = op_i32_load,
+ [OP_I32_STORE] = op_i32_store,
+ [OP_I32_CONST] = op_i32_const,
+ [OP_I64_CONST] = op_i64_const,
+ [OP_F32_CONST] = op_f32_const,
+ [OP_F64_CONST] = op_f64_const,
+ [OP_I32_SUB] = op_i32_sub
+};
+
+int interp_run(struct nw_interp *const i)
+{
+ uint8_t op;
+
+ if (!fread(&op, sizeof op, 1, i->f))
+ {
+ LOG("%s: fread(3) failed, feof=%d, ferror=%d\n", __func__, feof(i->f),
+ ferror(i->f));
+ i->exception = "I/O error";
+ return -1;
+ }
+ else if (op >= sizeof ops / sizeof *ops)
+ {
+ LOG("%s: invalid opcode %#" PRIx8 "\n", __func__, op);
+ i->exception = "invalid opcode";
+ return -1;
+ }
+ else if (!ops[op])
+ {
+ LOG("%s: unsupported opcode %#" PRIx8 "\n", __func__, op);
+ i->exception = "invalid opcode";
+ return -1;
+ }
+
+ return ops[op](i);
+}
+
+static int run_opcode_limited(struct nw_interp *const in,
+ const struct interp_set *const set)
+{
+ uint8_t op;
+
+ if (!fread(&op, sizeof op, 1, in->f))
+ {
+ LOG("%s: fread(3) failed, feof=%d, ferror=%d\n",
+ __func__, feof(in->f), ferror(in->f));
+ in->exception = "I/O error";
+ return -1;
+ }
+
+ for (size_t i = 0; i < set->n; i++)
+ if (op == set->opcodes[i])
+ return ops[op](in);
+
+ LOG("%s: unexpected opcode %#" PRIx8 "\n", __func__, op);
+ in->exception = "invalid opcode";
+ return -1;
+}
+
+int interp_run_limited(struct nw_interp *const i,
+ const struct interp_set *const set)
+{
+ while (!i->exit)
+ if (run_opcode_limited(i, set))
+ {
+ LOG("%s: run_opcode_limited failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int interp_check_opcode(const uint8_t op, FILE *const f)
+{
+ static int (*const checks[])(FILE *) =
+ {
+ [OP_UNREACHABLE] = check_unreachable,
+ [OP_NOP] = check_nop,
+ [OP_BLOCK] = check_block,
+ [OP_LOOP] = check_loop,
+ [OP_IF] = check_if,
+ [OP_ELSE] = check_else,
+ [OP_END] = check_end,
+ [OP_BR] = check_br,
+ [OP_BR_IF] = check_br_if,
+ [OP_BR_TABLE] = check_br_table,
+ [OP_RETURN] = check_return,
+ [OP_CALL] = check_call,
+ [OP_CALL_INDIRECT] = check_call_indirect,
+ [OP_GET_LOCAL] = check_get_local,
+ [OP_SET_LOCAL] = check_set_local,
+ [OP_TEE_LOCAL] = check_tee_local,
+ [OP_GET_GLOBAL] = check_get_global,
+ [OP_SET_GLOBAL] = check_set_global,
+ [OP_I32_LOAD] = check_i32_load,
+ [OP_I32_STORE] = check_i32_store,
+ [OP_I32_CONST] = check_i32_const,
+ [OP_I64_CONST] = check_i64_const,
+ [OP_F32_CONST] = check_f32_const,
+ [OP_F64_CONST] = check_f64_const,
+ [OP_I32_SUB] = check_i32_sub
+ };
+
+ if (op >= sizeof checks / sizeof *checks)
+ {
+ LOG("%s: invalid opcode %#" PRIx8 "\n", __func__, op);
+ return 1;
+ }
+ else if (!checks[op])
+ {
+ LOG("%s: unsupported opcode %#" PRIx8 "\n", __func__, op);
+ return 1;
+ }
+
+ return checks[op](f);
+}
+
+int interp_start(const struct nw_interp_cfg *const cfg, FILE *const f,
+ struct nw_interp *const i)
+{
+ *i = (const struct nw_interp)
+ {
+ .f = f,
+ .cfg = *cfg
+ };
+
+ return 0;
+}
+
+void *interp_stackptr(const struct nw_interp *const i)
+{
+ return (char *)i->cfg.stack.buf + i->stack_i;
+}
+
+static int push_frame(struct nw_interp *const i,
+ const struct nw_frame *const src)
+{
+ struct nw_frame *const dst = interp_stackptr(i);
+
+ if (interp_stack_push(i, src, sizeof (*src)))
+ {
+ LOG("%s: interp_stack_push failed\n", __func__);
+ return -1;
+ }
+ else if (i->fp)
+ i->fp->next = dst;
+
+ i->fp = dst;
+ return 0;
+}
+
+static bool mul_overflow(const size_t a, const size_t b)
+{
+ return a && (a * b) / a != b;
+}
+
+static void init_i32(const varuint32 count, void *const p)
+{
+ for (int32_t *i = p; i - (const int32_t *)p < count; i++)
+ *i = 0;
+}
+
+static void init_i64(const varuint32 count, void *const p)
+{
+ for (int64_t *i = p; i - (const int64_t *)p < count; i++)
+ *i = 0;
+}
+
+static void init_f32(const varuint32 count, void *const p)
+{
+ for (float *i = p; i - (const float *)p < count; i++)
+ *i = 0;
+}
+
+static void init_f64(const varuint32 count, void *const p)
+{
+ for (double *i = p; i - (const double *)p < count; i++)
+ *i = 0;
+}
+
+static void init_locals(const enum value_type type, const varuint32 count,
+ void *const p)
+{
+ static void (*const init[])(varuint32, void *) =
+ {
+ [VALUE_TYPE_I32] = init_i32,
+ [VALUE_TYPE_I64] = init_i64,
+ [VALUE_TYPE_F32] = init_f32,
+ [VALUE_TYPE_F64] = init_f64
+ };
+
+ init[type](count, p);
+}
+
+static int push_locals(struct nw_interp *const i,
+ const struct nw_frame *const f)
+{
+ const enum value_type t = f->local_type;
+ const size_t typesz = get_type_size(t);
+ const unsigned long n = f->n_locals;
+
+ if (mul_overflow(typesz, f->n_locals))
+ {
+ LOG("%s: local variables size mul overflow", __func__);
+ i->exception = "mul overfllow";
+ return -1;
+ }
+
+ const size_t totalsz = typesz * n, max = i->cfg.stack.n;
+
+ if (totalsz > max || i->stack_i > max - totalsz)
+ {
+ LOG("%s: cannot allocate locals\n", __func__);
+ i->exception = "stack overflow";
+ return 1;
+ }
+
+ init_locals(t, n, interp_stackptr(i));
+ i->stack_i += totalsz;
+ return 0;
+}
+
+static int do_push_labels(struct nw_interp *const i,
+ const struct nw_frame *const fr)
+{
+ size_t n = 0;
+
+ for (;;)
+ {
+
+ interp_check_opcode();
+ }
+}
+
+static int push_labels(struct nw_interp *const i,
+ const struct nw_frame *const fr)
+{
+ FILE *const f = i->f;
+ const long orig = ftell(f);
+
+ if (orig < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (do_push_labels(i, fr))
+ {
+ LOG("%s: do_push_labels failed\n", __func__);
+ return -1;
+ }
+ else if (fseek(f, orig, SEEK_SET))
+ {
+ LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ return -1;
+}
+
+int interp_push(struct nw_interp *const i, const struct nw_frame *const f)
+{
+ if (push_frame(i, f))
+ {
+ LOG("%s: push_frame failed\n", __func__);
+ return -1;
+ }
+ else if (push_locals(i, f))
+ {
+ LOG("%s: push_locals failed\n", __func__);
+ return -1;
+ }
+ else if (push_labels(i, f))
+ {
+ LOG("%s: push_labels failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+struct retval_priv
+{
+ enum value_type type;
+
+ union
+ {
+ int32_t i32;
+ int64_t i64;
+ float f32;
+ double f64;
+ } u;
+};
+
+static void get_i32(const void *const buf, struct retval_priv *const rp)
+{
+ rp->u.i32 = *(((const int32_t *)buf) - 1);
+}
+
+static void get_i64(const void *const buf, struct retval_priv *const rp)
+{
+ rp->u.i64 = *(((const int64_t *)buf) - 1);
+}
+
+static void get_f32(const void *const buf, struct retval_priv *const rp)
+{
+ rp->u.f32 = *(((const float *)buf) - 1);
+}
+
+static void get_f64(const void *const buf, struct retval_priv *const rp)
+{
+ rp->u.f64 = *(((const double *)buf) - 1);
+}
+
+static int get_retval(struct nw_interp *const i,
+ struct retval_priv *const rp)
+{
+ const struct retval *const r = &i->fp->retval;
+ const size_t sz = get_type_size(r->type), max = i->cfg.stack.n;
+
+ if (max < sz || i->stack_i >= max - sz)
+ {
+ static const char exc[] = "stack underflow";
+
+ LOG("%s: %s\n", __func__, exc);
+ i->exception = exc;
+ return -1;
+ }
+
+ const void *const buf = (const char *)interp_stackptr(i) - sz;
+ static void (*const get[])(const void *, struct retval_priv *) =
+ {
+ [VALUE_TYPE_I32] = get_i32,
+ [VALUE_TYPE_I64] = get_i64,
+ [VALUE_TYPE_F32] = get_f32,
+ [VALUE_TYPE_F64] = get_f64
+ };
+
+ get[r->type](buf, rp);
+ rp->type = r->type;
+ i->stack_i -= sz;
+ return 0;
+}
+
+int interp_pop(struct nw_interp *const i)
+{
+ if (!i->fp)
+ {
+ static const char exc[] = "no frame pointer";
+
+ LOG("%s: %s\n", __func__, exc);
+ i->exception = exc;
+ return -1;
+ }
+
+ const struct retval *const r = &i->fp->retval;
+ struct retval_priv rp;
+
+ if (r->returns && get_retval(i, &rp))
+ {
+ LOG("%s: get_retval failed\n", __func__);
+ return -1;
+ }
+
+ const char *const stackptr = interp_stackptr(i);
+ const ptrdiff_t diff = stackptr - (const char *)i->fp;
+
+ i->stack_i -= diff;
+
+ if (!(i->fp = i->fp->prev))
+ {
+ /* Entry point is always assumed to return int, which is
+ * defined as an i32 variable according to Wasm. */
+ if (rp.type != VALUE_TYPE_I32)
+ {
+ LOG("%s: expected i32 return type, got %s\n", __func__,
+ value_type_tostr(rp.type));
+ i->exception = "unexpected return type";
+ return -1;
+ }
+
+ i->retval = rp.u.i32;
+ i->exit = true;
+ }
+
+ return 0;
+}
+
+int interp_stack_push(struct nw_interp *const i, const void *const src,
+ const size_t n)
+{
+ void *const dst = interp_stackptr(i);
+
+ if (i->cfg.stack.n < n || i->stack_i > i->cfg.stack.n - n)
+ {
+ LOG("%s: stack overflow\n", __func__);
+ i->exception = "stack overflow";
+ return -1;
+ }
+
+ memcpy(dst, src, n);
+ i->stack_i += n;
+ return 0;
+}
+
+int interp_stack_pop(struct nw_interp *const i, void *const dst,
+ const size_t n)
+{
+ if (i->stack_i < n)
+ {
+ LOG("%s: stack underflow\n", __func__);
+ i->exception = "stack underflow";
+ return -1;
+ }
+
+ i->stack_i -= n;
+ memcpy(dst, interp_stackptr(i), n);
+ return 0;
+}
+
+int interp_heap_store(struct nw_interp *const i, const size_t addr,
+ const void *const src, const size_t n)
+{
+ if (i->cfg.heap.n < n || addr > i->cfg.heap.n - n)
+ {
+ static const char exc[] = "heap overflow";
+
+ LOG("%s: %s\n", __func__, exc);
+ i->exception = exc;
+ return -1;
+ }
+
+ void *const dst = (char *)i->cfg.heap.buf + addr;
+
+ memcpy(dst, src, n);
+ return 0;
+}
+
+int interp_heap_load(struct nw_interp *const i, const size_t addr,
+ void *const dst, const size_t n)
+{
+ const size_t max = i->cfg.heap.n;
+
+ if (max < n || addr > max - n)
+ {
+ static const char exc[] = "heap out-of-bounds access";
+
+ LOG("%s: %s (%lu, max %zu)\n", __func__, exc, (unsigned long)addr, max);
+ i->exception = exc;
+ return -1;
+ }
+
+ const void *const src = (const char *)i->cfg.heap.buf + addr;
+
+ memcpy(dst, src, n);
+ return 0;
+}
diff --git a/src/leb128.c b/src/leb128.c
new file mode 100644
index 0000000..3c3c8a5
--- /dev/null
+++ b/src/leb128.c
@@ -0,0 +1,71 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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/leb128.h>
+#include <nw/log.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+/* The functions below are (somewhat heavily) modified versions of those
+ * 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/
+ */
+
+static int read_LEB(FILE *const f, const unsigned maxbits,
+ const bool sign, unsigned long long *const out)
+{
+ unsigned long long result = 0;
+ unsigned shift = 0, bcnt = 0;
+ uint8_t byte;
+
+ for (;;)
+ {
+ if (!fread(&byte, sizeof byte, 1, f))
+ return -1;
+
+ result |= (unsigned long long)(byte & 0x7f) << shift;
+ shift += 7;
+
+ if (!(byte & 0x80))
+ break;
+ else if (++bcnt > (maxbits + 7 - 1) / 7)
+ {
+ LOG("%s: overflow\n", __func__);
+ return -1;
+ }
+ }
+
+ if (sign && (shift < maxbits) && (byte & 0x40))
+ result |= -1ll << shift;
+
+ *out = result;
+ return 0;
+}
+
+int leb128_read_unsigned(FILE *const f, const unsigned maxbits,
+ unsigned long long *const out)
+{
+ return read_LEB(f, maxbits, false, out);
+}
+
+int leb128_read_signed(FILE *const f, const unsigned maxbits,
+ long long *const out)
+{
+ unsigned long long value;
+ const int ret = read_LEB(f, maxbits, true, &value);
+
+ *out = value;
+ return ret;
+}
diff --git a/src/load.c b/src/load.c
new file mode 100644
index 0000000..2ba0fcc
--- /dev/null
+++ b/src/load.c
@@ -0,0 +1,178 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nanowasm/nw.h>
+#include <nw/sections.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int check_magic(FILE *const f)
+{
+ static const uint8_t m[] = {'\0', 'a', 's', 'm'};
+ uint8_t magic[sizeof m];
+
+ if (!fread(magic, sizeof magic, 1, f))
+ {
+ LOG("%s: fread(3) failed, feof=%d, ferror=%d\n",
+ __func__, feof(f), ferror(f));
+ return -1;
+ }
+ else if (memcmp(magic, m, sizeof magic))
+ {
+ LOG("%s: wrong magic bytes\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_version(FILE *const f)
+{
+ static const uint8_t v[] = {1, 0, 0, 0};
+ uint8_t version[sizeof v];
+
+ if (!fread(version, sizeof version, 1, f))
+ {
+ LOG("%s: fread(3) failed, feof=%d, ferror=%d\n", __func__, feof(f),
+ ferror(f));
+ return -1;
+ }
+ else if (memcmp(version, v, sizeof version))
+ {
+ LOG("%s: wrong version\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int load_section(const struct nw_mod_cfg *const cfg,
+ struct nw_mod *const m, FILE *const f)
+{
+ static int (*const fn[])(const struct section *, struct nw_mod *,
+ unsigned long) =
+ {
+ section_custom_check,
+ section_type_check,
+ section_import_check,
+ section_function_check,
+ section_table_check,
+ section_memory_check,
+ section_global_check,
+ section_export_check,
+ section_start_check,
+ section_element_check,
+ section_code_check,
+ section_data_check
+ };
+
+ const struct section s =
+ {
+ .cfg = cfg,
+ .f = f
+ };
+
+ varuint7 section;
+ varuint32 len;
+ int ret;
+
+ if (varuint7_read(f, &section))
+ {
+ if (feof(f))
+ return 0;
+
+ LOG("%s: varuint7_read failed\n", __func__);
+ return 1;
+ }
+ else if (section >= sizeof fn / sizeof *fn)
+ {
+ LOG("%s: invalid section %u\n", __func__, (unsigned)section);
+ return 1;
+ }
+ else if (varuint32_read(f, &len))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+ else if ((ret = fn[section](&s, m, len)))
+ LOG("%s: failed to load section %u\n", __func__, (unsigned)section);
+
+ return ret;
+}
+
+static int load_sections(const struct nw_mod_cfg *const cfg,
+ struct nw_mod *const m, FILE *const f)
+{
+ while (!feof(f))
+ if (load_section(cfg, m, f))
+ {
+ LOG("%s: load_section failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int load(const struct nw_mod_cfg *const cfg, struct nw_mod *const m,
+ FILE *const f)
+{
+ if (check_magic(f))
+ {
+ LOG("%s: load_magic failed\n", __func__);
+ return -1;
+ }
+ else if (check_version(f))
+ {
+ LOG("%s: check_version failed\n", __func__);
+ return -1;
+ }
+ else if (load_sections(cfg, m, f))
+ {
+ LOG("%s: load_sections failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int nw_load(const struct nw_mod_cfg *const cfg, struct nw_mod *const m)
+{
+ int ret = -1;
+ FILE *const f = fopen(cfg->path, "rb");
+
+ *m = (const struct nw_mod){.cfg = *cfg};
+
+ if (!f)
+ {
+ LOG("%s: fopen(3) %s: %s\n", __func__, cfg->path, strerror(errno));
+ goto end;
+ }
+ else if (load(cfg, m, f))
+ {
+ LOG("%s: load failed\n", __func__);
+ goto end;
+ }
+
+ ret = 0;
+
+end:
+ if (f && fclose(f))
+ {
+ LOG("%s: fclose(3) %s: %s\n", __func__, cfg->path, strerror(errno));
+ ret = -1;
+ }
+
+ return ret;
+}
diff --git a/src/op/CMakeLists.txt b/src/op/CMakeLists.txt
new file mode 100644
index 0000000..7bf27de
--- /dev/null
+++ b/src/op/CMakeLists.txt
@@ -0,0 +1,13 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2024 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/.
+
+add_subdirectory(call)
+add_subdirectory(constants)
+add_subdirectory(control_flow)
+add_subdirectory(memory)
+add_subdirectory(numeric)
+add_subdirectory(variable_access)
diff --git a/src/op/call/CMakeLists.txt b/src/op/call/CMakeLists.txt
new file mode 100644
index 0000000..527f35d
--- /dev/null
+++ b/src/op/call/CMakeLists.txt
@@ -0,0 +1,11 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2024 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
+ call.c
+ call_indirect.c
+)
diff --git a/src/op/call/call.c b/src/op/call/call.c
new file mode 100644
index 0000000..19355ca
--- /dev/null
+++ b/src/op/call/call.c
@@ -0,0 +1,79 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/search.h>
+#include <nw/sections.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+static int call(struct nw_interp *const i, const varuint32 function_index)
+{
+ FILE *const f = i->f;
+ const struct nw_mod *const m = i->cfg.m;
+ struct nw_frame fr = {0};
+ struct search_fn fn;
+
+ /* TODO: treat import functions. */
+ if (search_fn(function_index, m, f, &fn))
+ {
+ LOG("%s: search_fn failed\n", __func__);
+ return -1;
+ }
+ else if (section_type_push(f, m, fn.index, &fr))
+ {
+ LOG("%s: section_type_push failed\n", __func__);
+ return -1;
+ }
+ else if (section_code_push(f, fn.start, &fr))
+ {
+ LOG("%s: section_code_push failed\n", __func__);
+ return -1;
+ }
+ else if (interp_push(i, &fr))
+ {
+ LOG("%s: interp_push failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 function_index;
+
+ if (varuint32_read(f, &function_index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+ else if (i && call(i, function_index))
+ {
+ LOG("%s: call failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_call(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_call(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/call/call_indirect.c b/src/op/call/call_indirect.c
new file mode 100644
index 0000000..bae8a57
--- /dev/null
+++ b/src/op/call/call_indirect.c
@@ -0,0 +1,49 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 type_index;
+ varuint1 reserved;
+
+ if (varuint32_read(f, &type_index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+ else if (varuint1_read(f, &reserved))
+ {
+ LOG("%s: varuint1_read failed\n", __func__);
+ return 1;
+ }
+ else if (reserved)
+ {
+ LOG("%s: unexpected non-zero reserved value %u\n",
+ __func__, (unsigned)reserved);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_call_indirect(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_call_indirect(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/constants/CMakeLists.txt b/src/op/constants/CMakeLists.txt
new file mode 100644
index 0000000..e5cc8c5
--- /dev/null
+++ b/src/op/constants/CMakeLists.txt
@@ -0,0 +1,13 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2024 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
+ f32_const.c
+ f64_const.c
+ i32_const.c
+ i64_const.c
+)
diff --git a/src/op/constants/f32_const.c b/src/op/constants/f32_const.c
new file mode 100644
index 0000000..c0c51b9
--- /dev/null
+++ b/src/op/constants/f32_const.c
@@ -0,0 +1,38 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 value;
+
+ if (varuint32_read(f, &value))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_f32_const(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_f32_const(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/constants/f64_const.c b/src/op/constants/f64_const.c
new file mode 100644
index 0000000..d88398c
--- /dev/null
+++ b/src/op/constants/f64_const.c
@@ -0,0 +1,38 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint64 value;
+
+ if (varuint64_read(f, &value))
+ {
+ LOG("%s: varuint64_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_f64_const(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_f64_const(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/constants/i32_const.c b/src/op/constants/i32_const.c
new file mode 100644
index 0000000..19161d3
--- /dev/null
+++ b/src/op/constants/i32_const.c
@@ -0,0 +1,51 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <nw/interp.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+static int set_value(struct nw_interp *const i, const int32_t src)
+{
+ return interp_stack_push(i, &src, sizeof src);
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varint32 value;
+
+ if (varint32_read(f, &value))
+ {
+ LOG("%s: varint32_read failed\n", __func__);
+ return -1;
+ }
+ else if (i && set_value(i, value))
+ {
+ LOG("%s: set_value failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_i32_const(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_i32_const(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/constants/i64_const.c b/src/op/constants/i64_const.c
new file mode 100644
index 0000000..53f4237
--- /dev/null
+++ b/src/op/constants/i64_const.c
@@ -0,0 +1,38 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varint64 value;
+
+ if (varint64_read(f, &value))
+ {
+ LOG("%s: varint32_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_i64_const(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_i64_const(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/control_flow/CMakeLists.txt b/src/op/control_flow/CMakeLists.txt
new file mode 100644
index 0000000..832f09e
--- /dev/null
+++ b/src/op/control_flow/CMakeLists.txt
@@ -0,0 +1,20 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2024 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
+ br_table.c
+ else.c
+ end.c
+ if.c
+ loop.c
+ nop.c
+ return.c
+ unreachable.c
+)
diff --git a/src/op/control_flow/block.c b/src/op/control_flow/block.c
new file mode 100644
index 0000000..56f72f1
--- /dev/null
+++ b/src/op/control_flow/block.c
@@ -0,0 +1,75 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+static int push(struct nw_interp *const i, FILE *const f)
+{
+ const long pc = ftell(f);
+
+ if (pc < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ i->exception = "I/O error";
+ return -1;
+ }
+
+ struct nw_frame *const fp = i->fp;
+ struct nw_block *const p = interp_stackptr(i);
+ const struct nw_block b =
+ {
+ .pc = pc,
+ .prev = fp->last_block
+ };
+
+ if (interp_stack_push(i, &b, sizeof b))
+ {
+ LOG("%s: interp_stack_push failed\n", __func__);
+ return -1;
+ }
+
+ fp->last_block = p;
+ return 0;
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varint7 sig;
+
+ if (varint7_read(f, &sig))
+ {
+ LOG("%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+ else if (i && push(i, f))
+ {
+ LOG("%s: push failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_block(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_block(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/control_flow/br.c b/src/op/control_flow/br.c
new file mode 100644
index 0000000..df3aacb
--- /dev/null
+++ b/src/op/control_flow/br.c
@@ -0,0 +1,38 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 relative_depth;
+
+ if (varuint32_read(f, &relative_depth))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_br(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_br(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/control_flow/br_if.c b/src/op/control_flow/br_if.c
new file mode 100644
index 0000000..25bfdee
--- /dev/null
+++ b/src/op/control_flow/br_if.c
@@ -0,0 +1,95 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+static int branch(struct nw_interp *const i, const varuint32 relative_depth)
+{
+ const struct nw_block *b = i->fp->last_block;
+ static const char exc[] = "relative depth and block count mismatch";
+
+ for (varuint32 d = 0; b && d < relative_depth; d++)
+ b = b->prev;
+
+ if (!b)
+ {
+ LOG("%s: %s\n", __func__, exc);
+ i->exception = exc;
+ return -1;
+ }
+
+ if (fseek(i->f, b->pc, SEEK_SET))
+ {
+ LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
+ i->exception = "I/O error";
+ return -1;
+ }
+
+ return 0;
+}
+
+static bool cond(struct nw_interp *const i)
+{
+ int32_t value;
+
+ if (interp_stack_pop(i, &value, sizeof value))
+ {
+ LOG("%s: interp_stack_pop failed\n", __func__);
+ return -1;
+ }
+
+ return value;
+}
+
+static int br_if(struct nw_interp *const i, const varuint32 relative_depth)
+{
+ if (cond(i) && branch(i, relative_depth))
+ {
+ LOG("%s: branch failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 relative_depth;
+
+ if (varuint32_read(f, &relative_depth))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+ else if (i && br_if(i, relative_depth))
+ {
+ LOG("%s: br_if failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_br_if(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_br_if(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/control_flow/br_table.c b/src/op/control_flow/br_table.c
new file mode 100644
index 0000000..837cdcd
--- /dev/null
+++ b/src/op/control_flow/br_table.c
@@ -0,0 +1,56 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 target_count, default_target;
+
+ if (varuint32_read(f, &target_count))
+ {
+ LOG("%s: varuint32_read target_count failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < target_count; i++)
+ {
+ varuint32 target_table;
+
+ if (varuint32_read(f, &target_table))
+ {
+ LOG("%s: varuint32_read target_table failed\n",
+ __func__);
+ return -1;
+ }
+ }
+
+ if (varuint32_read(f, &default_target))
+ {
+ LOG("%s: varuint32_read default_target failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_br_table(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_br_table(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/control_flow/else.c b/src/op/control_flow/else.c
new file mode 100644
index 0000000..fb839f0
--- /dev/null
+++ b/src/op/control_flow/else.c
@@ -0,0 +1,21 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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/ops.h>
+#include <nanowasm/nw.h>
+
+int op_else(struct nw_interp *const i)
+{
+ return -1;
+}
+
+int check_else(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/control_flow/end.c b/src/op/control_flow/end.c
new file mode 100644
index 0000000..5f193d9
--- /dev/null
+++ b/src/op/control_flow/end.c
@@ -0,0 +1,24 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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/ops.h>
+#include <nanowasm/nw.h>
+#include <stdbool.h>
+
+int op_end(struct nw_interp *const i)
+{
+ /* TODO: this is not correct. end can appear anywhere in a function. */
+ i->exit = true;
+ return 0;
+}
+
+int check_end(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/control_flow/if.c b/src/op/control_flow/if.c
new file mode 100644
index 0000000..1d5e838
--- /dev/null
+++ b/src/op/control_flow/if.c
@@ -0,0 +1,36 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varint7 sig;
+
+ if (varint7_read(f, &sig))
+ {
+ LOG("%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_if(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_if(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/control_flow/loop.c b/src/op/control_flow/loop.c
new file mode 100644
index 0000000..5020f23
--- /dev/null
+++ b/src/op/control_flow/loop.c
@@ -0,0 +1,36 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varint7 sig;
+
+ if (varint7_read(f, &sig))
+ {
+ LOG("%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_loop(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_loop(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/control_flow/nop.c b/src/op/control_flow/nop.c
new file mode 100644
index 0000000..356b7b0
--- /dev/null
+++ b/src/op/control_flow/nop.c
@@ -0,0 +1,22 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+
+int op_nop(struct nw_interp *const i)
+{
+ return 0;
+}
+
+int check_nop(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/control_flow/return.c b/src/op/control_flow/return.c
new file mode 100644
index 0000000..abddea7
--- /dev/null
+++ b/src/op/control_flow/return.c
@@ -0,0 +1,30 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/interp.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <stdio.h>
+
+int op_return(struct nw_interp *const i)
+{
+ if (interp_pop(i))
+ {
+ LOG("%s: interp_pop failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int check_return(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/control_flow/unreachable.c b/src/op/control_flow/unreachable.c
new file mode 100644
index 0000000..8f1982c
--- /dev/null
+++ b/src/op/control_flow/unreachable.c
@@ -0,0 +1,25 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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/ops.h>
+#include <nanowasm/nw.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+int op_unreachable(struct nw_interp *const i)
+{
+ i->exception = "Unreachable instruction";
+ i->exit = true;
+ return 1;
+}
+
+int check_unreachable(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/memory/CMakeLists.txt b/src/op/memory/CMakeLists.txt
new file mode 100644
index 0000000..ca4ba5a
--- /dev/null
+++ b/src/op/memory/CMakeLists.txt
@@ -0,0 +1,12 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2024 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
+ current_memory.c
+ i32_load.c
+ i32_store.c
+)
diff --git a/src/op/memory/current_memory.c b/src/op/memory/current_memory.c
new file mode 100644
index 0000000..c67d425
--- /dev/null
+++ b/src/op/memory/current_memory.c
@@ -0,0 +1,44 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint1 reserved;
+
+ if (varuint1_read(f, &reserved))
+ {
+ LOG("%s: varuint1_read failed\n", __func__);
+ return 1;
+ }
+ else if (reserved)
+ {
+ LOG("%s: unexpected non-zero reserved value %u\n",
+ __func__, (unsigned)reserved);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_current_memory(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_current_memory(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/memory/i32_load.c b/src/op/memory/i32_load.c
new file mode 100644
index 0000000..04dbec7
--- /dev/null
+++ b/src/op/memory/i32_load.c
@@ -0,0 +1,76 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int load(struct nw_interp *const i, const varuint32 flags,
+ const varuint32 offset)
+{
+ enum {ALIGN = 2};
+ int32_t value;
+
+ if (flags != ALIGN)
+ {
+ LOG("%s: expected 32-bit alignment (%d), got %lu\n",
+ __func__, ALIGN, (unsigned long)flags);
+ i->exception = "unaligned access";
+ return -1;
+ }
+ else if (interp_heap_load(i, offset, &value, sizeof value))
+ {
+ LOG("%s: interp_heap_load failed\n", __func__);
+ return -1;
+ }
+ else if (interp_stack_push(i, &value, sizeof value))
+ {
+ LOG("%s: interp_stack_push failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varint32 flags, offset;
+
+ if (varint32_read(f, &flags))
+ {
+ LOG("%s: varint32_read flags failed\n", __func__);
+ return 1;
+ }
+ else if (varint32_read(f, &offset))
+ {
+ LOG("%s: varint32_read offset failed\n", __func__);
+ return 1;
+ }
+ else if (i && load(i, flags, offset))
+ {
+ LOG("%s: load failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_i32_load(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_i32_load(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/memory/i32_store.c b/src/op/memory/i32_store.c
new file mode 100644
index 0000000..212cd86
--- /dev/null
+++ b/src/op/memory/i32_store.c
@@ -0,0 +1,125 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <nw/interp.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int store(struct nw_interp *const i, const varuint32 flags,
+ const varuint32 offset)
+{
+ enum {ALIGN = 2};
+
+ if (flags != ALIGN)
+ {
+ LOG("%s: expected 32-bit alignment (%d), got %lu\n",
+ __func__, ALIGN, (unsigned long)flags);
+ i->exception = "unaligned access";
+ return -1;
+ }
+
+ int32_t value, addr;
+
+ if (interp_stack_pop(i, &value, sizeof value))
+ {
+ LOG("%s: inter_stack_pop value failed\n", __func__);
+ return -1;
+ }
+ else if (interp_stack_pop(i, &addr, sizeof addr))
+ {
+ LOG("%s: inter_stack_pop addr failed\n", __func__);
+ return -1;
+ }
+ else if (offset > SIZE_MAX - addr)
+ {
+ static const char exc[] = "offset address overflow";
+
+ LOG("%s: %s\n", __func__, exc);
+ i->exception = exc;
+ return -1;
+ }
+ else if (interp_heap_store(i, offset + addr, &value, sizeof value))
+ {
+ LOG("%s: interp_heap_store failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+#if 0
+ stack_i -= sizeof addr;
+
+ if (max < addr || offset >= max - addr)
+ {
+ LOG("%s: heap overflow\n", __func__);
+ i->exception = "heap overflow";
+ return -1;
+ }
+
+ const size_t raddr = offset + addr;
+
+ if (raddr % sizeof addr)
+ {
+ LOG("%s: unaligned memory address\n", __func__);
+ i->exception = "unaligned access";
+ return -1;
+ }
+ else if (stack_i < sizeof (uint32_t))
+ {
+ LOG("%s: stack underflow (value)\n", __func__);
+ i->exception = "stack underflow";
+ return -1;
+ }
+
+ const int32_t value = htoni32(*stackptr);
+ void *dst = &((char *)i->cfg.heap.buf)[raddr];
+
+ *(int32_t *)dst = value;
+ stack_i -= sizeof addr;
+ i->stack_i = stack_i;
+ return 0;
+#endif
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 flags, offset;
+
+ if (varuint32_read(f, &flags))
+ {
+ LOG("%s: varint32_read flags failed\n", __func__);
+ return -1;
+ }
+ else if (varuint32_read(f, &offset))
+ {
+ LOG("%s: varint32_read offset failed\n", __func__);
+ return -1;
+ }
+ else if (i && store(i, flags, offset))
+ {
+ LOG("%s: store failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_i32_store(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_i32_store(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/numeric/CMakeLists.txt b/src/op/numeric/CMakeLists.txt
new file mode 100644
index 0000000..a259506
--- /dev/null
+++ b/src/op/numeric/CMakeLists.txt
@@ -0,0 +1,10 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2024 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
+ i32_sub.c
+)
diff --git a/src/op/numeric/i32_sub.c b/src/op/numeric/i32_sub.c
new file mode 100644
index 0000000..c3d1352
--- /dev/null
+++ b/src/op/numeric/i32_sub.c
@@ -0,0 +1,66 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/types.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+/* Inspired by "INT32-C. Ensure that operations on signed integers do
+ * not result in overflow". */
+static bool signed_overflow(const int32_t a, const int32_t b)
+{
+ return (b > 0 && a < INT32_MIN + b)
+ || (b < 0 && a > INT32_MAX + b);
+}
+
+int op_i32_sub(struct nw_interp *const i)
+{
+ int32_t op1, op2;
+
+ if (interp_stack_pop(i, &op2, sizeof op2))
+ {
+ LOG("%s: interp_stack_pop %s failed\n", __func__, "op2");
+ return -1;
+ }
+ else if (interp_stack_pop(i, &op1, sizeof op1))
+ {
+ LOG("%s: interp_stack_pop %s failed\n", __func__, "op1");
+ return -1;
+ }
+ else if (signed_overflow(op2, op1))
+ {
+ LOG("%s: signed integer overflow (op1=%" PRIi32 ", op2=%" PRIi32 ")\n",
+ __func__, op1, op2);
+ i->exception = "signed integer overflow";
+ return 1;
+ }
+
+ const int32_t result = op2 - op1;
+
+ if (interp_stack_push(i, &result, sizeof result))
+ {
+ LOG("%s: interp_stack_push failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int check_i32_sub(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/variable_access/CMakeLists.txt b/src/op/variable_access/CMakeLists.txt
new file mode 100644
index 0000000..314b5bf
--- /dev/null
+++ b/src/op/variable_access/CMakeLists.txt
@@ -0,0 +1,14 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2024 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_global.c
+ get_local.c
+ set_global.c
+ set_local.c
+ tee_local.c
+)
diff --git a/src/op/variable_access/get_global.c b/src/op/variable_access/get_global.c
new file mode 100644
index 0000000..d38f2d0
--- /dev/null
+++ b/src/op/variable_access/get_global.c
@@ -0,0 +1,100 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/interp.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+static int get_i32(struct nw_interp *const i, const void *const src)
+{
+ return interp_stack_push(i, src, sizeof (int32_t));
+}
+
+static int get_i64(struct nw_interp *const i, const void *const src)
+{
+ return interp_stack_push(i, src, sizeof (int64_t));
+}
+
+static int get_f32(struct nw_interp *const i, const void *const src)
+{
+ return interp_stack_push(i, src, sizeof (float));
+}
+
+static int get_f64(struct nw_interp *const i, const void *const src)
+{
+ return interp_stack_push(i, src, sizeof (double));
+}
+
+static int get_global(struct nw_interp *const i, const varuint32 global_index)
+{
+ struct nw_gframe *gfr;
+ varuint32 idx;
+
+ for (idx = global_index, gfr = i->gfp; gfr; gfr = gfr->next)
+ if (idx == global_index)
+ break;
+
+ if (!gfr)
+ {
+ LOG("%s: cannot access global variable %lu\n", __func__,
+ (unsigned long)global_index);
+ i->exception = "global variable index out of bounds";
+ return -1;
+ }
+
+ static int (*const get[])(struct nw_interp *, const void *) =
+ {
+ [VALUE_TYPE_I32] = get_i32,
+ [VALUE_TYPE_I64] = get_i64,
+ [VALUE_TYPE_F32] = get_f32,
+ [VALUE_TYPE_F64] = get_f64,
+ };
+
+ if (get[gfr->type](i, gfr + 1))
+ {
+ LOG("%s: get type %s failed\n", __func__, value_type_tostr(gfr->type));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 global_index;
+
+ if (varuint32_read(f, &global_index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+ else if (i && get_global(i, global_index))
+ {
+ LOG("%s: get_global failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_get_global(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_get_global(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/variable_access/get_local.c b/src/op/variable_access/get_local.c
new file mode 100644
index 0000000..745a2e7
--- /dev/null
+++ b/src/op/variable_access/get_local.c
@@ -0,0 +1,112 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <nw/interp.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+static int get_i32(struct nw_interp *const i, const void *const data,
+ const varuint32 idx)
+{
+ const int32_t *const src = (const int32_t *)data + idx;
+
+ return interp_stack_push(i, src, sizeof (*src));
+}
+
+static int get_i64(struct nw_interp *const i, const void *const data,
+ const varuint32 idx)
+{
+ const int64_t *const src = (const int64_t *)data + idx;
+
+ return interp_stack_push(i, src, sizeof (*src));
+}
+
+static int get_f32(struct nw_interp *const i, const void *const data,
+ const varuint32 idx)
+{
+ const float *const src = (const float *)data + idx;
+
+ return interp_stack_push(i, src, sizeof (*src));
+}
+
+static int get_f64(struct nw_interp *const i, const void *const data,
+ const varuint32 idx)
+{
+ const double *const src = (const double *)data + idx;
+
+ return interp_stack_push(i, src, sizeof (*src));
+}
+
+static int get_local(const varuint32 local_index, struct nw_interp *const i)
+{
+ struct nw_frame *fr;
+ varuint32 idx;
+
+ for (idx = local_index, fr = i->fp; fr; fr = fr->next)
+ if (idx < fr->n_locals)
+ break;
+
+ if (!fr)
+ {
+ LOG("%s: cannot access local variable %lu\n", __func__,
+ (unsigned long)local_index);
+ i->exception = "local variable index out of bounds";
+ return -1;
+ }
+
+ static int (*const get[])(struct nw_interp *, const void *, varuint32) =
+ {
+ [VALUE_TYPE_I32] = get_i32,
+ [VALUE_TYPE_I64] = get_i64,
+ [VALUE_TYPE_F32] = get_f32,
+ [VALUE_TYPE_F64] = get_f64,
+ };
+
+ if (get[fr->local_type](i, fr + 1, idx))
+ {
+ LOG("%s: get type %d failed\n", __func__, fr->local_type);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 local_index;
+
+ if (varuint32_read(f, &local_index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+ else if (i && get_local(local_index, i))
+ {
+ LOG("%s: get_local failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_get_local(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_get_local(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/variable_access/set_global.c b/src/op/variable_access/set_global.c
new file mode 100644
index 0000000..d1229c5
--- /dev/null
+++ b/src/op/variable_access/set_global.c
@@ -0,0 +1,38 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 global_index;
+
+ if (varuint32_read(f, &global_index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_set_global(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_set_global(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/variable_access/set_local.c b/src/op/variable_access/set_local.c
new file mode 100644
index 0000000..8cf6440
--- /dev/null
+++ b/src/op/variable_access/set_local.c
@@ -0,0 +1,124 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+static int set_i32(struct nw_interp *const i, void *const data,
+ const varuint32 idx)
+{
+ const void *const stackptr = (const char *)i->cfg.stack.buf + i->stack_i;
+ const int32_t *const src = (const int32_t *)stackptr - 1;
+ int32_t *const dst = (int32_t *)data + idx;
+
+ *dst = *src;
+ return 0;
+}
+
+static int set_i64(struct nw_interp *const i, void *const data,
+ const varuint32 idx)
+{
+ const void *const stackptr = (const char *)i->cfg.stack.buf + i->stack_i;
+ const int64_t *const src = (const int64_t *)stackptr - 1;
+ int64_t *const dst = (int64_t *)data + idx;
+
+ *dst = *src;
+ return 0;
+}
+
+static int set_f32(struct nw_interp *const i, void *const data,
+ const varuint32 idx)
+{
+ const void *const stackptr = (const char *)i->cfg.stack.buf + i->stack_i;
+ const float *const src = (const float *)stackptr - 1;
+ float *const dst = (float *)data + idx;
+
+ *dst = *src;
+ return 0;
+}
+
+static int set_f64(struct nw_interp *const i, void *const data,
+ const varuint32 idx)
+{
+ const void *const stackptr = (const char *)i->cfg.stack.buf + i->stack_i;
+ const double *const src = (const double *)stackptr - 1;
+ double *const dst = (double *)data + idx;
+
+ *dst = *src;
+ return 0;
+}
+
+static int set_local(const varuint32 local_index, struct nw_interp *const i)
+{
+ struct nw_frame *fr;
+ varuint32 idx;
+
+ for (idx = local_index, fr = i->fp; fr; fr = fr->next)
+ if (idx < fr->n_locals)
+ break;
+
+ if (!fr)
+ {
+ LOG("%s: cannot access local variable %lu\n", __func__,
+ (unsigned long)local_index);
+ i->exception = "local variable index out of bounds";
+ return -1;
+ }
+
+ static int (*const set[])(struct nw_interp *, void *, varuint32) =
+ {
+ [VALUE_TYPE_I32] = set_i32,
+ [VALUE_TYPE_I64] = set_i64,
+ [VALUE_TYPE_F32] = set_f32,
+ [VALUE_TYPE_F64] = set_f64,
+ };
+
+ if (set[fr->local_type](i, fr + 1, idx))
+ {
+ LOG("%s: set type %d failed\n", __func__, fr->local_type);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 local_index;
+
+ if (varuint32_read(f, &local_index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+ else if (i && set_local(local_index, i))
+ {
+ LOG("%s: set_local failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_set_local(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_set_local(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/variable_access/tee_local.c b/src/op/variable_access/tee_local.c
new file mode 100644
index 0000000..00d7832
--- /dev/null
+++ b/src/op/variable_access/tee_local.c
@@ -0,0 +1,38 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 local_index;
+
+ if (varuint32_read(f, &local_index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_tee_local(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_tee_local(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/run.c b/src/run.c
new file mode 100644
index 0000000..e1cca7d
--- /dev/null
+++ b/src/run.c
@@ -0,0 +1,42 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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/search.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+enum nw_state nw_run(struct nw_inst *const i)
+{
+ struct nw_interp *const in = &i->interp;
+ union nw_inst_state *const s = &i->state;
+
+ if (interp_run(in))
+ {
+ LOG("%s: interp_run failed\n", __func__);
+
+ if (in->exception)
+ {
+ s->exception = in->exception;
+ return NW_STATE_EXCEPTION;
+ }
+
+ return NW_STATE_FATAL;
+ }
+ else if (in->exit)
+ {
+ s->retval = in->retval;
+ return NW_STATE_RETURNED;
+ }
+
+ return NW_STATE_AGAIN;
+}
diff --git a/src/search.c b/src/search.c
new file mode 100644
index 0000000..622b74c
--- /dev/null
+++ b/src/search.c
@@ -0,0 +1,181 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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/search.h>
+#include <nw/log.h>
+#include <nw/fstring.h>
+#include <nw/sections.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+
+static int ensure_export(const char *const fn,
+ const struct nw_mod *const m, FILE *const f, varuint32 *const out)
+{
+ if (!m->sections.export)
+ {
+ LOG("%s: export section not found", __func__);
+ return -1;
+ }
+ else if (fseek(f, m->sections.export, SEEK_SET))
+ {
+ LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ varuint32 count;
+
+ if (varuint32_read(f, &count))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ {
+ varuint32 len;
+ bool found = false;
+
+ if (varuint32_read(f, &len))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+ else if (len != strlen(fn))
+ {
+ if (fseek(f, len, SEEK_CUR))
+ {
+ LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ }
+ else if (!fstrcmp(fn, f, false))
+ found = true;
+
+ uint8_t kind;
+
+ if (!fread(&kind, sizeof kind, 1, f))
+ {
+ LOG("%s: fread(3) failed: feof=%d, ferror=%d\n", __func__,
+ feof(f), ferror(f));
+ return -1;
+ }
+
+ varuint32 index;
+
+ if (varuint32_read(f, &index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+ else if (found)
+ {
+ if (kind != NW_KIND_FUNCTION)
+ {
+ LOG("%s: expected kind %d, got %d\n", __func__,
+ NW_KIND_FUNCTION, kind);
+ return -1;
+ }
+
+ *out = index;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int get_function_index(const struct nw_mod *const m,
+ const varuint32 index, varuint32 *const findex)
+{
+ const size_t n = m->cfg.n_imports;
+
+ if (index < n)
+ {
+ LOG("%s: index (%lu) must be greater than number of imports (%zu)\n",
+ __func__, (unsigned long)index, n);
+ return -1;
+ }
+
+ *findex = index - n;
+ return 0;
+}
+
+static int get_fn_start(const struct nw_mod *const m, FILE *const f,
+ const varuint32 index, struct search_fn *const out)
+{
+ const struct section s =
+ {
+ .cfg = &m->cfg,
+ .f = f
+ };
+
+ struct section_function sf;
+ struct section_code c;
+
+ if (section_function(&s, m, index, &sf))
+ {
+ LOG("%s: section_function failed\n", __func__);
+ return -1;
+ }
+ else if (section_code(&s, m, index, &c))
+ {
+ LOG("%s: section_code failed\n", __func__);
+ return -1;
+ }
+
+ out->start = c.start;
+ out->index = index;
+ return 0;
+}
+
+int search_exported_fn(const char *const fn, const struct nw_mod *const m,
+ FILE *const f, struct search_fn *const out)
+{
+ varuint32 index, findex;
+
+ if (ensure_export(fn, m, f, &index))
+ {
+ LOG("%s: %s not an exported function\n", __func__, fn);
+ return -1;
+ }
+ else if (get_function_index(m, index, &findex))
+ {
+ LOG("%s: get_function_index failed\n", __func__);
+ return -1;
+ }
+ else if (get_fn_start(m, f, findex, out))
+ {
+ LOG("%s: get_fn_start %s failed\n", __func__, fn);
+ return -1;
+ }
+
+ return 0;
+}
+
+int search_fn(const varuint32 index, const struct nw_mod *const m,
+ FILE *const f, struct search_fn *const out)
+{
+ varuint32 findex;
+
+ if (get_function_index(m, index, &findex))
+ {
+ LOG("%s: get_function_index failed\n", __func__);
+ return -1;
+ }
+ else if (get_fn_start(m, f, findex, out))
+ {
+ LOG("%s: get_fn_start failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/section/CMakeLists.txt b/src/section/CMakeLists.txt
new file mode 100644
index 0000000..772dd3f
--- /dev/null
+++ b/src/section/CMakeLists.txt
@@ -0,0 +1,22 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2024 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
+ common.c
+ custom.c
+ data.c
+ element.c
+ export.c
+ function.c
+ global.c
+ import.c
+ memory.c
+ start.c
+ table.c
+ type.c
+)
diff --git a/src/section/code.c b/src/section/code.c
new file mode 100644
index 0000000..da45047
--- /dev/null
+++ b/src/section/code.c
@@ -0,0 +1,429 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nanowasm/nw.h>
+#include <nw/opcodes.h>
+#include <nw/interp.h>
+#include <nw/sections.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+static int check_body(FILE *const f, const unsigned long n)
+{
+ unsigned long rem = n;
+
+ while (rem)
+ {
+ const long before = ftell(f);
+ uint8_t byte;
+
+ if (before < 0)
+ {
+ LOG("%s: ftell(3) before: %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (!fread(&byte, sizeof byte, 1, f))
+ {
+ LOG("%s: fread(3) failed, feof=%d, ferror=%d\n",
+ __func__, feof(f), ferror(f));
+ return -1;
+ }
+ else if (rem == 1 && byte != OP_END)
+ {
+ LOG("%s: unexpected opcode %#" PRIx8 " at body end\n",
+ __func__, byte);
+ return -1;
+ }
+ else if (interp_check_opcode(byte, f))
+ {
+ LOG("%s: interp_check_opcode failed\n", __func__);
+ return -1;
+ }
+
+ const long after = ftell(f);
+
+ if (after < 0)
+ {
+ LOG("%s: ftell(3) after: %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ rem -= after - before;
+ }
+
+ return 0;
+}
+
+#if 0
+
+static int push_locals(struct nw_interp *const i,
+ const struct nw_frame *const f)
+{
+ const enum value_type t = f->local_type;
+ const size_t typesz = get_type_size(t);
+ const unsigned long n = f->n_locals;
+
+ if (mul_overflow(typesz, f->n_locals))
+ {
+ LOG("%s: local variables size mul overflow", __func__);
+ i->exception = "mul overfllow";
+ return -1;
+ }
+
+ const size_t totalsz = typesz * n, max = i->cfg.stack.n;
+
+ if (totalsz > max || i->stack_i > max - totalsz)
+ {
+ LOG("%s: cannot allocate locals\n", __func__);
+ i->exception = "stack overflow";
+ return 1;
+ }
+
+ init_locals(t, n, interp_stackptr(i));
+ i->stack_i += totalsz;
+ return 0;
+}
+
+static int push_block(struct nw_interp *const i)
+{
+ const long pc = ftell(i->f);
+
+ if (pc < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const struct nw_block b =
+ {
+ .pc = pc
+ };
+
+ if (interp_stack_push(i, &b, sizeof b))
+ {
+ LOG("%s: interp_stack_push failed\n", __func__);
+ return -1;
+ }
+
+ struct nw_block *const p = (struct nw_block *)interp_stackptr(i) - 1;
+
+ if (i->fp->block)
+ {
+ for (struct nw_block *b = i->fp->block; b; b = b->next)
+ if (!b->next)
+ {
+ b->next = p;
+ break;
+ }
+ }
+ else
+ i->fp->block = p;
+
+ return 0;
+}
+
+static int loop_push_blocks(struct nw_interp *const i)
+{
+ for (;;)
+ {
+ uint8_t op;
+
+ if (!fread(&op, sizeof op, 1, i->f))
+ {
+ LOG("%s: fread(3) failed, feof=%d, ferror=%d\n", __func__,
+ feof(i->f), ferror(i->f));
+ i->exception = "I/O error";
+ return -1;
+ }
+ else if (op >= sizeof ops / sizeof *ops)
+ {
+ LOG("%s: invalid opcode %#" PRIx8 "\n", __func__, op);
+ i->exception = "invalid opcode";
+ return -1;
+ }
+ else if (interp_check_opcode(op, i->f))
+ {
+ LOG("%s: interp_check_opcode failed\n", __func__);
+ return -1;
+ }
+
+ switch (op)
+ {
+ case OP_END:
+ return 0;
+
+ case OP_BLOCK:
+ if (push_block(i))
+ {
+ LOG("%s: push_block failed\n", __func__);
+ return -1;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static int push_blocks(struct nw_interp *const i,
+ const struct nw_frame *const fr)
+{
+ FILE *const f = i->f;
+ const long orig = ftell(f);
+
+ if (orig < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (loop_push_blocks(i))
+ {
+ LOG("%s: loop_push_blocks failed\n", __func__);
+ return -1;
+ }
+ else if (fseek(f, orig, SEEK_SET))
+ {
+ LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+static int push_local_count(struct nw_frame *const f, const varuint32 count,
+ const varint7 type)
+{
+ enum value_type vtype;
+
+ if (get_value_type(type, &vtype))
+ {
+ LOG("%s: get_value_type failed\n", __func__);
+ return -1;
+ }
+
+ f->n_locals = count;
+ f->local_type = vtype;
+ return 0;
+}
+
+static int check_local(FILE *const f, struct nw_frame *const fr)
+{
+ varuint32 count;
+ varint7 type;
+
+ if (varuint32_read(f, &count))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+ else if (varint7_read(f, &type))
+ {
+ LOG("%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+ else if (fr && push_local_count(fr, count, type))
+ {
+ LOG("%s: push_local_count failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_locals(FILE *const f, struct nw_frame *const fr)
+{
+ varuint32 local_count;
+
+ if (varuint32_read(f, &local_count))
+ {
+ LOG("%s: varuint32_read local_count failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < local_count; i++)
+ if (check_local(f, fr))
+ {
+ LOG("%s: check_local failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_function_body(FILE *const f, long *const out)
+{
+ varuint32 body_size;
+
+ if (varuint32_read(f, &body_size))
+ {
+ LOG("%s: varuint32_read body_size failed\n", __func__);
+ return -1;
+ }
+
+ const long start = ftell(f);
+
+ if (start < 0)
+ {
+ LOG("%s: ftell(3) start: %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check_locals(f, NULL))
+ {
+ LOG("%s: check_locals failed\n", __func__);
+ return -1;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ LOG("%s: ftell(3) end: %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long consumed = end - start;
+
+ if (consumed > body_size)
+ {
+ LOG("%s: exceeded function body size\n", __func__);
+ return -1;
+ }
+ else if (check_body(f, body_size - consumed))
+ {
+ LOG("%s: check_body failed\n", __func__);
+ return -1;
+ }
+
+ *out = start;
+ return 0;
+}
+
+static int run(FILE *const f, const varuint32 idx,
+ struct section_code *const out)
+{
+ varuint32 count;
+
+ if (varuint32_read(f, &count))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+ else if (count > ULONG_MAX - 1)
+ {
+ fprintf(stderr, "%s: count overflow\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ {
+ long start;
+
+ if (check_function_body(f, &start))
+ {
+ LOG("%s: check_function_body failed\n", __func__);
+ return -1;
+ }
+ else if (out && i == idx)
+ {
+ out->start = start;
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+int section_code_check(const struct section *const s, struct nw_mod *const m,
+ const unsigned long len)
+{
+ FILE *const f = s->f;
+
+ if (m->sections.code)
+ {
+ LOG("%s: ignoring duplicate section\n", __func__);
+ return fseek(f, len, SEEK_CUR);
+ }
+
+ const long start = ftell(f);
+
+ if (start < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (run(f, 0, NULL))
+ {
+ LOG("%s: run failed\n", __func__);
+ return -1;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ LOG("%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ m->sections.code = start;
+ return 0;
+}
+
+int section_code(const struct section *const s, const struct nw_mod *const m,
+ const varuint32 idx, struct section_code *const out)
+{
+ const long offset = m->sections.code;
+
+ if (offset <= 0)
+ {
+ LOG("%s: code section not found", __func__);
+ return -1;
+ }
+ else if (fseek(s->f, offset, SEEK_SET))
+ {
+ LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ return run(s->f, idx, out);
+}
+
+int section_code_push(FILE *const f, const long pc, struct nw_frame *const fr)
+{
+ if (fseek(f, pc, SEEK_SET))
+ {
+ LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check_locals(f, fr))
+ {
+ LOG("%s: check_local failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/section/common.c b/src/section/common.c
new file mode 100644
index 0000000..b768470
--- /dev/null
+++ b/src/section/common.c
@@ -0,0 +1,150 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/sections.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+int check_resizable_limits(FILE *const f, struct resizable_limits *const r)
+{
+ varuint1 flags;
+ varuint32 initial;
+
+ *r = (const struct resizable_limits){0};
+
+ if (varuint1_read(f, &flags))
+ {
+ LOG("%s: varuint1_read failed\n", __func__);
+ return -1;
+ }
+ else if (varuint32_read(f, &initial))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ if (flags)
+ {
+ varuint32 maximum;
+
+ if (varuint32_read(f, &maximum))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ r->max = maximum;
+ r->max_available = true;
+ }
+
+ static const unsigned long page_size = 64lu * 1024lu;
+
+ if (initial > UINT32_MAX / page_size)
+ {
+ LOG("%s: initial size (%lu) overflow \n", __func__,
+ (unsigned long)initial);
+ return 1;
+ }
+
+ r->sz = initial * page_size;
+ return 0;
+}
+
+size_t get_type_size(const enum value_type type)
+{
+ static const size_t list[] =
+ {
+ [VALUE_TYPE_I32] = sizeof (int32_t),
+ [VALUE_TYPE_I64] = sizeof (int64_t),
+ [VALUE_TYPE_F32] = sizeof (float),
+ [VALUE_TYPE_F64] = sizeof (double)
+ };
+
+ return list[type];
+}
+
+enum
+{
+ I32 = 0x7f,
+ I64 = 0x7e,
+ F32 = 0x7d,
+ F64 = 0x7c,
+ ANYFUNC = 0x70,
+ FUNC = 0x60,
+ BLOCK_TYPE = 0x40
+};
+
+int get_value_type(const varint7 type, enum value_type *const vtype)
+{
+ static const struct size
+ {
+ varint7 type;
+ enum value_type vtype;
+ } sizes[] =
+ {
+ {.type = I32, .vtype = VALUE_TYPE_I32},
+ {.type = I64, .vtype = VALUE_TYPE_I64},
+ {.type = F32, .vtype = VALUE_TYPE_F32},
+ {.type = F64, .vtype = VALUE_TYPE_F64},
+ /* TODO: check this. */
+ {.type = ANYFUNC, .vtype = VALUE_TYPE_I32},
+ /* TODO: check this. */
+ {.type = FUNC, .vtype = VALUE_TYPE_I32},
+ /* TODO: check this. */
+ {.type = BLOCK_TYPE, .vtype = VALUE_TYPE_I32}
+ };
+
+ for (size_t i = 0; i < sizeof sizes / sizeof *sizes; i++)
+ {
+ const struct size *const s = &sizes[i];
+
+ if (type == s->type)
+ {
+ *vtype = s->vtype;
+ return 0;
+ }
+ }
+
+ LOG("%s: unknown type %#hhx\n", __func__, (char)type);
+ return -1;
+}
+
+static int32_t swap_i32(const int32_t in)
+{
+ const int8_t *const p = (const int8_t *)&in;
+
+ return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+}
+
+int32_t htoni32(const int32_t in)
+{
+ return swap_i32(in);
+}
+
+int32_t ntohi32(const int32_t in)
+{
+ return swap_i32(in);
+}
+
+const char *value_type_tostr(const enum value_type v)
+{
+ static const char *const s[] =
+ {
+#define X(x) [x] = #x,
+ VALUE_TYPES
+#undef X
+ };
+
+ return s[v];
+}
diff --git a/src/section/custom.c b/src/section/custom.c
new file mode 100644
index 0000000..fe06b74
--- /dev/null
+++ b/src/section/custom.c
@@ -0,0 +1,30 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/sections.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+int section_custom_check(const struct section *const s, struct nw_mod *const m,
+ const unsigned long len)
+{
+ FILE *const f = s->f;
+
+ if (fseek(f, len, SEEK_CUR))
+ {
+ LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/section/data.c b/src/section/data.c
new file mode 100644
index 0000000..6ea12d6
--- /dev/null
+++ b/src/section/data.c
@@ -0,0 +1,18 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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/sections.h>
+#include <nw/types.h>
+
+int section_data_check(const struct section *const s, struct nw_mod *const m,
+ const unsigned long len)
+{
+ return -1;
+}
diff --git a/src/section/element.c b/src/section/element.c
new file mode 100644
index 0000000..39bd5b7
--- /dev/null
+++ b/src/section/element.c
@@ -0,0 +1,17 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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/sections.h>
+#include <nanowasm/nw.h>
+
+int section_element_check(const struct section *const s,
+ struct nw_mod *const m, const unsigned long len)
+{
+ return -1;
+}
diff --git a/src/section/export.c b/src/section/export.c
new file mode 100644
index 0000000..0948067
--- /dev/null
+++ b/src/section/export.c
@@ -0,0 +1,161 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/sections.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+static int check_string(FILE *const f)
+{
+ varuint32 len;
+
+ if (varuint32_read(f, &len))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < len; i++)
+ {
+ uint8_t byte;
+
+ if (!fread(&byte, sizeof byte, 1, f))
+ {
+ LOG("%s: fread(3) failed, feof=%d, ferror=%d\n",
+ __func__, feof(f), ferror(f));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int check_kind(FILE *const f)
+{
+ uint8_t kind;
+
+ if (!fread(&kind, sizeof kind, 1, f))
+ {
+ LOG("%s: fread(3) failed: feof=%d, ferror=%d\n", __func__,
+ feof(f), ferror(f));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_index(FILE *const f)
+{
+ varuint32 index;
+
+ if (varuint32_read(f, &index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_export_entry(FILE *const f)
+{
+ if (check_string(f))
+ {
+ LOG("%s: check_string failed\n", __func__);
+ return -1;
+ }
+ else if (check_kind(f))
+ {
+ LOG("%s: check_kind failed\n", __func__);
+ return -1;
+ }
+ else if (check_index(f))
+ {
+ LOG("%s: check_index failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check(FILE *const f, const unsigned long len)
+{
+ const long start = ftell(f);
+ varuint32 count;
+
+ if (start < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (varuint32_read(f, &count))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ if (check_export_entry(f))
+ {
+ LOG("%s: check_export_entry failed\n", __func__);
+ return -1;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ LOG("%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ return 0;
+}
+
+int section_export_check(const struct section *const s, struct nw_mod *const m,
+ const unsigned long len)
+{
+ FILE *const f = s->f;
+
+ if (m->sections.export)
+ {
+ LOG("%s: ignoring duplicate section\n", __func__);
+ return fseek(f, len, SEEK_CUR);
+ }
+
+ const long offset = ftell(f);
+
+ if (offset < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check(f, len))
+ {
+ LOG("%s: check failed\n", __func__);
+ return -1;
+ }
+
+ m->sections.export = offset;
+ return 0;
+}
diff --git a/src/section/function.c b/src/section/function.c
new file mode 100644
index 0000000..3ffed36
--- /dev/null
+++ b/src/section/function.c
@@ -0,0 +1,127 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/sections.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+static int run(FILE *const f, const varuint32 idx,
+ struct section_function *const out)
+{
+ varuint32 count;
+
+ if (varuint32_read(f, &count))
+ {
+ LOG("%s: varuint32_read count failed\n", __func__);
+ return -1;
+ }
+ else if (count > ULONG_MAX - 1)
+ {
+ fprintf(stderr, "%s: count overflow\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ {
+ varuint32 type;
+
+ if (varuint32_read(f, &type))
+ {
+ LOG("%s: varuint32_read type failed\n", __func__);
+ return -1;
+ }
+ else if (out && i == idx)
+ {
+ out->type = type;
+ out->nreturn = count;
+ return 0;
+ }
+ }
+
+ if (out)
+ {
+ LOG("%s: could not find function index %ju\n", __func__,
+ (uintmax_t)idx);
+ return -1;
+ }
+
+ return 0;
+}
+
+int section_function_check(const struct section *const s,
+ struct nw_mod *const m, const unsigned long len)
+{
+ FILE *const f = s->f;
+
+ if (m->sections.function)
+ {
+ LOG("%s: ignoring duplicate section\n", __func__);
+ return fseek(f, len, SEEK_CUR);
+ }
+
+ const long start = ftell(f);
+
+ if (start < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (run(f, 0, NULL))
+ {
+ LOG("%s: run failed\n", __func__);
+ return -1;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ LOG("%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ m->sections.function = start;
+ return 0;
+}
+
+int section_function(const struct section *const s,
+ const struct nw_mod *const m, const varuint32 idx,
+ struct section_function *const f)
+{
+ const long offset = m->sections.function;
+
+ if (offset <= 0)
+ {
+ LOG("%s: function section not found", __func__);
+ return -1;
+ }
+ else if (fseek(s->f, offset, SEEK_SET))
+ {
+ LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ return run(s->f, idx, f);
+}
diff --git a/src/section/global.c b/src/section/global.c
new file mode 100644
index 0000000..eff2e0b
--- /dev/null
+++ b/src/section/global.c
@@ -0,0 +1,346 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/sections.h>
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct global_type
+{
+ bool mutable;
+ enum value_type type;
+
+ union global_value
+ {
+ int32_t i32;
+ int64_t i64;
+ float f32;
+ double f64;
+ } value;
+};
+
+static int get_i32(const struct nw_interp *const i,
+ union global_value *const value)
+{
+ if (i->stack_i != sizeof value->i32)
+ {
+ LOG("%s: expected %zu used stack bytes, got %zu\n",
+ __func__, i->stack_i, sizeof value->i32);
+ return -1;
+ }
+
+ value->i32 = *(const int32_t *)i->cfg.stack.buf;
+ return 0;
+}
+
+static int get_i64(const struct nw_interp *const i,
+ union global_value *const value)
+{
+ if (i->stack_i != sizeof value->i64)
+ {
+ LOG("%s: expected %zu used stack bytes, got %zu\n",
+ __func__, i->stack_i, sizeof value->i64);
+ return -1;
+ }
+
+ value->i64 = *(const int64_t *)i->cfg.stack.buf;
+ return 0;
+}
+
+static int get_f32(const struct nw_interp *const i,
+ union global_value *const value)
+{
+ if (i->stack_i != sizeof value->f32)
+ {
+ LOG("%s: expected %zu used stack bytes, got %zu\n",
+ __func__, i->stack_i, sizeof value->f32);
+ return -1;
+ }
+
+ value->f32 = *(const float *)i->cfg.stack.buf;
+ return 0;
+}
+
+static int get_f64(const struct nw_interp *const i,
+ union global_value *const value)
+{
+ if (i->stack_i != sizeof value->f64)
+ {
+ LOG("%s: expected %zu used stack bytes, got %zu\n",
+ __func__, i->stack_i, sizeof value->f64);
+ return -1;
+ }
+
+ value->f64 = *(const double *)i->cfg.stack.buf;
+ return 0;
+}
+
+static int get_value(const struct nw_interp *const i,
+ const enum value_type type, union global_value *const value)
+{
+ static int (*const get[])(const struct nw_interp *, union global_value *) =
+ {
+ [VALUE_TYPE_I32] = get_i32,
+ [VALUE_TYPE_I64] = get_i64,
+ [VALUE_TYPE_F32] = get_f32,
+ [VALUE_TYPE_F64] = get_f64
+ };
+
+ if (get[type](i, value))
+ {
+ LOG("%s: get failed with type %d\n", __func__, type);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_global_type(FILE *const f, struct global_type *const g)
+{
+ const struct nw_interp_cfg cfg =
+ {
+ .stack = NW_BUF(sizeof (union global_value))
+ };
+
+ struct nw_interp i;
+ enum value_type vtype;
+ varint7 content_type;
+ varuint1 mutability;
+ union global_value value;
+
+ if (varint7_read(f, &content_type))
+ {
+ LOG("%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+ else if (get_value_type(content_type, &vtype))
+ {
+ LOG("%s: get_value_type failed\n", __func__);
+ return -1;
+ }
+ else if (varuint1_read(f, &mutability))
+ {
+ LOG("%s: varuint1_read failed\n", __func__);
+ return -1;
+ }
+ else if (interp_start(&cfg, f, &i))
+ {
+ LOG("%s: interp_start failed\n", __func__);
+ return -1;
+ }
+ else if (interp_run_limited(&i, &interp_initexpr_set))
+ {
+ LOG("%s: interp_run failed\n", __func__);
+ return -1;
+ }
+ else if (get_value(&i, vtype, &value))
+ {
+ LOG("%s: get_value failed\n", __func__);
+ return -1;
+ }
+
+ *g = (const struct global_type)
+ {
+ .mutable = mutability,
+ .type = vtype,
+ .value = value
+ };
+
+ return 0;
+}
+
+static int set_i32(struct nw_interp *const i, void *const dst,
+ const struct global_type *const g)
+{
+ *(int32_t *)dst = g->value.i32;
+ return 0;
+}
+
+static int set_i64(struct nw_interp *const i, void *const dst,
+ const struct global_type *const g)
+{
+ *(int64_t *)dst = g->value.i64;
+ return 0;
+}
+
+static int set_f32(struct nw_interp *const i, void *const dst,
+ const struct global_type *const g)
+{
+ *(float *)dst = g->value.f32;
+ return 0;
+}
+
+static int set_f64(struct nw_interp *const i, void *const dst,
+ const struct global_type *const g)
+{
+ *(double *)dst = g->value.f64;
+ return 0;
+}
+
+static int add_global(struct nw_interp *const i,
+ const struct global_type *const g)
+{
+ const struct nw_gframe f =
+ {
+ .mutable = g->mutable,
+ .type = g->type
+ };
+
+ const size_t sz = sizeof f + get_type_size(g->type),
+ max = i->cfg.global.n;
+
+ if (max < sz || i->global_i > max - sz)
+ {
+ LOG("%s: global memory overflow\n", __func__);
+ i->exception = "global memory overflow";
+ return -1;
+ }
+
+ void *const buf = (char *)i->cfg.global.buf + i->global_i;
+ struct nw_gframe *const dst = buf;
+
+ if (i->gfp)
+ i->gfp->next = dst;
+
+ *dst = f;
+
+ static int (*const set[])(struct nw_interp *, void *,
+ const struct global_type *) =
+ {
+ [VALUE_TYPE_I32] = set_i32,
+ [VALUE_TYPE_I64] = set_i64,
+ [VALUE_TYPE_F32] = set_f32,
+ [VALUE_TYPE_F64] = set_f64
+ };
+
+ if (set[g->type](i, dst + 1, g))
+ {
+ LOG("%s: set failed with type %d\n", __func__, g->type);
+ return -1;
+ }
+
+ i->global_i += sz;
+ i->gfp = dst;
+ return 0;
+}
+
+static int check_global_types(FILE *const f,
+ struct nw_interp *const in)
+{
+ varuint32 count;
+
+ if (varuint32_read(f, &count))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ {
+ struct global_type g;
+
+ if (check_global_type(f, &g))
+ {
+ LOG("%s: check_global_type\n", __func__);
+ return -1;
+ }
+ else if (in && add_global(in, &g))
+ {
+ LOG("%s: add_global failed\n", __func__);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int check(FILE *const f, const unsigned long len)
+{
+ const long start = ftell(f);
+
+ if (start < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check_global_types(f, NULL))
+ {
+ LOG("%s: check_global_types failed\n", __func__);
+ return -1;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ LOG("%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ return 0;
+}
+
+int section_global_check(const struct section *const s, struct nw_mod *const m,
+ const unsigned long len)
+{
+ FILE *const f = s->f;
+
+ if (m->sections.global)
+ {
+ LOG("%s: ignoring duplicate section\n", __func__);
+ return fseek(f, len, SEEK_CUR);
+ }
+
+ const long offset = ftell(f);
+
+ if (offset < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check(f, len))
+ {
+ LOG("%s: check failed\n", __func__);
+ return -1;
+ }
+
+ m->sections.global = offset;
+ return 0;
+}
+
+int section_global_push(FILE *const f, const struct nw_mod *const m,
+ struct nw_interp *const i)
+{
+ if (fseek(f, m->sections.global, SEEK_SET))
+ {
+ LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check_global_types(f, i))
+ {
+ LOG("%s: check_global_types failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/section/import.c b/src/section/import.c
new file mode 100644
index 0000000..5dd708e
--- /dev/null
+++ b/src/section/import.c
@@ -0,0 +1,274 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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/sections.h>
+#include <nw/fstring.h>
+#include <nw/log.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+static int dump_import_name(FILE *const f, const varuint32 len)
+{
+ for (varuint32 i = 0; i < len; i++)
+ {
+ uint8_t byte;
+
+ if (!fread(&byte, sizeof byte, 1, f))
+ {
+ LOG("%s: fread(3) failed, feof=%d, ferror=%d\n",
+ __func__, feof(f), ferror(f));
+ return -1;
+ }
+
+ LOG("%c", (char)byte);
+ }
+
+ return 0;
+}
+
+static int check_module_string(const struct nw_mod_cfg *const cfg,
+ FILE *const f)
+{
+ varuint32 len;
+
+ if (varuint32_read(f, &len))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (size_t i = 0; i < cfg->n_imports; i++)
+ {
+ const struct nw_import *const im = &cfg->imports[i];
+ const char *const mod = im->module;
+
+ if (strlen(mod) == len)
+ {
+ const long offset = ftell(f);
+
+ if (offset < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (!fstrcmp(mod, f, true))
+ return 0;
+ else if (fseek(f, offset, SEEK_SET))
+ {
+ LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ }
+ }
+
+ LOG("%s: required imported module ", __func__);
+ dump_import_name(f, len);
+ LOG(" not found\n");
+ return -1;
+}
+
+static int check_field_string(const struct nw_mod_cfg *const cfg,
+ FILE *const f)
+{
+ varuint32 len;
+
+ if (varuint32_read(f, &len))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (size_t i = 0; i < cfg->n_imports; i++)
+ {
+ const struct nw_import *const im = &cfg->imports[i];
+ const char *const field = im->field;
+
+ if (strlen(field) == len)
+ {
+ const long offset = ftell(f);
+
+ if (offset < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (!fstrcmp(field, f, true))
+ return 0;
+ else if (fseek(f, offset, SEEK_SET))
+ {
+ LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ }
+ }
+
+ LOG("%s: required imported field ", __func__);
+ dump_import_name(f, len);
+ LOG(" not found\n");
+ return -1;
+}
+
+static int check_function_kind(FILE *const f)
+{
+ varuint32 type;
+
+ if (varuint32_read(f, &type))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_table_kind(FILE *const f)
+{
+ varint7 type;
+
+ if (varint7_read(f, &type))
+ {
+ LOG("%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_memory_kind(FILE *const f)
+{
+ /* TODO. */
+ return -1;
+}
+
+static int check_global_kind(FILE *const f)
+{
+ /* TODO. */
+ return -1;
+}
+
+static int check_import_entry(const struct nw_mod_cfg *const cfg,
+ FILE *const f)
+{
+ if (check_module_string(cfg, f))
+ {
+ LOG("%s: check_module_string failed\n", __func__);
+ return -1;
+ }
+ else if (check_field_string(cfg, f))
+ {
+ LOG("%s: check_field_string failed\n", __func__);
+ return -1;
+ }
+
+ uint8_t kind;
+
+ if (!fread(&kind, sizeof kind, 1, f))
+ {
+ LOG("%s: fread(3) failed, feof=%d, ferror=%d\n",
+ __func__, feof(f), ferror(f));
+ return -1;
+ }
+
+ static int (*const fn[])(FILE *) =
+ {
+ [NW_KIND_FUNCTION] = check_function_kind,
+ [NW_KIND_TABLE] = check_table_kind,
+ [NW_KIND_MEMORY] = check_memory_kind,
+ [NW_KIND_GLOBAL] = check_global_kind
+ };
+
+ if (kind >= sizeof fn / sizeof *fn)
+ {
+ LOG("%s: unexpected kind %#" PRIx8 "\n", __func__, kind);
+ return -1;
+ }
+
+ return fn[kind](f);
+}
+
+static int check(const struct nw_mod_cfg *const cfg, FILE *const f,
+ const unsigned long len)
+{
+ const long start = ftell(f);
+
+ if (start < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ varuint32 count;
+
+ if (varuint32_read(f, &count))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ if (check_import_entry(cfg, f))
+ {
+ LOG("%s: check_import_entry failed\n", __func__);
+ return -1;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ LOG("%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ return 0;
+}
+
+int section_import_check(const struct section *const s, struct nw_mod *m,
+ const unsigned long len)
+{
+ const struct nw_mod_cfg *const cfg = s->cfg;
+ FILE *const f = s->f;
+
+ if (m->sections.import)
+ {
+ LOG("%s: ignoring duplicate section\n", __func__);
+ return fseek(f, len, SEEK_CUR);
+ }
+
+ const long offset = ftell(f);
+
+ if (offset < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check(cfg, f, len))
+ {
+ LOG("%s: check failed\n", __func__);
+ return -1;
+ }
+
+ m->sections.import = offset;
+ return 0;
+}
diff --git a/src/section/memory.c b/src/section/memory.c
new file mode 100644
index 0000000..0de5b4c
--- /dev/null
+++ b/src/section/memory.c
@@ -0,0 +1,102 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/sections.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+static int check_memory_type(FILE *const f, struct resizable_limits *const r)
+{
+ return check_resizable_limits(f, r);
+}
+
+static int check(FILE *const f, const unsigned long len,
+ struct resizable_limits *const r)
+{
+ const long start = ftell(f);
+
+ if (start < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ varuint32 count;
+
+ if (varuint32_read(f, &count))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+ else if (count > 1)
+ {
+ LOG("%s: only 1 memory allowed\n", __func__);
+ return 1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ if (check_memory_type(f, r))
+ {
+ LOG("%s: check_memory_type failed\n", __func__);
+ return -1;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ LOG("%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ return 0;
+}
+
+int section_memory_check(const struct section *const s, struct nw_mod *const m,
+ const unsigned long len)
+{
+ FILE *const f = s->f;
+
+ if (m->sections.memory)
+ {
+ LOG("%s: ignoring duplicate section\n", __func__);
+ return fseek(f, len, SEEK_CUR);
+ }
+
+ const long offset = ftell(f);
+ struct resizable_limits r;
+
+ if (offset < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check(f, len, &r))
+ {
+ LOG("%s: check failed\n", __func__);
+ return -1;
+ }
+
+ m->heap_len = r.sz;
+ m->sections.memory = offset;
+ return 0;
+}
diff --git a/src/section/start.c b/src/section/start.c
new file mode 100644
index 0000000..dd44652
--- /dev/null
+++ b/src/section/start.c
@@ -0,0 +1,18 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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/sections.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+
+int section_start_check(const struct section *const s, struct nw_mod *const m,
+ const unsigned long len)
+{
+ return -1;
+}
diff --git a/src/section/table.c b/src/section/table.c
new file mode 100644
index 0000000..15fc619
--- /dev/null
+++ b/src/section/table.c
@@ -0,0 +1,112 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/sections.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+static int check_table_type(FILE *const f)
+{
+ enum {ANYFUNC = 0x70};
+ varint7 elem_type;
+
+ if (varint7_read(f, &elem_type))
+ {
+ LOG("%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+ else if (elem_type != ANYFUNC)
+ {
+ LOG("%s: expected %x, got %x", __func__, ANYFUNC,
+ (int)elem_type);
+ return -1;
+ }
+
+ /* TODO: what to do with this? */
+ struct resizable_limits r;
+
+ return check_resizable_limits(f, &r);
+}
+
+static int check(FILE *const f, const unsigned long len)
+{
+ const long start = ftell(f);
+
+ if (start < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ varuint32 count;
+
+ if (varuint32_read(f, &count))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ if (check_table_type(f))
+ {
+ LOG("%s: check_table_type failed\n", __func__);
+ return -1;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ LOG("%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ return 0;
+}
+
+int section_table_check(const struct section *const s, struct nw_mod *const m,
+ const unsigned long len)
+{
+ FILE *const f = s->f;
+
+ if (m->sections.table)
+ {
+ LOG("%s: ignoring duplicate section\n", __func__);
+ return fseek(f, len, SEEK_CUR);
+ }
+
+ const long offset = ftell(f);
+
+ if (offset < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check(f, len))
+ {
+ LOG("%s: check failed\n", __func__);
+ return -1;
+ }
+
+ m->sections.table = offset;
+ return 0;
+}
diff --git a/src/section/type.c b/src/section/type.c
new file mode 100644
index 0000000..0bf7a06
--- /dev/null
+++ b/src/section/type.c
@@ -0,0 +1,180 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <nw/sections.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+static int check_value_type(FILE *const f, enum value_type *const vtype)
+{
+ varint7 value_type;
+
+ if (varint7_read(f, &value_type))
+ {
+ LOG("%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+ else if (vtype && get_value_type(value_type, vtype))
+ {
+ LOG("%s: get_value_type failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_func_type(FILE *const f, struct retval *const r)
+{
+ varint7 form;
+ varuint32 param_count;
+
+ if (varint7_read(f, &form))
+ {
+ LOG("%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+ else if (varuint32_read(f, &param_count))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < param_count; i++)
+ if (check_value_type(f, NULL))
+ {
+ LOG("%s: check_value_type failed\n", __func__);
+ return -1;
+ }
+
+ varuint1 return_count;
+ enum value_type vtype;
+
+ if (varuint1_read(f, &return_count))
+ {
+ LOG("%s: varuint1_read failed\n", __func__);
+ return -1;
+ }
+ else if (return_count && check_value_type(f, &vtype))
+ {
+ LOG("%s: check_value_type 2 failed\n", __func__);
+ return -1;
+ }
+
+ *r = (const struct retval)
+ {
+ .returns = return_count,
+ .type = vtype
+ };
+
+ return 0;
+}
+
+static int run(FILE *const f, const unsigned long len, const varuint32 idx,
+ struct nw_frame *const fr)
+{
+ const long start = ftell(f);
+ varuint32 count;
+
+ if (start < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (varuint32_read(f, &count))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ {
+ struct retval r;
+
+ if (check_func_type(f, &r))
+ {
+ LOG("%s: check_func_type failed\n", __func__);
+ return -1;
+ }
+ else if (fr && i == idx)
+ {
+ fr->retval = r;
+ return 0;
+ }
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ LOG("%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ return 0;
+}
+
+int section_type_check(const struct section *const s, struct nw_mod *const m,
+ const unsigned long len)
+{
+ FILE *const f = s->f;
+
+ if (m->sections.type)
+ {
+ LOG("%s: ignoring duplicate section\n", __func__);
+ return fseek(f, len, SEEK_CUR);
+ }
+
+ const long offset = ftell(f);
+
+ if (offset < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (run(f, len, 0, NULL))
+ {
+ LOG("%s: check failed\n", __func__);
+ return -1;
+ }
+
+ m->sections.type = offset;
+ return 0;
+}
+
+int section_type_push(FILE *const f, const struct nw_mod *const m,
+ const varuint32 idx, struct nw_frame *const fr)
+{
+ const long offset = m->sections.type;
+
+ if (offset <= 0)
+ {
+ LOG("%s: function section not found", __func__);
+ return -1;
+ }
+ else if (fseek(f, offset, SEEK_SET))
+ {
+ LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ return run(f, 0, idx, fr);
+}
diff --git a/src/start.c b/src/start.c
new file mode 100644
index 0000000..c22bddf
--- /dev/null
+++ b/src/start.c
@@ -0,0 +1,81 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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/search.h>
+#include <nw/sections.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+int nw_start(const struct nw_inst_cfg *const icfg, struct nw_inst *const i)
+{
+ int ret = -1;
+ const struct nw_mod *const m = icfg->interp.m;
+ const struct nw_mod_cfg *const mcfg = &m->cfg;
+ FILE *const f = fopen(mcfg->path, "rb");
+ struct search_fn fn;
+ struct nw_frame fr = {0};
+
+ *i = (const struct nw_inst){0};
+
+ if (!f)
+ {
+ LOG("%s: fopen(3) %s: %s\n", __func__, mcfg->path, strerror(errno));
+ goto end;
+ }
+ else if (m->sections.start)
+ {
+ /* TODO. */
+ }
+ else if (interp_start(&icfg->interp, f, &i->interp))
+ {
+ LOG("%s: interp_start failed\n", __func__);
+ goto end;
+ }
+ else if (search_exported_fn("_start", m, f, &fn))
+ {
+ LOG("%s: search_exported_fn failed\n", __func__);
+ goto end;
+ }
+ else if (section_global_push(f, m, &i->interp))
+ {
+ LOG("%s: section_global_push failed\n", __func__);
+ goto end;
+ }
+ else if (section_type_push(f, m, fn.index, &fr))
+ {
+ LOG("%s: section_type_push failed\n", __func__);
+ goto end;
+ }
+ else if (section_code_push(f, fn.start, &fr))
+ {
+ LOG("%s: section_code_push failed\n", __func__);
+ goto end;
+ }
+ else if (interp_push(&i->interp, &fr))
+ {
+ LOG("%s: interp_push failed\n", __func__);
+ goto end;
+ }
+
+ ret = 0;
+
+end:
+
+ if (ret && f && fclose(f))
+ {
+ LOG("%s: fclose(3) %s: %s\n", __func__, mcfg->path, strerror(errno));
+ ret = -1;
+ }
+
+ return ret;
+}
diff --git a/src/stop.c b/src/stop.c
new file mode 100644
index 0000000..1d5d604
--- /dev/null
+++ b/src/stop.c
@@ -0,0 +1,27 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+int nw_stop(struct nw_inst *const i)
+{
+ struct nw_interp *const in = &i->interp;
+
+ if (in->f && fclose(in->f))
+ {
+ LOG("%s: fclose(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/types.c b/src/types.c
new file mode 100644
index 0000000..88c883e
--- /dev/null
+++ b/src/types.c
@@ -0,0 +1,89 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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/leb128.h>
+#include <nw/types.h>
+#include <stdio.h>
+
+int varuint1_read(FILE *const f, varuint1 *const out)
+{
+ unsigned long long value;
+
+ if (leb128_read_unsigned(f, 1, &value))
+ return -1;
+
+ *out = value;
+ return 0;
+}
+
+int varint7_read(FILE *const f, varint7 *const out)
+{
+ long long value;
+
+ if (leb128_read_signed(f, 7, &value))
+ return -1;
+
+ *out = value;
+ return 0;
+}
+
+int varuint7_read(FILE *const f, varuint7 *const out)
+{
+ unsigned long long value;
+
+ if (leb128_read_unsigned(f, 7, &value))
+ return -1;
+
+ *out = value;
+ return 0;
+}
+
+int varuint32_read(FILE *const f, varuint32 *out)
+{
+ unsigned long long value;
+
+ if (leb128_read_unsigned(f, 32, &value))
+ return -1;
+
+ *out = value;
+ return 0;
+}
+
+int varint32_read(FILE *const f, varint32 *const out)
+{
+ long long value;
+
+ if (leb128_read_signed(f, 32, &value))
+ return -1;
+
+ *out = value;
+ return 0;
+}
+
+int varuint64_read(FILE *const f, varuint64 *const out)
+{
+ unsigned long long value;
+
+ if (leb128_read_unsigned(f, 64, &value))
+ return -1;
+
+ *out = value;
+ return 0;
+}
+
+int varint64_read(FILE *const f, varint64 *const out)
+{
+ long long value;
+
+ if (leb128_read_signed(f, 64, &value))
+ return -1;
+
+ *out = value;
+ return 0;
+}
diff --git a/src/unload.c b/src/unload.c
new file mode 100644
index 0000000..5b5ef9b
--- /dev/null
+++ b/src/unload.c
@@ -0,0 +1,19 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+void nw_unload(struct nw_mod *const m)
+{
+ free(m);
+}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000..60ebec3
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,24 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2024 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.18)
+project(test C)
+add_executable(${PROJECT_NAME} main.c)
+target_link_libraries(${PROJECT_NAME} PRIVATE nanowasm)
+
+include(CheckLinkerFlag)
+set(ldflags -Wl,--gc-sections)
+
+foreach(f ${ldflags})
+ string(REPLACE "-" "_" var supported_${f})
+ string(REPLACE "," "_" var ${var})
+ check_linker_flag(C ${f} ${var})
+
+ if(${var})
+ target_link_options(${PROJECT_NAME} PRIVATE ${f})
+ endif()
+endforeach()
diff --git a/test/example.wasm b/test/example.wasm
new file mode 100755
index 0000000..afea5ed
--- /dev/null
+++ b/test/example.wasm
Binary files differ
diff --git a/test/main.c b/test/main.c
new file mode 100644
index 0000000..70cacd8
--- /dev/null
+++ b/test/main.c
@@ -0,0 +1,105 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2024 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int proc_exit(const struct nw_args *const params,
+ union nw_value *const ret, void *const user)
+{
+ return -1;
+}
+
+int main(int argc, char *argv[])
+{
+ static const struct nw_import imports[] =
+ {
+ {
+ .kind = NW_KIND_FUNCTION,
+ .module = "wasi_snapshot_preview1",
+ .field = "proc_exit",
+ .u.function =
+ {
+ .signature = "i32()",
+ .fn = proc_exit
+ }
+ }
+ };
+
+ static const struct nw_mod_cfg cfg =
+ {
+ .path = "example.wasm",
+ .imports = imports,
+ .n_imports = sizeof imports / sizeof *imports
+ };
+
+ struct nw_mod m;
+
+ if (nw_load(&cfg, &m))
+ {
+ fprintf(stderr, "%s: nw_load failed\n", __func__);
+ return EXIT_FAILURE;
+ }
+
+ const struct nw_inst_cfg icfg =
+ {
+ .interp =
+ {
+ .stack = NW_BUF(256),
+ .heap = NW_BUF(2048),
+ .global = NW_BUF(24),
+ .m = &m
+ }
+ };
+
+ struct nw_inst inst;
+
+ if (nw_start(&icfg, &inst))
+ {
+ fprintf(stderr, "%s: nw_start failed\n", __func__);
+ return EXIT_FAILURE;
+ }
+
+ const union nw_inst_state *s = &inst.state;
+ int ret = EXIT_FAILURE;
+
+again:
+
+ switch (nw_run(&inst))
+ {
+ case NW_STATE_AGAIN:
+ goto again;
+
+ case NW_STATE_RETURNED:
+ fprintf(stderr, "instance returned %ld\n", s->retval);
+ break;
+
+ case NW_STATE_EXCEPTION:
+ fprintf(stderr, "exception: %s\n", s->exception);
+ goto end;
+
+ case NW_STATE_FATAL:
+ goto end;
+ }
+
+ ret = EXIT_SUCCESS;
+
+end:
+
+ if (nw_stop(&inst))
+ {
+ fprintf(stderr, "%s: nw_stop failed\n", __func__);
+ ret = EXIT_FAILURE;
+ }
+
+ return ret;
+}