use zig-wasm2c for bootstrapping

This commit is contained in:
Jacob Young 2022-11-27 19:55:12 -05:00 committed by Andrew Kelley
parent a63305bc50
commit 1263346774
8 changed files with 4202 additions and 4581 deletions

View File

@ -179,8 +179,8 @@ set(ZIG_STD_DEST "${ZIG_LIB_DIR}/std")
set(ZIG_CONFIG_H_OUT "${CMAKE_BINARY_DIR}/config.h")
set(ZIG_CONFIG_ZIG_OUT "${CMAKE_BINARY_DIR}/config.zig")
set(STAGE1_SOURCES
"${CMAKE_SOURCE_DIR}/stage1/zig1.c"
set(ZIG_WASM2C_SOURCES
"${CMAKE_SOURCE_DIR}/stage1/wasm2c.c"
"${CMAKE_SOURCE_DIR}/stage1/zstd/lib/decompress/huf_decompress.c"
"${CMAKE_SOURCE_DIR}/stage1/zstd/lib/decompress/zstd_ddict.c"
"${CMAKE_SOURCE_DIR}/stage1/zstd/lib/decompress/zstd_decompress.c"
@ -709,30 +709,53 @@ target_link_libraries(zigcpp LINK_PUBLIC
)
if(MSVC)
set(ZIG1_COMPILE_FLAGS "/std:c99")
set(ZIG2_COMPILE_FLAGS "/std:c99")
set(ZIG_WASM2C_COMPILE_FLAGS "/std:c99 /O2")
set(ZIG1_COMPILE_FLAGS "/std:c99 /Os")
set(ZIG2_COMPILE_FLAGS "/std:c99 /O0")
set(ZIG2_LINK_FLAGS "/STACK:16777216")
else()
set(ZIG_WASM2C_COMPILE_FLAGS "-std=c99 -O2")
set(ZIG1_COMPILE_FLAGS "-std=c99")
set(ZIG2_COMPILE_FLAGS "-std=c99")
set(ZIG2_LINK_FLAGS "-Wl,-z,stack-size=0x10000000")
endif()
add_executable(zig1 ${STAGE1_SOURCES})
set_target_properties(zig1 PROPERTIES COMPILE_FLAGS ${ZIG1_COMPILE_FLAGS})
string(TOLOWER "${CMAKE_HOST_SYSTEM_PROCESSOR}-${CMAKE_HOST_SYSTEM_NAME}" HOST_TARGET_TRIPLE)
set(ZIG1_WASM_SOURCE "${CMAKE_SOURCE_DIR}/stage1/zig1.wasm.zst")
set(ZIG1_C_SOURCE "${CMAKE_BINARY_DIR}/zig1.c")
set(ZIG2_C_SOURCE "${CMAKE_BINARY_DIR}/zig2.c")
set(ZIG_COMPILER_RT_C_SOURCE "${CMAKE_BINARY_DIR}/compiler_rt.c")
add_executable(zig-wasm2c ${ZIG_WASM2C_SOURCES})
set_target_properties(zig-wasm2c PROPERTIES COMPILE_FLAGS ${ZIG_WASM2C_COMPILE_FLAGS})
target_include_directories(zig-wasm2c PUBLIC "${CMAKE_SOURCE_DIR}/stage1/zstd/lib")
target_compile_definitions(zig-wasm2c PRIVATE ZSTD_DISABLE_ASM)
add_custom_command(
OUTPUT "${ZIG1_C_SOURCE}"
COMMAND zig-wasm2c "${ZIG1_WASM_SOURCE}" "${ZIG1_C_SOURCE}"
DEPENDS zig-wasm2c "${ZIG1_WASM_SOURCE}"
COMMENT STATUS "Converting ${ZIG1_WASM_SOURCE} to ${ZIG1_C_SOURCE}"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
)
add_executable(zig1 ${ZIG1_C_SOURCE} "${CMAKE_SOURCE_DIR}/stage1/wasi.c")
set_target_properties(zig1 PROPERTIES
COMPILE_FLAGS ${ZIG1_COMPILE_FLAGS}
LINK_FLAGS ${ZIG2_LINK_FLAGS})
target_link_libraries(zig1 LINK_PUBLIC m)
target_include_directories(zig1 PUBLIC "${CMAKE_SOURCE_DIR}/stage1/zstd/lib")
target_compile_definitions(zig1 PRIVATE ZSTD_DISABLE_ASM)
set(ZIG2_C_SOURCE "${CMAKE_BINARY_DIR}/zig2.c")
set(ZIG1_WASM_ZST_SOURCE "${CMAKE_SOURCE_DIR}/stage1/zig1.wasm.zst")
set(BUILD_ZIG2_ARGS
"${CMAKE_SOURCE_DIR}/lib"
"${CMAKE_BINARY_DIR}"
zig2
"${ZIG1_WASM_ZST_SOURCE}"
build-exe src/main.zig -ofmt=c -lc
-OReleaseSmall
--name zig2 -femit-bin="${ZIG2_C_SOURCE}"
--pkg-begin build_options "${CMAKE_BINARY_DIR}/config.zig" --pkg-end
-target "${HOST_TARGET_TRIPLE}"
--color on
)
add_custom_command(
@ -743,14 +766,14 @@ add_custom_command(
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
)
set(ZIG_COMPILER_RT_C_SOURCE "${CMAKE_BINARY_DIR}/compiler_rt.c")
set(BUILD_COMPILER_RT_ARGS
"${CMAKE_SOURCE_DIR}/lib"
"${CMAKE_BINARY_DIR}"
compiler_rt
"${CMAKE_SOURCE_DIR}/stage1/zig1.wasm.zst"
build-obj lib/compiler_rt.zig -ofmt=c
-OReleaseSmall
--name compiler_rt -femit-bin="${ZIG_COMPILER_RT_C_SOURCE}"
--pkg-begin build_options "${CMAKE_BINARY_DIR}/config.zig" --pkg-end
-target "${HOST_TARGET_TRIPLE}"
--color on
)
add_custom_command(

164
stage1/FuncGen.h Normal file
View File

@ -0,0 +1,164 @@
#ifndef FUNC_GEN_H
#define FUNC_GEN_H
#include "panic.h"
#include "wasm.h"
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
struct Block {
uint32_t type;
uint32_t label;
uint32_t stack_i;
};
struct FuncGen {
int8_t *type;
uint32_t *stack;
struct Block *block;
uint32_t type_i;
uint32_t stack_i;
uint32_t block_i;
uint32_t type_len;
uint32_t stack_len;
uint32_t block_len;
};
static void FuncGen_init(struct FuncGen *self) {
memset(self, 0, sizeof(struct FuncGen));
}
static void FuncGen_reset(struct FuncGen *self) {
self->type_i = 0;
self->stack_i = 0;
self->block_i = 0;
}
static void FuncGen_free(struct FuncGen *self) {
free(self->block);
free(self->stack);
free(self->type);
}
static void FuncGen_outdent(struct FuncGen *self, FILE *out) {
for (uint32_t i = 0; i < self->block_i; i += 1) fputs(" ", out);
}
static void FuncGen_indent(struct FuncGen *self, FILE *out) {
FuncGen_outdent(self, out);
fputs(" ", out);
}
static void FuncGen_cont(struct FuncGen *self, FILE *out) {
FuncGen_indent(self, out);
fputs(" ", out);
}
static uint32_t FuncGen_localAlloc(struct FuncGen *self, int8_t type) {
if (self->type_i == self->type_len) {
self->type_len += 10;
self->type_len *= 2;
self->type = realloc(self->type, sizeof(int8_t) * self->type_len);
if (self->type == NULL) panic("out of memory");
}
uint32_t local_i = self->type_i;
self->type[local_i] = type;
self->type_i += 1;
return local_i;
}
static uint32_t FuncGen_localDeclare(struct FuncGen *self, FILE *out, enum WasmValType val_type) {
uint32_t local_i = FuncGen_localAlloc(self, (int8_t)val_type);
fprintf(out, "%s l%" PRIu32, WasmValType_toC(val_type), local_i);
return local_i;
}
static enum WasmValType FuncGen_localType(const struct FuncGen *self, uint32_t local_idx) {
return self->type[local_idx];
}
static void FuncGen_stackPush(struct FuncGen *self, FILE *out, enum WasmValType val_type) {
if (self->stack_i == self->stack_len) {
self->stack_len += 10;
self->stack_len *= 2;
self->stack = realloc(self->stack, sizeof(uint32_t) * self->stack_len);
if (self->stack == NULL) panic("out of memory");
}
FuncGen_indent(self, out);
fputs("const ", out);
self->stack[self->stack_i] = FuncGen_localDeclare(self, out, val_type);
self->stack_i += 1;
fputs(" = ", out);
}
static uint32_t FuncGen_stackAt(const struct FuncGen *self, uint32_t stack_idx) {
return self->stack[self->stack_i - 1 - stack_idx];
}
static uint32_t FuncGen_stackPop(struct FuncGen *self) {
self->stack_i -= 1;
return self->stack[self->stack_i];
}
static void FuncGen_label(struct FuncGen *self, FILE *out, uint32_t label) {
FuncGen_indent(self, out);
fprintf(out, "goto l%" PRIu32 ";\n", label);
FuncGen_outdent(self, out);
fprintf(out, "l%" PRIu32 ":;\n", label);
}
static void FuncGen_blockBegin(struct FuncGen *self, FILE *out, enum WasmOpcode kind, int64_t type) {
if (self->block_i == self->block_len) {
self->block_len += 10;
self->block_len *= 2;
self->block = realloc(self->block, sizeof(struct Block) * self->block_len);
if (self->block == NULL) panic("out of memory");
}
uint32_t label = FuncGen_localAlloc(self, type < 0 ? ~(int8_t)kind : (int8_t)kind);
FuncGen_indent(self, out);
if (kind == WasmOpcode_if) fprintf(out, "if (l%" PRIu32 ") ", FuncGen_stackPop(self));
fputs("{\n", out);
self->block[self->block_i].type = type < 0 ? ~type : type;
self->block[self->block_i].label = label;
self->block[self->block_i].stack_i = self->stack_i;
self->block_i += 1;
if (kind == WasmOpcode_loop) FuncGen_label(self, out, label);
}
static enum WasmOpcode FuncGen_blockKind(const struct FuncGen *self, uint32_t label_idx) {
int8_t kind = self->type[self->block[self->block_i - 1 - label_idx].label];
return (enum WasmOpcode)(kind < 0 ? ~kind : kind);
}
static int64_t FuncGen_blockType(const struct FuncGen *self, uint32_t label_idx) {
struct Block *block = &self->block[self->block_i - 1 - label_idx];
return self->type[block->label] < 0 ? ~(int64_t)block->type : (int64_t)block->type;
}
static uint32_t FuncGen_blockLabel(const struct FuncGen *self, uint32_t label_idx) {
return self->block[self->block_i - 1 - label_idx].label;
}
static void FuncGen_blockEnd(struct FuncGen *self, FILE *out) {
enum WasmOpcode kind = FuncGen_blockKind(self, 0);
uint32_t label = FuncGen_blockLabel(self, 0);
if (kind != WasmOpcode_loop) FuncGen_label(self, out, label);
self->block_i -= 1;
FuncGen_indent(self, out);
fputs("}\n", out);
if (self->stack_i != self->block[self->block_i].stack_i) {
FuncGen_indent(self, out);
fprintf(out, "// stack mismatch %u != %u\n", self->stack_i, self->block[self->block_i].stack_i);
}
self->stack_i = self->block[self->block_i].stack_i;
}
static bool FuncGen_done(const struct FuncGen *self) {
return self->block_i == 0;
}
#endif /* FUNC_GEN_H */

241
stage1/InputStream.h Normal file
View File

@ -0,0 +1,241 @@
#ifndef INPUT_STREAM_H
#define INPUT_STREAM_H
#include "panic.h"
#include "wasm.h"
#include <zstd.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct InputStream {
FILE *stream;
ZSTD_DStream *ds;
ZSTD_outBuffer out;
ZSTD_inBuffer in;
size_t pos;
};
static void InputStream_open(struct InputStream *self, const char *path) {
self->stream = fopen(path, "rb");
if (self->stream == NULL) panic("unable to open input file");
self->ds = ZSTD_createDStream();
if (self->ds == NULL) panic("unable to create zstd context");
size_t in_size = ZSTD_initDStream(self->ds);
if (ZSTD_isError(in_size)) panic(ZSTD_getErrorName(in_size));
self->out.size = ZSTD_DStreamOutSize();
self->out.dst = malloc(self->out.size + ZSTD_DStreamInSize());
if (self->out.dst == NULL) panic("unable to allocate input buffers");
self->out.pos = 0;
self->in.src = (const char *)self->out.dst + self->out.size;
self->in.size = fread((void *)self->in.src, 1, in_size, self->stream);
self->in.pos = 0;
self->pos = 0;
}
static void InputStream_close(struct InputStream *self) {
free(self->out.dst);
ZSTD_freeDStream(self->ds);
fclose(self->stream);
}
static bool InputStream_atEnd(struct InputStream *self) {
while (self->pos >= self->out.pos) {
self->out.pos = 0;
self->pos = 0;
size_t in_size = ZSTD_decompressStream(self->ds, &self->out, &self->in);
if (ZSTD_isError(in_size)) panic(ZSTD_getErrorName(in_size));
if (self->in.pos >= self->in.size) {
size_t max_in_size = ZSTD_DStreamInSize();
if (in_size > max_in_size) in_size = max_in_size;
self->in.size = fread((void *)self->in.src, 1, in_size, self->stream);
self->in.pos = 0;
if (self->in.pos >= self->in.size) return true;
}
}
return false;
}
static uint8_t InputStream_readByte(struct InputStream *self) {
if (InputStream_atEnd(self)) panic("unexpected end of input stream");
uint8_t value = ((uint8_t *)self->out.dst)[self->pos];
self->pos += 1;
return value;
}
static uint32_t InputStream_readLittle_u32(struct InputStream *self) {
uint32_t value = 0;
value |= (uint32_t)InputStream_readByte(self) << 0;
value |= (uint32_t)InputStream_readByte(self) << 8;
value |= (uint32_t)InputStream_readByte(self) << 16;
value |= (uint32_t)InputStream_readByte(self) << 24;
return value;
}
static uint64_t InputStream_readLittle_u64(struct InputStream *self) {
uint64_t value = 0;
value |= (uint64_t)InputStream_readByte(self) << 0;
value |= (uint64_t)InputStream_readByte(self) << 8;
value |= (uint64_t)InputStream_readByte(self) << 16;
value |= (uint64_t)InputStream_readByte(self) << 24;
value |= (uint64_t)InputStream_readByte(self) << 32;
value |= (uint64_t)InputStream_readByte(self) << 40;
value |= (uint64_t)InputStream_readByte(self) << 48;
value |= (uint64_t)InputStream_readByte(self) << 56;
return value;
}
static float InputStream_readLittle_f32(struct InputStream *self) {
uint32_t value = InputStream_readLittle_u32(self);
float result;
memcpy(&result, &value, sizeof(result));
return result;
}
static double InputStream_readLittle_f64(struct InputStream *self) {
uint64_t value = InputStream_readLittle_u64(self);
double result;
memcpy(&result, &value, sizeof(result));
return result;
}
static uint32_t InputStream_readLeb128_u32(struct InputStream *self) {
uint32_t value = 0;
uint8_t shift = 0;
uint8_t byte;
do {
byte = InputStream_readByte(self);
assert(shift < 32);
value |= (uint32_t)(byte & 0x7F) << shift;
shift += 7;
} while (byte & 0x80);
return value;
}
static int32_t InputStream_readLeb128_i32(struct InputStream *self) {
uint32_t value = 0;
uint8_t shift = 0;
uint8_t byte;
do {
byte = InputStream_readByte(self);
assert(shift < 64);
value |= (uint32_t)(byte & 0x7F) << shift;
shift += 7;
} while (byte & 0x80);
if (shift < 32) {
uint32_t mask = -((uint32_t)1 << shift);
if (byte & 0x40) value |= mask; else value &= ~mask;
}
return (int32_t)value;
}
static int64_t InputStream_readLeb128_u64(struct InputStream *self) {
uint64_t value = 0;
uint8_t shift = 0;
uint8_t byte;
do {
byte = InputStream_readByte(self);
assert(shift < 64);
value |= (uint64_t)(byte & 0x7F) << shift;
shift += 7;
} while (byte & 0x80);
return value;
}
static int64_t InputStream_readLeb128_i64(struct InputStream *self) {
uint64_t value = 0;
uint8_t shift = 0;
uint8_t byte;
do {
byte = InputStream_readByte(self);
assert(shift < 64);
value |= (uint64_t)(byte & 0x7F) << shift;
shift += 7;
} while (byte & 0x80);
if (shift < 64) {
uint64_t mask = -((uint64_t)1 << shift);
if (byte & 0x40) value |= mask; else value &= ~mask;
}
return (int64_t)value;
}
static char *InputStream_readName(struct InputStream *self) {
uint32_t len = InputStream_readLeb128_u32(self);
char *name = malloc(len + 1);
if (name == NULL) panic("out of memory");
for (uint32_t i = 0; i < len; ) {
if (InputStream_atEnd(self)) panic("unexpected end of input stream");
size_t remaining = self->out.pos - self->pos;
if (remaining > len - i) remaining = len - i;
memcpy(&name[i], &((char *)self->out.dst)[self->pos], remaining);
i += remaining;
self->pos += remaining;
}
name[len] = '\0';
return name;
}
static void InputStream_skipBytes(struct InputStream *self, size_t len) {
for (size_t i = 0; i < len; ) {
if (InputStream_atEnd(self)) panic("unexpected end of input stream");
size_t remaining = self->out.pos - self->pos;
if (remaining > len - i) remaining = len - i;
i += remaining;
self->pos += remaining;
}
}
static uint32_t InputStream_skipToSection(struct InputStream *self, uint8_t expected_id) {
while (true) {
uint8_t id = InputStream_readByte(self);
uint32_t size = InputStream_readLeb128_u32(self);
if (id == expected_id) return size;
InputStream_skipBytes(self, size);
}
}
struct ResultType {
uint32_t len;
int8_t types[1];
};
static struct ResultType *InputStream_readResultType(struct InputStream *self) {
uint32_t len = InputStream_readLeb128_u32(self);
struct ResultType *result_type = malloc(offsetof(struct ResultType, types) + sizeof(int8_t) * len);
if (result_type == NULL) panic("out of memory");
result_type->len = len;
for (uint32_t i = 0; i < len; i += 1) {
int64_t val_type = InputStream_readLeb128_i64(self);
switch (val_type) {
case WasmValType_i32: case WasmValType_i64:
case WasmValType_f32: case WasmValType_f64:
break;
default: panic("unsupported valtype");
}
result_type->types[i] = val_type;
}
return result_type;
}
struct Limits {
uint32_t min;
uint32_t max;
};
static struct Limits InputStream_readLimits(struct InputStream *self) {
struct Limits limits;
uint8_t kind = InputStream_readByte(self);
limits.min = InputStream_readLeb128_u32(self);
switch (kind) {
case 0x00: limits.max = UINT32_MAX; break;
case 0x01: limits.max = InputStream_readLeb128_u32(self); break;
default: panic("unsupported limit kind");
}
return limits;
}
#endif /* INPUT_STREAM_H */

12
stage1/panic.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef PANIC_H
#define PANIC_H
#include <stdio.h>
#include <stdlib.h>
static void panic(const char *reason) {
fprintf(stderr, "%s\n", reason);
abort();
}
#endif /* PANIC_H */

948
stage1/wasi.c Normal file
View File

@ -0,0 +1,948 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "panic.h"
#define LOG_TRACE 0
enum wasi_errno {
wasi_errno_success = 0,
wasi_errno_2big = 1,
wasi_errno_acces = 2,
wasi_errno_addrinuse = 3,
wasi_errno_addrnotavail = 4,
wasi_errno_afnosupport = 5,
wasi_errno_again = 6,
wasi_errno_already = 7,
wasi_errno_badf = 8,
wasi_errno_badmsg = 9,
wasi_errno_busy = 10,
wasi_errno_canceled = 11,
wasi_errno_child = 12,
wasi_errno_connaborted = 13,
wasi_errno_connrefused = 14,
wasi_errno_connreset = 15,
wasi_errno_deadlk = 16,
wasi_errno_destaddrreq = 17,
wasi_errno_dom = 18,
wasi_errno_dquot = 19,
wasi_errno_exist = 20,
wasi_errno_fault = 21,
wasi_errno_fbig = 22,
wasi_errno_hostunreach = 23,
wasi_errno_idrm = 24,
wasi_errno_ilseq = 25,
wasi_errno_inprogress = 26,
wasi_errno_intr = 27,
wasi_errno_inval = 28,
wasi_errno_io = 29,
wasi_errno_isconn = 30,
wasi_errno_isdir = 31,
wasi_errno_loop = 32,
wasi_errno_mfile = 33,
wasi_errno_mlink = 34,
wasi_errno_msgsize = 35,
wasi_errno_multihop = 36,
wasi_errno_nametoolong = 37,
wasi_errno_netdown = 38,
wasi_errno_netreset = 39,
wasi_errno_netunreach = 40,
wasi_errno_nfile = 41,
wasi_errno_nobufs = 42,
wasi_errno_nodev = 43,
wasi_errno_noent = 44,
wasi_errno_noexec = 45,
wasi_errno_nolck = 46,
wasi_errno_nolink = 47,
wasi_errno_nomem = 48,
wasi_errno_nomsg = 49,
wasi_errno_noprotoopt = 50,
wasi_errno_nospc = 51,
wasi_errno_nosys = 52,
wasi_errno_notconn = 53,
wasi_errno_notdir = 54,
wasi_errno_notempty = 55,
wasi_errno_notrecoverable = 56,
wasi_errno_notsock = 57,
wasi_errno_opnotsupp = 58,
wasi_errno_notty = 59,
wasi_errno_nxio = 60,
wasi_errno_overflow = 61,
wasi_errno_ownerdead = 62,
wasi_errno_perm = 63,
wasi_errno_pipe = 64,
wasi_errno_proto = 65,
wasi_errno_protonosupport = 66,
wasi_errno_prototype = 67,
wasi_errno_range = 68,
wasi_errno_rofs = 69,
wasi_errno_spipe = 70,
wasi_errno_srch = 71,
wasi_errno_stale = 72,
wasi_errno_timedout = 73,
wasi_errno_txtbsy = 74,
wasi_errno_xdev = 75,
wasi_errno_notcapable = 76,
};
enum wasi_oflags {
wasi_oflags_creat = 1 << 0,
wasi_oflags_directory = 1 << 1,
wasi_oflags_excl = 1 << 2,
wasi_oflags_trunc = 1 << 3,
};
enum wasi_rights {
wasi_rights_fd_datasync = 1ull << 0,
wasi_rights_fd_read = 1ull << 1,
wasi_rights_fd_seek = 1ull << 2,
wasi_rights_fd_fdstat_set_flags = 1ull << 3,
wasi_rights_fd_sync = 1ull << 4,
wasi_rights_fd_tell = 1ull << 5,
wasi_rights_fd_write = 1ull << 6,
wasi_rights_fd_advise = 1ull << 7,
wasi_rights_fd_allocate = 1ull << 8,
wasi_rights_path_create_directory = 1ull << 9,
wasi_rights_path_create_file = 1ull << 10,
wasi_rights_path_link_source = 1ull << 11,
wasi_rights_path_link_target = 1ull << 12,
wasi_rights_path_open = 1ull << 13,
wasi_rights_fd_readdir = 1ull << 14,
wasi_rights_path_readlink = 1ull << 15,
wasi_rights_path_rename_source = 1ull << 16,
wasi_rights_path_rename_target = 1ull << 17,
wasi_rights_path_filestat_get = 1ull << 18,
wasi_rights_path_filestat_set_size = 1ull << 19,
wasi_rights_path_filestat_set_times = 1ull << 20,
wasi_rights_fd_filestat_get = 1ull << 21,
wasi_rights_fd_filestat_set_size = 1ull << 22,
wasi_rights_fd_filestat_set_times = 1ull << 23,
wasi_rights_path_symlink = 1ull << 24,
wasi_rights_path_remove_directory = 1ull << 25,
wasi_rights_path_unlink_file = 1ull << 26,
wasi_rights_poll_fd_readwrite = 1ull << 27,
wasi_rights_sock_shutdown = 1ull << 28,
wasi_rights_sock_accept = 1ull << 29,
};
enum wasi_clockid {
wasi_clockid_realtime = 0,
wasi_clockid_monotonic = 1,
wasi_clockid_process_cputime_id = 2,
wasi_clockid_thread_cputime_id = 3,
};
enum wasi_filetype {
wasi_filetype_unknown = 0,
wasi_filetype_block_device = 1,
wasi_filetype_character_device = 2,
wasi_filetype_directory = 3,
wasi_filetype_regular_file = 4,
wasi_filetype_socket_dgram = 5,
wasi_filetype_socket_stream = 6,
wasi_filetype_symbolic_link = 7,
};
enum wasi_fdflags {
wasi_fdflags_append = 1 << 0,
wasi_fdflags_dsync = 1 << 1,
wasi_fdflags_nonblock = 1 << 2,
wasi_fdflags_rsync = 1 << 3,
wasi_fdflags_sync = 1 << 4,
};
struct wasi_filestat {
uint64_t dev;
uint64_t ino;
uint64_t filetype;
uint64_t nlink;
uint64_t size;
uint64_t atim;
uint64_t mtim;
uint64_t ctim;
};
struct wasi_fdstat {
uint16_t fs_filetype;
uint16_t fs_flags;
uint32_t padding;
uint64_t fs_rights_inheriting;
};
struct wasi_ciovec {
uint32_t ptr;
uint32_t len;
};
extern uint8_t **const wasm_memory;
extern void wasm__start(void);
static int global_argc;
static char **global_argv;
static uint32_t de_len;
struct DirEntry {
enum wasi_filetype filetype;
time_t atim;
time_t mtim;
time_t ctim;
char *guest_path;
char *host_path;
} *des;
static uint32_t fd_len;
static struct FileDescriptor {
uint32_t de;
FILE *stream;
} *fds;
static void *dupe(const void *data, size_t len) {
void *copy = malloc(len);
if (copy == NULL) panic("out of memory");
memcpy(copy, data, len);
return copy;
}
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "usage: %s <zig-lib-path> <args...>\n", argv[0]);
return 1;
}
global_argc = argc;
global_argv = argv;
time_t now = time(NULL);
srand((unsigned)now);
de_len = 4;
des = calloc(de_len, sizeof(struct DirEntry));
if (des == NULL) panic("out of memory");
des[0].filetype = wasi_filetype_character_device;
des[1].filetype = wasi_filetype_directory;
des[1].guest_path = dupe(".", sizeof("."));
des[1].host_path = dupe(".", sizeof("."));
des[2].filetype = wasi_filetype_directory;
des[2].guest_path = dupe("/cache", sizeof("/cache"));
des[2].atim = now;
des[2].mtim = now;
des[2].ctim = now;
des[3].filetype = wasi_filetype_directory;
des[3].guest_path = dupe("/lib", sizeof("/lib"));
des[3].host_path = dupe(argv[1], strlen(argv[1]));
fd_len = 6;
fds = calloc(sizeof(struct FileDescriptor), fd_len);
if (fds == NULL) panic("out of memory");
fds[0].stream = stdin;
fds[1].stream = stdout;
fds[2].stream = stderr;
fds[3].de = 1;
fds[4].de = 2;
fds[5].de = 3;
wasm__start();
}
static enum wasi_errno DirEntry_create(uint32_t dir_fd, const char *path, uint32_t path_len, enum wasi_filetype filetype, time_t tim, uint32_t *res_de) {
if (path[0] != '/') {
if (dir_fd >= fd_len || fds[dir_fd].de >= de_len) return wasi_errno_badf;
if (des[fds[dir_fd].de].filetype != wasi_filetype_directory) return wasi_errno_notdir;
}
struct DirEntry *new_des = realloc(des, (de_len + 1) * sizeof(struct DirEntry));
if (new_des == NULL) return wasi_errno_nomem;
des = new_des;
struct DirEntry *de = &des[de_len];
de->filetype = filetype;
de->atim = tim;
de->mtim = tim;
de->ctim = tim;
if (path[0] == '/') {
de->guest_path = malloc(path_len + 1);
if (de->guest_path == NULL) return wasi_errno_nomem;
memcpy(&de->guest_path[0], path, path_len);
de->guest_path[path_len] = '\0';
de->host_path = malloc(path_len + 1);
if (de->host_path == NULL) return wasi_errno_nomem;
memcpy(&de->host_path[0], path, path_len);
de->host_path[path_len] = '\0';
} else {
const struct DirEntry *dir_de = &des[fds[dir_fd].de];
if (dir_de->guest_path != NULL) {
size_t dir_guest_path_len = strlen(dir_de->guest_path);
de->guest_path = malloc(dir_guest_path_len + 1 + path_len + 1);
if (de->guest_path == NULL) return wasi_errno_nomem;
memcpy(&de->guest_path[0], dir_de->guest_path, dir_guest_path_len);
de->guest_path[dir_guest_path_len] = '/';
memcpy(&de->guest_path[dir_guest_path_len + 1], path, path_len);
de->guest_path[dir_guest_path_len + 1 + path_len] = '\0';
} else de->guest_path = NULL;
if (dir_de->host_path != NULL) {
size_t dir_host_path_len = strlen(dir_de->host_path);
de->host_path = malloc(dir_host_path_len + 1 + path_len + 1);
if (de->host_path == NULL) { free(de->guest_path); return wasi_errno_nomem; }
memcpy(&de->host_path[0], dir_de->host_path, dir_host_path_len);
de->host_path[dir_host_path_len] = '/';
memcpy(&de->host_path[dir_host_path_len + 1], path, path_len);
de->host_path[dir_host_path_len + 1 + path_len] = '\0';
} else de->host_path = NULL;
}
if (res_de != NULL) *res_de = de_len;
de_len += 1;
return wasi_errno_success;
}
static enum wasi_errno DirEntry_lookup(uint32_t dir_fd, uint32_t flags, const char *path, uint32_t path_len, uint32_t *res_de) {
(void)flags;
if (path[0] == '/') {
for (uint32_t de = 0; de < de_len; de += 1) {
if (des[de].guest_path == NULL) continue;
if (memcmp(&des[de].guest_path[0], path, path_len) != 0) continue;
if (des[de].guest_path[path_len] != '\0') continue;
if (res_de != NULL) *res_de = de;
return wasi_errno_success;
}
} else {
if (dir_fd >= fd_len || fds[dir_fd].de >= de_len) return wasi_errno_badf;
const struct DirEntry *dir_de = &des[fds[dir_fd].de];
if (dir_de->filetype != wasi_filetype_directory) return wasi_errno_notdir;
size_t dir_guest_path_len = strlen(dir_de->guest_path);
for (uint32_t de = 0; de < de_len; de += 1) {
if (des[de].guest_path == NULL) continue;
if (memcmp(&des[de].guest_path[0], dir_de->guest_path, dir_guest_path_len) != 0) continue;
if (des[de].guest_path[dir_guest_path_len] != '/') continue;
if (memcmp(&des[de].guest_path[dir_guest_path_len + 1], path, path_len) != 0) continue;
if (des[de].guest_path[dir_guest_path_len + 1 + path_len] != '\0') continue;
if (res_de != NULL) *res_de = de;
return wasi_errno_success;
}
}
return wasi_errno_noent;
}
static void DirEntry_filestat(uint32_t de, struct wasi_filestat *res_filestat) {
res_filestat->dev = 0;
res_filestat->ino = de;
res_filestat->filetype = des[de].filetype;
res_filestat->nlink = 1;
res_filestat->size = 0;
res_filestat->atim = des[de].atim * UINT64_C(1000000000);
res_filestat->mtim = des[de].mtim * UINT64_C(1000000000);
res_filestat->ctim = des[de].ctim * UINT64_C(1000000000);
}
static void DirEntry_unlink(uint32_t de) {
free(des[de].guest_path);
des[de].guest_path = NULL;
free(des[de].host_path);
des[de].host_path = NULL;
}
uint32_t wasi_snapshot_preview1_args_sizes_get(uint32_t argv_size, uint32_t argv_buf_size) {
uint8_t *const m = *wasm_memory;
uint32_t *argv_size_ptr = (uint32_t *)&m[argv_size];
uint32_t *argv_buf_size_ptr = (uint32_t *)&m[argv_buf_size];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_args_sizes_get()\n");
#endif
int c_argc = global_argc;
char **c_argv = global_argv;
uint32_t size = 0;
for (int i = 0; i < c_argc; i += 1) {
if (i == 1) continue;
size += strlen(c_argv[i]) + 1;
}
*argv_size_ptr = c_argc - 1;
*argv_buf_size_ptr = size;
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_args_get(uint32_t argv, uint32_t argv_buf) {
uint8_t *const m = *wasm_memory;
uint32_t *argv_ptr = (uint32_t *)&m[argv];
char *argv_buf_ptr = (char *)&m[argv_buf];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_args_get()\n");
#endif
int c_argc = global_argc;
char **c_argv = global_argv;
uint32_t dst_i = 0;
uint32_t argv_buf_i = 0;
for (int src_i = 0; src_i < c_argc; src_i += 1) {
if (src_i == 1) continue;
argv_ptr[dst_i] = argv_buf + argv_buf_i;
dst_i += 1;
strcpy(&argv_buf_ptr[argv_buf_i], c_argv[src_i]);
argv_buf_i += strlen(c_argv[src_i]) + 1;
}
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_fd_prestat_get(uint32_t fd, uint32_t res_prestat) {
uint8_t *const m = *wasm_memory;
uint32_t *res_prestat_ptr = (uint32_t *)&m[res_prestat];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_fd_prestat_get(%u)\n", fd);
#endif
if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
res_prestat_ptr[0] = 0;
res_prestat_ptr[1] = strlen(des[fds[fd].de].guest_path);
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_fd_prestat_dir_name(uint32_t fd, uint32_t path, uint32_t path_len) {
uint8_t *const m = *wasm_memory;
char *path_ptr = (char *)&m[path];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_fd_prestat_dir_name(%u, \"%.*s\")\n", fd, (int)path_len, path_ptr);
#endif
if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
strncpy(path_ptr, des[fds[fd].de].guest_path, path_len);
return wasi_errno_success;
}
void wasi_snapshot_preview1_proc_exit(uint32_t rval) {
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_proc_exit(%u)\n", rval);
#endif
exit(rval);
}
uint32_t wasi_snapshot_preview1_fd_close(uint32_t fd) {
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_fd_close(%u)\n", fd);
#endif
if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
if (fds[fd].stream != NULL) fclose(fds[fd].stream);
fds[fd].de = ~0;
fds[fd].stream = NULL;
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_path_create_directory(uint32_t fd, uint32_t path, uint32_t path_len) {
uint8_t *const m = *wasm_memory;
const char *path_ptr = (const char *)&m[path];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_path_create_directory(%u, \"%.*s\")\n", fd, (int)path_len, path_ptr);
#endif
enum wasi_errno lookup_errno = DirEntry_lookup(fd, 0, path_ptr, path_len, NULL);
switch (lookup_errno) {
case wasi_errno_success: return wasi_errno_exist;
case wasi_errno_noent: break;
default: return lookup_errno;
}
return DirEntry_create(fd, path_ptr, path_len, wasi_filetype_directory, time(NULL), NULL);
}
uint32_t wasi_snapshot_preview1_fd_read(uint32_t fd, uint32_t iovs, uint32_t iovs_len, uint32_t res_size) {
uint8_t *const m = *wasm_memory;
struct wasi_ciovec *iovs_ptr = (struct wasi_ciovec *)&m[iovs];
uint32_t *res_size_ptr = (uint32_t *)&m[res_size];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_fd_read(%u, 0x%X, %u)\n", fd, iovs, iovs_len);
#endif
if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
switch (des[fds[fd].de].filetype) {
case wasi_filetype_character_device: break;
case wasi_filetype_regular_file: break;
case wasi_filetype_directory: return wasi_errno_inval;
default: panic("unimplemented");
}
size_t size = 0;
for (uint32_t i = 0; i < iovs_len; i += 1) {
size_t read_size = 0;
if (fds[fd].stream != NULL)
read_size = fread(&m[iovs_ptr[i].ptr], 1, iovs_ptr[i].len, fds[fd].stream);
else
panic("unimplemented");
size += read_size;
if (read_size < iovs_ptr[i].len) break;
}
if (size > 0) des[fds[fd].de].atim = time(NULL);
*res_size_ptr = size;
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_fd_filestat_get(uint32_t fd, uint32_t res_filestat) {
uint8_t *const m = *wasm_memory;
struct wasi_filestat *res_filestat_ptr = (struct wasi_filestat *)&m[res_filestat];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_fd_filestat_get(%u)\n", fd);
#endif
if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
DirEntry_filestat(fds[fd].de, res_filestat_ptr);
if (des[fds[fd].de].filetype != wasi_filetype_regular_file) return wasi_errno_success;
if (fds[fd].stream == NULL) return wasi_errno_success;
fpos_t pos;
if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
if (fseek(fds[fd].stream, 0, SEEK_END) < 0) return wasi_errno_io;
long size = ftell(fds[fd].stream);
if (size < 0) return wasi_errno_io;
res_filestat_ptr->size = size;
if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_path_rename(uint32_t fd, uint32_t old_path, uint32_t old_path_len, uint32_t new_fd, uint32_t new_path, uint32_t new_path_len) {
uint8_t *const m = *wasm_memory;
const char *old_path_ptr = (const char *)&m[old_path];
const char *new_path_ptr = (const char *)&m[new_path];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_path_rename(%u, \"%.*s\", %u, \"%.*s\")\n", fd, (int)old_path_len, old_path_ptr, new_fd, (int)new_path_len, new_path_ptr);
#endif
uint32_t old_de;
enum wasi_errno old_lookup_errno = DirEntry_lookup(fd, 0, old_path_ptr, old_path_len, &old_de);
if (old_lookup_errno != wasi_errno_success) return old_lookup_errno;
DirEntry_unlink(old_de);
uint32_t de;
enum wasi_errno new_lookup_errno = DirEntry_lookup(new_fd, 0, new_path_ptr, new_path_len, &de);
switch (new_lookup_errno) {
case wasi_errno_success: DirEntry_unlink(de); break;
case wasi_errno_noent: break;
default: return new_lookup_errno;
}
uint32_t new_de;
enum wasi_errno create_errno =
DirEntry_create(new_fd, new_path_ptr, new_path_len, des[old_de].filetype, 0, &new_de);
if (create_errno != wasi_errno_success) return create_errno;
des[new_de].atim = des[old_de].atim;
des[new_de].mtim = des[old_de].mtim;
des[new_de].ctim = time(NULL);
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_fd_filestat_set_size(uint32_t fd, uint64_t size) {
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_fd_filestat_set_size(%u, %llu)\n", fd, (unsigned long long)size);
#endif
if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
if (des[fds[fd].de].filetype != wasi_filetype_regular_file) return wasi_errno_inval;
if (fds[fd].stream == NULL) return wasi_errno_success;
fpos_t pos;
if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
if (fseek(fds[fd].stream, 0, SEEK_END) < 0) return wasi_errno_io;
long old_size = ftell(fds[fd].stream);
if (old_size < 0) return wasi_errno_io;
if (size > (unsigned long)old_size) {
if (fseek(fds[fd].stream, size - 1, SEEK_SET) < 0) return wasi_errno_io;
fputc(0, fds[fd].stream);
} else if (size < (unsigned long)old_size) panic("unimplemented");
if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_fd_pwrite(uint32_t fd, uint32_t iovs, uint32_t iovs_len, uint64_t offset, uint32_t res_size) {
uint8_t *const m = *wasm_memory;
struct wasi_ciovec *iovs_ptr = (struct wasi_ciovec *)&m[iovs];
uint32_t *res_size_ptr = (uint32_t *)&m[res_size];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_fd_pwrite(%u, 0x%X, %u)\n", fd, iovs, iovs_len);
#endif
if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
switch (des[fds[fd].de].filetype) {
case wasi_filetype_character_device: break;
case wasi_filetype_regular_file: break;
case wasi_filetype_directory: return wasi_errno_inval;
default: panic("unimplemented");
}
fpos_t pos;
if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io;
size_t size = 0;
for (uint32_t i = 0; i < iovs_len; i += 1) {
size_t written_size = 0;
if (fds[fd].stream != NULL)
written_size = fwrite(&m[iovs_ptr[i].ptr], 1, iovs_ptr[i].len, fds[fd].stream);
else
written_size = iovs_ptr[i].len;
size += written_size;
if (written_size < iovs_ptr[i].len) break;
}
if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
if (size > 0) {
time_t now = time(NULL);
des[fds[fd].de].atim = now;
des[fds[fd].de].mtim = now;
}
*res_size_ptr = size;
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_random_get(uint32_t buf, uint32_t buf_len) {
uint8_t *const m = *wasm_memory;
uint8_t *buf_ptr = (uint8_t *)&m[buf];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_random_get(%u)\n", buf_len);
#endif
for (uint32_t i = 0; i < buf_len; i += 1) buf_ptr[i] = (uint8_t)rand();
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_fd_filestat_set_times(uint32_t fd, uint64_t atim, uint64_t mtim, uint32_t fst_flags) {
(void)fd;
(void)atim;
(void)mtim;
(void)fst_flags;
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_fd_filestat_set_times(%u, %llu, %llu, 0x%X)\n", fd, (unsigned long long)atim, (unsigned long long)mtim, fst_flags);
#endif
panic("unimplemented");
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_environ_sizes_get(uint32_t environ_size, uint32_t environ_buf_size) {
(void)environ_size;
(void)environ_buf_size;
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_environ_sizes_get()\n");
#endif
panic("unimplemented");
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_environ_get(uint32_t environ, uint32_t environ_buf) {
(void)environ;
(void)environ_buf;
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_environ_get()\n");
#endif
panic("unimplemented");
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_path_filestat_get(uint32_t fd, uint32_t flags, uint32_t path, uint32_t path_len, uint32_t res_filestat) {
uint8_t *const m = *wasm_memory;
const char *path_ptr = (const char *)&m[path];
struct wasi_filestat *res_filestat_ptr = (struct wasi_filestat *)&m[res_filestat];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_path_filestat_get(%u, 0x%X, \"%.*s\")\n", fd, flags, (int)path_len, path_ptr);
#endif
uint32_t de;
enum wasi_errno lookup_errno = DirEntry_lookup(fd, flags, path_ptr, path_len, &de);
if (lookup_errno != wasi_errno_success) return lookup_errno;
DirEntry_filestat(de, res_filestat_ptr);
if (des[de].filetype == wasi_filetype_regular_file && des[de].host_path != NULL) {
FILE *stream = fopen(des[de].host_path, "rb");
if (stream != NULL) {
if (fseek(stream, 0, SEEK_END) >= 0) {
long size = ftell(stream);
if (size >= 0) res_filestat_ptr->size = size;
}
fclose(stream);
}
}
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_fd_fdstat_get(uint32_t fd, uint32_t res_fdstat) {
(void)fd;
(void)res_fdstat;
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_fd_fdstat_get(%u)\n", fd);
#endif
panic("unimplemented");
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_fd_readdir(uint32_t fd, uint32_t buf, uint32_t buf_len, uint64_t cookie, uint32_t res_size) {
(void)fd;
(void)buf;
(void)buf_len;
(void)cookie;
(void)res_size;
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_fd_readdir(%u, 0x%X, %u, %llu)\n", fd, buf, buf_len, (unsigned long long)cookie);
#endif
panic("unimplemented");
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_fd_write(uint32_t fd, uint32_t iovs, uint32_t iovs_len, uint32_t res_size) {
uint8_t *const m = *wasm_memory;
struct wasi_ciovec *iovs_ptr = (struct wasi_ciovec *)&m[iovs];
uint32_t *res_size_ptr = (uint32_t *)&m[res_size];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_fd_write(%u, 0x%X, %u)\n", fd, iovs, iovs_len);
#endif
if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
switch (des[fds[fd].de].filetype) {
case wasi_filetype_character_device: break;
case wasi_filetype_regular_file: break;
case wasi_filetype_directory: return wasi_errno_inval;
default: panic("unimplemented");
}
size_t size = 0;
for (uint32_t i = 0; i < iovs_len; i += 1) {
size_t written_size = 0;
if (fds[fd].stream != NULL)
written_size = fwrite(&m[iovs_ptr[i].ptr], 1, iovs_ptr[i].len, fds[fd].stream);
else
written_size = iovs_ptr[i].len;
size += written_size;
if (written_size < iovs_ptr[i].len) break;
}
if (size > 0) {
time_t now = time(NULL);
des[fds[fd].de].atim = now;
des[fds[fd].de].mtim = now;
}
*res_size_ptr = size;
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_path_open(uint32_t fd, uint32_t dirflags, uint32_t path, uint32_t path_len, uint32_t oflags, uint64_t fs_rights_base, uint64_t fs_rights_inheriting, uint32_t fdflags, uint32_t res_fd) {
uint8_t *const m = *wasm_memory;
const char *path_ptr = (const char *)&m[path];
(void)fs_rights_inheriting;
uint32_t *res_fd_ptr = (uint32_t *)&m[res_fd];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_path_open(%u, 0x%X, \"%.*s\", 0x%X, 0x%llX, 0x%llX, 0x%X)\n", fd, dirflags, (int)path_len, path_ptr, oflags, (unsigned long long)fs_rights_base, (unsigned long long)fs_rights_inheriting, fdflags);
#endif
bool creat = (oflags & wasi_oflags_creat) != 0;
bool directory = (oflags & wasi_oflags_directory) != 0;
bool excl = (oflags & wasi_oflags_excl) != 0;
bool trunc = (oflags & wasi_oflags_trunc) != 0;
bool append = (fdflags & wasi_fdflags_append) != 0;
uint32_t de;
enum wasi_errno lookup_errno = DirEntry_lookup(fd, dirflags, path_ptr, path_len, &de);
if (lookup_errno == wasi_errno_success) {
if (directory && des[de].filetype != wasi_filetype_directory) return wasi_errno_notdir;
struct FileDescriptor *new_fds = realloc(fds, (fd_len + 1) * sizeof(struct FileDescriptor));
if (new_fds == NULL) return wasi_errno_nomem;
fds = new_fds;
fds[fd_len].de = de;
switch (des[de].filetype) {
case wasi_filetype_directory: fds[fd_len].stream = NULL; break;
default: panic("unimplemented");
}
#if LOG_TRACE
fprintf(stderr, "fd = %u\n", fd_len);
#endif
*res_fd_ptr = fd_len;
fd_len += 1;
}
if (lookup_errno != wasi_errno_noent) return lookup_errno;
struct FileDescriptor *new_fds = realloc(fds, (fd_len + 1) * sizeof(struct FileDescriptor));
if (new_fds == NULL) return wasi_errno_nomem;
fds = new_fds;
enum wasi_filetype filetype = directory ? wasi_filetype_directory : wasi_filetype_regular_file;
enum wasi_errno create_errno = DirEntry_create(fd, path_ptr, path_len, filetype, 0, &de);
if (create_errno != wasi_errno_success) return create_errno;
FILE *stream;
if (!directory) {
if (des[de].host_path == NULL) {
if (!creat) { DirEntry_unlink(de); de_len -= 1; return wasi_errno_noent; }
time_t now = time(NULL);
des[de].atim = now;
des[de].mtim = now;
des[de].ctim = now;
stream = NULL;
} else {
if (oflags != (append ? wasi_oflags_creat : wasi_oflags_creat | wasi_oflags_trunc)) {
char mode[] = "rb+";
if ((fs_rights_base & wasi_rights_fd_write) == 0) mode[2] = '\0';
stream = fopen(des[de].host_path, mode);
if (stream != NULL) {
if (append || excl || trunc) fclose(stream);
if (excl) {
DirEntry_unlink(de);
de_len -= 1;
return wasi_errno_exist;
}
} else if (!creat) { DirEntry_unlink(de); de_len -= 1; return wasi_errno_noent; }
}
if (append || trunc || stream == NULL) {
char mode[] = "wb+";
if ((fs_rights_base & wasi_rights_fd_read) == 0) mode[2] = '\0';
if (trunc || !append) {
stream = fopen(des[de].host_path, mode);
if (append && stream != NULL) fclose(stream);
}
if (append) {
mode[0] = 'a';
stream = fopen(des[de].host_path, mode);
}
}
if (stream == NULL) { DirEntry_unlink(de); de_len -= 1; return wasi_errno_isdir; }
}
} else stream = NULL;
#if LOG_TRACE
fprintf(stderr, "fd = %u\n", fd_len);
#endif
fds[fd_len].de = de;
fds[fd_len].stream = stream;
*res_fd_ptr = fd_len;
fd_len += 1;
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_clock_time_get(uint32_t id, uint64_t precision, uint32_t res_timestamp) {
uint8_t *const m = *wasm_memory;
(void)precision;
uint64_t *res_timestamp_ptr = (uint64_t *)&m[res_timestamp];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_clock_time_get(%u, %llu)\n", id, (unsigned long long)precision);
#endif
switch (id) {
case wasi_clockid_realtime:
*res_timestamp_ptr = time(NULL) * UINT64_C(1000000000);
break;
case wasi_clockid_monotonic:
case wasi_clockid_process_cputime_id:
case wasi_clockid_thread_cputime_id:
*res_timestamp_ptr = clock() * (UINT64_C(1000000000) / CLOCKS_PER_SEC);
break;
default: return wasi_errno_inval;
}
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_path_remove_directory(uint32_t fd, uint32_t path, uint32_t path_len) {
uint8_t *const m = *wasm_memory;
const char *path_ptr = (const char *)&m[path];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_path_remove_directory(%u, \"%.*s\")\n", fd, (int)path_len, path_ptr);
#endif
uint32_t de;
enum wasi_errno lookup_errno = DirEntry_lookup(fd, 0, path_ptr, path_len, &de);
if (lookup_errno != wasi_errno_success) return lookup_errno;
if (des[de].filetype != wasi_filetype_directory) return wasi_errno_notdir;
DirEntry_unlink(de);
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_path_unlink_file(uint32_t fd, uint32_t path, uint32_t path_len) {
uint8_t *const m = *wasm_memory;
const char *path_ptr = (const char *)&m[path];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_path_unlink_file(%u, \"%.*s\")\n", fd, (int)path_len, path_ptr);
#endif
uint32_t de;
enum wasi_errno lookup_errno = DirEntry_lookup(fd, 0, path_ptr, path_len, &de);
if (lookup_errno != wasi_errno_success) return lookup_errno;
if (des[de].filetype == wasi_filetype_directory) return wasi_errno_isdir;
if (des[de].filetype != wasi_filetype_regular_file) panic("unimplemented");
DirEntry_unlink(de);
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_fd_pread(uint32_t fd, uint32_t iovs, uint32_t iovs_len, uint64_t offset, uint32_t res_size) {
uint8_t *const m = *wasm_memory;
struct wasi_ciovec *iovs_ptr = (struct wasi_ciovec *)&m[iovs];
uint32_t *res_size_ptr = (uint32_t *)&m[res_size];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_fd_pread(%u, 0x%X, %u)\n", fd, iovs, iovs_len);
#endif
if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
switch (des[fds[fd].de].filetype) {
case wasi_filetype_character_device: break;
case wasi_filetype_regular_file: break;
case wasi_filetype_directory: return wasi_errno_inval;
default: panic("unimplemented");
}
fpos_t pos;
if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io;
size_t size = 0;
for (uint32_t i = 0; i < iovs_len; i += 1) {
size_t read_size = 0;
if (fds[fd].stream != NULL)
read_size = fread(&m[iovs_ptr[i].ptr], 1, iovs_ptr[i].len, fds[fd].stream);
else
panic("unimplemented");
size += read_size;
if (read_size < iovs_ptr[i].len) break;
}
if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
if (size > 0) des[fds[fd].de].atim = time(NULL);
*res_size_ptr = size;
return wasi_errno_success;
}
uint32_t wasi_snapshot_preview1_poll_oneoff(uint32_t in, uint32_t out, uint32_t nsubscriptions, uint32_t res_nevents) {
(void)in;
(void)out;
(void)nsubscriptions;
(void)res_nevents;
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_poll_oneoff(%u)\n", nsubscriptions);
#endif
panic("unimplemented");
return wasi_errno_success;
}
void wasi_snapshot_preview1_debug(uint32_t string, uint64_t x) {
uint8_t *const m = *wasm_memory;
const char *string_ptr = (const char *)&m[string];
#if LOG_TRACE
fprintf(stderr, "wasi_snapshot_preview1_debug(\"%s\", %llu, 0x%llX)\n", string_ptr, (unsigned long long)x, (unsigned long long)x);
#endif
(void)string_ptr;
(void)x;
}

280
stage1/wasm.h Normal file
View File

@ -0,0 +1,280 @@
#ifndef WASM_H
#define WASM_H
#include "panic.h"
enum WasmSectionId {
WasmSectionId_type = 1,
WasmSectionId_import = 2,
WasmSectionId_func = 3,
WasmSectionId_table = 4,
WasmSectionId_mem = 5,
WasmSectionId_global = 6,
WasmSectionId_export = 7,
WasmSectionId_start = 8,
WasmSectionId_elem = 9,
WasmSectionId_code = 10,
WasmSectionId_data = 11,
WasmSectionId_datacount = 12,
};
enum WasmValType {
WasmValType_i32 = -0x01,
WasmValType_i64 = -0x02,
WasmValType_f32 = -0x03,
WasmValType_f64 = -0x04,
WasmValType_v128 = -0x05,
WasmValType_funcref = -0x10,
WasmValType_externref = -0x11,
WasmValType_empty = -0x40,
};
static const char *WasmValType_toC(enum WasmValType val_type) {
switch (val_type) {
case WasmValType_i32: return "uint32_t";
case WasmValType_i64: return "uint64_t";
case WasmValType_f32: return "float";
case WasmValType_f64: return "double";
case WasmValType_v128: panic("vector types are unsupported");
case WasmValType_funcref: return "void (*)(void)";
case WasmValType_externref: return "void *";
default: panic("unsupported value type");
}
return NULL;
}
enum WasmMut {
WasmMut_const = 0x00,
WasmMut_var = 0x01,
};
static const char *WasmMut_toC(enum WasmMut val_type) {
switch (val_type) {
case WasmMut_const: return "const ";
case WasmMut_var: return "";
default: panic("unsupported mut");
}
}
enum WasmOpcode {
WasmOpcode_unreachable = 0x00,
WasmOpcode_nop = 0x01,
WasmOpcode_block = 0x02,
WasmOpcode_loop = 0x03,
WasmOpcode_if = 0x04,
WasmOpcode_else = 0x05,
WasmOpcode_end = 0x0B,
WasmOpcode_br = 0x0C,
WasmOpcode_br_if = 0x0D,
WasmOpcode_br_table = 0x0E,
WasmOpcode_return = 0x0F,
WasmOpcode_call = 0x10,
WasmOpcode_call_indirect = 0x11,
WasmOpcode_drop = 0x1A,
WasmOpcode_select = 0x1B,
WasmOpcode_select_t = 0x1C,
WasmOpcode_local_get = 0x20,
WasmOpcode_local_set = 0x21,
WasmOpcode_local_tee = 0x22,
WasmOpcode_global_get = 0x23,
WasmOpcode_global_set = 0x24,
WasmOpcode_table_get = 0x25,
WasmOpcode_table_set = 0x26,
WasmOpcode_i32_load = 0x28,
WasmOpcode_i64_load = 0x29,
WasmOpcode_f32_load = 0x2A,
WasmOpcode_f64_load = 0x2B,
WasmOpcode_i32_load8_s = 0x2C,
WasmOpcode_i32_load8_u = 0x2D,
WasmOpcode_i32_load16_s = 0x2E,
WasmOpcode_i32_load16_u = 0x2F,
WasmOpcode_i64_load8_s = 0x30,
WasmOpcode_i64_load8_u = 0x31,
WasmOpcode_i64_load16_s = 0x32,
WasmOpcode_i64_load16_u = 0x33,
WasmOpcode_i64_load32_s = 0x34,
WasmOpcode_i64_load32_u = 0x35,
WasmOpcode_i32_store = 0x36,
WasmOpcode_i64_store = 0x37,
WasmOpcode_f32_store = 0x38,
WasmOpcode_f64_store = 0x39,
WasmOpcode_i32_store8 = 0x3A,
WasmOpcode_i32_store16 = 0x3B,
WasmOpcode_i64_store8 = 0x3C,
WasmOpcode_i64_store16 = 0x3D,
WasmOpcode_i64_store32 = 0x3E,
WasmOpcode_memory_size = 0x3F,
WasmOpcode_memory_grow = 0x40,
WasmOpcode_i32_const = 0x41,
WasmOpcode_i64_const = 0x42,
WasmOpcode_f32_const = 0x43,
WasmOpcode_f64_const = 0x44,
WasmOpcode_i32_eqz = 0x45,
WasmOpcode_i32_eq = 0x46,
WasmOpcode_i32_ne = 0x47,
WasmOpcode_i32_lt_s = 0x48,
WasmOpcode_i32_lt_u = 0x49,
WasmOpcode_i32_gt_s = 0x4A,
WasmOpcode_i32_gt_u = 0x4B,
WasmOpcode_i32_le_s = 0x4C,
WasmOpcode_i32_le_u = 0x4D,
WasmOpcode_i32_ge_s = 0x4E,
WasmOpcode_i32_ge_u = 0x4F,
WasmOpcode_i64_eqz = 0x50,
WasmOpcode_i64_eq = 0x51,
WasmOpcode_i64_ne = 0x52,
WasmOpcode_i64_lt_s = 0x53,
WasmOpcode_i64_lt_u = 0x54,
WasmOpcode_i64_gt_s = 0x55,
WasmOpcode_i64_gt_u = 0x56,
WasmOpcode_i64_le_s = 0x57,
WasmOpcode_i64_le_u = 0x58,
WasmOpcode_i64_ge_s = 0x59,
WasmOpcode_i64_ge_u = 0x5A,
WasmOpcode_f32_eq = 0x5B,
WasmOpcode_f32_ne = 0x5C,
WasmOpcode_f32_lt = 0x5D,
WasmOpcode_f32_gt = 0x5E,
WasmOpcode_f32_le = 0x5F,
WasmOpcode_f32_ge = 0x60,
WasmOpcode_f64_eq = 0x61,
WasmOpcode_f64_ne = 0x62,
WasmOpcode_f64_lt = 0x63,
WasmOpcode_f64_gt = 0x64,
WasmOpcode_f64_le = 0x65,
WasmOpcode_f64_ge = 0x66,
WasmOpcode_i32_clz = 0x67,
WasmOpcode_i32_ctz = 0x68,
WasmOpcode_i32_popcnt = 0x69,
WasmOpcode_i32_add = 0x6A,
WasmOpcode_i32_sub = 0x6B,
WasmOpcode_i32_mul = 0x6C,
WasmOpcode_i32_div_s = 0x6D,
WasmOpcode_i32_div_u = 0x6E,
WasmOpcode_i32_rem_s = 0x6F,
WasmOpcode_i32_rem_u = 0x70,
WasmOpcode_i32_and = 0x71,
WasmOpcode_i32_or = 0x72,
WasmOpcode_i32_xor = 0x73,
WasmOpcode_i32_shl = 0x74,
WasmOpcode_i32_shr_s = 0x75,
WasmOpcode_i32_shr_u = 0x76,
WasmOpcode_i32_rotl = 0x77,
WasmOpcode_i32_rotr = 0x78,
WasmOpcode_i64_clz = 0x79,
WasmOpcode_i64_ctz = 0x7A,
WasmOpcode_i64_popcnt = 0x7B,
WasmOpcode_i64_add = 0x7C,
WasmOpcode_i64_sub = 0x7D,
WasmOpcode_i64_mul = 0x7E,
WasmOpcode_i64_div_s = 0x7F,
WasmOpcode_i64_div_u = 0x80,
WasmOpcode_i64_rem_s = 0x81,
WasmOpcode_i64_rem_u = 0x82,
WasmOpcode_i64_and = 0x83,
WasmOpcode_i64_or = 0x84,
WasmOpcode_i64_xor = 0x85,
WasmOpcode_i64_shl = 0x86,
WasmOpcode_i64_shr_s = 0x87,
WasmOpcode_i64_shr_u = 0x88,
WasmOpcode_i64_rotl = 0x89,
WasmOpcode_i64_rotr = 0x8A,
WasmOpcode_f32_abs = 0x8B,
WasmOpcode_f32_neg = 0x8C,
WasmOpcode_f32_ceil = 0x8D,
WasmOpcode_f32_floor = 0x8E,
WasmOpcode_f32_trunc = 0x8F,
WasmOpcode_f32_nearest = 0x90,
WasmOpcode_f32_sqrt = 0x91,
WasmOpcode_f32_add = 0x92,
WasmOpcode_f32_sub = 0x93,
WasmOpcode_f32_mul = 0x94,
WasmOpcode_f32_div = 0x95,
WasmOpcode_f32_min = 0x96,
WasmOpcode_f32_max = 0x97,
WasmOpcode_f32_copysign = 0x98,
WasmOpcode_f64_abs = 0x99,
WasmOpcode_f64_neg = 0x9A,
WasmOpcode_f64_ceil = 0x9B,
WasmOpcode_f64_floor = 0x9C,
WasmOpcode_f64_trunc = 0x9D,
WasmOpcode_f64_nearest = 0x9E,
WasmOpcode_f64_sqrt = 0x9F,
WasmOpcode_f64_add = 0xA0,
WasmOpcode_f64_sub = 0xA1,
WasmOpcode_f64_mul = 0xA2,
WasmOpcode_f64_div = 0xA3,
WasmOpcode_f64_min = 0xA4,
WasmOpcode_f64_max = 0xA5,
WasmOpcode_f64_copysign = 0xA6,
WasmOpcode_i32_wrap_i64 = 0xA7,
WasmOpcode_i32_trunc_f32_s = 0xA8,
WasmOpcode_i32_trunc_f32_u = 0xA9,
WasmOpcode_i32_trunc_f64_s = 0xAA,
WasmOpcode_i32_trunc_f64_u = 0xAB,
WasmOpcode_i64_extend_i32_s = 0xAC,
WasmOpcode_i64_extend_i32_u = 0xAD,
WasmOpcode_i64_trunc_f32_s = 0xAE,
WasmOpcode_i64_trunc_f32_u = 0xAF,
WasmOpcode_i64_trunc_f64_s = 0xB0,
WasmOpcode_i64_trunc_f64_u = 0xB1,
WasmOpcode_f32_convert_i32_s = 0xB2,
WasmOpcode_f32_convert_i32_u = 0xB3,
WasmOpcode_f32_convert_i64_s = 0xB4,
WasmOpcode_f32_convert_i64_u = 0xB5,
WasmOpcode_f32_demote_f64 = 0xB6,
WasmOpcode_f64_convert_i32_s = 0xB7,
WasmOpcode_f64_convert_i32_u = 0xB8,
WasmOpcode_f64_convert_i64_s = 0xB9,
WasmOpcode_f64_convert_i64_u = 0xBA,
WasmOpcode_f64_promote_f32 = 0xBB,
WasmOpcode_i32_reinterpret_f32 = 0xBC,
WasmOpcode_i64_reinterpret_f64 = 0xBD,
WasmOpcode_f32_reinterpret_i32 = 0xBE,
WasmOpcode_f64_reinterpret_i64 = 0xBF,
WasmOpcode_i32_extend8_s = 0xC0,
WasmOpcode_i32_extend16_s = 0xC1,
WasmOpcode_i64_extend8_s = 0xC2,
WasmOpcode_i64_extend16_s = 0xC3,
WasmOpcode_i64_extend32_s = 0xC4,
WasmOpcode_prefixed = 0xFC,
};
enum WasmPrefixedOpcode {
WasmPrefixedOpcode_i32_trunc_sat_f32_s = 0,
WasmPrefixedOpcode_i32_trunc_sat_f32_u = 1,
WasmPrefixedOpcode_i32_trunc_sat_f64_s = 2,
WasmPrefixedOpcode_i32_trunc_sat_f64_u = 3,
WasmPrefixedOpcode_i64_trunc_sat_f32_s = 4,
WasmPrefixedOpcode_i64_trunc_sat_f32_u = 5,
WasmPrefixedOpcode_i64_trunc_sat_f64_s = 6,
WasmPrefixedOpcode_i64_trunc_sat_f64_u = 7,
WasmPrefixedOpcode_memory_init = 8,
WasmPrefixedOpcode_data_drop = 9,
WasmPrefixedOpcode_memory_copy = 10,
WasmPrefixedOpcode_memory_fill = 11,
WasmPrefixedOpcode_table_init = 12,
WasmPrefixedOpcode_elem_drop = 13,
WasmPrefixedOpcode_table_copy = 14,
WasmPrefixedOpcode_table_grow = 15,
WasmPrefixedOpcode_table_size = 16,
WasmPrefixedOpcode_table_fill = 17,
};
#endif /* WASM_H */

2520
stage1/wasm2c.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff