diff --git a/Cargo.lock b/Cargo.lock index 7c2186966..86d38e4d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2196,6 +2196,7 @@ version = "0.2.1" dependencies = [ "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-clif-backend 0.2.0", diff --git a/Cargo.toml b/Cargo.toml index 306e7edff..2a3b41171 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ include = [ errno = "0.2.4" structopt = "0.2.11" wabt = "0.7.2" +hashbrown = "0.1.8" wasmer-clif-backend = { path = "lib/clif-backend" } wasmer-dynasm-backend = { path = "lib/dynasm-backend", optional = true } wasmer-runtime = { path = "lib/runtime" } diff --git a/lib/clif-backend/src/lib.rs b/lib/clif-backend/src/lib.rs index 88de51e12..d09da6812 100644 --- a/lib/clif-backend/src/lib.rs +++ b/lib/clif-backend/src/lib.rs @@ -16,7 +16,7 @@ use target_lexicon::Triple; use wasmer_runtime_core::cache::{Artifact, Error as CacheError}; use wasmer_runtime_core::{ - backend::{Compiler, Token}, + backend::{Compiler, CompilerConfig, Token}, error::{CompileError, CompileResult}, module::ModuleInner, }; @@ -39,12 +39,17 @@ impl CraneliftCompiler { impl Compiler for CraneliftCompiler { /// Compiles wasm binary to a wasmer module. - fn compile(&self, wasm: &[u8], _: Token) -> CompileResult { + fn compile( + &self, + wasm: &[u8], + compiler_config: CompilerConfig, + _: Token, + ) -> CompileResult { validate(wasm)?; let isa = get_isa(); - let mut module = module::Module::new(); + let mut module = module::Module::new(&compiler_config); let module_env = module_env::ModuleEnv::new(&mut module, &*isa); let func_bodies = module_env.translate(wasm)?; diff --git a/lib/clif-backend/src/module.rs b/lib/clif-backend/src/module.rs index 521f41cef..521918185 100644 --- a/lib/clif-backend/src/module.rs +++ b/lib/clif-backend/src/module.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use wasmer_runtime_core::cache::{Artifact, Error as CacheError}; use wasmer_runtime_core::{ - backend::Backend, + backend::{Backend, CompilerConfig}, error::CompileResult, module::{ModuleInfo, ModuleInner, StringTable}, structures::{Map, TypedIndex}, @@ -25,7 +25,7 @@ pub struct Module { } impl Module { - pub fn new() -> Self { + pub fn new(compiler_config: &CompilerConfig) -> Self { Self { info: ModuleInfo { memories: Map::new(), @@ -50,6 +50,7 @@ impl Module { namespace_table: StringTable::new(), name_table: StringTable::new(), + em_symbol_map: compiler_config.symbol_map.clone(), custom_sections: HashMap::new(), }, diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index 94713e0b2..e2706b6e1 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -374,20 +374,26 @@ fn round_up(n: usize, multiple: usize) -> usize { } extern "C" fn i32_print(_ctx: &mut vm::Ctx, n: i32) { - print!(" i32: {},", n); + eprint!(" i32: {},", n); } extern "C" fn i64_print(_ctx: &mut vm::Ctx, n: i64) { - print!(" i64: {},", n); + eprint!(" i64: {},", n); } extern "C" fn f32_print(_ctx: &mut vm::Ctx, n: f32) { - print!(" f32: {},", n); + eprint!(" f32: {},", n); } extern "C" fn f64_print(_ctx: &mut vm::Ctx, n: f64) { - print!(" f64: {},", n); + eprint!(" f64: {},", n); } -extern "C" fn start_debug(_ctx: &mut vm::Ctx, func_index: u32) { - print!("func ({}), args: [", func_index); +extern "C" fn start_debug(ctx: &mut vm::Ctx, func_index: u32) { + if let Some(symbol_map) = unsafe { ctx.borrow_symbol_map() } { + if let Some(fn_name) = symbol_map.get(&func_index) { + eprint!("func ({} ({})), args: [", fn_name, func_index); + return; + } + } + eprint!("func ({}), args: [", func_index); } extern "C" fn end_debug(_ctx: &mut vm::Ctx) { - println!(" ]"); + eprintln!(" ]"); } diff --git a/lib/dynasm-backend/src/lib.rs b/lib/dynasm-backend/src/lib.rs index 1647611bb..5d1aa5f96 100644 --- a/lib/dynasm-backend/src/lib.rs +++ b/lib/dynasm-backend/src/lib.rs @@ -25,7 +25,7 @@ mod stack; use crate::codegen::{CodegenError, ModuleCodeGenerator}; use crate::parse::LoadError; use wasmer_runtime_core::{ - backend::{sys::Memory, Backend, CacheGen, Compiler, Token}, + backend::{sys::Memory, Backend, CacheGen, Compiler, CompilerConfig, Token}, cache::{Artifact, Error as CacheError}, error::{CompileError, CompileResult}, module::{ModuleInfo, ModuleInner}, @@ -51,9 +51,14 @@ impl SinglePassCompiler { } impl Compiler for SinglePassCompiler { - fn compile(&self, wasm: &[u8], _: Token) -> CompileResult { + fn compile( + &self, + wasm: &[u8], + compiler_config: CompilerConfig, + _: Token, + ) -> CompileResult { let mut mcg = codegen_x64::X64ModuleCodeGenerator::new(); - let info = parse::read_module(wasm, Backend::Dynasm, &mut mcg)?; + let info = parse::read_module(wasm, Backend::Dynasm, &mut mcg, &compiler_config)?; let (ec, resolver) = mcg.finalize(&info)?; Ok(ModuleInner { cache_gen: Box::new(Placeholder), diff --git a/lib/dynasm-backend/src/parse.rs b/lib/dynasm-backend/src/parse.rs index fce3188d7..b198ea4c5 100644 --- a/lib/dynasm-backend/src/parse.rs +++ b/lib/dynasm-backend/src/parse.rs @@ -1,7 +1,7 @@ use crate::codegen::{CodegenError, FunctionCodeGenerator, ModuleCodeGenerator}; use hashbrown::HashMap; use wasmer_runtime_core::{ - backend::{Backend, FuncResolver, ProtectedCaller}, + backend::{Backend, CompilerConfig, FuncResolver, ProtectedCaller}, module::{ DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder, TableInitializer, @@ -71,6 +71,7 @@ pub fn read_module< wasm: &[u8], backend: Backend, mcg: &mut MCG, + compiler_config: &CompilerConfig, ) -> Result { validate(wasm)?; let mut info = ModuleInfo { @@ -97,6 +98,8 @@ pub fn read_module< namespace_table: StringTable::new(), name_table: StringTable::new(), + em_symbol_map: compiler_config.symbol_map.clone(), + custom_sections: HashMap::new(), }; diff --git a/lib/emscripten/src/syscalls/emscripten_vfs.rs b/lib/emscripten/src/syscalls/emscripten_vfs.rs index b9e8a9439..927b07c34 100644 --- a/lib/emscripten/src/syscalls/emscripten_vfs.rs +++ b/lib/emscripten/src/syscalls/emscripten_vfs.rs @@ -123,7 +123,7 @@ impl EmscriptenVfs { Some(FileHandle::Vf(file)) => { let count = { let mut result = RefCell::borrow_mut(&file); - let result = result.read(buf_slice); + let result = result.read_file(buf_slice, 0); result.unwrap() }; count as _ diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs index 85bcc7e19..ee4e02bbc 100644 --- a/lib/llvm-backend/src/lib.rs +++ b/lib/llvm-backend/src/lib.rs @@ -6,7 +6,7 @@ use inkwell::{ OptimizationLevel, }; use wasmer_runtime_core::{ - backend::{Compiler, Token}, + backend::{Compiler, CompilerConfig, Token}, cache::{Artifact, Error as CacheError}, error::CompileError, module::ModuleInner, @@ -32,10 +32,15 @@ impl LLVMCompiler { } impl Compiler for LLVMCompiler { - fn compile(&self, wasm: &[u8], _: Token) -> Result { + fn compile( + &self, + wasm: &[u8], + compiler_config: CompilerConfig, + _: Token, + ) -> Result { validate(wasm)?; - let (info, code_reader) = read_info::read_module(wasm).unwrap(); + let (info, code_reader) = read_info::read_module(wasm, compiler_config).unwrap(); let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); let (backend, protected_caller) = backend::LLVMBackend::new(module, intrinsics); @@ -121,7 +126,7 @@ fn test_read_module() { "#; let wasm = wat2wasm(wat).unwrap(); - let (info, code_reader) = read_info::read_module(&wasm).unwrap(); + let (info, code_reader) = read_info::read_module(&wasm, Default::default()).unwrap(); let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); diff --git a/lib/llvm-backend/src/read_info.rs b/lib/llvm-backend/src/read_info.rs index 596e06b52..5ebfa5868 100644 --- a/lib/llvm-backend/src/read_info.rs +++ b/lib/llvm-backend/src/read_info.rs @@ -1,5 +1,5 @@ use wasmer_runtime_core::{ - backend::Backend, + backend::{Backend, CompilerConfig}, module::{ DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder, TableInitializer, @@ -20,7 +20,10 @@ use wasmparser::{ use hashbrown::HashMap; -pub fn read_module(wasm: &[u8]) -> Result<(ModuleInfo, CodeSectionReader), BinaryReaderError> { +pub fn read_module( + wasm: &[u8], + compiler_config: CompilerConfig, +) -> Result<(ModuleInfo, CodeSectionReader), BinaryReaderError> { let mut info = ModuleInfo { memories: Map::new(), globals: Map::new(), @@ -45,6 +48,8 @@ pub fn read_module(wasm: &[u8]) -> Result<(ModuleInfo, CodeSectionReader), Binar namespace_table: StringTable::new(), name_table: StringTable::new(), + em_symbol_map: compiler_config.symbol_map.clone(), + custom_sections: HashMap::new(), }; diff --git a/lib/runtime-abi/src/vfs/device_file.rs b/lib/runtime-abi/src/vfs/device_file.rs index fb0616644..e7ca46530 100644 --- a/lib/runtime-abi/src/vfs/device_file.rs +++ b/lib/runtime-abi/src/vfs/device_file.rs @@ -1,7 +1,7 @@ use crate::vfs::file_like::{FileLike, Metadata}; use failure::Error; use std::io; -use std::io::{Read, Write}; +use std::io::{Seek, Write}; pub struct Stdin; pub struct Stdout; @@ -15,10 +15,18 @@ impl FileLike for Stdin { fn write_file(&mut self, _buf: &[u8], _offset: usize) -> Result { unimplemented!() } + + fn read_file(&mut self, _buf: &mut [u8], _offset: usize) -> Result { + unimplemented!() + } + + fn set_file_len(&mut self, _len: usize) -> Result<(), failure::Error> { + panic!("Cannot set length of stdin"); + } } -impl Read for Stdin { - fn read(&mut self, _buf: &mut [u8]) -> Result { +impl io::Seek for Stdin { + fn seek(&mut self, _pos: io::SeekFrom) -> Result { unimplemented!() } } @@ -33,10 +41,19 @@ impl FileLike for Stdout { let mut handle = stdout.lock(); handle.write(buf) } + + fn read_file(&mut self, _buf: &mut [u8], _offset: usize) -> Result { + unimplemented!() + } + + fn set_file_len(&mut self, _len: usize) -> Result<(), failure::Error> { + panic!("Cannot set length of stdout"); + + } } -impl Read for Stdout { - fn read(&mut self, _buf: &mut [u8]) -> Result { +impl io::Seek for Stdout { + fn seek(&mut self, _pos: io::SeekFrom) -> Result { unimplemented!() } } @@ -51,10 +68,18 @@ impl FileLike for Stderr { let mut handle = stderr.lock(); handle.write(buf) } + + fn read_file(&mut self, _buf: &mut [u8], _offset: usize) -> Result { + unimplemented!() + } + + fn set_file_len(&mut self, _len: usize) -> Result<(), failure::Error> { + panic!("Cannot set length of stderr"); + } } -impl Read for Stderr { - fn read(&mut self, _buf: &mut [u8]) -> Result { +impl io::Seek for Stderr { + fn seek(&mut self, _pos: io::SeekFrom) -> Result { unimplemented!() } } diff --git a/lib/runtime-abi/src/vfs/file_like.rs b/lib/runtime-abi/src/vfs/file_like.rs index e0ec5bd91..0aee10290 100644 --- a/lib/runtime-abi/src/vfs/file_like.rs +++ b/lib/runtime-abi/src/vfs/file_like.rs @@ -8,10 +8,16 @@ pub struct Metadata { pub is_file: bool, } -pub trait FileLike: std::io::Read { +pub trait FileLike: std::io::Seek { // get metadata fn metadata(&self) -> Result; // write fn write_file(&mut self, buf: &[u8], offset: usize) -> Result; + + // read + fn read_file(&mut self, buf: &mut [u8], offset: usize) -> Result; + + // set_file_len + fn set_file_len(&mut self, len: usize) -> Result<(), failure::Error>; } diff --git a/lib/runtime-abi/src/vfs/virtual_file.rs b/lib/runtime-abi/src/vfs/virtual_file.rs index 50e7745fb..14ffe6959 100644 --- a/lib/runtime-abi/src/vfs/virtual_file.rs +++ b/lib/runtime-abi/src/vfs/virtual_file.rs @@ -2,7 +2,7 @@ use failure::Error; use crate::vfs::file_like::{FileLike, Metadata}; use std::io; -use std::io::{Seek, SeekFrom, Write}; +use std::io::{Seek, SeekFrom, Write, Read}; impl FileLike for zbox::File { fn metadata(&self) -> Result { @@ -20,4 +20,13 @@ impl FileLike for zbox::File { self.finish().unwrap(); result } + + fn read_file(&mut self, buf: &mut [u8], offset: usize) -> Result { + self.seek(io::SeekFrom::Start(offset as u64))?; + self.read(buf) + } + + fn set_file_len(&mut self, len: usize) -> Result<(), failure::Error> { + self.set_len(len).map_err(|e| e.into()) + } } diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index a09909ebe..94c5c87e3 100644 --- a/lib/runtime-core/src/backend.rs +++ b/lib/runtime-core/src/backend.rs @@ -14,6 +14,8 @@ use crate::{ }; use std::{any::Any, ptr::NonNull}; +use hashbrown::HashMap; + pub mod sys { pub use crate::sys::*; } @@ -38,11 +40,28 @@ impl Token { } } +/// Configuration data for the compiler +pub struct CompilerConfig { + /// Symbol information generated from emscripten; used for more detailed debug messages + pub symbol_map: Option>, +} + +impl Default for CompilerConfig { + fn default() -> CompilerConfig { + CompilerConfig { symbol_map: None } + } +} + pub trait Compiler { /// Compiles a `Module` from WebAssembly binary format. /// The `CompileToken` parameter ensures that this can only /// be called from inside the runtime. - fn compile(&self, wasm: &[u8], _: Token) -> CompileResult; + fn compile( + &self, + wasm: &[u8], + comp_conf: CompilerConfig, + _: Token, + ) -> CompileResult; unsafe fn from_cache(&self, cache: Artifact, _: Token) -> Result; } diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index c780dfd87..0e8d189f8 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -67,13 +67,26 @@ pub fn compile_with( compiler: &dyn backend::Compiler, ) -> CompileResult { let token = backend::Token::generate(); - compiler.compile(wasm, token).map(|mut inner| { + compiler.compile(wasm, Default::default(), token).map(|mut inner| { let inner_info: &mut crate::module::ModuleInfo = &mut inner.info; inner_info.import_custom_sections(wasm).unwrap(); module::Module::new(Arc::new(inner)) }) } +/// The same as `compile_with` but changes the compiler behavior +/// with the values in the `CompilerConfig` +pub fn compile_with_config( + wasm: &[u8], + compiler: &dyn backend::Compiler, + compiler_config: backend::CompilerConfig, +) -> CompileResult { + let token = backend::Token::generate(); + compiler + .compile(wasm, compiler_config, token) + .map(|inner| module::Module::new(Arc::new(inner))) +} + /// Perform validation as defined by the /// WebAssembly specification. Returns `true` if validation /// succeeded, `false` if validation failed. diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index 04a8b3b9a..b0c33406e 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -57,6 +57,9 @@ pub struct ModuleInfo { pub namespace_table: StringTable, pub name_table: StringTable, + /// Symbol information from emscripten + pub em_symbol_map: Option>, + pub custom_sections: HashMap>, } diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index 4a81fff0b..d3c3f3e4d 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -7,6 +7,8 @@ use crate::{ }; use std::{ffi::c_void, mem, ptr}; +use hashbrown::HashMap; + /// The context of the currently running WebAssembly instance. /// /// @@ -156,6 +158,11 @@ impl Ctx { }, } } + + /// Gives access to the emscripten symbol map, used for debugging + pub unsafe fn borrow_symbol_map(&self) -> &Option> { + &(*self.module).info.em_symbol_map + } } #[doc(hidden)] @@ -609,6 +616,8 @@ mod vm_ctx_tests { namespace_table: StringTable::new(), name_table: StringTable::new(), + em_symbol_map: None, + custom_sections: HashMap::new(), }, } diff --git a/lib/runtime/src/lib.rs b/lib/runtime/src/lib.rs index 27945aa10..66d9c6ace 100644 --- a/lib/runtime/src/lib.rs +++ b/lib/runtime/src/lib.rs @@ -110,7 +110,7 @@ pub mod units { pub mod cache; -use wasmer_runtime_core::backend::Compiler; +use wasmer_runtime_core::backend::{Compiler, CompilerConfig}; /// Compile WebAssembly binary code into a [`Module`]. /// This function is useful if it is necessary to @@ -129,6 +129,15 @@ pub fn compile(wasm: &[u8]) -> error::CompileResult { wasmer_runtime_core::compile_with(&wasm[..], default_compiler()) } +/// The same as `compile` but takes a `CompilerConfig` for the purpose of +/// changing the compiler's behavior +pub fn compile_with_config( + wasm: &[u8], + compiler_config: CompilerConfig, +) -> error::CompileResult { + wasmer_runtime_core::compile_with_config(&wasm[..], default_compiler(), compiler_config) +} + /// Compile and instantiate WebAssembly code without /// creating a [`Module`]. /// diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 34064f833..61bd50dac 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -1,18 +1,20 @@ extern crate structopt; use std::env; -use std::fs::File; +use std::fs::{read_to_string, File}; use std::io; use std::io::Read; use std::path::PathBuf; use std::process::exit; +use hashbrown::HashMap; use structopt::StructOpt; use wasmer::webassembly::InstanceABI; use wasmer::*; use wasmer_emscripten; use wasmer_runtime::cache::{Cache as BaseCache, FileSystemCache, WasmHash, WASMER_VERSION_HASH}; +use wasmer_runtime_core::backend::CompilerConfig; #[derive(Debug, StructOpt)] #[structopt(name = "wasmer", about = "Wasm execution runtime.")] @@ -44,6 +46,10 @@ struct Run { /// Application arguments #[structopt(name = "--", raw(multiple = "true"))] args: Vec, + + /// Emscripten symbol map + #[structopt(long = "em-symbol-map", parse(from_os_str))] + em_symbol_map: Option, } #[derive(Debug, StructOpt)] @@ -98,6 +104,48 @@ fn execute_wasm(options: &Run) -> Result<(), String> { ) })?; + let em_symbol_map = if let Some(em_symbol_map_path) = options.em_symbol_map.clone() { + let em_symbol_map_content: String = read_to_string(&em_symbol_map_path) + .map_err(|err| { + format!( + "Can't read symbol map file {}: {}", + em_symbol_map_path.as_os_str().to_string_lossy(), + err, + ) + })? + .to_owned(); + let mut em_symbol_map = HashMap::new(); + for line in em_symbol_map_content.lines() { + let mut split = line.split(':'); + let num_str = if let Some(ns) = split.next() { + ns + } else { + return Err(format!( + "Can't parse symbol map (expected each entry to be of the form: `0:func_name`)" + )); + }; + let num: u32 = num_str.parse::().map_err(|err| { + format!( + "Failed to parse {} as a number in symbol map: {}", + num_str, err + ) + })?; + let name_str: String = if let Some(name_str) = split.next() { + name_str + } else { + return Err(format!( + "Can't parse symbol map (expected each entry to be of the form: `0:func_name`)" + )); + } + .to_owned(); + + em_symbol_map.insert(num, name_str); + } + Some(em_symbol_map) + } else { + None + }; + if !utils::is_wasm_binary(&wasm_binary) { wasm_binary = wabt::wat2wasm(wasm_binary) .map_err(|e| format!("Can't convert from wast to wasm: {:?}", e))?; @@ -128,9 +176,13 @@ fn execute_wasm(options: &Run) -> Result<(), String> { module } Err(_) => { - let module = webassembly::compile(&wasm_binary[..]) - .map_err(|e| format!("Can't compile module: {:?}", e))?; - + let module = webassembly::compile_with_config( + &wasm_binary[..], + CompilerConfig { + symbol_map: em_symbol_map, + }, + ) + .map_err(|e| format!("Can't compile module: {:?}", e))?; // We try to save the module into a cache file cache.store(hash, module.clone()).unwrap_or_default(); @@ -139,8 +191,13 @@ fn execute_wasm(options: &Run) -> Result<(), String> { }; module } else { - webassembly::compile(&wasm_binary[..]) - .map_err(|e| format!("Can't compile module: {:?}", e))? + webassembly::compile_with_config( + &wasm_binary[..], + CompilerConfig { + symbol_map: em_symbol_map, + }, + ) + .map_err(|e| format!("Can't compile module: {:?}", e))? }; let (_abi, import_object, _em_globals) = if wasmer_emscripten::is_emscripten_module(&module) { diff --git a/src/webassembly.rs b/src/webassembly.rs index 8ae38935b..d34966060 100644 --- a/src/webassembly.rs +++ b/src/webassembly.rs @@ -4,6 +4,7 @@ use wasmer_runtime::{ error::{CallResult, Result}, ImportObject, Instance, Module, }; +use wasmer_runtime_core::backend::CompilerConfig; use wasmer_runtime_core::types::Value; use wasmer_emscripten::{is_emscripten_module, run_emscripten_instance}; @@ -76,6 +77,16 @@ pub fn compile(buffer_source: &[u8]) -> Result { Ok(module) } +/// The same as `compile` but takes a `CompilerConfig` for the purpose of +/// changing the compiler's behavior +pub fn compile_with_config( + buffer_source: &[u8], + compiler_config: CompilerConfig, +) -> Result { + let module = runtime::compile_with_config(buffer_source, compiler_config)?; + Ok(module) +} + /// Performs common instance operations needed when an instance is first run /// including data setup, handling arguments and calling a main function pub fn run_instance(