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
This commit is contained in:
Fabian 2018-08-08 16:04:19 -05:00
parent 6de0bb374b
commit 01061dc4b6
19 changed files with 821 additions and 294 deletions

3
.gitignore vendored
View file

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

View file

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

View file

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

View file

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

View file

@ -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"
);
}
}

View file

@ -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);

View file

@ -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);
}
});
//});
});
}

View file

@ -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 */

View file

@ -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);
}
};

View file

@ -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) }

86
src/rust/cpu2/Makefile Normal file
View file

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

46
src/rust/cpu2/imports.rs Normal file
View file

@ -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);
};
}

View file

@ -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);
}
}};
}

20
src/rust/cpu2/mod.rs Normal file
View file

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

View file

@ -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();
}

View file

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

View file

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

38
tools/rust-lld-wrapper Executable file
View file

@ -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()

View file

@ -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;) <initial> <maximum> anyfunc))
//
// Remove the <maximum> 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();