diff options
Diffstat (limited to 'weeify.c')
| -rw-r--r-- | weeify.c | 647 |
1 files changed, 647 insertions, 0 deletions
diff --git a/weeify.c b/weeify.c new file mode 100644 index 0000000..adf3b66 --- /dev/null +++ b/weeify.c @@ -0,0 +1,647 @@ +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <unistd.h> +#include <stddef.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "common.h" +#include "vm.h" +#include "test.h" +#include "weewasm.h" +#include "illegal.h" + +int weeify(int out_fd, const byte* start, const byte* end); + +// Parse arguments and {weeify()} a single file. +int main(int argc, char* argv[]) { + const char* in_path = NULL; + const char* out_path = NULL; + + // parse arguments + for (int i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (strcmp(arg, "-trace") == 0) { + g_trace = 1; + continue; + } + if (strcmp(arg, "-o") == 0 && i < (argc - 1)) { + out_path = argv[++i]; + continue; + } + if (in_path != NULL) { + out_path = NULL; + break; + } + in_path = arg; + } + + // check both {in_path} and {out_path} are specified + if (in_path == NULL || out_path == NULL) { + printf("Usage: weeify [-trace] -o <out_file> <in_file>\n"); + return 1; + } + + // load input file and create output file + { + byte* start = NULL; + byte* end = NULL; + ssize_t r = load_file(in_path, &start, &end); + if (r < 0) { + ERR("failed to load: %s\n", in_path); + return 2; + } + TRACE("loaded %s: %ld bytes\n", in_path, r); + + int out_fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (out_fd < 0) { + ERR("failed to create: %s\n", out_path); + return 3; + } + weeify(out_fd, start, end); + close(out_fd); + unload_file(&start, &end); + return 0; + } +} + +// emits a single byte +void emit1(int out_fd, byte val) { + write(out_fd, &val, 1); +} + +// emits an unsigned LEB, not padded +void emit_u32leb(int out_fd, uint32_t val) { + off_t cur = lseek(out_fd, 0, SEEK_CUR); + TRACE(" cur=0x%llx: emit_u32leb = %u\n", (long long)cur, val); + do { + uint32_t next = val >> 7; + uint32_t v = val & 0x7F; + if (next != 0) v |= 0x80; + emit1(out_fd, v); + val = next; + } while (val != 0); +} + +// emits an unsigned LEB, padded to 4 bytes +void emit_u32leb4(int out_fd, uint32_t val) { + off_t cur = lseek(out_fd, 0, SEEK_CUR); + TRACE(" cur=0x%llx: emit_u32leb4 = %u\n", (long long)cur, val); + byte buf[4]; + buf[0] = 0x80 | (val & 0x7F); + buf[1] = 0x80 | ((val >> 7) & 0x7F); + buf[2] = 0x80 | ((val >> 14) & 0x7F); + buf[3] = ((val >> 21) & 0x7F); + write(out_fd, buf, 4); +} + +// emits an unsigned LEB, padded to 5 bytes +void emit_u32leb5(int out_fd, uint32_t val) { + off_t cur = lseek(out_fd, 0, SEEK_CUR); + TRACE(" cur=0x%llx: emit_u32leb5 = %u\n", (long long)cur, val); + byte buf[5]; + buf[0] = 0x80 | (val & 0x7F); + buf[1] = 0x80 | ((val >> 7) & 0x7F); + buf[2] = 0x80 | ((val >> 14) & 0x7F); + buf[3] = 0x80 | ((val >> 21) & 0x7F); + buf[4] = ((val >> 28) & 0x7F); + write(out_fd, buf, 5); +} + +// emits the bytes from {start ... buf->ptr} +void emit_from(int out_fd, const byte* start, buffer_t* buf) { + off_t cur = lseek(out_fd, 0, SEEK_CUR); + TRACE(" cur=0x%llx: emit_from: %d bytes\n", (long long)cur, (int)(buf->ptr - start)); + write(out_fd, start, (buf->ptr - start)); +} + +off_t emit_reserved_length(int out_fd) { + off_t before = lseek(out_fd, 0, SEEK_CUR); + TRACE(" cur=0x%llx: reserve 5 byte LEB for length\n", (long long)before); + emit_u32leb5(out_fd, 0); + return before; +} + +void emit_patched_length(int out_fd, off_t before) { + off_t cur = lseek(out_fd, 0, SEEK_CUR); + off_t diff = cur - before - 5; + TRACE(" cur=0x%llx: patch 5 byte LEB for length @ 0x%llx = %lld\n", (long long)cur, (long long)before, (long long)diff); + lseek(out_fd, before, SEEK_SET); + emit_u32leb5(out_fd, diff); + lseek(out_fd, cur, SEEK_SET); +} + +// Returns the string name of a section code. +const char* section_name(byte code) { + switch (code) { + case WASM_SECT_TYPE: return "type"; + case WASM_SECT_IMPORT: return "import"; + case WASM_SECT_FUNCTION: return "function"; + case WASM_SECT_TABLE: return "table"; + case WASM_SECT_MEMORY: return "memory"; + case WASM_SECT_GLOBAL: return "global"; + case WASM_SECT_EXPORT: return "export"; + case WASM_SECT_START: return "start"; + case WASM_SECT_ELEMENT: return "element"; + case WASM_SECT_CODE: return "code"; + case WASM_SECT_DATA: return "data"; + case 0: return "custom"; + default: + return "unknown"; + } +} + +#define WASM_TYPE_I32 -1 +#define WASM_TYPE_I64 -2 +#define WASM_TYPE_F32 -3 +#define WASM_TYPE_F64 -4 +#define WASM_TYPE_V128 -5 +#define WASM_TYPE_EXTERNREF -17 +#define WASM_TYPE_FUNCREF -16 + +// Returns the string name of a type code. +const char* type_name(uint32_t code) { + switch (code) { + case WASM_TYPE_I32: return "i32"; + case WASM_TYPE_I64: return "<!illegal i64>"; + case WASM_TYPE_F32: return "<!illegal f32>"; + case WASM_TYPE_F64: return "f64"; + case WASM_TYPE_V128: return "<!illegal v128>"; + case WASM_TYPE_EXTERNREF: return "externref"; + case WASM_TYPE_FUNCREF: return "funcref"; + default: return "<!unknown type>"; + } +} + +int transform_code_section(int out_fd, buffer_t* buf); +int transform_body(int out_fd, buffer_t* buf); + +// Transforms the wasm file in {start ... end} and outputs the bytes to {out_fd}. +int weeify(int out_fd, const byte* start, const byte* end) { + buffer_t onstack_buf = { + start, + start, + end + }; + buffer_t* buf = &onstack_buf; + ssize_t len = 0; + + //==== Check magic word ============================================= + uint32_t magic = decode_u32(buf->ptr, buf->end, &len); + TRACE("read_magic = 0x%08x\n", magic); + if (len != 4 || magic != WASM_MAGIC) { + ERR("invalid magic word, expected %x\n", WASM_MAGIC); + return -1; + } + buf->ptr += 4; + + //==== Check Wasm Version =========================================== + uint32_t version = decode_u32(buf->ptr, buf->end, &len); + TRACE("read_version = 0x%08x\n", version); + if (len != 4 || version != WASM_VERSION) { + ERR("invalid Wasm version, expected %x\n", WASM_VERSION); + return -2; + } + buf->ptr += 4; + + TRACE(" copy header\n"); + emit_from(out_fd, buf->start, buf); + + //==== Read Wasm sections =========================================== + while (buf->ptr < buf->end) { + const byte* section_start = buf->ptr; + + byte code = read_u8(buf); + uint32_t sectlen = read_u32leb(buf); + TRACE("read_section_code = 0x%02x (%s), %u bytes\n", code, section_name(code), sectlen); + const byte* section_end = buf->ptr + sectlen; + if (section_end > end) { + ERR("invalid: section length too large"); + return -4; + } + + if (code == WASM_SECT_CODE) { + // transform a code section + buffer_t code_buf = { // use a sub-buffer to avoid going OOB + section_start, + buf->ptr, + section_end + }; + int result = transform_code_section(out_fd, &code_buf); + if (result != 0) return result; + } else { + // not a code section; copy section verbatim + ptrdiff_t diff = (section_end - section_start); + TRACE(" copy %td byte section\n", diff); + write(out_fd, section_start, diff); + } + + buf->ptr = section_end; + + if (section_end == section_start) { + ERR("internal error: zero-length section"); + return -3; + } + } + off_t cur = lseek(out_fd, 0, SEEK_CUR); + TRACE("file size = %lld bytes\n", (long long)cur); + return 0; +} + +// Transforms a code section, padding branch instructions to be at least 4-byte LEBs. +int transform_code_section(int out_fd, buffer_t* buf) { + emit1(out_fd, WASM_SECT_CODE); + off_t before = emit_reserved_length(out_fd); + + uint32_t body_count = read_u32leb(buf); + emit_u32leb(out_fd, body_count); + for (uint32_t i = 0; i < body_count; i++) { + TRACE("transform body #%u\n", i); + const byte* body_start = buf->ptr; + uint32_t body_len = read_u32leb(buf); + buffer_t body_buf = { // use a sub-buffer to avoid going OOB + body_start, + buf->ptr, + buf->ptr + body_len + }; + + transform_body(out_fd, &body_buf); + buf->ptr = body_buf.end; + } + emit_patched_length(out_fd, before); + return 0; +} + +// Support for disassembling/transforming the bytecode +typedef struct { + const char* mnemonic; + int immkind; + int illegal; +} opcode_imm_t; + +#define IMM_NONE 0 +#define IMM_BLOCKT 1 +#define IMM_LABEL 2 +#define IMM_LABELS 3 +#define IMM_FUNC 4 +#define IMM_SIG_TABLE 5 +#define IMM_LOCAL 6 +#define IMM_GLOBAL 7 +#define IMM_TABLE 8 +#define IMM_MEMARG 9 +#define IMM_I32 10 +#define IMM_F64 11 +#define IMM_MEMORY 12 +#define IMM_TAG 13 +#define IMM_I64 14 +#define IMM_F32 15 +#define IMM_REFNULLT 16 +#define IMM_VALTS 17 + +opcode_imm_t opcode_imm_table[256] = { + [WASM_OP_UNREACHABLE] = {"unreachable" }, + [WASM_OP_NOP] = {"nop" }, + [WASM_OP_BLOCK] = {"block", IMM_BLOCKT }, + [WASM_OP_LOOP] = {"loop", IMM_BLOCKT }, + [WASM_OP_END] = {"end" }, + [WASM_OP_BR] = {"br", IMM_LABEL }, + [WASM_OP_BR_IF] = {"br_if", IMM_LABEL }, + [WASM_OP_BR_TABLE] = {"br_table", IMM_LABELS }, + [WASM_OP_RETURN] = {"return" }, + [WASM_OP_CALL] = {"call", IMM_FUNC }, + [WASM_OP_CALL_INDIRECT] = {"call_indirect", IMM_SIG_TABLE }, + [WASM_OP_DROP] = {"drop" }, + [WASM_OP_SELECT] = {"select" }, + [WASM_OP_LOCAL_GET] = {"local.get", IMM_LOCAL }, + [WASM_OP_LOCAL_SET] = {"local.set", IMM_LOCAL }, + [WASM_OP_LOCAL_TEE] = {"local.tee", IMM_LOCAL }, + [WASM_OP_GLOBAL_GET] = {"global.get", IMM_GLOBAL }, + [WASM_OP_GLOBAL_SET] = {"global.set", IMM_GLOBAL }, + [WASM_OP_TABLE_GET] = {"table.get", IMM_TABLE }, + [WASM_OP_TABLE_SET] = {"table.set", IMM_TABLE }, + [WASM_OP_I32_LOAD] = {"i32.load", IMM_MEMARG }, + [WASM_OP_F64_LOAD] = {"f64.load", IMM_MEMARG }, + [WASM_OP_I32_LOAD8_S] = {"i32.load8_s", IMM_MEMARG }, + [WASM_OP_I32_LOAD8_U] = {"i32.load8_u", IMM_MEMARG }, + [WASM_OP_I32_LOAD16_S] = {"i32.load16_s", IMM_MEMARG }, + [WASM_OP_I32_LOAD16_U] = {"i32.load16_u", IMM_MEMARG }, + [WASM_OP_I32_STORE] = {"i32.store", IMM_MEMARG }, + [WASM_OP_F64_STORE] = {"f64.store", IMM_MEMARG }, + [WASM_OP_I32_STORE8] = {"i32.store8", IMM_MEMARG }, + [WASM_OP_I32_STORE16] = {"i32.store16", IMM_MEMARG }, + [WASM_OP_I32_CONST] = {"i32.const", IMM_I32 }, + [WASM_OP_F64_CONST] = {"f64.const", IMM_F64 }, + [WASM_OP_I32_EQZ] = {"i32.eqz" }, + [WASM_OP_I32_EQ] = {"i32.eq" }, + [WASM_OP_I32_NE] = {"i32.ne" }, + [WASM_OP_I32_LT_S] = {"i32.lt_s" }, + [WASM_OP_I32_LT_U] = {"i32.lt_u" }, + [WASM_OP_I32_GT_S] = {"i32.gt_s" }, + [WASM_OP_I32_GT_U] = {"i32.gt_u" }, + [WASM_OP_I32_LE_S] = {"i32.le_s" }, + [WASM_OP_I32_LE_U] = {"i32.le_u" }, + [WASM_OP_I32_GE_S] = {"i32.ge_s" }, + [WASM_OP_I32_GE_U] = {"i32.ge_u" }, + [WASM_OP_F64_EQ] = {"f64.eq" }, + [WASM_OP_F64_NE] = {"f64.ne" }, + [WASM_OP_F64_LT] = {"f64.lt" }, + [WASM_OP_F64_GT] = {"f64.gt" }, + [WASM_OP_F64_LE] = {"f64.le" }, + [WASM_OP_F64_GE] = {"f64.ge" }, + [WASM_OP_I32_CLZ] = {"i32.clz" }, + [WASM_OP_I32_CTZ] = {"i32.ctz" }, + [WASM_OP_I32_POPCNT] = {"i32.popcnt" }, + [WASM_OP_I32_ADD] = {"i32.add" }, + [WASM_OP_I32_SUB] = {"i32.sub" }, + [WASM_OP_I32_MUL] = {"i32.mul" }, + [WASM_OP_I32_DIV_S] = {"i32.div_s" }, + [WASM_OP_I32_DIV_U] = {"i32.div_u" }, + [WASM_OP_I32_REM_S] = {"i32.rem_s" }, + [WASM_OP_I32_REM_U] = {"i32.rem_u" }, + [WASM_OP_I32_AND] = {"i32.and" }, + [WASM_OP_I32_OR] = {"i32.or" }, + [WASM_OP_I32_XOR] = {"i32.xor" }, + [WASM_OP_I32_SHL] = {"i32.shl" }, + [WASM_OP_I32_SHR_S] = {"i32.shr_s" }, + [WASM_OP_I32_SHR_U] = {"i32.shr_u" }, + [WASM_OP_I32_ROTL] = {"i32.rotl" }, + [WASM_OP_I32_ROTR] = {"i32.rotr" }, + [WASM_OP_F64_ADD] = {"f64.add" }, + [WASM_OP_F64_SUB] = {"f64.sub" }, + [WASM_OP_F64_MUL] = {"f64.mul" }, + [WASM_OP_F64_DIV] = {"f64.div" }, + [WASM_OP_I32_TRUNC_F64_S] = {"i32.trunc_f64_s" }, + [WASM_OP_I32_TRUNC_F64_U] = {"i32.trunc_f64_u" }, + [WASM_OP_F64_CONVERT_I32_S] = {"f64.convert_i32_s" }, + [WASM_OP_F64_CONVERT_I32_U] = {"f64.convert_i32_u" }, + [WASM_OP_F64_CONVERT_I64_S] = {"f64.convert_i64_s" }, + [WASM_OP_F64_CONVERT_I64_U] = {"f64.convert_i64_u" }, + [WASM_OP_I32_EXTEND8_S] = {"i32.extend8_s" }, + [WASM_OP_I32_EXTEND16_S] = {"i32.extend16_s" }, + // illegal bytecodes + [WASM_OP_IF] = {"if", IMM_BLOCKT, -1 }, + [WASM_OP_ELSE] = {"else", -1 }, + [WASM_OP_TRY] = {"try", IMM_BLOCKT, -1}, + [WASM_OP_CATCH] = {"catch", IMM_TAG, -1}, + [WASM_OP_THROW] = {"throw", IMM_TAG, -1}, + [WASM_OP_RETHROW] = {"rethrow", IMM_NONE, -1}, + [WASM_OP_RETURN_CALL] = {"return_call", IMM_FUNC, -1}, + [WASM_OP_RETURN_CALL_INDIRECT]= {"return_call_indirect", IMM_SIG_TABLE, -1}, + [WASM_OP_CALL_REF] = {"call_ref", IMM_NONE, -1}, + [WASM_OP_RETURN_CALL_REF] = {"return_call_ref", IMM_NONE, -1}, + [WASM_OP_DELEGATE] = {"delegate", IMM_NONE, -1}, + [WASM_OP_CATCH_ALL] = {"catch_all", IMM_NONE, -1}, + [WASM_OP_SELECT_T] = {"select", IMM_VALTS, -1}, + [WASM_OP_I64_LOAD] = {"i64.load", IMM_MEMARG, -1}, + [WASM_OP_F32_LOAD] = {"f32.load", IMM_MEMARG, -1}, + [WASM_OP_I64_LOAD8_S] = {"i64.load8_s", IMM_MEMARG, -1}, + [WASM_OP_I64_LOAD8_U] = {"i64.load8_u", IMM_MEMARG, -1}, + [WASM_OP_I64_LOAD16_S] = {"i64.load16_s", IMM_MEMARG, -1}, + [WASM_OP_I64_LOAD16_U] = {"i64.load16_u", IMM_MEMARG, -1}, + [WASM_OP_I64_LOAD32_S] = {"i64.load32_s", IMM_MEMARG, -1}, + [WASM_OP_I64_LOAD32_U] = {"i64.load32_u", IMM_MEMARG, -1}, + [WASM_OP_I64_STORE] = {"i64.store", IMM_MEMARG, -1}, + [WASM_OP_F32_STORE] = {"f32.store", IMM_MEMARG, -1}, + [WASM_OP_I64_STORE8] = {"i64.store8", IMM_MEMARG, -1}, + [WASM_OP_I64_STORE16] = {"i64.store16", IMM_MEMARG, -1}, + [WASM_OP_I64_STORE32] = {"i64.store32", IMM_MEMARG, -1}, + [WASM_OP_MEMORY_SIZE] = {"memory.size", IMM_MEMORY, -1}, + [WASM_OP_MEMORY_GROW] = {"memory.grow", IMM_MEMORY, -1}, + [WASM_OP_I64_CONST] = {"i64.const", IMM_I64, -1}, + [WASM_OP_F32_CONST] = {"f32.const", IMM_F32, -1}, + [WASM_OP_I64_EQZ] = {"i64.eqz", IMM_NONE, -1}, + [WASM_OP_I64_EQ] = {"i64.eq", IMM_NONE, -1}, + [WASM_OP_I64_NE] = {"i64.ne", IMM_NONE, -1}, + [WASM_OP_I64_LT_S] = {"i64.lt_s", IMM_NONE, -1}, + [WASM_OP_I64_LT_U] = {"i64.lt_u", IMM_NONE, -1}, + [WASM_OP_I64_GT_S] = {"i64.gt_s", IMM_NONE, -1}, + [WASM_OP_I64_GT_U] = {"i64.gt_u", IMM_NONE, -1}, + [WASM_OP_I64_LE_S] = {"i64.le_s", IMM_NONE, -1}, + [WASM_OP_I64_LE_U] = {"i64.le_u", IMM_NONE, -1}, + [WASM_OP_I64_GE_S] = {"i64.ge_s", IMM_NONE, -1}, + [WASM_OP_I64_GE_U] = {"i64.ge_u", IMM_NONE, -1}, + [WASM_OP_F32_EQ] = {"f32.eq", IMM_NONE, -1}, + [WASM_OP_F32_NE] = {"f32.ne", IMM_NONE, -1}, + [WASM_OP_F32_LT] = {"f32.lt", IMM_NONE, -1}, + [WASM_OP_F32_GT] = {"f32.gt", IMM_NONE, -1}, + [WASM_OP_F32_LE] = {"f32.le", IMM_NONE, -1}, + [WASM_OP_F32_GE] = {"f32.ge", IMM_NONE, -1}, + [WASM_OP_I64_CLZ] = {"i64.clz", IMM_NONE, -1}, + [WASM_OP_I64_CTZ] = {"i64.ctz", IMM_NONE, -1}, + [WASM_OP_I64_POPCNT] = {"i64.popcnt", IMM_NONE, -1}, + [WASM_OP_I64_ADD] = {"i64.add", IMM_NONE, -1}, + [WASM_OP_I64_SUB] = {"i64.sub", IMM_NONE, -1}, + [WASM_OP_I64_MUL] = {"i64.mul", IMM_NONE, -1}, + [WASM_OP_I64_DIV_S] = {"i64.div_s", IMM_NONE, -1}, + [WASM_OP_I64_DIV_U] = {"i64.div_u", IMM_NONE, -1}, + [WASM_OP_I64_REM_S] = {"i64.rem_s", IMM_NONE, -1}, + [WASM_OP_I64_REM_U] = {"i64.rem_u", IMM_NONE, -1}, + [WASM_OP_I64_AND] = {"i64.and", IMM_NONE, -1}, + [WASM_OP_I64_OR] = {"i64.or", IMM_NONE, -1}, + [WASM_OP_I64_XOR] = {"i64.xor", IMM_NONE, -1}, + [WASM_OP_I64_SHL] = {"i64.shl", IMM_NONE, -1}, + [WASM_OP_I64_SHR_S] = {"i64.shr_s", IMM_NONE, -1}, + [WASM_OP_I64_SHR_U] = {"i64.shr_u", IMM_NONE, -1}, + [WASM_OP_I64_ROTL] = {"i64.rotl", IMM_NONE, -1}, + [WASM_OP_I64_ROTR] = {"i64.rotr", IMM_NONE, -1}, + [WASM_OP_F32_ABS] = {"f32.abs", IMM_NONE, -1}, + [WASM_OP_F32_NEG] = {"f32.neg", IMM_NONE, -1}, + [WASM_OP_F32_CEIL] = {"f32.ceil", IMM_NONE, -1}, + [WASM_OP_F32_FLOOR] = {"f32.floor", IMM_NONE, -1}, + [WASM_OP_F32_TRUNC] = {"f32.trunc", IMM_NONE, -1}, + [WASM_OP_F32_NEAREST] = {"f32.nearest", IMM_NONE, -1}, + [WASM_OP_F32_SQRT] = {"f32.sqrt", IMM_NONE, -1}, + [WASM_OP_F32_ADD] = {"f32.add", IMM_NONE, -1}, + [WASM_OP_F32_SUB] = {"f32.sub", IMM_NONE, -1}, + [WASM_OP_F32_MUL] = {"f32.mul", IMM_NONE, -1}, + [WASM_OP_F32_DIV] = {"f32.div", IMM_NONE, -1}, + [WASM_OP_F32_MIN] = {"f32.min", IMM_NONE, -1}, + [WASM_OP_F32_MAX] = {"f32.max", IMM_NONE, -1}, + [WASM_OP_F32_COPYSIGN] = {"f32.copysign", IMM_NONE, -1}, + [WASM_OP_F64_ABS] = {"f64.abs", IMM_NONE, -1}, + [WASM_OP_F64_NEG] = {"f64.neg", IMM_NONE, -1}, + [WASM_OP_F64_CEIL] = {"f64.ceil", IMM_NONE, -1}, + [WASM_OP_F64_FLOOR] = {"f64.floor", IMM_NONE, -1}, + [WASM_OP_F64_TRUNC] = {"f64.trunc", IMM_NONE, -1}, + [WASM_OP_F64_NEAREST] = {"f64.nearest", IMM_NONE, -1}, + [WASM_OP_F64_SQRT] = {"f64.sqrt", IMM_NONE, -1}, + [WASM_OP_F64_MIN] = {"f64.min", IMM_NONE, -1}, + [WASM_OP_F64_MAX] = {"f64.max", IMM_NONE, -1}, + [WASM_OP_F64_COPYSIGN] = {"f64.copysign", IMM_NONE, -1}, + [WASM_OP_I32_WRAP_I64] = {"i32.wrap_i64", IMM_NONE, -1}, + [WASM_OP_I32_TRUNC_F32_S] = {"i32.trunc_f32_s", IMM_NONE, -1}, + [WASM_OP_I32_TRUNC_F32_U] = {"i32.trunc_f32_u", IMM_NONE, -1}, + [WASM_OP_I64_EXTEND_I32_S] = {"i64.extend_i32_s", IMM_NONE, -1}, + [WASM_OP_I64_EXTEND_I32_U] = {"i64.extend_i32_u", IMM_NONE, -1}, + [WASM_OP_I64_TRUNC_F32_S] = {"i64.trunc_f32_s", IMM_NONE, -1}, + [WASM_OP_I64_TRUNC_F32_U] = {"i64.trunc_f32_u", IMM_NONE, -1}, + [WASM_OP_I64_TRUNC_F64_S] = {"i64.trunc_f64_s", IMM_NONE, -1}, + [WASM_OP_I64_TRUNC_F64_U] = {"i64.trunc_f64_u", IMM_NONE, -1}, + [WASM_OP_F32_CONVERT_I32_S] = {"f32.convert_i32_s", IMM_NONE, -1}, + [WASM_OP_F32_CONVERT_I32_U] = {"f32.convert_i32_u", IMM_NONE, -1}, + [WASM_OP_F32_CONVERT_I64_S] = {"f32.convert_i64_s", IMM_NONE, -1}, + [WASM_OP_F32_CONVERT_I64_U] = {"f32.convert_i64_u", IMM_NONE, -1}, + [WASM_OP_F32_DEMOTE_F64] = {"f32.demote_f64", IMM_NONE, -1}, + [WASM_OP_F64_PROMOTE_F32] = {"f64.promote_f32", IMM_NONE, -1}, + [WASM_OP_I32_REINTERPRET_F32] = {"i32.reinterpret_f32", IMM_NONE, -1}, + [WASM_OP_I64_REINTERPRET_F64] = {"i64.reinterpret_f64", IMM_NONE, -1}, + [WASM_OP_F32_REINTERPRET_I32] = {"f32.reinterpret_i32", IMM_NONE, -1}, + [WASM_OP_F64_REINTERPRET_I64] = {"f64.reinterpret_i64", IMM_NONE, -1}, + [WASM_OP_I64_EXTEND8_S] = {"i64.extend8_s", IMM_NONE, -1}, + [WASM_OP_I64_EXTEND16_S] = {"i64.extend16_s", IMM_NONE, -1}, + [WASM_OP_I64_EXTEND32_S] = {"i64.extend32_s", IMM_NONE, -1}, + [WASM_OP_REF_NULL] = {"ref.null", IMM_REFNULLT, -1}, + [WASM_OP_REF_IS_NULL] = {"ref.is_null", IMM_NONE, -1}, + [WASM_OP_REF_FUNC] = {"ref.func", IMM_FUNC, -1}, + [WASM_OP_REF_AS_NON_NULL] = {"ref.as_non_null", IMM_NONE, -1}, + [WASM_OP_BR_ON_NULL] = {"br_on_null", IMM_LABEL, -1}, + [WASM_OP_REF_EQ] = {"ref.eq", IMM_NONE, -1}, + [WASM_OP_BR_ON_NON_NULL] = {"br_on_non_null", IMM_LABEL, -1}, +}; + +int transform_bytecode(int out_fd, buffer_t* buf) { + TRACE(" "); + const byte* start = buf->ptr; + + byte code = read_u8(buf); + opcode_imm_t* entry = &opcode_imm_table[code]; + if (entry->mnemonic == NULL) { + ERR("<!illegal bytecode %02X>", code); + return -3; + } else if (entry->illegal) { + ERR("<!illegal %s>", entry->mnemonic); + return -4; + } else { + TRACE("%s", entry->mnemonic); + } + switch (entry->immkind) { + case IMM_LABEL: { + // expand to 4-byte LEB + uint32_t imm = read_u32leb(buf); + TRACE(" %u", imm); + emit1(out_fd, code); + emit_u32leb4(out_fd, imm); + return 0; + } + case IMM_LABELS: { + // expand to 4-byte LEBs + uint32_t count = read_u32leb(buf); + TRACE(" %u", count); + emit1(out_fd, code); + emit_u32leb4(out_fd, count); + for (uint32_t i = 0; i <= count && buf->ptr < buf->end; i++) { + uint32_t lbl = read_u32leb(buf); + emit_u32leb4(out_fd, lbl); + TRACE(" %u", lbl); + } + return 0; + } + case IMM_FUNC: // fallthru + case IMM_LOCAL: // fallthru + case IMM_GLOBAL: // fallthru + case IMM_MEMORY: // fallthru + case IMM_TAG: // fallthru + case IMM_REFNULLT: + case IMM_TABLE: { + uint32_t imm = read_u32leb(buf); + TRACE(" %u", imm); + break; + } + + case IMM_NONE: break; + case IMM_BLOCKT: { + int32_t imm = read_i32leb(buf); + if (imm != -64) { + ERR(" <!illegal blocktype %d>", imm); + return -5; + } + break; + } + case IMM_SIG_TABLE: { + uint32_t sig = read_u32leb(buf); + TRACE(" %d", sig); + uint32_t tbl = read_u32leb(buf); + if (tbl != 0) { + ERR(" <!illegal table %d>", tbl);\ + return -2; + } + break; + } + case IMM_MEMARG: { + byte b = read_u8(buf); + uint32_t offset = read_u32leb(buf); + TRACE(" %u", offset); + break; + } + case IMM_I32: { + int32_t imm = read_i32leb(buf); + TRACE(" %d", imm); + break; + } + case IMM_F64: { + if (buf->end - buf->ptr >= 8) { + uint64_t val = *((uint64_t*)buf->ptr); + TRACE(" %016" PRIx64, val); + buf->ptr += 8; + } else { + ERR(" <!invalid f64>"); + return -6; + } + break; + } + case IMM_I64: { + ssize_t leblen = 0; + if (buf->ptr >= buf->end) break; + int64_t imm = decode_i64leb(buf->ptr, buf->end, &leblen); + if (leblen <= 0) buf->ptr = buf->end; // force failure + else buf->ptr += leblen; + TRACE(" %" PRId64 "", imm); + break; + } + case IMM_F32: { + for (int i = 0; i < 4; i++) { + byte b = read_u8(buf); + TRACE(" %02X", b); + } + break; + } + case IMM_VALTS: { + int32_t tcode = read_i32leb(buf); + TRACE(" %s", type_name(tcode)); + break; + } + default: break; + } + TRACE("\n"); + // emit the bytes as-is + emit_from(out_fd, start, buf); + return 0; +} + +int transform_body(int out_fd, buffer_t* buf) { + off_t before = emit_reserved_length(out_fd); + + const byte* start = buf->ptr; + uint32_t cnt = read_u32leb(buf); + TRACE(" locals %d\n", cnt); + for (uint32_t i = 0; i < cnt; i++) { + uint32_t cnt = read_u32leb(buf); + TRACE(" %u", cnt); + int32_t type = read_i32leb(buf); + TRACE(" %s\n", type_name(type)); + } + emit_from(out_fd, start, buf); + // disassemble code + while (buf->ptr < buf->end) { + int result = transform_bytecode(out_fd, buf); + if (result != 0) return result; + } + + emit_patched_length(out_fd, before); + return 0; +} |
