First commit

This commit is contained in:
Xavier Del Campo Romero 2023-11-26 22:43:30 +01:00
commit 9f8a6581b8
Signed by: xavi
GPG Key ID: 84FF3612A9BF43F2
75 changed files with 3684 additions and 0 deletions

38
CMakeLists.txt Normal file
View File

@ -0,0 +1,38 @@
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)
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})
check_compiler_flag(C ${f} supported)
message(STATUS "C compile option ${f} supported: ${supported}")
if(supported)
target_compile_options(${PROJECT_NAME} PRIVATE ${f})
endif()
endforeach()
if(NW_LOG)
target_compile_definitions(${PROJECT_NAME} PRIVATE ENABLE_LOG)
endif()
target_compile_features(${PROJECT_NAME} PUBLIC c_std_99)
set_target_properties(${PROJECT_NAME} PROPERTIES C_STANDARD 99 C_EXTENSIONS OFF)
add_subdirectory(src)
add_subdirectory(test)

373
LICENSE Normal file
View File

@ -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.

27
README.md Normal file
View File

@ -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/.
```

20
include/nanowasm/nw.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef NANOWASM_H
#define NANOWASM_H
#include <nanowasm/types.h>
#ifdef __cplusplus
extern "C"
{
#endif
int nw_load(const struct nw_mod_cfg *cfg, struct nw_mod *m);
int nw_start(const struct nw_inst_cfg *icfg, struct nw_inst *i);
enum nw_state nw_run(struct nw_inst *i);
int nw_stop(struct nw_inst *i);
#ifdef __cplusplus
}
#endif
#endif

132
include/nanowasm/types.h Normal file
View File

@ -0,0 +1,132 @@
#ifndef NANOWASM_TYPES_H
#define NANOWASM_TYPES_H
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C"
{
#endif
union nw_value
{
long i32;
long long i64;
float f32;
double f64;
/* TODO: define {de}serialization between Wasm and host. */
void *p;
};
enum nw_kind
{
NW_KIND_FUNCTION,
NW_KIND_TABLE,
NW_KIND_MEMORY,
NW_KIND_GLOBAL
};
struct nw_args
{
const union nw_value *args;
size_t n;
};
struct nw_buf
{
void *buf;
size_t n;
};
enum nw_state
{
NW_STATE_AGAIN,
NW_STATE_RETURNED,
NW_STATE_EXCEPTION,
NW_STATE_FATAL
};
struct nw_mod_cfg
{
const struct nw_import
{
enum nw_kind kind;
const char *module, *field;
union
{
struct
{
/* This must follow the format below, whitespace ignored:
* [return type]([param type][, ...])
* For convenience, the interpreter can do automatic checks on
* pointer arguments and/or return values if using '*' as type.
* Examples:
* "()"
* "i32(f32)"
* "*(f64, i32, *, i64)"
* As per WebAssembly MVP spec, multiple return types are not
* supported yet. */
const char *signature;
int (*fn)(const struct nw_args *params,
union nw_value *ret, void *user);
} function;
} u;
} *imports;
size_t n_imports;
const char *path;
void *user;
};
struct nw_interp_cfg
{
struct nw_buf stack, heap;
};
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

View File

@ -0,0 +1,9 @@
#ifndef FSTRING_H
#define FSTRING_H
#include <stdbool.h>
#include <stdio.h>
int fstrcmp(const char *str, FILE *f, bool abort);
#endif

23
private_include/interp.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef INTERP_H
#define INTERP_H
#include <nanowasm/nw.h>
#include <opcodes.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
struct interp_set
{
const enum opcode *opcodes;
size_t n;
};
extern const struct interp_set interp_initexpr_set;
int interp_start(const struct nw_interp_cfg *cfg, 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

9
private_include/leb128.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef LEB128_H
#define LEB128_H
#include <stdio.h>
int leb128_read_unsigned(FILE *f, unsigned maxbits, unsigned long long *out);
int leb128_read_signed(FILE *f, unsigned maxbits, long long *out);
#endif

12
private_include/log.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef LOG_H
#define LOG_H
#include <stdio.h>
#ifdef ENABLE_LOG
#define LOG(...) fprintf(stderr, __VA_ARGS__)
#else
#define LOG(...)
#endif
#endif

58
private_include/opcodes.h Normal file
View File

@ -0,0 +1,58 @@
#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

60
private_include/ops.h Normal file
View File

@ -0,0 +1,60 @@
#ifndef OPS_H
#define OPS_H
#include <nanowasm/nw.h>
#include <stdio.h>
int op_unreachable(struct nw_interp *i);
int op_nop(struct nw_interp *i);
int op_block(struct nw_interp *i);
int op_loop(struct nw_interp *i);
int op_if(struct nw_interp *i);
int op_else(struct nw_interp *i);
int op_end(struct nw_interp *i);
int op_br(struct nw_interp *i);
int op_br_if(struct nw_interp *i);
int op_br_table(struct nw_interp *i);
int op_return(struct nw_interp *i);
int op_call(struct nw_interp *i);
int op_call_indirect(struct nw_interp *i);
int op_get_local(struct nw_interp *i);
int op_set_local(struct nw_interp *i);
int op_tee_local(struct nw_interp *i);
int op_get_global(struct nw_interp *i);
int op_set_global(struct nw_interp *i);
int op_i32_load(struct nw_interp *i);
int op_i32_store(struct nw_interp *i);
int op_current_memory(struct nw_interp *i);
int op_i32_const(struct nw_interp *i);
int op_i64_const(struct nw_interp *i);
int op_f32_const(struct nw_interp *i);
int op_f64_const(struct nw_interp *i);
int op_i32_sub(struct nw_interp *i);
int check_unreachable(FILE *f);
int check_nop(FILE *f);
int check_block(FILE *f);
int check_loop(FILE *f);
int check_if(FILE *f);
int check_else(FILE *f);
int check_end(FILE *f);
int check_br(FILE *f);
int check_br_if(FILE *f);
int check_br_table(FILE *f);
int check_return(FILE *f);
int check_call(FILE *f);
int check_call_indirect(FILE *f);
int check_get_local(FILE *f);
int check_set_local(FILE *f);
int check_tee_local(FILE *f);
int check_get_global(FILE *f);
int check_set_global(FILE *f);
int check_i32_load(FILE *f);
int check_i32_store(FILE *f);
int check_i32_const(FILE *f);
int check_i64_const(FILE *f);
int check_f32_const(FILE *f);
int check_f64_const(FILE *f);
int check_i32_sub(FILE *f);
#endif

15
private_include/search.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef SEARCH_H
#define SEARCH_H
#include <nanowasm/nw.h>
#include <stdio.h>
struct search_fn
{
long start;
};
int search_exported_fn(const char *fn, const struct nw_mod *m, FILE *f,
struct search_fn *out);
#endif

View File

@ -0,0 +1,45 @@
#ifndef SECTIONS_H
#define SECTIONS_H
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
struct section
{
const struct nw_mod_cfg *cfg;
struct nw_mod *m;
FILE *f;
};
int section_custom_check(const struct section *s, unsigned long len);
int section_type_check(const struct section *s, unsigned long len);
int section_import_check(const struct section *s, unsigned long len);
int section_function_check(const struct section *s, unsigned long len);
int section_table_check(const struct section *s, unsigned long len);
int section_memory_check(const struct section *s, unsigned long len);
int section_global_check(const struct section *s, unsigned long len);
int section_export_check(const struct section *s, unsigned long len);
int section_start_check(const struct section *s, unsigned long len);
int section_element_check(const struct section *s, unsigned long len);
int section_code_check(const struct section *s, unsigned long len);
int section_data_check(const struct section *s, unsigned long len);
struct section_function
{
varuint32 type;
};
struct section_type
{
int dummy;
};
int section_function(const struct section *s, size_t idx,
struct section_function *f);
int section_type(const struct section *s, struct section_type *t);
int check_resizable_limits(FILE *f);
#endif

View File

@ -0,0 +1,24 @@
#ifndef WASM_TYPES_H
#define WASM_TYPES_H
#include <nanowasm/nw.h>
#include <stdbool.h>
#include <stdio.h>
typedef bool varuint1;
typedef signed char varint7;
typedef unsigned char varuint7;
typedef unsigned long varuint32;
typedef long varint32;
typedef unsigned long long varuint64;
typedef long long varint64;
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

13
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,13 @@
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)

32
src/fstrcmp.c Normal file
View File

@ -0,0 +1,32 @@
#include <fstring.h>
#include <log.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
int fstrcmp(const char *str, FILE *const f, const bool abort)
{
int ret = 0;
for (size_t i = 0; i < strlen(str); i++)
{
uint8_t byte;
if (!fread(&byte, sizeof byte, 1, f))
{
LOG("%s: fread(3) failed, feof=%d, ferror=%d\n",
__func__, feof(f), ferror(f));
return -1;
}
else if (byte != str[i])
{
if (abort)
return -1;
else
ret = -1;
}
}
return ret;
}

171
src/interp.c Normal file
View File

@ -0,0 +1,171 @@
#include <nanowasm/nw.h>
#include <log.h>
#include <interp.h>
#include <opcodes.h>
#include <ops.h>
#include <wasm_types.h>
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static const enum opcode initexpr_opcodes[] =
{
OP_NOP,
OP_END,
OP_I32_CONST,
OP_I64_CONST,
OP_F32_CONST,
OP_F64_CONST
};
const struct interp_set interp_initexpr_set =
{
.opcodes = initexpr_opcodes,
.n = sizeof initexpr_opcodes / sizeof *initexpr_opcodes
};
static int (*const ops[])(struct nw_interp *) =
{
[OP_UNREACHABLE] = op_unreachable,
[OP_NOP] = op_nop,
[OP_BLOCK] = op_block,
[OP_LOOP] = op_loop,
[OP_IF] = op_if,
[OP_ELSE] = op_else,
[OP_END] = op_end,
[OP_BR] = op_br,
[OP_BR_IF] = op_br_if,
[OP_BR_TABLE] = op_br_table,
[OP_RETURN] = op_return,
[OP_CALL] = op_call,
[OP_CALL_INDIRECT] = op_call_indirect,
[OP_GET_LOCAL] = op_get_local,
[OP_SET_LOCAL] = op_set_local,
[OP_TEE_LOCAL] = op_tee_local,
[OP_GET_GLOBAL] = op_get_global,
[OP_SET_GLOBAL] = op_set_global,
[OP_I32_LOAD] = op_i32_load,
[OP_I32_STORE] = op_i32_store,
[OP_I32_CONST] = op_i32_const,
[OP_F64_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_F64_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;
}

62
src/leb128.c Normal file
View File

@ -0,0 +1,62 @@
#include <leb128.h>
#include <log.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
/* The functions below are (somewhat heavily) modified versions of those
* provided by the wac project: https://github.com/kanaka/wac */
/*
* Copyright (C) Joel Martin <github@martintribe.org>
* The wac project is licensed under the MPL 2.0 (Mozilla Public License
* 2.0). The text of the MPL 2.0 license is included below and can be
* found at https://www.mozilla.org/MPL/2.0/
*/
static int read_LEB(FILE *const f, const unsigned maxbits,
const bool sign, unsigned long long *const out)
{
unsigned long long result = 0;
unsigned shift = 0, bcnt = 0;
uint8_t byte;
for (;;)
{
if (!fread(&byte, sizeof byte, 1, f))
return -1;
result |= (unsigned long long)(byte & 0x7f) << shift;
shift += 7;
if (!(byte & 0x80))
break;
else if (++bcnt > (maxbits + 7 - 1) / 7)
{
LOG("%s: overflow\n", __func__);
return -1;
}
}
if (sign && (shift < maxbits) && (byte & 0x40))
result |= -1ll << shift;
*out = result;
return 0;
}
int leb128_read_unsigned(FILE *const f, const unsigned maxbits,
unsigned long long *const out)
{
return read_LEB(f, maxbits, false, out);
}
int leb128_read_signed(FILE *const f, const unsigned maxbits,
long long *const out)
{
unsigned long long value;
const int ret = read_LEB(f, maxbits, true, &value);
*out = value;
return ret;
}

169
src/load.c Normal file
View File

@ -0,0 +1,169 @@
#include <log.h>
#include <nanowasm/nw.h>
#include <sections.h>
#include <wasm_types.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int check_magic(FILE *const f)
{
static const uint8_t m[] = {'\0', 'a', 's', 'm'};
uint8_t magic[sizeof m];
if (!fread(magic, sizeof magic, 1, f))
{
LOG("%s: fread(3) failed, feof=%d, ferror=%d\n",
__func__, feof(f), ferror(f));
return -1;
}
else if (memcmp(magic, m, sizeof magic))
{
LOG("%s: wrong magic bytes\n", __func__);
return -1;
}
return 0;
}
static int check_version(FILE *const f)
{
static const uint8_t v[] = {1, 0, 0, 0};
uint8_t version[sizeof v];
if (!fread(version, sizeof version, 1, f))
{
LOG("%s: fread(3) failed, feof=%d, ferror=%d\n", __func__, feof(f),
ferror(f));
return -1;
}
else if (memcmp(version, v, sizeof version))
{
LOG("%s: wrong version\n", __func__);
return -1;
}
return 0;
}
static int load_section(const struct nw_mod_cfg *const cfg,
struct nw_mod *const m, FILE *const f)
{
static int (*const fn[])(const struct section *, 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,
.m = m
};
varuint7 section;
varuint32 len;
int ret;
if (varuint7_read(f, &section))
{
if (feof(f))
return 0;
LOG("%s: varuint7_read failed\n", __func__);
return 1;
}
else if (section >= sizeof fn / sizeof *fn)
{
LOG("%s: invalid section %u\n", __func__, (unsigned)section);
return 1;
}
else if (varuint32_read(f, &len))
{
LOG("%s: varuint32_read failed\n", __func__);
return 1;
}
else if ((ret = fn[section](&s, 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;
}

6
src/op/CMakeLists.txt Normal file
View File

@ -0,0 +1,6 @@
add_subdirectory(call)
add_subdirectory(constants)
add_subdirectory(control_flow)
add_subdirectory(memory)
add_subdirectory(numeric)
add_subdirectory(variable_access)

View File

@ -0,0 +1,4 @@
target_sources(${PROJECT_NAME} PRIVATE
call.c
call_indirect.c
)

28
src/op/call/call.c Normal file
View File

@ -0,0 +1,28 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stdio.h>
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);
}

View File

@ -0,0 +1,40 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stdio.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varuint32 type_index;
varuint1 reserved;
if (varuint32_read(f, &type_index))
{
LOG("%s: varuint32_read failed\n", __func__);
return 1;
}
else if (varuint1_read(f, &reserved))
{
LOG("%s: varuint1_read failed\n", __func__);
return 1;
}
else if (reserved)
{
LOG("%s: unexpected non-zero reserved value %u\n",
__func__, (unsigned)reserved);
return 1;
}
return 0;
}
int op_call_indirect(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_call_indirect(FILE *const f)
{
return op(NULL, f);
}

View File

@ -0,0 +1,6 @@
target_sources(${PROJECT_NAME} PRIVATE
f32_const.c
f64_const.c
i32_const.c
i64_const.c
)

View File

@ -0,0 +1,29 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varuint32 value;
if (varuint32_read(f, &value))
{
LOG("%s: varuint32_read failed\n", __func__);
return -1;
}
return 0;
}
int op_f32_const(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_f32_const(FILE *const f)
{
return op(NULL, f);
}

View File

@ -0,0 +1,29 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varuint64 value;
if (varuint64_read(f, &value))
{
LOG("%s: varuint64_read failed\n", __func__);
return -1;
}
return 0;
}
int op_f64_const(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_f64_const(FILE *const f)
{
return op(NULL, f);
}

View File

@ -0,0 +1,29 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
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);
}

View File

@ -0,0 +1,29 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varint64 value;
if (varint64_read(f, &value))
{
LOG("%s: varint32_read failed\n", __func__);
return -1;
}
return 0;
}
int op_i64_const(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_i64_const(FILE *const f)
{
return op(NULL, f);
}

View File

@ -0,0 +1,13 @@
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
)

View File

@ -0,0 +1,29 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varint7 sig;
if (varint7_read(f, &sig))
{
LOG("%s: varint7_read failed\n", __func__);
return -1;
}
return 0;
}
int op_block(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_block(FILE *const f)
{
return op(NULL, f);
}

29
src/op/control_flow/br.c Normal file
View File

@ -0,0 +1,29 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varuint32 relative_depth;
if (varuint32_read(f, &relative_depth))
{
LOG("%s: varuint32_read failed\n", __func__);
return -1;
}
return 0;
}
int op_br(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_br(FILE *const f)
{
return op(NULL, f);
}

View File

@ -0,0 +1,29 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varuint32 relative_depth;
if (varuint32_read(f, &relative_depth))
{
LOG("%s: varuint32_read failed\n", __func__);
return 1;
}
return 0;
}
int op_br_if(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_br_if(FILE *const f)
{
return op(NULL, f);
}

View File

@ -0,0 +1,47 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varuint32 target_count, default_target;
if (varuint32_read(f, &target_count))
{
LOG("%s: varuint32_read target_count failed\n", __func__);
return -1;
}
for (varuint32 i = 0; i < target_count; i++)
{
varuint32 target_table;
if (varuint32_read(f, &target_table))
{
LOG("%s: varuint32_read target_table failed\n",
__func__);
return -1;
}
}
if (varuint32_read(f, &default_target))
{
LOG("%s: varuint32_read default_target failed\n", __func__);
return -1;
}
return 0;
}
int op_br_table(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_br_table(FILE *const f)
{
return op(NULL, f);
}

View File

@ -0,0 +1,12 @@
#include <ops.h>
#include <nanowasm/nw.h>
int op_else(struct nw_interp *const i)
{
return -1;
}
int check_else(FILE *const f)
{
return 0;
}

14
src/op/control_flow/end.c Normal file
View File

@ -0,0 +1,14 @@
#include <ops.h>
#include <nanowasm/nw.h>
#include <stdbool.h>
int op_end(struct nw_interp *const i)
{
i->exit = true;
return 0;
}
int check_end(FILE *const f)
{
return 0;
}

27
src/op/control_flow/if.c Normal file
View File

@ -0,0 +1,27 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varint7 sig;
if (varint7_read(f, &sig))
{
LOG("%s: varint7_read failed\n", __func__);
return -1;
}
return 0;
}
int op_if(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_if(FILE *const f)
{
return op(NULL, f);
}

View File

@ -0,0 +1,27 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varint7 sig;
if (varint7_read(f, &sig))
{
LOG("%s: varint7_read failed\n", __func__);
return -1;
}
return 0;
}
int op_loop(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_loop(FILE *const f)
{
return op(NULL, f);
}

13
src/op/control_flow/nop.c Normal file
View File

@ -0,0 +1,13 @@
#include <ops.h>
#include <nanowasm/nw.h>
#include <interp.h>
int op_nop(struct nw_interp *const i)
{
return 0;
}
int check_nop(FILE *const f)
{
return 0;
}

View File

@ -0,0 +1,13 @@
#include <ops.h>
#include <nanowasm/nw.h>
#include <stdio.h>
int op_return(struct nw_interp *const i)
{
return -1;
}
int check_return(FILE *const f)
{
return 0;
}

View File

@ -0,0 +1,16 @@
#include <ops.h>
#include <nanowasm/nw.h>
#include <stdbool.h>
#include <stdio.h>
int op_unreachable(struct nw_interp *const i)
{
i->exception = "Unreachable instruction";
i->exit = true;
return 1;
}
int check_unreachable(FILE *const f)
{
return 0;
}

View File

@ -0,0 +1,5 @@
target_sources(${PROJECT_NAME} PRIVATE
current_memory.c
i32_load.c
i32_store.c
)

View File

@ -0,0 +1,35 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varuint1 reserved;
if (varuint1_read(f, &reserved))
{
LOG("%s: varuint1_read failed\n", __func__);
return 1;
}
else if (reserved)
{
LOG("%s: unexpected non-zero reserved value %u\n",
__func__, (unsigned)reserved);
return 1;
}
return 0;
}
int op_current_memory(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_current_memory(FILE *const f)
{
return op(NULL, f);
}

34
src/op/memory/i32_load.c Normal file
View File

@ -0,0 +1,34 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
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);
}

34
src/op/memory/i32_store.c Normal file
View File

@ -0,0 +1,34 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
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);
}

View File

@ -0,0 +1,3 @@
target_sources(${PROJECT_NAME} PRIVATE
i32_sub.c
)

16
src/op/numeric/i32_sub.c Normal file
View File

@ -0,0 +1,16 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
int op_i32_sub(struct nw_interp *const i)
{
return -1;
}
int check_i32_sub(FILE *const f)
{
return 0;
}

View File

@ -0,0 +1,7 @@
target_sources(${PROJECT_NAME} PRIVATE
get_global.c
get_local.c
set_global.c
set_local.c
tee_local.c
)

View File

@ -0,0 +1,29 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varuint32 global_index;
if (varuint32_read(f, &global_index))
{
LOG("%s: varuint32_read failed\n", __func__);
return 1;
}
return 0;
}
int op_get_global(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_get_global(FILE *const f)
{
return op(NULL, f);
}

View File

@ -0,0 +1,29 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varuint32 local_index;
if (varuint32_read(f, &local_index))
{
LOG("%s: varuint32_read failed\n", __func__);
return 1;
}
return 0;
}
int op_get_local(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_get_local(FILE *const f)
{
return op(NULL, f);
}

View File

@ -0,0 +1,29 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varuint32 global_index;
if (varuint32_read(f, &global_index))
{
LOG("%s: varuint32_read failed\n", __func__);
return 1;
}
return 0;
}
int op_set_global(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_set_global(FILE *const f)
{
return op(NULL, f);
}

View File

@ -0,0 +1,29 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varuint32 local_index;
if (varuint32_read(f, &local_index))
{
LOG("%s: varuint32_read failed\n", __func__);
return 1;
}
return 0;
}
int op_set_local(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_set_local(FILE *const f)
{
return op(NULL, f);
}

View File

@ -0,0 +1,29 @@
#include <log.h>
#include <ops.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <stddef.h>
#include <stdio.h>
static int op(struct nw_interp *const i, FILE *const f)
{
varuint32 local_index;
if (varuint32_read(f, &local_index))
{
LOG("%s: varuint32_read failed\n", __func__);
return 1;
}
return 0;
}
int op_tee_local(struct nw_interp *const i)
{
return op(i, i->f);
}
int check_tee_local(FILE *const f)
{
return op(NULL, f);
}

11
src/run.c Normal file
View File

@ -0,0 +1,11 @@
#include <nanowasm/nw.h>
#include <log.h>
#include <search.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
enum nw_state nw_run(struct nw_inst *const i)
{
return NW_STATE_AGAIN;
}

109
src/search.c Normal file
View File

@ -0,0 +1,109 @@
#include <search.h>
#include <log.h>
#include <fstring.h>
#include <sections.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
static int ensure_export(const char *const fn,
const struct nw_mod *const m, FILE *const f, varuint32 *const out)
{
if (!m->sections.export)
{
LOG("%s: export section not found", __func__);
return -1;
}
else if (fseek(f, m->sections.export, SEEK_SET))
{
LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
return -1;
}
varuint32 count;
if (varuint32_read(f, &count))
{
LOG("%s: varuint32_read failed\n", __func__);
return -1;
}
for (varuint32 i = 0; i < count; i++)
{
varuint32 len;
bool found = false;
if (varuint32_read(f, &len))
{
LOG("%s: varuint32_read failed\n", __func__);
return -1;
}
else if (!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)
{
if (!m->sections.function)
{
LOG("%s: function section not found", __func__);
return -1;
}
LOG("%s: index=%ju\n", __func__, (uintmax_t)index);
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;
}

View File

@ -0,0 +1,15 @@
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
)

238
src/section/code.c Normal file
View File

@ -0,0 +1,238 @@
#include <log.h>
#include <nanowasm/nw.h>
#include <opcodes.h>
#include <interp.h>
#include <sections.h>
#include <wasm_types.h>
#include <errno.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
static int check_body(FILE *const f, const unsigned long n)
{
unsigned long rem = n;
while (rem)
{
const long before = ftell(f);
uint8_t byte;
if (before < 0)
{
LOG("%s: ftell(3) before: %s\n", __func__, strerror(errno));
return -1;
}
else if (!fread(&byte, sizeof byte, 1, f))
{
LOG("%s: fread(3) failed, feof=%d, ferror=%d\n",
__func__, feof(f), ferror(f));
return -1;
}
else if (rem == 1 && byte != OP_END)
{
LOG("%s: unexpected opcode %#" PRIx8 " at body end\n",
__func__, byte);
return -1;
}
else if (interp_check_opcode(byte, f))
{
LOG("%s: interp_check_opcode failed\n", __func__);
return -1;
}
const long after = ftell(f);
if (after < 0)
{
LOG("%s: ftell(3) after: %s\n", __func__, strerror(errno));
return -1;
}
rem -= after - before;
}
return 0;
}
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, const unsigned long len)
{
struct nw_mod *const m = s->m;
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;
}

36
src/section/common.c Normal file
View File

@ -0,0 +1,36 @@
#include <log.h>
#include <sections.h>
#include <wasm_types.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
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;
}

21
src/section/custom.c Normal file
View File

@ -0,0 +1,21 @@
#include <log.h>
#include <sections.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
int section_custom_check(const struct section *const s,
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;
}

8
src/section/data.c Normal file
View File

@ -0,0 +1,8 @@
#include <nanowasm/nw.h>
#include <sections.h>
#include <wasm_types.h>
int section_data_check(const struct section *const s, const unsigned long len)
{
return -1;
}

8
src/section/element.c Normal file
View File

@ -0,0 +1,8 @@
#include <sections.h>
#include <nanowasm/nw.h>
int section_element_check(const struct section *const s,
const unsigned long len)
{
return -1;
}

153
src/section/export.c Normal file
View File

@ -0,0 +1,153 @@
#include <log.h>
#include <sections.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
static int check_string(FILE *const f)
{
varuint32 len;
if (varuint32_read(f, &len))
{
LOG("%s: varuint32_read failed\n", __func__);
return -1;
}
for (varuint32 i = 0; i < len; i++)
{
uint8_t byte;
if (!fread(&byte, sizeof byte, 1, f))
{
LOG("%s: fread(3) failed, feof=%d, ferror=%d\n",
__func__, feof(f), ferror(f));
return -1;
}
}
return 0;
}
static int check_kind(FILE *const f)
{
uint8_t kind;
if (!fread(&kind, sizeof kind, 1, f))
{
LOG("%s: fread(3) failed: feof=%d, ferror=%d\n", __func__,
feof(f), ferror(f));
return -1;
}
return 0;
}
static int check_index(FILE *const f)
{
varuint32 index;
if (varuint32_read(f, &index))
{
LOG("%s: varuint32_read failed\n", __func__);
return -1;
}
return 0;
}
static int check_export_entry(FILE *const f)
{
if (check_string(f))
{
LOG("%s: check_string failed\n", __func__);
return -1;
}
else if (check_kind(f))
{
LOG("%s: check_kind failed\n", __func__);
return -1;
}
else if (check_index(f))
{
LOG("%s: check_index failed\n", __func__);
return -1;
}
return 0;
}
static int check(FILE *const f, const unsigned long len)
{
const long start = ftell(f);
varuint32 count;
if (start < 0)
{
LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
return -1;
}
else if (varuint32_read(f, &count))
{
LOG("%s: varuint32_read failed\n", __func__);
return -1;
}
for (varuint32 i = 0; i < count; i++)
if (check_export_entry(f))
{
LOG("%s: check_export_entry failed\n", __func__);
return -1;
}
const long end = ftell(f);
if (end < 0)
{
LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
return -1;
}
const unsigned long size = end - start;
if (size != len)
{
LOG("%s: size exceeded (%lu expected, got %lu)\n",
__func__, len, size);
return -1;
}
return 0;
}
int section_export_check(const struct section *const s,
const unsigned long len)
{
struct nw_mod *const m = s->m;
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;
}

96
src/section/function.c Normal file
View File

@ -0,0 +1,96 @@
#include <log.h>
#include <sections.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
static int run(FILE *const f, const size_t 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;
break;
}
}
return 0;
}
static int check(const struct section *const s, const unsigned long len)
{
struct nw_mod *const m = s->m;
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_check(const struct section *const s,
const unsigned long len)
{
return check(s, len);
}
int section_function(const struct section *const s, const size_t idx,
struct section_function *const f)
{
return run(s->f, idx, f);
}

101
src/section/global.c Normal file
View File

@ -0,0 +1,101 @@
#include <log.h>
#include <sections.h>
#include <nanowasm/nw.h>
#include <interp.h>
#include <wasm_types.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
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,
const unsigned long len)
{
struct nw_mod *const m = s->m;
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;
}

266
src/section/import.c Normal file
View File

@ -0,0 +1,266 @@
#include <sections.h>
#include <fstring.h>
#include <log.h>
#include <wasm_types.h>
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
static int dump_import_name(FILE *const f, const varuint32 len)
{
for (varuint32 i = 0; i < len; i++)
{
uint8_t byte;
if (!fread(&byte, sizeof byte, 1, f))
{
LOG("%s: fread(3) failed, feof=%d, ferror=%d\n",
__func__, feof(f), ferror(f));
return -1;
}
LOG("%c", (char)byte);
}
return 0;
}
static int check_module_string(const struct nw_mod_cfg *const cfg,
FILE *const f)
{
varuint32 len;
if (varuint32_read(f, &len))
{
LOG("%s: varuint32_read failed\n", __func__);
return -1;
}
for (size_t i = 0; i < cfg->n_imports; i++)
{
const struct nw_import *const im = &cfg->imports[i];
const char *const mod = im->module;
if (strlen(mod) == len)
{
const long offset = ftell(f);
if (offset < 0)
{
LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
return -1;
}
else if (!fstrcmp(mod, f, true))
return 0;
else if (fseek(f, offset, SEEK_SET))
{
LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
return -1;
}
}
}
LOG("%s: required imported module ", __func__);
dump_import_name(f, len);
LOG(" not found\n");
return -1;
}
static int check_field_string(const struct nw_mod_cfg *const cfg,
FILE *const f)
{
varuint32 len;
if (varuint32_read(f, &len))
{
LOG("%s: varuint32_read failed\n", __func__);
return -1;
}
for (size_t i = 0; i < cfg->n_imports; i++)
{
const struct nw_import *const im = &cfg->imports[i];
const char *const field = im->field;
if (strlen(field) == len)
{
const long offset = ftell(f);
if (offset < 0)
{
LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
return -1;
}
else if (!fstrcmp(field, f, true))
return 0;
else if (fseek(f, offset, SEEK_SET))
{
LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
return -1;
}
}
}
LOG("%s: required imported field ", __func__);
dump_import_name(f, len);
LOG(" not found\n");
return -1;
}
static int check_function_kind(FILE *const f)
{
varuint32 type;
if (varuint32_read(f, &type))
{
LOG("%s: varuint32_read failed\n", __func__);
return -1;
}
return 0;
}
static int check_table_kind(FILE *const f)
{
varint7 type;
if (varint7_read(f, &type))
{
LOG("%s: varint7_read failed\n", __func__);
return -1;
}
return 0;
}
static int check_memory_kind(FILE *const f)
{
/* TODO. */
return -1;
}
static int check_global_kind(FILE *const f)
{
/* TODO. */
return -1;
}
static int check_import_entry(const struct nw_mod_cfg *const cfg,
FILE *const f)
{
if (check_module_string(cfg, f))
{
LOG("%s: check_module_string failed\n", __func__);
return -1;
}
else if (check_field_string(cfg, f))
{
LOG("%s: check_field_string failed\n", __func__);
return -1;
}
uint8_t kind;
if (!fread(&kind, sizeof kind, 1, f))
{
LOG("%s: fread(3) failed, feof=%d, ferror=%d\n",
__func__, feof(f), ferror(f));
return -1;
}
static int (*const fn[])(FILE *) =
{
[NW_KIND_FUNCTION] = check_function_kind,
[NW_KIND_TABLE] = check_table_kind,
[NW_KIND_MEMORY] = check_memory_kind,
[NW_KIND_GLOBAL] = check_global_kind
};
if (kind >= sizeof fn / sizeof *fn)
{
LOG("%s: unexpected kind %#" PRIx8 "\n", __func__, kind);
return -1;
}
return fn[kind](f);
}
static int check(const struct nw_mod_cfg *const cfg, FILE *const f,
const unsigned long len)
{
const long start = ftell(f);
if (start < 0)
{
LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
return -1;
}
varuint32 count;
if (varuint32_read(f, &count))
{
LOG("%s: varuint32_read failed\n", __func__);
return -1;
}
for (varuint32 i = 0; i < count; i++)
if (check_import_entry(cfg, f))
{
LOG("%s: check_import_entry failed\n", __func__);
return -1;
}
const long end = ftell(f);
if (end < 0)
{
LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
return -1;
}
const unsigned long size = end - start;
if (size != len)
{
LOG("%s: size exceeded (%lu expected, got %lu)\n",
__func__, len, size);
return -1;
}
return 0;
}
int section_import_check(const struct section *const s,
const unsigned long len)
{
const struct nw_mod_cfg *const cfg = s->cfg;
struct nw_mod *const m = s->m;
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;
}

86
src/section/memory.c Normal file
View File

@ -0,0 +1,86 @@
#include <log.h>
#include <sections.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
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,
const unsigned long len)
{
struct nw_mod *const m = s->m;
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;
}

8
src/section/start.c Normal file
View File

@ -0,0 +1,8 @@
#include <sections.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
int section_start_check(const struct section *const s, const unsigned long len)
{
return -1;
}

100
src/section/table.c Normal file
View File

@ -0,0 +1,100 @@
#include <log.h>
#include <sections.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
static int check_table_type(FILE *const f)
{
enum {ANYFUNC = 0x70};
varint7 elem_type;
if (varint7_read(f, &elem_type))
{
LOG("%s: varint7_read failed\n", __func__);
return -1;
}
else if (elem_type != ANYFUNC)
{
LOG("%s: expected %x, got %x", __func__, ANYFUNC,
(int)elem_type);
return -1;
}
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, const unsigned long len)
{
struct nw_mod *const m = s->m;
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;
}

136
src/section/type.c Normal file
View File

@ -0,0 +1,136 @@
#include <log.h>
#include <sections.h>
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
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, &param_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, const unsigned long len)
{
struct nw_mod *const m = s->m;
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;
}

56
src/start.c Normal file
View File

@ -0,0 +1,56 @@
#include <nanowasm/nw.h>
#include <interp.h>
#include <log.h>
#include <search.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
int nw_start(const struct nw_inst_cfg *const icfg, struct nw_inst *const i)
{
int ret = -1;
const struct nw_mod *const m = icfg->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;
}

18
src/stop.c Normal file
View File

@ -0,0 +1,18 @@
#include <nanowasm/nw.h>
#include <log.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
int nw_stop(struct nw_inst *const i)
{
struct nw_interp *const in = &i->interp;
if (i && in->f && fclose(in->f))
{
LOG("%s: fclose(3): %s\n", __func__, strerror(errno));
return -1;
}
return 0;
}

80
src/types.c Normal file
View File

@ -0,0 +1,80 @@
#include <leb128.h>
#include <wasm_types.h>
#include <stdio.h>
int varuint1_read(FILE *const f, varuint1 *const out)
{
unsigned long long value;
if (leb128_read_unsigned(f, 1, &value))
return -1;
*out = value;
return 0;
}
int varint7_read(FILE *const f, varint7 *const out)
{
long long value;
if (leb128_read_signed(f, 7, &value))
return -1;
*out = value;
return 0;
}
int varuint7_read(FILE *const f, varuint7 *const out)
{
unsigned long long value;
if (leb128_read_unsigned(f, 7, &value))
return -1;
*out = value;
return 0;
}
int varuint32_read(FILE *const f, varuint32 *out)
{
unsigned long long value;
if (leb128_read_unsigned(f, 32, &value))
return -1;
*out = value;
return 0;
}
int varint32_read(FILE *const f, varint32 *const out)
{
long long value;
if (leb128_read_signed(f, 32, &value))
return -1;
*out = value;
return 0;
}
int varuint64_read(FILE *const f, varuint64 *const out)
{
unsigned long long value;
if (leb128_read_unsigned(f, 64, &value))
return -1;
*out = value;
return 0;
}
int varint64_read(FILE *const f, varint64 *const out)
{
long long value;
if (leb128_read_signed(f, 64, &value))
return -1;
*out = value;
return 0;
}

10
src/unload.c Normal file
View File

@ -0,0 +1,10 @@
#include <nanowasm/nw.h>
#include <wasm_types.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
void nw_unload(struct nw_mod *const m)
{
free(m);
}

5
test/CMakeLists.txt Normal file
View File

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.13)
project(test C)
add_executable(${PROJECT_NAME} main.c)
target_link_libraries(${PROJECT_NAME} PRIVATE nanowasm)
target_link_options(${PROJECT_NAME} PRIVATE -Wl,--gc-sections)

BIN
test/example.wasm Executable file

Binary file not shown.

83
test/main.c Normal file
View File

@ -0,0 +1,83 @@
#include <nanowasm/nw.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
static int proc_exit(const struct nw_args *const params,
union nw_value *const ret, void *const user)
{
return -1;
}
int main(int argc, char *argv[])
{
static const struct nw_import imports[] =
{
{
.kind = NW_KIND_FUNCTION,
.module = "wasi_snapshot_preview1",
.field = "proc_exit",
.u.function =
{
.signature = "i32()",
.fn = proc_exit
}
}
};
static const struct nw_mod_cfg cfg =
{
.path = "example.wasm",
.imports = imports,
.n_imports = sizeof imports / sizeof *imports
};
struct nw_mod m;
if (nw_load(&cfg, &m))
{
fprintf(stderr, "%s: nw_load failed\n", __func__);
return EXIT_FAILURE;
}
const struct nw_inst_cfg icfg =
{
.interp =
{
.stack = NW_BUF(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;
}