commit 36c1820e5826c03284081093964ebe8fcc601d1b Author: Xavier Del Campo Romero Date: Sun Nov 26 22:43:30 2023 +0100 First commit 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..b0c8395 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# `nanowasm`, a tiny WebAssembly/Wasm interpreter + +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 + +#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..55f3041 --- /dev/null +++ b/include/nanowasm/types.h @@ -0,0 +1,141 @@ +/* + * 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 +#include +#include + +#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; +}; + +struct nw_inst_cfg +{ + struct nw_interp_cfg interp; + const struct nw_mod *m; +}; + +struct nw_mod +{ + struct + { + long type, import, function, table, memory, global, export, + start, element, code, data; + } sections; + + struct nw_mod_cfg cfg; +}; + +struct nw_inst +{ + struct nw_interp + { + FILE *f; + const char *exception; + bool exit; + struct nw_interp_cfg cfg; + size_t stack_i, heap_i; + } interp; + + union nw_inst_state + { + int retval; + const char *exception; + } state; + + const struct nw_mod *m; +}; + +#define NW_BUF(sz) {.buf = (char[sz]){0}, .n = sz} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/private_include/fstring.h b/private_include/fstring.h new file mode 100644 index 0000000..a8feda4 --- /dev/null +++ b/private_include/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 +#include + +int fstrcmp(const char *str, FILE *f, bool abort); + +#endif diff --git a/private_include/interp.h b/private_include/interp.h new file mode 100644 index 0000000..e0c8e0b --- /dev/null +++ b/private_include/interp.h @@ -0,0 +1,32 @@ +/* + * 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 +#include +#include +#include +#include + +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, 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); + +#endif diff --git a/private_include/leb128.h b/private_include/leb128.h new file mode 100644 index 0000000..4338255 --- /dev/null +++ b/private_include/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 + +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/log.h b/private_include/log.h new file mode 100644 index 0000000..3eafc88 --- /dev/null +++ b/private_include/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 + +#ifdef ENABLE_LOG +#define LOG(...) fprintf(stderr, __VA_ARGS__) +#else +#define LOG(...) +#endif + +#endif diff --git a/private_include/opcodes.h b/private_include/opcodes.h new file mode 100644 index 0000000..2822b0d --- /dev/null +++ b/private_include/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/ops.h b/private_include/ops.h new file mode 100644 index 0000000..02f191e --- /dev/null +++ b/private_include/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 +#include + +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/search.h b/private_include/search.h new file mode 100644 index 0000000..83a6d73 --- /dev/null +++ b/private_include/search.h @@ -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/. + */ + +#ifndef SEARCH_H +#define SEARCH_H + +#include +#include + +struct search_fn +{ + long start; +}; + +int search_exported_fn(const char *fn, const struct nw_mod *m, FILE *f, + struct search_fn *out); + +#endif diff --git a/private_include/sections.h b/private_include/sections.h new file mode 100644 index 0000000..4ebcd70 --- /dev/null +++ b/private_include/sections.h @@ -0,0 +1,53 @@ +/* + * 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 +#include +#include +#include + +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; +}; + +struct section_type +{ + int dummy; +}; + +int section_function(const struct section *s, const struct nw_mod *const m, + varuint32 idx, struct section_function *f); +int section_type(const struct section *s, struct section_type *t); + +int check_resizable_limits(FILE *f); + +#endif diff --git a/private_include/wasm_types.h b/private_include/wasm_types.h new file mode 100644 index 0000000..833b708 --- /dev/null +++ b/private_include/wasm_types.h @@ -0,0 +1,33 @@ +/* + * 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 +#include +#include + +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; + +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); + +#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..bf9bbc4 --- /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 +#include +#include +#include +#include +#include + +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..027aab3 --- /dev/null +++ b/src/interp.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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)); + return 1; + } + else if (op >= sizeof ops / sizeof *ops) + { + LOG("%s: invalid opcode %#" PRIx8 "\n", __func__, op); + return 1; + } + else if (!ops[op]) + { + LOG("%s: unsupported opcode %#" PRIx8 "\n", __func__, op); + 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)); + 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); + 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, + struct nw_interp *const i) +{ + *i = (const struct nw_interp) + { + .cfg = *cfg + }; + + return 0; +} diff --git a/src/leb128.c b/src/leb128.c new file mode 100644 index 0000000..4242ab6 --- /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 +#include +#include +#include +#include + +/* The functions below are (somewhat heavily) modified versions of those + * provided by the wac project: https://github.com/kanaka/wac */ + +/* + * Copyright (C) Joel Martin + * 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..d2f0f1e --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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, §ion)) + { + 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..6951a47 --- /dev/null +++ b/src/op/call/call.c @@ -0,0 +1,37 @@ +/* + * 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 +#include +#include +#include +#include + +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; + } + + 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..756a61c --- /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 +#include +#include +#include +#include + +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..d9ca9e4 --- /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 +#include +#include +#include +#include +#include + +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..3c034d6 --- /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 +#include +#include +#include +#include +#include + +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..8a57bef --- /dev/null +++ b/src/op/constants/i32_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 +#include +#include +#include +#include +#include + +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; + } + + 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..ab38ad3 --- /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 +#include +#include +#include +#include +#include + +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..43e6ce4 --- /dev/null +++ b/src/op/control_flow/block.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 +#include +#include +#include +#include +#include + +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_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..0257c71 --- /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 +#include +#include +#include +#include +#include + +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..e7af40f --- /dev/null +++ b/src/op/control_flow/br_if.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 +#include +#include +#include +#include +#include + +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_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..eea521f --- /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 +#include +#include +#include +#include +#include + +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..c184e14 --- /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 +#include + +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..e53fbdf --- /dev/null +++ b/src/op/control_flow/end.c @@ -0,0 +1,23 @@ +/* + * 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 +#include +#include + +int op_end(struct nw_interp *const i) +{ + 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..01ecb86 --- /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 +#include +#include +#include + +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..12e72ea --- /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 +#include +#include +#include + +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..0725ac5 --- /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 +#include +#include + +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..42f0f6a --- /dev/null +++ b/src/op/control_flow/return.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 +#include +#include + +int op_return(struct nw_interp *const i) +{ + return -1; +} + +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..e5d46d7 --- /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 +#include +#include +#include + +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..dc42d33 --- /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 +#include +#include +#include +#include +#include + +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..7cf145b --- /dev/null +++ b/src/op/memory/i32_load.c @@ -0,0 +1,43 @@ +/* + * 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 +#include +#include +#include +#include +#include + +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; + } + + 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..63459ba --- /dev/null +++ b/src/op/memory/i32_store.c @@ -0,0 +1,43 @@ +/* + * 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 +#include +#include +#include +#include +#include + +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; + } + + 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..086be39 --- /dev/null +++ b/src/op/numeric/i32_sub.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 +#include +#include +#include +#include +#include + +int op_i32_sub(struct nw_interp *const i) +{ + return -1; +} + +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..4a49f77 --- /dev/null +++ b/src/op/variable_access/get_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 +#include +#include +#include +#include +#include + +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_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..ff407ac --- /dev/null +++ b/src/op/variable_access/get_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 +#include +#include +#include +#include +#include + +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_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..040428a --- /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 +#include +#include +#include +#include +#include + +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..c07cc34 --- /dev/null +++ b/src/op/variable_access/set_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 +#include +#include +#include +#include +#include + +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_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..4e111a4 --- /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 +#include +#include +#include +#include +#include + +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..6ead584 --- /dev/null +++ b/src/run.c @@ -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/. + */ + +#include +#include +#include +#include +#include +#include + +enum nw_state nw_run(struct nw_inst *const i) +{ + return NW_STATE_AGAIN; +} diff --git a/src/search.c b/src/search.c new file mode 100644 index 0000000..f331873 --- /dev/null +++ b/src/search.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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 (!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_fn_start(const char *const fn, 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; + + if (section_function(&s, m, index, &sf)) + { + LOG("%s: section_function failed\n", __func__); + return -1; + } + + return -1; +} + +int search_exported_fn(const char *const fn, const struct nw_mod *const m, + FILE *const f, struct search_fn *const out) +{ + varuint32 index; + + if (ensure_export(fn, m, f, &index)) + { + LOG("%s: %s not an exported function\n", __func__, fn); + return -1; + } + else if (get_fn_start(fn, m, f, index, out)) + { + LOG("%s: get_fn_start %s failed\n", __func__, fn); + 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..9e5f778 --- /dev/null +++ b/src/section/code.c @@ -0,0 +1,247 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} + +static int check_type(FILE *const f) +{ + varint7 type; + + if (varint7_read(f, &type)) + { + LOG("%s: varint7_read failed\n", __func__); + return -1; + } + + return 0; +} + +static int check_local(FILE *const f) +{ + varuint32 count; + + if (varuint32_read(f, &count)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + else if (check_type(f)) + { + LOG("%s: check_type failed\n", __func__); + return -1; + } + + return 0; +} + +static int check_locals(FILE *const f) +{ + 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)) + { + LOG("%s: check_local failed\n", __func__); + return -1; + } + + return 0; +} + +static int check_function_body(FILE *const f) +{ + 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)) + { + 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; + } + + /* TODO: add function body pointers to struct nanowasm. */ + return 0; +} + +static int check_function_bodies(FILE *const f) +{ + 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_function_body(f)) + { + LOG("%s: check_function_body 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_function_bodies(f)) + { + LOG("%s: check_function_bodies 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_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 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.code = offset; + return 0; +} diff --git a/src/section/common.c b/src/section/common.c new file mode 100644 index 0000000..a0c19cc --- /dev/null +++ b/src/section/common.c @@ -0,0 +1,45 @@ +/* + * 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 +#include +#include +#include +#include +#include + +int check_resizable_limits(FILE *const f) +{ + varuint1 flags; + varuint32 initial; + + 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; + } + } + + return 0; +} diff --git a/src/section/custom.c b/src/section/custom.c new file mode 100644 index 0000000..6cd1879 --- /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 +#include +#include +#include +#include +#include +#include + +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..6bc8ad3 --- /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 +#include +#include + +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..5327415 --- /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 +#include + +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..03c8c37 --- /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 +#include +#include +#include +#include +#include +#include +#include + +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..52adb8b --- /dev/null +++ b/src/section/function.c @@ -0,0 +1,121 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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; + } + + for (varuint32 i = 0; i < count; i++) + { + varuint32 type; + + if (varuint32_read(f, &type)) + { + LOG("%s: varuint32_read type failed\n", __func__); + return -1; + } + + if (out && i == idx) + { + out->type = type; + 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..8ab5f33 --- /dev/null +++ b/src/section/global.c @@ -0,0 +1,109 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +struct global_type +{ + varint7 value_type; + varuint1 mutability; +}; + +static int check_global_type(FILE *const f, struct global_type *const g) +{ + if (varint7_read(f, &g->value_type)) + { + LOG("%s: varint7_read failed\n", __func__); + return -1; + } + else if (varuint1_read(f, &g->mutability)) + { + LOG("%s: varuint1_read failed\n", __func__); + return -1; + } + + return 0; +} + +static int check(FILE *const f, const unsigned long len) +{ + struct global_type g; + const long start = ftell(f); + struct nw_interp i = {.f = f}; + + if (start < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (check_global_type(f, &g)) + { + LOG("%s: check_global_type\n", __func__); + return -1; + } + else if (interp_run_limited(&i, &interp_initexpr_set)) + { + LOG("%s: interp_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; + } + + 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; +} diff --git a/src/section/import.c b/src/section/import.c new file mode 100644 index 0000000..04c0a67 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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..289df86 --- /dev/null +++ b/src/section/memory.c @@ -0,0 +1,94 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +static int check_memory_type(FILE *const f) +{ + return check_resizable_limits(f); +} + +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_memory_type(f)) + { + 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); + + 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.memory = offset; + return 0; +} diff --git a/src/section/start.c b/src/section/start.c new file mode 100644 index 0000000..22d1329 --- /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 +#include +#include + +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..d00898d --- /dev/null +++ b/src/section/table.c @@ -0,0 +1,109 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +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; + } + + return check_resizable_limits(f); +} + +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..27e0431 --- /dev/null +++ b/src/section/type.c @@ -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/. + */ + +#include +#include +#include +#include +#include +#include +#include + +static int check_value_type(FILE *const f) +{ + varint7 value_type; + + if (varint7_read(f, &value_type)) + { + LOG("%s: varint7_read failed\n", __func__); + return -1; + } + + return 0; +} + +static int check_func_type(FILE *const f) +{ + varint7 form; + varuint32 param_count; + + if (varint7_read(f, &form)) + { + LOG("%s: varint7_read failed\n", __func__); + return -1; + } + else if (varuint32_read(f, ¶m_count)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < param_count; i++) + if (check_value_type(f)) + { + LOG("%s: check_value_type failed\n", __func__); + return -1; + } + + varuint1 return_count; + + if (varuint1_read(f, &return_count)) + { + LOG("%s: varuint1_read failed\n", __func__); + return -1; + } + else if (return_count && check_value_type(f)) + { + LOG("%s: check_value_type 2 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_func_type(f)) + { + LOG("%s: check_func_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_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 (check(f, len)) + { + LOG("%s: check failed\n", __func__); + return -1; + } + + m->sections.type = offset; + return 0; +} + +int section_type(const struct section *const s, struct section_type *const out) +{ + return -1; +} diff --git a/src/start.c b/src/start.c new file mode 100644 index 0000000..7d7b1c2 --- /dev/null +++ b/src/start.c @@ -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/. + */ + +#include +#include +#include +#include +#include +#include +#include + +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->m; + const struct nw_mod_cfg *const mcfg = &m->cfg; + FILE *const f = fopen(mcfg->path, "rb"); + struct search_fn fn; + + if (!f) + { + LOG("%s: fopen(3) %s: %s\n", __func__, mcfg->path, strerror(errno)); + goto end; + } + else if (m->sections.start) + { + /* TODO. */ + } + else if (search_exported_fn("_start", m, f, &fn)) + { + LOG("%s: search_exported_fn failed\n", __func__); + goto end; + } + else if (fseek(f, fn.start, SEEK_SET)) + { + LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); + goto end; + } + + *i = (const struct nw_inst){.m = m}; + + if (interp_start(&icfg->interp, &i->interp)) + { + fprintf(stderr, "%s: interp_start 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..2141fb7 --- /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 +#include +#include +#include +#include + +int nw_stop(struct nw_inst *const i) +{ + struct nw_interp *const in = &i->interp; + + if (i && 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..d0323d6 --- /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 +#include +#include + +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..7cfad08 --- /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 +#include +#include +#include +#include + +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..3e4bd01 Binary files /dev/null and b/test/example.wasm differ diff --git a/test/main.c b/test/main.c new file mode 100644 index 0000000..7d547d6 --- /dev/null +++ b/test/main.c @@ -0,0 +1,92 @@ +/* + * 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 +#include +#include +#include + +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(1024), + .heap = NW_BUF(1024), + }, + + .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; + +again: + switch (nw_run(&inst)) + { + case NW_STATE_AGAIN: + goto again; + + case NW_STATE_RETURNED: + fprintf(stderr, "instance returned %d\n", s->retval); + break; + + case NW_STATE_EXCEPTION: + fprintf(stderr, "exception: %s\n", s->exception); + return EXIT_FAILURE; + + case NW_STATE_FATAL: + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +}