From 01061dc4b692d7f91f8a7fe6ec42c4f1567bc7b9 Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 8 Aug 2018 16:04:19 -0500 Subject: [PATCH] The final Rust porting This commit contains the final changes requires for porting all C code to Rust and from emscripten to llvm: - tools/wasm-patch-indirect-function-table.js: A script that rewrites the wasm generated by llvm to remove the table limit - tools/rust-lld-wrapper: A wrapper around rust-lld that removes arguments forced by rustc that break compilation for us - src/rust/cpu2/Makefile: A monstrosity to postprocess c2rust's output - gen/generate_interpreter.js: Ported to produce Rust instead of C - src/rust/*: A few functions and macros to connect the old Rust code and the new Rust code - src/*.js: Removes the loading of the old emscripten wasm module and adapts imports and exports from emscripten to llvm --- .gitignore | 3 + Cargo.toml | 5 +- Makefile | 45 +-- docker/test-image/Dockerfile | 3 +- gen/generate_interpreter.js | 89 ++++-- src/browser/serial.js | 2 +- src/browser/starter.js | 249 +++++++++-------- src/const.js | 2 +- src/cpu.js | 289 +++++++++++--------- src/rust/cpu.rs | 4 +- src/rust/cpu2/Makefile | 86 ++++++ src/rust/cpu2/imports.rs | 46 ++++ src/rust/cpu2/instruction_helpers.rs | 41 +++ src/rust/cpu2/mod.rs | 20 ++ src/rust/dbg.rs | 2 + src/rust/gen/mod.rs | 4 + src/rust/lib.rs | 3 +- tools/rust-lld-wrapper | 38 +++ tools/wasm-patch-indirect-function-table.js | 184 +++++++++++++ 19 files changed, 821 insertions(+), 294 deletions(-) create mode 100644 src/rust/cpu2/Makefile create mode 100644 src/rust/cpu2/imports.rs create mode 100644 src/rust/cpu2/instruction_helpers.rs create mode 100644 src/rust/cpu2/mod.rs create mode 100755 tools/rust-lld-wrapper create mode 100755 tools/wasm-patch-indirect-function-table.js diff --git a/.gitignore b/.gitignore index a63e1fea..d976ea74 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,9 @@ package-lock.json profile*.json Cargo.lock build-head +src/rust/gen/interpreter.rs +src/rust/gen/interpreter0f_16.rs +src/rust/gen/interpreter0f_32.rs src/rust/gen/analyzer.rs src/rust/gen/analyzer0f_16.rs src/rust/gen/analyzer0f_32.rs diff --git a/Cargo.toml b/Cargo.toml index 0b45eeac..2d0d9e5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,11 @@ path = "src/rust/lib.rs" [profile.dev] lto = false -opt-level=2 +# XXX: 2 -> removes debug information in later rust verions +# 0 -> requires more imports that are optimised away +opt-level = 2 panic = "abort" +overflow-checks = false [profile.release] lto = true diff --git a/Makefile b/Makefile index e9809f6c..58cf68c5 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ NASM_TEST_DIR=./tests/nasm COVERAGE_DIR=./tests/coverage INSTRUCTION_TABLES=src/rust/gen/jit.rs src/rust/gen/jit0f_16.rs src/rust/gen/jit0f_32.rs \ - build/interpreter.c build/interpreter0f_16.c build/interpreter0f_32.c \ + src/rust/gen/interpreter.rs src/rust/gen/interpreter0f_16.rs src/rust/gen/interpreter0f_32.rs \ src/rust/gen/analyzer.rs src/rust/gen/analyzer0f_16.rs src/rust/gen/analyzer0f_32.rs \ # Only the dependencies common to both generate_{jit,interpreter}.js @@ -102,7 +102,9 @@ CC_FLAGS=\ CARGO_FLAGS=\ --target wasm32-unknown-unknown \ - -- -Clink-args="--import-memory" \ + -- \ + -C linker=tools/rust-lld-wrapper \ + -C link-args="--import-table --global-base=142606336" \ --verbose CORE_FILES=const.js config.js io.js main.js lib.js coverage.js ide.js pci.js floppy.js \ @@ -116,6 +118,7 @@ BROWSER_FILES=screen.js \ network.js lib.js starter.js worker_bus.js dummy_screen.js print_stats.js RUST_FILES=$(shell find src/rust/ -name '*.rs') \ + src/rust/gen/interpreter.rs src/rust/gen/interpreter0f_16.rs src/rust/gen/interpreter0f_32.rs \ src/rust/gen/jit.rs src/rust/gen/jit0f_16.rs src/rust/gen/jit0f_32.rs \ src/rust/gen/analyzer.rs src/rust/gen/analyzer0f_16.rs src/rust/gen/analyzer0f_32.rs @@ -183,11 +186,11 @@ src/rust/gen/jit0f_16.rs: $(JIT_DEPENDENCIES) src/rust/gen/jit0f_32.rs: $(JIT_DEPENDENCIES) ./gen/generate_jit.js --output-dir build/ --table jit0f_32 -build/interpreter.c: $(INTERPRETER_DEPENDENCIES) +src/rust/gen/interpreter.rs: $(INTERPRETER_DEPENDENCIES) ./gen/generate_interpreter.js --output-dir build/ --table interpreter -build/interpreter0f_16.c: $(INTERPRETER_DEPENDENCIES) +src/rust/gen/interpreter0f_16.rs: $(INTERPRETER_DEPENDENCIES) ./gen/generate_interpreter.js --output-dir build/ --table interpreter0f_16 -build/interpreter0f_32.c: $(INTERPRETER_DEPENDENCIES) +src/rust/gen/interpreter0f_32.rs: $(INTERPRETER_DEPENDENCIES) ./gen/generate_interpreter.js --output-dir build/ --table interpreter0f_32 src/rust/gen/analyzer.rs: $(ANALYZER_DEPENDENCIES) @@ -200,38 +203,38 @@ src/rust/gen/analyzer0f_32.rs: $(ANALYZER_DEPENDENCIES) build/v86.wasm: src/native/*.c src/native/*.h src/native/profiler/* src/native/*.ll $(INSTRUCTION_TABLES) mkdir -p build -ls -lh build/v86.wasm - emcc src/native/*.c src/native/profiler/*.c src/native/*.ll \ - $(CC_FLAGS) \ - -DDEBUG=false \ - -DNDEBUG \ - -O3 \ - --llvm-opts 3 \ - --llvm-lto 3 \ - -o build/v86.wasm + #emcc src/native/*.c src/native/profiler/*.c src/native/*.ll \ + # $(CC_FLAGS) \ + # -DDEBUG=false \ + # -DNDEBUG \ + # -O3 \ + # --llvm-opts 3 \ + # --llvm-lto 3 \ + # -o build/v86.wasm ls -lh build/v86.wasm build/v86-debug.wasm: src/native/*.c src/native/*.h src/native/profiler/* src/native/*.ll $(INSTRUCTION_TABLES) mkdir -p build/coverage -ls -lh build/v86-debug.wasm - emcc src/native/*.c src/native/profiler/*.c src/native/*.ll \ - $(CC_FLAGS) \ - $(CC_COVERAGE_FLAGS) \ - -Os \ - -o build/v86-debug.wasm - ls -lh build/v86-debug.wasm + #emcc src/native/*.c src/native/profiler/*.c src/native/*.ll \ + # $(CC_FLAGS) \ + # $(CC_COVERAGE_FLAGS) \ + # -Os \ + # -o build/v86-debug.wasm + #ls -lh build/v86-debug.wasm build/v86oxide.wasm: $(RUST_FILES) Cargo.toml mkdir -p build/ -ls -lh build/v86oxide.wasm cargo +nightly rustc --release $(CARGO_FLAGS) - cp build/wasm32-unknown-unknown/release/v86oxide.wasm build/v86oxide.wasm + ./tools/wasm-patch-indirect-function-table.js < build/wasm32-unknown-unknown/release/v86oxide.wasm > build/v86oxide.wasm ls -lh build/v86oxide.wasm build/v86oxide-debug.wasm: $(RUST_FILES) Cargo.toml mkdir -p build/ -ls -lh build/v86oxide-debug.wasm cargo +nightly rustc $(CARGO_FLAGS) - cp build/wasm32-unknown-unknown/debug/v86oxide.wasm build/v86oxide-debug.wasm + ./tools/wasm-patch-indirect-function-table.js < build/wasm32-unknown-unknown/debug/v86oxide.wasm > build/v86oxide-debug.wasm ls -lh build/v86oxide-debug.wasm clean: diff --git a/docker/test-image/Dockerfile b/docker/test-image/Dockerfile index bb6ebfcb..02fbb8bc 100644 --- a/docker/test-image/Dockerfile +++ b/docker/test-image/Dockerfile @@ -3,7 +3,7 @@ FROM ttt43ttt/emscripten RUN \ dpkg --add-architecture i386 && \ apt-get update -qq && \ - apt-get install -y clang-tidy gcc-multilib nasm gdb unzip openjdk-8-jre wget python qemu-system-x86 && \ + apt-get install -y clang-tidy gcc-multilib nasm gdb unzip openjdk-8-jre wget python python3 qemu-system-x86 && \ wget https://nodejs.org/dist/v8.9.4/node-v8.9.4-linux-x64.tar.xz && \ tar xfv node-v8.9.4-linux-x64.tar.xz && \ rm node-v8.9.4-linux-x64.tar.xz && \ @@ -12,5 +12,6 @@ RUN \ rm ./rustup.sh && \ export PATH="$HOME/.cargo/bin:$PATH" && \ rustup toolchain install nightly && \ + rustup default nightly && \ rustup target add wasm32-unknown-unknown --toolchain nightly && \ rustup component add rustfmt-preview --toolchain nightly diff --git a/gen/generate_interpreter.js b/gen/generate_interpreter.js index 0924c0fa..9c317ac7 100755 --- a/gen/generate_interpreter.js +++ b/gen/generate_interpreter.js @@ -4,11 +4,10 @@ const fs = require("fs"); const path = require("path"); const x86_table = require("./x86_table"); -const c_ast = require("./c_ast"); -const { hex, mkdirpSync, get_switch_value, get_switch_exist, finalize_table } = require("./util"); +const rust_ast = require("./rust_ast"); +const { hex, mkdirpSync, get_switch_value, get_switch_exist, finalize_table_rust } = require("./util"); -const OUT_DIR = get_switch_value("--output-dir") || - path.join(__dirname, "..", "build"); +const OUT_DIR = path.join(__dirname, "..", "src/rust/gen/"); mkdirpSync(OUT_DIR); @@ -125,7 +124,7 @@ function gen_instruction_body(encodings, size) if(encoding.e) { - code.push("int32_t modrm_byte = read_imm8();"); + code.push("let modrm_byte = read_imm8();"); } if(has_66.length || has_F2.length || has_F3.length) @@ -134,28 +133,28 @@ function gen_instruction_body(encodings, size) if(has_66.length) { const body = gen_instruction_body_after_prefix(has_66, size); - if_blocks.push({ condition: "prefixes_ & PREFIX_66", body, }); + if_blocks.push({ condition: "prefixes_ & PREFIX_66 != 0", body, }); } if(has_F2.length) { const body = gen_instruction_body_after_prefix(has_F2, size); - if_blocks.push({ condition: "prefixes_ & PREFIX_F2", body, }); + if_blocks.push({ condition: "prefixes_ & PREFIX_F2 != 0", body, }); } if(has_F3.length) { const body = gen_instruction_body_after_prefix(has_F3, size); - if_blocks.push({ condition: "prefixes_ & PREFIX_F3", body, }); + if_blocks.push({ condition: "prefixes_ & PREFIX_F3 != 0", body, }); } const check_prefixes = encoding.sse ? "(PREFIX_66 | PREFIX_F2 | PREFIX_F3)" : "(PREFIX_F2 | PREFIX_F3)"; const else_block = { body: [].concat( - "dbg_assert((prefixes_ & " + check_prefixes + ") == 0);", + "dbg_assert!((prefixes_ & " + check_prefixes + ") == 0);", gen_instruction_body_after_prefix(no_prefix, size) ) }; return [].concat( - "int32_t prefixes_ = *prefixes;", + "let prefixes_ = *prefixes as i32;", code, { type: "if-else", @@ -206,7 +205,7 @@ function gen_instruction_body_after_prefix(encodings, size) default_case: { body: [ - "assert(false);", + "assert!(false);", "trigger_ud();", ], } @@ -366,6 +365,7 @@ function gen_table() console.assert(encoding && encoding.length); let opcode_hex = hex(opcode, 2); + let opcode_high_hex = hex(opcode | 0x100, 2); if(encoding[0].os) { @@ -374,14 +374,14 @@ function gen_table() body: gen_instruction_body(encoding, 16), }); cases.push({ - conditions: [`0x${opcode_hex}|0x100`], + conditions: [`0x${opcode_high_hex}`], body: gen_instruction_body(encoding, 32), }); } else { cases.push({ - conditions: [`0x${opcode_hex}`, `0x${opcode_hex}|0x100`], + conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`], body: gen_instruction_body(encoding, undefined), }); } @@ -391,15 +391,28 @@ function gen_table() condition: "opcode", cases, default_case: { - body: ["assert(false);"] + body: ["assert!(false);"] }, }; if(to_generate.interpreter) { - finalize_table( + const code = [ + "use cpu2::cpu::*;", + "use cpu2::instructions::*;", + "use cpu2::instructions_0f::*;", + "use cpu2::modrm::*;", + "use cpu2::global_pointers::*;", + + "#[cfg_attr(rustfmt, rustfmt_skip)]", + "pub unsafe fn run(opcode: u32) {", + table, + "}", + ]; + + finalize_table_rust( OUT_DIR, - "interpreter", - c_ast.print_syntax_tree([table]).join("\n") + "\n" + "interpreter.rs", + rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n" ); } @@ -440,7 +453,7 @@ function gen_table() condition: "opcode", cases: cases0f_16, default_case: { - body: ["assert(false);"] + body: ["assert!(false);"] }, }; const table0f_32 = { @@ -448,25 +461,51 @@ function gen_table() condition: "opcode", cases: cases0f_32, default_case: { - body: ["assert(false);"] + body: ["assert!(false);"] }, }; if(to_generate.interpreter0f_16) { - finalize_table( + const code = [ + "use cpu2::cpu::*;", + "use cpu2::instructions::*;", + "use cpu2::instructions_0f::*;", + "use cpu2::modrm::*;", + "use cpu2::global_pointers::*;", + + "#[cfg_attr(rustfmt, rustfmt_skip)]", + "pub unsafe fn run(opcode: u8) {", + table0f_16, + "}", + ]; + + finalize_table_rust( OUT_DIR, - "interpreter0f_16", - c_ast.print_syntax_tree([table0f_16]).join("\n") + "\n" + "interpreter0f_16.rs", + rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n" ); } if(to_generate.interpreter0f_32) { - finalize_table( + const code = [ + "use cpu2::cpu::*;", + "use cpu2::instructions::*;", + "use cpu2::instructions_0f::*;", + "use cpu2::modrm::*;", + "use cpu2::global_pointers::*;", + + "#[cfg_attr(rustfmt, rustfmt_skip)]", + "pub unsafe fn run(opcode: u8) {", + table0f_32, + "}", + ]; + + finalize_table_rust( OUT_DIR, - "interpreter0f_32", - c_ast.print_syntax_tree([table0f_32]).join("\n") + "\n" + "interpreter0f_32.rs", + rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n" ); } } diff --git a/src/browser/serial.js b/src/browser/serial.js index 1ca3c17a..dd5af3b0 100644 --- a/src/browser/serial.js +++ b/src/browser/serial.js @@ -79,7 +79,7 @@ function SerialAdapter(element, bus) this.update_timer = setTimeout(() => { this.update_timer = undefined; var now = Date.now(); - dbg_assert(now - this.last_update >= 16); + dbg_assert(now - this.last_update >= 15); this.last_update = now; this.render(); }, 16 - delta); diff --git a/src/browser/starter.js b/src/browser/starter.js index 9cb866bb..4810f5ee 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -94,21 +94,22 @@ function V86Starter(options) this.emulator_bus = bus[1]; var emulator; var cpu; - var mem; - var mem8; + var v86oxide; const coverage_logger = new CoverageLogger(); - if(coverage_logger.ENABLED) - { - this.bus.register("emulator-stopped", function() - { - coverage_logger.dump_to_files(); - }, this); - } + //if(coverage_logger.ENABLED) + //{ + // this.bus.register("emulator-stopped", function() + // { + // coverage_logger.dump_to_files(); + // }, this); + //} + + const wasm_table = new WebAssembly.Table({ element: "anyfunc", initial: 0x10000 + 0x100 }); var wasm_shared_funcs = { - "___assert_fail": (condition, file, line, fun) => { - const memory = mem8; + "__assert_fail": (condition, file, line, fun) => { + const memory = new Uint8Array(v86oxide.exports.memory.buffer); function read_string(memory, offset) { @@ -130,88 +131,90 @@ function V86Starter(options) dbg_assert(false); }, - "_throw_cpu_exception": () => { + "throw_cpu_exception": () => { throw MAGIC_CPU_EXCEPTION; }, - "_cpu_exception_hook": (n) => { + "cpu_exception_hook": (n) => { return this["cpu_exception_hook"] && this["cpu_exception_hook"](n); }, - "_hlt_op": function() { return cpu.hlt_op(); }, + "hlt_op": function() { return cpu.hlt_op(); }, "abort": function() { dbg_assert(false); }, - "__dbg_trace": function() { return dbg_trace(); }, - "_logop": function(eip, op) { return cpu.debug.logop(eip, op); }, - "_undefined_instruction": function() { return cpu.undefined_instruction.apply(cpu, arguments); }, - "_unimplemented_sse": function() { return cpu.unimplemented_sse(); }, - "_microtick": v86.microtick, - "_get_rand_int": function() { return v86util.get_rand_int(); }, - "_has_rand_int": function() { return v86util.has_rand_int(); }, - "_printf": function(format_string_offset, stack_top) { + "_dbg_trace": function() { return dbg_trace(); }, + "logop": function(eip, op) { return cpu.debug.logop(eip, op); }, + "undefined_instruction": function() { return cpu.undefined_instruction.apply(cpu, arguments); }, + "unimplemented_sse": function() { return cpu.unimplemented_sse(); }, + "microtick": v86.microtick, + "get_rand_int": function() { return v86util.get_rand_int(); }, + "has_rand_int": function() { return v86util.has_rand_int(); }, + "printf": function(format_string_offset, stack_top) { dbg_assert(arguments.length === 2); - dbg_log_wasm(mem, format_string_offset, stack_top); + dbg_log_wasm(v86oxide.exports.memory.buffer, format_string_offset, stack_top); }, - "_memcpy_large": function(dest, source, length) { + "memcpy_large": function(dest, source, length) { + const mem8 = new Uint8Array(v86oxide.exports.memory.buffer); mem8.set(mem8.subarray(source, source + length), dest); return dest; }, - "_memcpy": function(dest, source, length) { + "memcpy": function(dest, source, length) { + const mem8 = new Uint8Array(v86oxide.exports.memory.buffer); mem8.set(mem8.subarray(source, source + length), dest); return dest; }, - "_call_interrupt_vector": function(interrupt_nr, is_software_int, has_error_code, error_code) { + "call_interrupt_vector": function(interrupt_nr, is_software_int, has_error_code, error_code) { cpu.call_interrupt_vector(interrupt_nr, is_software_int, !!has_error_code, error_code); }, - "_far_jump": function(eip, selector, is_call) { return cpu.far_jump(eip, selector, !!is_call); }, - "_far_return": function(eip, selector, stack_adjust) { return cpu.far_return(eip, selector, stack_adjust); }, - "_switch_seg": function(reg, selector) { return cpu.switch_seg(reg, selector); }, - "_iret16": function() { return cpu.iret16(); }, - "_iret32": function() { return cpu.iret32(); }, - "_handle_irqs": function() { return cpu.handle_irqs(); }, + "far_jump": function(eip, selector, is_call) { return cpu.far_jump(eip, selector, !!is_call); }, + "far_return": function(eip, selector, stack_adjust) { return cpu.far_return(eip, selector, stack_adjust); }, + "switch_seg": function(reg, selector) { return cpu.switch_seg(reg, selector); }, + "iret16": function() { return cpu.iret16(); }, + "iret32": function() { return cpu.iret32(); }, + "handle_irqs": function() { return cpu.handle_irqs(); }, - "_io_port_read8": function(addr) { return cpu.io.port_read8(addr); }, - "_io_port_read16": function(addr) { return cpu.io.port_read16(addr); }, - "_io_port_read32": function(addr) { return cpu.io.port_read32(addr); }, - "_io_port_write8": function(addr, value) { cpu.io.port_write8(addr, value); }, - "_io_port_write16": function(addr, value) { cpu.io.port_write16(addr, value); }, - "_io_port_write32": function(addr, value) { cpu.io.port_write32(addr, value); }, + "io_port_read8": function(addr) { return cpu.io.port_read8(addr); }, + "io_port_read16": function(addr) { return cpu.io.port_read16(addr); }, + "io_port_read32": function(addr) { return cpu.io.port_read32(addr); }, + "io_port_write8": function(addr, value) { cpu.io.port_write8(addr, value); }, + "io_port_write16": function(addr, value) { cpu.io.port_write16(addr, value); }, + "io_port_write32": function(addr, value) { cpu.io.port_write32(addr, value); }, - "_mmap_read8": function(addr) { return cpu.mmap_read8(addr); }, - "_mmap_read16": function(addr) { return cpu.mmap_read16(addr); }, - "_mmap_read32": function(addr) { return cpu.mmap_read32(addr); }, - "_mmap_write8": function(addr, value) { return cpu.mmap_write8(addr, value); }, - "_mmap_write16": function(addr, value) { return cpu.mmap_write16(addr, value); }, - "_mmap_write32": function(addr, value) { return cpu.mmap_write32(addr, value); }, - "_mmap_write128": function(addr, value0, value1, value2, value3) { return cpu.mmap_write128(addr, value0, value1, value2, value3); }, + "mmap_read8": function(addr) { return cpu.mmap_read8(addr); }, + "mmap_read16": function(addr) { return cpu.mmap_read16(addr); }, + "mmap_read32": function(addr) { return cpu.mmap_read32(addr); }, + "mmap_write8": function(addr, value) { return cpu.mmap_write8(addr, value); }, + "mmap_write16": function(addr, value) { return cpu.mmap_write16(addr, value); }, + "mmap_write32": function(addr, value) { return cpu.mmap_write32(addr, value); }, + "mmap_write128": function(addr, value0, value1, value2, value3) { return cpu.mmap_write128(addr, value0, value1, value2, value3); }, - "_int_log2": function(val) { return v86util.int_log2(val); }, + "int_log2": function(val) { return v86util.int_log2(val); }, - "_popa16": function() { return cpu.popa16.apply(cpu, arguments); }, - "_popa32": function() { return cpu.popa32.apply(cpu, arguments); }, - "_arpl": function() { return cpu.arpl.apply(cpu, arguments); }, + "popa16": function() { return cpu.popa16.apply(cpu, arguments); }, + "popa32": function() { return cpu.popa32.apply(cpu, arguments); }, + "arpl": function() { return cpu.arpl.apply(cpu, arguments); }, - "_bswap": function() { return cpu.bswap.apply(cpu, arguments); }, + "bswap": function() { return cpu.bswap.apply(cpu, arguments); }, - "_lar": function() { return cpu.lar.apply(cpu, arguments); }, - "_lsl": function() { return cpu.lsl.apply(cpu, arguments); }, - "_verw": function() { return cpu.verw.apply(cpu, arguments); }, - "_verr": function() { return cpu.verr.apply(cpu, arguments); }, + "lar": function() { return cpu.lar.apply(cpu, arguments); }, + "lsl": function() { return cpu.lsl.apply(cpu, arguments); }, + "verw": function() { return cpu.verw.apply(cpu, arguments); }, + "verr": function() { return cpu.verr.apply(cpu, arguments); }, - "_cpl_changed": function() { return cpu.cpl_changed.apply(cpu, arguments); }, - "_set_cr0": function() { return cpu.set_cr0.apply(cpu, arguments); }, - "_update_cs_size": function() { return cpu.update_cs_size.apply(cpu, arguments); }, - "_cpuid": function() { return cpu.cpuid.apply(cpu, arguments); }, + "cpl_changed": function() { return cpu.cpl_changed.apply(cpu, arguments); }, + "set_cr0": function() { return cpu.set_cr0.apply(cpu, arguments); }, + "update_cs_size": function() { return cpu.update_cs_size.apply(cpu, arguments); }, + "cpuid": function() { return cpu.cpuid.apply(cpu, arguments); }, - "_load_ldt": function() { return cpu.load_ldt.apply(cpu, arguments); }, - "_load_tr": function() { return cpu.load_tr.apply(cpu, arguments); }, + "load_ldt": function() { return cpu.load_ldt.apply(cpu, arguments); }, + "load_tr": function() { return cpu.load_tr.apply(cpu, arguments); }, - "_lss16": function() { return cpu.lss16.apply(cpu, arguments); }, - "_lss32": function() { return cpu.lss32.apply(cpu, arguments); }, - "_enter16": function() { return cpu.enter16.apply(cpu, arguments); }, - "_enter32": function() { return cpu.enter32.apply(cpu, arguments); }, + "lss16": function() { return cpu.lss16.apply(cpu, arguments); }, + "lss32": function() { return cpu.lss32.apply(cpu, arguments); }, + "enter16": function() { return cpu.enter16.apply(cpu, arguments); }, + "enter32": function() { return cpu.enter32.apply(cpu, arguments); }, - "_test_privileges_for_io": function() { return cpu.test_privileges_for_io.apply(cpu, arguments); }, + "test_privileges_for_io": function() { return cpu.test_privileges_for_io.apply(cpu, arguments); }, - "_convert_f64_to_i32": function(f) { + "convert_f64_to_i32": function(f) { // implemented here due to emscripten bug if(!(f <= 0x7FFFFFFF && f >= -0x80000000)) { @@ -220,30 +223,37 @@ function V86Starter(options) return f | 0; }, - "_get_time": Date.now, + "get_time": Date.now, - "_coverage_log": (fn_name_offset, num_blocks, visited_block) => { - coverage_logger.log(fn_name_offset, num_blocks, visited_block); + "coverage_log": (fn_name_offset, num_blocks, visited_block) => { + //coverage_logger.log(fn_name_offset, num_blocks, visited_block); }, // see https://github.com/kripken/emscripten/blob/incoming/src/library.js - "_atan2": Math.atan2, - "_sin": Math.sin, - "_cos": Math.cos, - "_tan": Math.tan, - "_trunc": Math.trunc, - "_fmod": (x, y) => x % y, - "_llvm_exp2_f64": (x) => Math.pow(2, x), - "_log": Math.log, - "_round": Math.round, - "_ldexp": function(x, exp) { + "atan2": Math.atan2, + "sin": Math.sin, + "cos": Math.cos, + "tan": Math.tan, + "trunc": Math.trunc, + "fmod": (x, y) => x % y, + "llvm_exp2_f64": (x) => Math.pow(2, x), + "log": Math.log, + "round": Math.round, + "ldexp": function(x, exp) { return x * Math.pow(2, exp); }, - "_llvm_round_f64": function(d) { + "llvm_round_f64": function(d) { d = +d; return d >= +0 ? +Math.floor(d + 0.5) : +Math.ceil(d - 0.5); }, - "_llvm_trunc_f64": Math.trunc, + "llvm_trunc_f64": Math.trunc, + + "log_from_wasm": function(offset, len) { + const str = v86util.read_sized_string_from_mem(v86oxide.exports.memory, offset, len); + dbg_log(str, LOG_CPU); + }, + "codegen_finalize": (wasm_table_index, start, end, first_opcode, state_flags) => cpu.codegen_finalize(wasm_table_index, start, end, first_opcode, state_flags), + "__indirect_function_table": wasm_table, }; const wasm_globals = { @@ -251,25 +261,16 @@ function V86Starter(options) "NaN": NaN, }; - const v86oxide_mem = new WebAssembly.Memory({ "initial": 250 }); - const v86oxide_externs = { - "memory": v86oxide_mem, - "log_from_wasm": function(offset, len) { - const str = v86util.read_sized_string_from_mem(v86oxide_mem, offset, len); - dbg_log(str, LOG_CPU); - }, - "abort": function() { - dbg_assert(false); - }, - - "read8": addr => cpu.read8(addr), - "read16": addr => cpu.read16(addr), - "read32": addr => cpu.read32s(addr), - "tlb_set_has_code": (page, has_code) => cpu.wm.exports["_tlb_set_has_code"](page, has_code), - "check_tlb_invariants": () => cpu.wm.exports["_check_tlb_invariants"](), - "codegen_finalize": (wasm_table_index, start, end, first_opcode, state_flags) => cpu.codegen_finalize(wasm_table_index, start, end, first_opcode, state_flags), - "profiler_stat_increment": (name) => cpu.wm.exports["_profiler_stat_increment"](name), - }; + //const v86oxide_mem = new WebAssembly.Memory({ "initial": 250 }); + //const v86oxide_externs = { + // "memory": v86oxide_mem, + // "read8": addr => cpu.read8(addr), + // "read16": addr => cpu.read16(addr), + // "read32": addr => cpu.read32s(addr), + // "tlb_set_has_code": (page, has_code) => cpu.wm.exports["_tlb_set_has_code"](page, has_code), + // "check_tlb_invariants": () => cpu.wm.exports["_check_tlb_invariants"](), + // "profiler_stat_increment": (name) => cpu.wm.exports["_profiler_stat_increment"](name), + //}; let wasm_file = DEBUG ? "v86-debug.wasm" : "v86.wasm"; let v86oxide_bin = DEBUG ? "v86oxide-debug.wasm" : "v86oxide.wasm"; @@ -304,28 +305,38 @@ function V86Starter(options) "jit_dirty_cache", ]; - v86util.minimal_load_wasm(v86oxide_bin, { "env": v86oxide_externs }, (v86oxide) => { - for(const fn_name of v86oxide_exports) - { - dbg_assert(typeof v86oxide.exports[fn_name] === "function", `Function ${fn_name} not found in v86oxide exports`); - wasm_shared_funcs[`_${fn_name}`] = v86oxide.exports[fn_name]; - } + v86util.minimal_load_wasm(v86oxide_bin, { "env": wasm_shared_funcs }, (v86oxide_) => { + v86oxide = v86oxide_; + //for(const fn_name of v86oxide_exports) + //{ + // dbg_assert(typeof v86oxide.exports[fn_name] === "function", `Function ${fn_name} not found in v86oxide exports`); + // wasm_shared_funcs[`_${fn_name}`] = v86oxide.exports[fn_name]; + //} v86oxide.exports["rust_setup"](); + //v86oxide.exports[WASM_EXPORT_TABLE_NAME].grow(WASM_TABLE_SIZE); + + const wm = v86oxide; + + //mem = v86oxide.exports.memory.buffer; + //mem8 = new Uint8Array(mem); + emulator = this.v86 = new v86(this.emulator_bus, wm, v86oxide, coverage_logger); + cpu = emulator.cpu; + //XXX: fix indentation break - v86util.load_wasm( - wasm_file, - { "env": wasm_shared_funcs, "global" : wasm_globals }, - options["memory_size"] + GUEST_MEMORY_START, - WASM_TABLE_SIZE, - wm => { - mem = wm.memory.buffer; - mem8 = new Uint8Array(mem); - wm.instance.exports["__post_instantiate"](); - coverage_logger.init(wm); - emulator = this.v86 = new v86(this.emulator_bus, wm, v86oxide, coverage_logger); - cpu = emulator.cpu; + //v86util.load_wasm( + // wasm_file, + // { "env": wasm_shared_funcs, "global" : wasm_globals }, + // options["memory_size"] + GUEST_MEMORY_START, + // WASM_TABLE_SIZE, + // wm => { + // mem = wm.memory.buffer; + // mem8 = new Uint8Array(mem); + // wm.instance.exports["__post_instantiate"](); + // coverage_logger.init(wm); + // emulator = this.v86 = new v86(this.emulator_bus, wm, v86oxide, coverage_logger); + // cpu = emulator.cpu; // XXX: Leaving unindented to minimize diff; still a part of the cb to load_wasm! this.bus.register("emulator-stopped", function() @@ -671,7 +682,7 @@ function V86Starter(options) }.bind(this), 0); } - }); + //}); }); } diff --git a/src/const.js b/src/const.js index 2e6cfda5..c300bc73 100644 --- a/src/const.js +++ b/src/const.js @@ -362,7 +362,7 @@ var WASM_TABLE_SIZE = 0x10000; var JIT_CACHE_ARRAY_SIZE = 0x40000; /** @const */ -const WASM_EXPORT_TABLE_NAME = "table"; +const WASM_EXPORT_TABLE_NAME = "__indirect_function_table"; /** @const */ diff --git a/src/cpu.js b/src/cpu.js index 5db958b2..39b12be5 100644 --- a/src/cpu.js +++ b/src/cpu.js @@ -19,46 +19,64 @@ function CPU(bus, wm, v86oxide, coverage_logger) this.wasm_patch(wm); this.create_jit_imports(); - this.memory_size = new Uint32Array(wm.memory.buffer, 812, 1); + const memory = v86oxide.instance.exports.memory; + + Object.defineProperty(this, "memory_size", { get: () => { return new Uint32Array(memory.buffer, 812, 1); } }); + if(false) this.memory_size = new Uint32Array(memory.buffer, 812, 1); // Note: Currently unused (degrades performance and not required by any OS // that we support) - this.a20_enabled = new Int32Array(wm.memory.buffer, 552, 1); + if(false) this.a20_enabled = new Int32Array(memory.buffer, 552, 1); + Object.defineProperty(this, "a20_enabled", { get: () => { return new Int32Array(memory.buffer, 552, 1); } }); this.a20_enabled[0] = +true; - this.mem_page_infos = undefined; + if(false) this.mem8 = new Uint8Array(0); + if(false) this.mem16 = new Uint16Array(this.mem8.buffer); + if(false) this.mem32s = new Int32Array(this.mem8.buffer); - this.mem8 = new Uint8Array(0); - this.mem16 = new Uint16Array(this.mem8.buffer); - this.mem32s = new Int32Array(this.mem8.buffer); + Object.defineProperty(this, "mem8", { get: () => { return new Uint8Array(memory.buffer, GUEST_MEMORY_START, this.memory_size[0]); } }); + Object.defineProperty(this, "mem16", { get: () => { return new Uint16Array(memory.buffer, GUEST_MEMORY_START, this.memory_size[0] >> 1); } }); + Object.defineProperty(this, "mem32s", { get: () => { return new Int32Array(memory.buffer, GUEST_MEMORY_START, this.memory_size[0] >> 2); } }); - this.segment_is_null = new Uint8Array(wm.memory.buffer, 724, 8); - this.segment_offsets = new Int32Array(wm.memory.buffer, 736, 8); - this.segment_limits = new Uint32Array(wm.memory.buffer, 768, 8); - //this.segment_infos = []; + if(false) this.segment_is_null = new Uint8Array(memory.buffer, 724, 8); + Object.defineProperty(this, "segment_is_null", { get: () => { return new Uint8Array(memory.buffer, 724, 8); } }); + + if(false) this.segment_offsets = new Int32Array(memory.buffer, 736, 8); + Object.defineProperty(this, "segment_offsets", { get: () => { return new Int32Array(memory.buffer, 736, 8); } }); + + if(false) this.segment_limits = new Uint32Array(memory.buffer, 768, 8); + Object.defineProperty(this, "segment_limits", { get: () => { return new Uint32Array(memory.buffer, 768, 8); } }); /** * Wheter or not in protected mode */ - this.protected_mode = new Int32Array(wm.memory.buffer, 800, 1); + if(false) this.protected_mode = new Int32Array(memory.buffer, 800, 1); + Object.defineProperty(this, "protected_mode", { get: () => { return new Int32Array(memory.buffer, 800, 1); } }); - this.idtr_size = new Int32Array(wm.memory.buffer, 564, 1); - this.idtr_offset = new Int32Array(wm.memory.buffer, 568, 1); + if(false) this.idtr_size = new Int32Array(memory.buffer, 564, 1); + if(false) this.idtr_offset = new Int32Array(memory.buffer, 568, 1); + Object.defineProperty(this, "idtr_size", { get: () => { return new Int32Array(memory.buffer, 564, 1); } }); + Object.defineProperty(this, "idtr_offset", { get: () => { return new Int32Array(memory.buffer, 568, 1); } }); /** * global descriptor table register */ - this.gdtr_size = new Int32Array(wm.memory.buffer, 572, 1); - this.gdtr_offset = new Int32Array(wm.memory.buffer, 576, 1); + if(false) this.gdtr_size = new Int32Array(memory.buffer, 572, 1); + if(false) this.gdtr_offset = new Int32Array(memory.buffer, 576, 1); + + Object.defineProperty(this, "gdtr_size", { get: () => { return new Int32Array(memory.buffer, 572, 1); } }); + Object.defineProperty(this, "gdtr_offset", { get: () => { return new Int32Array(memory.buffer, 576, 1); } }); this.tss_size_32 = false; /* * whether or not a page fault occured */ - this.page_fault = new Uint32Array(wm.memory.buffer, 540, 8); + if(false) this.page_fault = new Uint32Array(memory.buffer, 540, 8); + Object.defineProperty(this, "page_fault", { get: () => { return new Uint32Array(memory.buffer, 540, 8); } }); - this.cr = new Int32Array(wm.memory.buffer, 580, 8); + if(false) this.cr = new Int32Array(memory.buffer, 580, 8); + Object.defineProperty(this, "cr", { get: () => { return new Int32Array(memory.buffer, 580, 8); } }); /** @type {number} */ this.cr[0] = 0; @@ -70,62 +88,76 @@ function CPU(bus, wm, v86oxide, coverage_logger) this.cr[4] = 0; // current privilege level - this.cpl = new Int32Array(wm.memory.buffer, 612, 1); + if(false) this.cpl = new Int32Array(memory.buffer, 612, 1); + Object.defineProperty(this, "cpl", { get: () => { return new Int32Array(memory.buffer, 612, 1); } }); // current operand/address size - this.is_32 = new Int32Array(wm.memory.buffer, 804, 1); + if(false) this.is_32 = new Int32Array(memory.buffer, 804, 1); + Object.defineProperty(this, "is_32", { get: () => { return new Int32Array(memory.buffer, 804, 1); } }); - this.stack_size_32 = new Int32Array(wm.memory.buffer, 808, 1); + if(false) this.stack_size_32 = new Int32Array(memory.buffer, 808, 1); + Object.defineProperty(this, "stack_size_32", { get: () => { return new Int32Array(memory.buffer, 808, 1); } }); /** * Was the last instruction a hlt? */ - this.in_hlt = new Uint8Array(wm.memory.buffer, 616, 1); + if(false) this.in_hlt = new Uint8Array(memory.buffer, 616, 1); + Object.defineProperty(this, "in_hlt", { get: () => { return new Uint8Array(memory.buffer, 616, 1); } }); - this.last_virt_eip = new Int32Array(wm.memory.buffer, 620, 1); + this.last_virt_eip = new Int32Array(memory.buffer, 620, 1); - this.eip_phys = new Int32Array(wm.memory.buffer, 624, 1); + this.eip_phys = new Int32Array(memory.buffer, 624, 1); - this.last_virt_esp = new Int32Array(wm.memory.buffer, 628, 1); + this.last_virt_esp = new Int32Array(memory.buffer, 628, 1); - this.esp_phys = new Int32Array(wm.memory.buffer, 632, 1); + this.esp_phys = new Int32Array(memory.buffer, 632, 1); - this.sysenter_cs = new Int32Array(wm.memory.buffer, 636, 1); + this.sysenter_cs = new Int32Array(memory.buffer, 636, 1); - this.sysenter_esp = new Int32Array(wm.memory.buffer, 640, 1); + this.sysenter_esp = new Int32Array(memory.buffer, 640, 1); - this.sysenter_eip = new Int32Array(wm.memory.buffer, 644, 1); + this.sysenter_eip = new Int32Array(memory.buffer, 644, 1); - this.prefixes = new Int32Array(wm.memory.buffer, 648, 1); + if(false) this.prefixes = new Int32Array(memory.buffer, 648, 1); + Object.defineProperty(this, "prefixes", { get: () => { return new Int32Array(memory.buffer, 648, 1); } }); - this.flags = new Int32Array(wm.memory.buffer, 536, 1); + if(false) this.flags = new Int32Array(memory.buffer, 536, 1); + Object.defineProperty(this, "flags", { get: () => { return new Int32Array(memory.buffer, 536, 1); } }); /** * bitmap of flags which are not updated in the flags variable * changed by arithmetic instructions, so only relevant to arithmetic flags */ - this.flags_changed = new Int32Array(wm.memory.buffer, 532, 1); + if(false) this.flags_changed = new Int32Array(memory.buffer, 532, 1); + Object.defineProperty(this, "flags_changed", { get: () => { return new Int32Array(memory.buffer, 532, 1); } }); /** * the last 2 operators and the result and size of the last arithmetic operation */ - this.last_op1 = new Int32Array(wm.memory.buffer, 512, 1); - this.last_op2 = new Int32Array(wm.memory.buffer, 516, 1); - this.last_op_size = new Int32Array(wm.memory.buffer, 520, 1); + if(false) this.last_op1 = new Int32Array(memory.buffer, 512, 1); + Object.defineProperty(this, "last_op1", { get: () => { return new Int32Array(memory.buffer, 512, 1); } }); + if(false) this.last_op2 = new Int32Array(memory.buffer, 516, 1); + Object.defineProperty(this, "last_op2", { get: () => { return new Int32Array(memory.buffer, 516, 1); } }); + if(false) this.last_op_size = new Int32Array(memory.buffer, 520, 1); + Object.defineProperty(this, "last_op_size", { get: () => { return new Int32Array(memory.buffer, 520, 1); } }); - this.last_add_result = new Int32Array(wm.memory.buffer, 524, 1); + if(false) this.last_add_result = new Int32Array(memory.buffer, 524, 1); + Object.defineProperty(this, "last_add_result", { get: () => { return new Int32Array(memory.buffer, 524, 1); } }); - this.last_result = new Int32Array(wm.memory.buffer, 528, 1); + if(false) this.last_result = new Int32Array(memory.buffer, 528, 1); + Object.defineProperty(this, "last_result", { get: () => { return new Int32Array(memory.buffer, 528, 1); } }); - this.current_tsc = new Uint32Array(wm.memory.buffer, 956, 2); // 64 bit + this.current_tsc = new Uint32Array(memory.buffer, 956, 2); // 64 bit /** @type {!Object} */ this.devices = {}; - this.instruction_pointer = new Int32Array(wm.memory.buffer, 556, 1); + if(false) this.instruction_pointer = new Int32Array(memory.buffer, 556, 1); + Object.defineProperty(this, "instruction_pointer", { get: () => { return new Int32Array(memory.buffer, 556, 1); } }); - this.previous_ip = new Int32Array(wm.memory.buffer, 560, 1); + if(false) this.previous_ip = new Int32Array(memory.buffer, 560, 1); + Object.defineProperty(this, "previous_ip", { get: () => { return new Int32Array(memory.buffer, 560, 1); } }); this.apic_enabled = true; @@ -144,56 +176,64 @@ function CPU(bus, wm, v86oxide, coverage_logger) vga: null, }; - this.timestamp_counter = new Uint32Array(wm.memory.buffer, 664, 1); + Object.defineProperty(this, "timestamp_counter", { get: () => { return new Int32Array(memory.buffer, 664, 1); } }); + if(false) this.timestamp_counter = new Uint32Array(memory.buffer, 664, 1); // registers - this.reg32s = new Int32Array(wm.memory.buffer, 4, 8); - this.reg32 = new Uint32Array(this.reg32s.buffer, 4, 8); - this.reg16s = new Int16Array(this.reg32s.buffer, 4, 16); - this.reg16 = new Uint16Array(this.reg32s.buffer, 4, 16); - this.reg8s = new Int8Array(this.reg32s.buffer, 4, 32); - this.reg8 = new Uint8Array(this.reg32s.buffer, 4, 32); + if(false) this.reg32s = new Int32Array(memory.buffer, 4, 8); + if(false) this.reg32 = new Uint32Array(this.reg32s.buffer, 4, 8); + if(false) this.reg16s = new Int16Array(this.reg32s.buffer, 4, 16); + if(false) this.reg16 = new Uint16Array(this.reg32s.buffer, 4, 16); + if(false) this.reg8s = new Int8Array(this.reg32s.buffer, 4, 32); + if(false) this.reg8 = new Uint8Array(this.reg32s.buffer, 4, 32); + Object.defineProperty(this, "reg32s", { get: () => { return new Int32Array(memory.buffer, 4, 8); } }); + Object.defineProperty(this, "reg32", { get: () => { return new Uint32Array(memory.buffer, 4, 8); } }); + Object.defineProperty(this, "reg16s", { get: () => { return new Int16Array(memory.buffer, 4, 16); } }); + Object.defineProperty(this, "reg16", { get: () => { return new Uint16Array(memory.buffer, 4, 16); } }); + Object.defineProperty(this, "reg8s", { get: () => { return new Int8Array(memory.buffer, 4, 32); } }); + Object.defineProperty(this, "reg8", { get: () => { return new Uint8Array(memory.buffer, 4, 32); } }); // Why no Float80Array :-( - this.fpu_st = new Float64Array(wm.memory.buffer, 968, 8); + this.fpu_st = new Float64Array(memory.buffer, 968, 8); - this.fpu_stack_empty = new Int32Array(wm.memory.buffer, 816, 1); + this.fpu_stack_empty = new Int32Array(memory.buffer, 816, 1); this.fpu_stack_empty[0] = 0xff; - this.fpu_stack_ptr = new Uint32Array(wm.memory.buffer, 1032, 1); + this.fpu_stack_ptr = new Uint32Array(memory.buffer, 1032, 1); this.fpu_stack_ptr[0] = 0; - this.fpu_control_word = new Int32Array(wm.memory.buffer, 1036, 1); + this.fpu_control_word = new Int32Array(memory.buffer, 1036, 1); this.fpu_control_word[0] = 0x37F; - this.fpu_status_word = new Int32Array(wm.memory.buffer, 1040, 1); + this.fpu_status_word = new Int32Array(memory.buffer, 1040, 1); this.fpu_status_word[0] = 0; - this.fpu_ip = new Int32Array(wm.memory.buffer, 1048, 1); + this.fpu_ip = new Int32Array(memory.buffer, 1048, 1); this.fpu_ip[0] = 0; - this.fpu_ip_selector = new Int32Array(wm.memory.buffer, 1052, 1); + this.fpu_ip_selector = new Int32Array(memory.buffer, 1052, 1); this.fpu_ip_selector[0] = 0; - this.fpu_opcode = new Int32Array(wm.memory.buffer, 1044, 1); + this.fpu_opcode = new Int32Array(memory.buffer, 1044, 1); this.fpu_opcode[0] = 0; - this.fpu_dp = new Int32Array(wm.memory.buffer, 1056, 1); + this.fpu_dp = new Int32Array(memory.buffer, 1056, 1); this.fpu_dp[0] = 0; - this.fpu_dp_selector = new Int32Array(wm.memory.buffer, 1060, 1); + this.fpu_dp_selector = new Int32Array(memory.buffer, 1060, 1); this.fpu_dp_selector[0] = 0; // mm0-mm7 split up into 32 bit pairs - this.reg_mmxs = new Int32Array(wm.memory.buffer, 1064, 16); + this.reg_mmxs = new Int32Array(memory.buffer, 1064, 16); this.reg_mmx = new Uint32Array(this.reg_mmxs.buffer, 1064, 16); this.reg_mmx8s = new Int8Array(this.reg_mmxs.buffer, 1064, 64); this.reg_mmx8 = new Uint8Array(this.reg_mmxs.buffer, 1064, 64); - this.reg_xmm32s = new Int32Array(wm.memory.buffer, 828, 8 * 4); + this.reg_xmm32s = new Int32Array(memory.buffer, 828, 8 * 4); - this.mxcsr = new Int32Array(wm.memory.buffer, 824, 1); + this.mxcsr = new Int32Array(memory.buffer, 824, 1); // segment registers, tr and ldtr - this.sreg = new Uint16Array(wm.memory.buffer, 668, 8); + if(false) this.sreg = new Uint16Array(memory.buffer, 668, 8); + Object.defineProperty(this, "sreg", { get: () => { return new Uint16Array(memory.buffer, 668, 8); } }); // debug registers - this.dreg = new Int32Array(wm.memory.buffer, 684, 8); + this.dreg = new Int32Array(memory.buffer, 684, 8); - this.fw_value = new Int32Array(wm.memory.buffer, 720, 1); + this.fw_value = new Int32Array(memory.buffer, 720, 1); this.io = undefined; @@ -213,7 +253,7 @@ CPU.prototype.wasmgen_get_module_code = function() const ptr = this.jit_get_op_ptr(); const len = this.jit_get_op_len(); - const output_buffer_view = new Uint8Array(this.v86oxide.memory.buffer, ptr, len); + const output_buffer_view = new Uint8Array(this.v86oxide.instance.exports.memory.buffer, ptr, len); return output_buffer_view; }; @@ -228,18 +268,21 @@ CPU.prototype.create_jit_imports = function() } // put all imports that don't change on the prototype - JITImports.prototype["m"] = this.wm.memory; + JITImports.prototype["m"] = this.v86oxide.memory; - const exports = this.wm.instance.exports; + const exports = this.v86oxide.instance.exports; + + JITImports.prototype["m"] = exports["memory"]; for(let name of Object.keys(exports)) { - if(name[0] !== "_") - { - continue; - } + //if(name[0] !== "_") + //{ + // continue; + //} - JITImports.prototype[name.slice(1)] = exports[name]; + //JITImports.prototype[name.slice(1)] = exports[name]; + JITImports.prototype[name] = exports[name]; } this.jit_imports = new JITImports(); @@ -247,56 +290,57 @@ CPU.prototype.create_jit_imports = function() CPU.prototype.wasm_patch = function(wm) { - this.getiopl = this.wm.exports["_getiopl"]; - this.vm86_mode = this.wm.exports["_vm86_mode"]; - this.get_eflags = this.wm.exports["_get_eflags"]; - this.update_eflags = this.wm.exports["_update_eflags"]; + this.getiopl = this.v86oxide.exports["getiopl"]; + this.vm86_mode = this.v86oxide.exports["vm86_mode"]; + this.get_eflags = this.v86oxide.exports["get_eflags"]; + this.update_eflags = this.v86oxide.exports["update_eflags"]; - this.trigger_gp_non_raising = this.wm.exports["_trigger_gp_non_raising"]; - this.trigger_ud = this.wm.exports["_trigger_ud"]; - this.trigger_np = this.wm.exports["_trigger_np"]; - this.trigger_ss = this.wm.exports["_trigger_ss"]; + this.trigger_gp_non_raising = this.v86oxide.exports["trigger_gp_non_raising"]; + this.trigger_ud = this.v86oxide.exports["trigger_ud"]; + this.trigger_np = this.v86oxide.exports["trigger_np"]; + this.trigger_ss = this.v86oxide.exports["trigger_ss"]; - this.do_many_cycles_unsafe = this.wm.exports["_do_many_cycles_unsafe"]; - this.cycle_internal = this.wm.exports["_cycle_internal"]; + //this.do_many_cycles_unsafe = this.wm.exports["_do_many_cycles_unsafe"]; + this.do_many_cycles_unsafe = this.v86oxide.exports["do_many_cycles_unsafe"]; + this.cycle_internal = this.v86oxide.exports["cycle_internal"]; - this.read8 = this.wm.exports["_read8"]; - this.read16 = this.wm.exports["_read16"]; - this.read32s = this.wm.exports["_read32s"]; - this.write8 = this.wm.exports["_write8"]; - this.write16 = this.wm.exports["_write16"]; - this.write32 = this.wm.exports["_write32"]; - this.in_mapped_range = this.wm.exports["_in_mapped_range"]; + this.read8 = this.v86oxide.exports["read8"]; + this.read16 = this.v86oxide.exports["read16"]; + this.read32s = this.v86oxide.exports["read32s"]; + this.write8 = this.v86oxide.exports["write8"]; + this.write16 = this.v86oxide.exports["write16"]; + this.write32 = this.v86oxide.exports["write32"]; + this.in_mapped_range = this.v86oxide.exports["in_mapped_range"]; - this.push16 = this.wm.exports["_push16"]; - this.push32 = this.wm.exports["_push32"]; - this.pop16 = this.wm.exports["_pop16"]; - this.pop32s = this.wm.exports["_pop32s"]; + this.push16 = this.v86oxide.exports["push16"]; + this.push32 = this.v86oxide.exports["push32"]; + this.pop16 = this.v86oxide.exports["pop16"]; + this.pop32s = this.v86oxide.exports["pop32s"]; - this.set_stack_reg = this.wm.exports["_set_stack_reg"]; + this.set_stack_reg = this.v86oxide.exports["set_stack_reg"]; - this.translate_address_read = this.wm.exports["_translate_address_read"]; - this.translate_address_system_read = this.wm.exports["_translate_address_system_read"]; - this.translate_address_system_write = this.wm.exports["_translate_address_system_write"]; + this.translate_address_read = this.v86oxide.exports["translate_address_read"]; + this.translate_address_system_read = this.v86oxide.exports["translate_address_system_read"]; + this.translate_address_system_write = this.v86oxide.exports["translate_address_system_write"]; - this.get_seg = this.wm.exports["_get_seg"]; - this.adjust_stack_reg = this.wm.exports["_adjust_stack_reg"]; - this.get_real_eip = this.wm.exports["_get_real_eip"]; - this.get_stack_pointer = this.wm.exports["_get_stack_pointer"]; + this.get_seg = this.v86oxide.exports["get_seg"]; + this.adjust_stack_reg = this.v86oxide.exports["adjust_stack_reg"]; + this.get_real_eip = this.v86oxide.exports["get_real_eip"]; + this.get_stack_pointer = this.v86oxide.exports["get_stack_pointer"]; - this.writable_or_pagefault = this.wm.exports["_writable_or_pagefault"]; - this.safe_write32 = this.wm.exports["_safe_write32"]; - this.safe_read32s = this.wm.exports["_safe_read32s"]; - this.safe_write16 = this.wm.exports["_safe_write16"]; - this.safe_read16 = this.wm.exports["_safe_read16"]; + this.writable_or_pagefault = this.v86oxide.exports["writable_or_pagefault"]; + this.safe_write32 = this.v86oxide.exports["safe_write32"]; + this.safe_read32s = this.v86oxide.exports["safe_read32s"]; + this.safe_write16 = this.v86oxide.exports["safe_write16"]; + this.safe_read16 = this.v86oxide.exports["safe_read16"]; - this.clear_tlb = this.wm.exports["_clear_tlb"]; - this.full_clear_tlb = this.wm.exports["_full_clear_tlb"]; + this.clear_tlb = this.v86oxide.exports["clear_tlb"]; + this.full_clear_tlb = this.v86oxide.exports["full_clear_tlb"]; - this.set_tsc = this.wm.exports["_set_tsc"]; - this.store_current_tsc = this.wm.exports["_store_current_tsc"]; + this.set_tsc = this.v86oxide.exports["set_tsc"]; + this.store_current_tsc = this.v86oxide.exports["store_current_tsc"]; - this.pack_current_state_flags = this.wm.exports["_pack_current_state_flags"]; + this.pack_current_state_flags = this.v86oxide.exports["pack_current_state_flags"]; this.jit_force_generate_unsafe = this.v86oxide.exports["jit_force_generate_unsafe"]; this.jit_empty_cache = this.v86oxide.exports["jit_empty_cache"]; @@ -317,7 +361,7 @@ CPU.prototype.jit_force_generate = function(addr) CPU.prototype.jit_clear_func = function(index) { dbg_assert(index >= 0 && index < WASM_TABLE_SIZE); - this.wm.imports.env[WASM_EXPORT_TABLE_NAME].set(index, null); + this.wm.imports.env[WASM_EXPORT_TABLE_NAME + 0x100].set(index, null); }; CPU.prototype.get_state = function() @@ -670,11 +714,11 @@ CPU.prototype.create_memory = function(size) this.memory_size[0] = size; - var buffer = this.wm.memory.buffer; + //var buffer = this.wm.memory.buffer; - this.mem8 = new Uint8Array(buffer, GUEST_MEMORY_START, size); - this.mem16 = new Uint16Array(buffer, GUEST_MEMORY_START, size >> 1); - this.mem32s = new Int32Array(buffer, GUEST_MEMORY_START, size >> 2); + //this.mem8 = new Uint8Array(buffer, GUEST_MEMORY_START, size); + //this.mem16 = new Uint16Array(buffer, GUEST_MEMORY_START, size >> 1); + //this.mem32s = new Int32Array(buffer, GUEST_MEMORY_START, size >> 2); }; CPU.prototype.init = function(settings, device_bus) @@ -833,7 +877,7 @@ CPU.prototype.init = function(settings, device_bus) this.debug.init(); } - this.wm.exports["_profiler_init"](); + //this.wm.exports["_profiler_init"](); }; CPU.prototype.load_multiboot = function(buffer) @@ -1142,7 +1186,7 @@ CPU.prototype.load_bios = function() CPU.prototype.do_run = function() { - this.wm.exports["_profiler_stat_increment_do_run"](); + //this.wm.exports["_profiler_stat_increment_do_run"](); /** @type {number} */ var start = v86.microtick(); @@ -1179,7 +1223,7 @@ let do_many_cycles_total = 0; CPU.prototype.do_many_cycles = function() { // Capture the total time we were executing instructions - this.coverage_logger.log_start(); + //this.coverage_logger.log_start(); if(ENABLE_PROFILER) { @@ -1200,7 +1244,7 @@ CPU.prototype.do_many_cycles = function() do_many_cycles_count++; } - this.coverage_logger.log_end(); + //this.coverage_logger.log_end(); }; /** @export */ @@ -1284,7 +1328,7 @@ CPU.prototype.codegen_finalize = function(wasm_table_index, start, end, first_op first_opcode, state_flags); // The following will throw if f isn't an exported function - this.wm.imports["env"][WASM_EXPORT_TABLE_NAME].set(wasm_table_index, f); + this.wm.imports["env"][WASM_EXPORT_TABLE_NAME].set(wasm_table_index + 0x100, f); if(this.test_hook_did_finalize_wasm) { @@ -1346,7 +1390,7 @@ CPU.prototype.dump_function_code = function(block_ptr, count) const SIZEOF_BASIC_BLOCK_IN_DWORDS = 7; - const mem32 = new Int32Array(this.wm.memory.buffer); + const mem32 = new Int32Array(this.v86oxide.instance.exports.memory.buffer); dbg_assert((block_ptr & 3) === 0); @@ -1463,11 +1507,12 @@ CPU.prototype.jit_clear_cache = function() { this.jit_empty_cache(); - const table = this.wm.imports["env"][WASM_EXPORT_TABLE_NAME]; + const table = this.wm.exports[WASM_EXPORT_TABLE_NAME] || this.wm.imports["env"][WASM_EXPORT_TABLE_NAME]; + const offset = 0x100; for(let i = 0; i < WASM_TABLE_SIZE; i++) { - table.set(i, null); + table.set(offset + i, null); } }; diff --git a/src/rust/cpu.rs b/src/rust/cpu.rs index ff45fdd9..674751d3 100644 --- a/src/rust/cpu.rs +++ b/src/rust/cpu.rs @@ -8,7 +8,7 @@ mod unsafe_cpu { pub fn tlb_set_has_code(physical_page: u32, has_code: bool); pub fn read8(addr: u32) -> u8; pub fn read16(addr: u32) -> u16; - pub fn read32(addr: u32) -> u32; + pub fn read32s(addr: u32) -> u32; pub fn check_tlb_invariants(); pub fn codegen_finalize( @@ -29,7 +29,7 @@ pub enum BitSize { pub fn read8(addr: u32) -> u8 { unsafe { unsafe_cpu::read8(addr) } } pub fn read16(addr: u32) -> u16 { unsafe { unsafe_cpu::read16(addr) } } -pub fn read32(addr: u32) -> u32 { unsafe { unsafe_cpu::read32(addr) } } +pub fn read32(addr: u32) -> u32 { unsafe { unsafe_cpu::read32s(addr) } } pub fn tlb_set_has_code(physical_page: Page, has_code: bool) { unsafe { unsafe_cpu::tlb_set_has_code(physical_page.to_u32(), has_code) } diff --git a/src/rust/cpu2/Makefile b/src/rust/cpu2/Makefile new file mode 100644 index 00000000..592e3d8c --- /dev/null +++ b/src/rust/cpu2/Makefile @@ -0,0 +1,86 @@ +ALL:= arith.rs cpu.rs fpu.rs instructions_0f.rs instructions.rs \ + memory.rs misc_instr.rs modrm.rs shared.rs sse_instr.rs string.rs \ + global_pointers.rs profiler.rs + +all: $(ALL) + +profiler.rs: profiler/profiler.rs + cp profiler/profiler.rs profiler.rs + +%.rs: + #citrus --api=rust $< -I ../../native/ ../../native/profiler/ > $@.tmp + #cat import-prefix $@.tmp | grep -v cpu2::$(basename $@):: > $@ + #rm $@.tmp + #sed -i 's/assert(/assert!(/' $@ + #sed -i 's/dbg_assert(/dbg_assert!(/' $@ + #sed -i 's/dbg_log(/dbg_log!(/' $@ + #sed -i 's/%x/{:x}/g' $@ + #sed -i 's/%d/{}/g' $@ + #sed -i '/:.*= ()/d' $@ + #sed -i 's/!= 0 != 0//' $@ + #sed -i 's/fn(,/fn(i32,/' $@ + #sed -i 's/is_32 != 0/is_32/' $@ + #sed -i 's/SAFE_READ_WRITE8/SAFE_READ_WRITE8!/' $@ + #sed -i 's/SAFE_READ_WRITE16/SAFE_READ_WRITE16!/' $@ + #sed -i 's/SAFE_READ_WRITE32/SAFE_READ_WRITE32!/' $@ + #sed -i 's/pub fn SAFE_READ_WRITE8!();//' $@ + #sed -i 's/pub fn SAFE_READ_WRITE16!();//' $@ + #sed -i 's/pub fn SAFE_READ_WRITE32!();//' $@ + + cp ~/DL/c2rust/scripts/v86/src/native/$@ $@ + #cat import-prefix $@.tmp | grep -v cpu2::$(basename $@):: > $@ + #cat $@.tmp | grep -v cpu2::$(basename $@):: > $@ + -rm $@.tmp + sed -i 's/assert(/assert_c!(/' $@ + #sed -i 's/dbg_assert(/dbg_assert!(/' $@ + sed -i 's/dbg_log(/dbg_log_c!(/' $@ + #sed -i 's/pub unsafe extern "C" fn assert_c!() -> () { }//' $@ + #sed -i 's/pub unsafe extern "C" fn dbg_assert_c!() -> () { }//' $@ + #sed -i 's/pub unsafe extern "C" fn dbg_log_c!() -> () { }//' $@ + + sed -i 's/fn assert_c!() -> ();//' $@ + sed -i 's/fn dbg_assert_c!() -> ();//' $@ + sed -i 's/fn dbg_log_c!() -> ();//' $@ + + sed -i 's/fn assert_c!/fn assert_c/' $@ + sed -i 's/fn dbg_assert_c!/fn dbg_assert_c/' $@ + sed -i 's/fn dbg_log_c!/fn dbg_log_c/' $@ + + sed -i 's/libc::c_char/i8/g' $@ + sed -i 's/libc::c_double/f64/g' $@ + sed -i 's/libc::c_float/f32/g' $@ + sed -i 's/libc::c_int/i32/g' $@ + sed -i 's/libc::c_longlong/i64/g' $@ + sed -i 's/libc::c_long/i64/g' $@ + sed -i 's/libc::c_schar/i8/g' $@ + sed -i 's/libc::c_short/i16/g' $@ + sed -i 's/libc::c_uchar/u8/g' $@ + sed -i 's/libc::c_uint/u32/g' $@ + sed -i 's/libc::c_ulonglong/u64/g' $@ + sed -i 's/libc::c_ulong/u64/g' $@ + sed -i 's/libc::c_ushort/u16/g' $@ + + sed -i 's/SAFE_READ_WRITE8/SAFE_READ_WRITE8!/' $@ + sed -i 's/SAFE_READ_WRITE16/SAFE_READ_WRITE16!/' $@ + sed -i 's/SAFE_READ_WRITE32/SAFE_READ_WRITE32!/' $@ + sed -i 's/fn SAFE_READ_WRITE8!() -> ();//' $@ + sed -i 's/fn SAFE_READ_WRITE16!() -> ();//' $@ + sed -i 's/fn SAFE_READ_WRITE32!() -> ();//' $@ + + sed -i 's/pub type _IO_FILE;//' $@ + sed -i 's/pub type FILE = _IO_FILE;//' $@ + sed -i ':a;N;$$!ba;s/#\[no_mangle]\n static stdin: \*mut FILE;//' $@ + sed -i ':a;N;$$!ba;s/#\[no_mangle]\n static stdout: \*mut FILE;//' $@ + sed -i ':a;N;$$!ba;s/#\[no_mangle]\n static stderr: \*mut FILE;//' $@ + + sed -i ':a;N;$$!ba;s/#\[no_mangle]\n static mut ___: int32_t;//' $@ + + sed -i 's/SAFE_READ_WRITE8!(/SAFE_READ_WRITE8!(___, /' $@ + sed -i 's/SAFE_READ_WRITE16!(/SAFE_READ_WRITE16!(___, /' $@ + sed -i 's/SAFE_READ_WRITE32!(/SAFE_READ_WRITE32!(___, /' $@ + + sed -i 's/extern crate libc;//' $@ + +clean: + -rm $(ALL) + -rm profiler/profiler.rs diff --git a/src/rust/cpu2/imports.rs b/src/rust/cpu2/imports.rs new file mode 100644 index 00000000..982548f9 --- /dev/null +++ b/src/rust/cpu2/imports.rs @@ -0,0 +1,46 @@ +#[no_mangle] +pub unsafe extern "C" fn run_instruction(opcode: u32) { ::gen::interpreter::run(opcode) } +#[no_mangle] +pub unsafe extern "C" fn run_instruction0f_16(opcode: u32) { + ::gen::interpreter0f_16::run(opcode as u8) +} +#[no_mangle] +pub unsafe extern "C" fn run_instruction0f_32(opcode: u32) { + ::gen::interpreter0f_32::run(opcode as u8) +} + +#[no_mangle] +pub fn sqrt(x: f64) -> f64 { x.sqrt() } + +#[no_mangle] +pub fn sqrtf(x: f32) -> f32 { x.sqrt() } + +#[no_mangle] +pub fn profiler_stat_increment(stat: u32) {} + +#[no_mangle] +pub fn profiler_stat_increment_by(stat: u32, by: u32) {} + +#[no_mangle] +pub fn call_indirect1(f: fn(u16), x: u16) { f(x); } + +macro_rules! dbg_assert_c { + ($fmt:expr) => { + dbg_assert!($fmt != 0); + }; + ($fmt:expr, $($arg:tt)*) => { + dbg_assert!($fmt != 0, $arg); + }; +} +macro_rules! dbg_log_c { + ($fmt:expr) => {}; + ($fmt:expr, $($arg:tt)*) => {}; +} +macro_rules! assert_c { + ($fmt:expr) => { + assert!($fmt != 0); + }; + ($fmt:expr, $($arg:tt)*) => { + assert!($fmt != 0, $arg); + }; +} diff --git a/src/rust/cpu2/instruction_helpers.rs b/src/rust/cpu2/instruction_helpers.rs new file mode 100644 index 00000000..b435de7e --- /dev/null +++ b/src/rust/cpu2/instruction_helpers.rs @@ -0,0 +1,41 @@ +macro_rules! SAFE_READ_WRITE8 { + ($value:ident, $addr:expr, $instruction:expr) => {{ + use cpu2::cpu::translate_address_write; + use cpu2::memory::{read8, write8}; + let phys_addr = translate_address_write($addr); + let $value = read8(phys_addr); + write8(phys_addr, $instruction); + }}; +} +macro_rules! SAFE_READ_WRITE16 { + ($value:ident, $addr:expr, $instruction:expr) => {{ + use cpu2::cpu::{translate_address_write, virt_boundary_read16, virt_boundary_write16}; + use cpu2::memory::{read16, write16}; + let phys_addr = translate_address_write($addr) as i32; + if phys_addr & 0xFFF == 0xFFF { + let phys_addr_high = translate_address_write($addr + 1) as i32; + let $value = virt_boundary_read16(phys_addr, phys_addr_high); + virt_boundary_write16(phys_addr, phys_addr_high, $instruction); + } + else { + let $value = read16(phys_addr as u32); + write16(phys_addr as u32, $instruction); + } + }}; +} +macro_rules! SAFE_READ_WRITE32 { + ($value:ident, $addr:expr, $instruction:expr) => {{ + use cpu2::cpu::{translate_address_write, virt_boundary_read32s, virt_boundary_write32}; + use cpu2::memory::{read32s, write32}; + let phys_addr = translate_address_write($addr); + if phys_addr & 0xFFF >= 0xFFD { + let phys_addr_high = translate_address_write($addr + 3 & !3) as i32 | $addr + 3 & 3; + let $value = virt_boundary_read32s(phys_addr as i32, phys_addr_high); + virt_boundary_write32(phys_addr as i32, phys_addr_high as i32, $instruction); + } + else { + let $value = read32s(phys_addr); + write32(phys_addr, $instruction); + } + }}; +} diff --git a/src/rust/cpu2/mod.rs b/src/rust/cpu2/mod.rs new file mode 100644 index 00000000..f828660d --- /dev/null +++ b/src/rust/cpu2/mod.rs @@ -0,0 +1,20 @@ +#![allow(unused_assignments, unused_variables, unused_unsafe)] + +#[macro_use] +pub mod imports; + +#[macro_use] +mod instruction_helpers; + +pub mod arith; +pub mod cpu; +pub mod fpu; +pub mod global_pointers; +pub mod instructions; +pub mod instructions_0f; +pub mod memory; +pub mod misc_instr; +pub mod modrm; +pub mod shared; +pub mod sse_instr; +pub mod string; diff --git a/src/rust/dbg.rs b/src/rust/dbg.rs index f507b94d..d03dbb7f 100644 --- a/src/rust/dbg.rs +++ b/src/rust/dbg.rs @@ -45,6 +45,7 @@ macro_rules! dbg_assert { column!(), stringify!($cond), )); + #[allow(unused_unsafe)] unsafe { abort(); } @@ -61,6 +62,7 @@ macro_rules! dbg_assert { stringify!($cond), $desc, )); + #[allow(unused_unsafe)] unsafe { abort(); } diff --git a/src/rust/gen/mod.rs b/src/rust/gen/mod.rs index e7a048ce..b9e11b0e 100644 --- a/src/rust/gen/mod.rs +++ b/src/rust/gen/mod.rs @@ -1,3 +1,7 @@ +pub mod interpreter; +pub mod interpreter0f_16; +pub mod interpreter0f_32; + pub mod jit; pub mod jit0f_16; pub mod jit0f_32; diff --git a/src/rust/lib.rs b/src/rust/lib.rs index d4461fba..6ec12ce0 100644 --- a/src/rust/lib.rs +++ b/src/rust/lib.rs @@ -1,4 +1,5 @@ #![feature(const_fn)] +#![feature(extern_types)] #[cfg(test)] #[macro_use] @@ -7,7 +8,7 @@ extern crate quickcheck; #[macro_use] mod dbg; -mod cpu2; +pub mod cpu2; pub mod c_api; diff --git a/tools/rust-lld-wrapper b/tools/rust-lld-wrapper new file mode 100755 index 00000000..70826f65 --- /dev/null +++ b/tools/rust-lld-wrapper @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +# A wrapper for rust-lld that removes certain arguments inserted by rustc that +# we'd like to override + +import sys +import subprocess +from os import path + +def main(): + args = sys.argv[1:] + + # filter out args inserted by rustc + TO_REMOVE = {"--stack-first"} + args = list(filter(lambda arg: arg not in TO_REMOVE, args)) + + lld = find_rust_lld() + + result = subprocess.run([lld] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + result.check_returncode() + + print(result.stderr, file=sys.stderr) + print(result.stdout) + +def find_rust_lld(): + which = subprocess.run(["rustup", "which", "rustc"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + which.check_returncode() + + rustc_path = which.stdout.decode("utf8").strip() + assert path.basename(rustc_path) == "rustc" + + bin_path = path.dirname(rustc_path) + rust_lld_path = path.join(bin_path, "../lib/rustlib/x86_64-unknown-linux-gnu/bin/rust-lld") + + assert path.isfile(rust_lld_path) + return rust_lld_path + +main() diff --git a/tools/wasm-patch-indirect-function-table.js b/tools/wasm-patch-indirect-function-table.js new file mode 100755 index 00000000..e1026368 --- /dev/null +++ b/tools/wasm-patch-indirect-function-table.js @@ -0,0 +1,184 @@ +#!/usr/bin/env node +"use strict"; + +// Read a wasm module in binary format from stdin, find table import entries, i.e.: +// +// (import "env" "table" (table (;0;) anyfunc)) +// +// Remove the and write the patched wasm module to stdout. + +process.on("unhandledRejection", exn => { throw exn; }); + +const fs = require("fs"); + +const SECTION_IMPORT = 2; + +const IMPORT_KIND_FUNCTION = 0; +const IMPORT_KIND_TABLE = 1; +const IMPORT_KIND_MEMORY = 2; +const IMPORT_KIND_GLOBAL = 3; + +function main() +{ + const wasm = fs.readFileSync("/dev/stdin"); + const view = new DataView(wasm.buffer); + var ptr = 0; + + // magic + console.assert(view.getUint32(ptr, true) === 0x6d736100); + ptr += 4; + + // version + console.assert(view.getUint32(ptr, true) === 1); + ptr += 4; + + while(ptr < view.byteLength) + { + const section_id = view.getUint8(ptr); + ptr++; + var { ptr, value: size } = read_leb_u32(ptr, view); + const section_end = ptr + size; + + if(section_id === SECTION_IMPORT) + { + patch_import_section(ptr, view); + } + + ptr = section_end; + } + + // sanity check + const module = new WebAssembly.Module(view.buffer); + + process.stdout.write(wasm); +} + +function patch_import_section(ptr, view) +{ + var { ptr, value: section_entry_count } = read_leb_u32(ptr, view); + + for(let i = 0; i < section_entry_count; i++) + { + var { ptr, value: module_str_length } = read_leb_u32(ptr, view); + ptr += module_str_length; + var { ptr, value: field_str_length } = read_leb_u32(ptr, view); + ptr += field_str_length; + + const kind = view.getUint8(ptr); + ptr++; + + if(kind === IMPORT_KIND_FUNCTION) + { + var { ptr, value: function_signature_index } = read_leb_u32(ptr, view); + } + else if(kind === IMPORT_KIND_TABLE) + { + const table_offset = ptr; + var { ptr, value: table_element_type } = read_leb_u32(ptr, view); + console.assert(table_element_type === 0x70); + + const maximum_present = new Uint8Array(view.buffer, ptr, 1); + console.assert(maximum_present[0] === 0 || maximum_present[0] === 1); + ptr++; + + var { ptr, value: initial_table_size, leb_view: initial_table_size_view } = read_leb_u32(ptr, view); + + if(maximum_present[0]) + { + var { ptr, value: maximum_table_size, leb_view: maximum_table_size_view } = read_leb_u32(ptr, view); + } + else + { + maximum_table_size = -1; + } + + console.error(`Found table import at offset` + + ` ${table_offset}` + + ` maximum_present=${maximum_present[0]}` + + ` initial=${initial_table_size}` + + ` maximum=${maximum_table_size}`); + + if(maximum_present[0]) + { + patch_maximum_limit(maximum_present, initial_table_size_view, maximum_table_size_view); + console.error("Patched!"); + } + else + { + console.error("No maximum present, skipped"); + } + } + else if(kind === IMPORT_KIND_MEMORY) + { + const maximum_present = view.getUint8(ptr); + console.assert(maximum_present === 0 || maximum_present === 1); + ptr++; + + var { ptr, value: initial_memory_size } = read_leb_u32(ptr, view); + + if(maximum_present) + { + var { ptr, value: maximum_memory_size } = read_leb_u32(ptr, view); + } + } + else if(kind === IMPORT_KIND_GLOBAL) + { + const content_type = view.getUint8(ptr); + ptr++; + const mutability = view.getUint8(ptr); + console.assert(mutability === 0 || mutability === 1); + ptr++; + } + else + { + console.assert(false, `Unexpected import kind: 0x${kind.toString(16)} at offset ${ptr - 1}`); + } + } +} + +function patch_maximum_limit(maximum_present, initial_size, maximum_size) +{ + // clear the maximum present bit + maximum_present[0] = 0; + + // set the highest bit of the initial size, in order to use it to pad the existing maximum size bytes + const last_byte_initial_size = initial_size[initial_size.length - 1]; + console.assert((last_byte_initial_size & 0x80) === 0); + initial_size[initial_size.length - 1] = last_byte_initial_size | 0x80; + + for(let i = 0; i < maximum_size.length - 1; i++) + { + // pad maximum value with 0x80 bytes + maximum_size[i] = 0x80; + } + + // pad the last byte of the maximum value with 0x00 + maximum_size[maximum_size.length - 1] = 0x00; +} + +function read_leb_u32(ptr, view) +{ + let value = 0; + let byte_length = 0; + + while(true) + { + let byte = view.getUint8(ptr++); + + value |= (byte & 0x7f) << (byte_length * 7); + byte_length++; + + if((byte & 0x80) === 0) + { + break; + } + } + + console.assert(byte_length <= 4); + + const leb_view = new Uint8Array(view.buffer, ptr - byte_length, byte_length); + + return { ptr, value, leb_view }; +} + +main();