diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index d82120a7d2f..c173fd31a47 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -49,13 +49,46 @@ enum WasmerCLIOptions { Compile(Compile), /// Compile a WebAssembly binary into a native executable + /// + /// To use, you need to set the `WASMER_DIR` environment variable + /// to the location of your Wasmer installation. This will probably be `~/.wasmer`. It + /// should include a `lib`, `include` and `bin` subdirectories. To create an executable + /// you will need `libwasmer`, so by setting `WASMER_DIR` the CLI knows where to look for + /// header files and libraries. + /// + /// Example usage: + /// + /// ```text + /// $ # in two lines: + /// $ export WASMER_DIR=/home/user/.wasmer/ + /// $ wasmer create-exe qjs.wasm -o qjs.exe # or in one line: + /// $ WASMER_DIR=/home/user/.wasmer/ wasmer create-exe qjs.wasm -o qjs.exe + /// $ file qjs.exe + /// qjs.exe: ELF 64-bit LSB pie executable, x86-64 ... + /// ``` #[cfg(any(feature = "static-artifact-create", feature = "wasmer-artifact-create"))] - #[structopt(name = "create-exe")] + #[structopt(name = "create-exe", verbatim_doc_comment)] CreateExe(CreateExe), /// Compile a WebAssembly binary into an object file + /// + /// To use, you need to set the `WASMER_DIR` environment variable to the location of your + /// Wasmer installation. This will probably be `~/.wasmer`. It should include a `lib`, + /// `include` and `bin` subdirectories. To create an object you will need `libwasmer`, so by + /// setting `WASMER_DIR` the CLI knows where to look for header files and libraries. + /// + /// Example usage: + /// + /// ```text + /// $ # in two lines: + /// $ export WASMER_DIR=/home/user/.wasmer/ + /// $ wasmer create-obj qjs.wasm --object-format symbols -o qjs.obj # or in one line: + /// $ WASMER_DIR=/home/user/.wasmer/ wasmer create-exe qjs.wasm --object-format symbols -o qjs.obj + /// $ file qjs.obj + /// qjs.obj: ELF 64-bit LSB relocatable, x86-64 ... + /// ``` #[cfg(feature = "static-artifact-create")] - #[structopt(name = "create-obj")] + #[structopt(name = "create-obj", verbatim_doc_comment)] CreateObj(CreateObj), /// Get various configuration information needed diff --git a/lib/cli/src/commands/create_exe.rs b/lib/cli/src/commands/create_exe.rs index cf445016fc0..a5d724a06c3 100644 --- a/lib/cli/src/commands/create_exe.rs +++ b/lib/cli/src/commands/create_exe.rs @@ -34,7 +34,12 @@ pub struct CreateExe { target_triple: Option, /// Object format options - #[structopt(name = "OBJECT_FORMAT", long = "object-format")] + /// + /// This flag accepts two options: `symbols` or `serialized`. + /// - (default) `symbols` creates an + /// executable where all functions and metadata of the module are regular object symbols + /// - `serialized` creates an executable where the module is zero-copy serialized as raw data + #[structopt(name = "OBJECT_FORMAT", long = "object-format", verbatim_doc_comment)] object_format: Option, #[structopt(short = "m", multiple = true, number_of_values = 1)] cpu_features: Vec, @@ -186,14 +191,19 @@ impl CreateExe { fn link( output_path: PathBuf, object_path: PathBuf, - header_code_path: PathBuf, + mut header_code_path: PathBuf, ) -> anyhow::Result<()> { + let linkcode = LinkCode { + object_paths: vec![object_path, "main_obj.obj".into()], + output_path, + ..Default::default() + }; let c_src_path = Path::new("wasmer_main.c"); let mut libwasmer_path = get_libwasmer_path()? .canonicalize() .context("Failed to find libwasmer")?; println!("Using libwasmer: {}", libwasmer_path.display()); - let lib_filename = libwasmer_path + let _lib_filename = libwasmer_path .file_name() .unwrap() .to_str() @@ -209,32 +219,47 @@ fn link( c_src_file.write_all(WASMER_STATIC_MAIN_C_SOURCE)?; } - println!( - "link output {:?}", - Command::new("cc") - .arg(&object_path) - .arg(&header_code_path) - .arg(&c_src_path) - .arg(&format!("-L{}", libwasmer_path.display())) - .arg(&format!("-I{}", get_wasmer_include_directory()?.display())) - .arg(&format!("-l:{}", lib_filename)) - // Add libraries required per platform. - // We need userenv, sockets (Ws2_32), advapi32 for some system calls and bcrypt for random numbers. - //#[cfg(windows)] - // .arg("-luserenv") - // .arg("-lWs2_32") - // .arg("-ladvapi32") - // .arg("-lbcrypt") - // On unix we need dlopen-related symbols, libmath for a few things, and pthreads. - //#[cfg(not(windows))] - .arg("-ldl") - .arg("-lm") - .arg("-pthread") - //.arg(&format!("-I{}", header_code_path.display())) - .arg("-o") - .arg(&output_path) - .output()? - ); + if !header_code_path.is_dir() { + header_code_path.pop(); + } + + /* Compile main function */ + let compilation = Command::new("cc") + .arg("-c") + .arg(&c_src_path) + .arg(if linkcode.optimization_flag.is_empty() { + "-O2" + } else { + linkcode.optimization_flag.as_str() + }) + .arg(&format!("-L{}", libwasmer_path.display())) + .arg(&format!("-I{}", get_wasmer_include_directory()?.display())) + //.arg(&format!("-l:{}", lib_filename)) + .arg("-lwasmer") + // Add libraries required per platform. + // We need userenv, sockets (Ws2_32), advapi32 for some system calls and bcrypt for random numbers. + //#[cfg(windows)] + // .arg("-luserenv") + // .arg("-lWs2_32") + // .arg("-ladvapi32") + // .arg("-lbcrypt") + // On unix we need dlopen-related symbols, libmath for a few things, and pthreads. + //#[cfg(not(windows))] + .arg("-ldl") + .arg("-lm") + .arg("-pthread") + .arg(&format!("-I{}", header_code_path.display())) + .arg("-v") + .arg("-o") + .arg("main_obj.obj") + .output()?; + if !compilation.status.success() { + return Err(anyhow::anyhow!(String::from_utf8_lossy( + &compilation.stderr + ) + .to_string())); + } + linkcode.run().context("Failed to link objects together")?; Ok(()) } diff --git a/lib/cli/src/commands/create_obj.rs b/lib/cli/src/commands/create_obj.rs index 64aff8b84c5..578c49718f3 100644 --- a/lib/cli/src/commands/create_obj.rs +++ b/lib/cli/src/commands/create_obj.rs @@ -15,6 +15,8 @@ use structopt::StructOpt; use wasmer::*; use wasmer_object::{emit_serialized, get_object_for_target}; +const WASMER_SERIALIZED_HEADER: &[u8] = include_bytes!("wasmer_create_exe.h"); + #[derive(Debug, StructOpt)] /// The options for the `wasmer create-exe` subcommand pub struct CreateObj { @@ -23,15 +25,27 @@ pub struct CreateObj { path: PathBuf, /// Output file - #[structopt(name = "OUTPUT PATH", short = "o", parse(from_os_str))] + #[structopt(name = "OUTPUT_PATH", short = "o", parse(from_os_str))] output: PathBuf, + /// Header output file + #[structopt( + name = "OUTPUT_HEADER_PATH", + long = "output-header-path", + parse(from_os_str) + )] + header_output: Option, + /// Compilation Target triple #[structopt(long = "target")] target_triple: Option, /// Object format options - #[structopt(name = "OBJECT_FORMAT", long = "object-format")] + /// + /// This flag accepts two options: `symbols` or `serialized`. + /// - (default) `symbols` creates an object where all functions and metadata of the module are regular object symbols + /// - `serialized` creates an object where the module is zero-copy serialized as raw data + #[structopt(name = "OBJECT_FORMAT", long = "object-format", verbatim_doc_comment)] object_format: Option, #[structopt(short = "m", multiple = true, number_of_values = 1)] @@ -44,7 +58,6 @@ pub struct CreateObj { impl CreateObj { /// Runs logic for the `create-obj` subcommand pub fn execute(&self) -> Result<()> { - println!("objectformat: {:?}", &self.object_format); let target = self .target_triple .as_ref() @@ -68,8 +81,15 @@ impl CreateObj { println!("Format: {:?}", object_format); let starting_cd = env::current_dir()?; + let output_path = starting_cd.join(&self.output); + let header_output = self.header_output.clone().unwrap_or_else(|| { + let mut retval = self.output.clone(); + retval.set_extension("h"); + retval + }); + let header_output_path = starting_cd.join(&header_output); let wasm_module_path = starting_cd.join(&self.path); match object_format { @@ -83,6 +103,9 @@ impl CreateObj { obj.write_stream(&mut writer) .map_err(|err| anyhow::anyhow!(err.to_string()))?; writer.flush()?; + let mut writer = BufWriter::new(File::create(&header_output_path)?); + writer.write_all(WASMER_SERIALIZED_HEADER)?; + writer.flush()?; } ObjectFormat::Symbols => { let engine = store.engine(); @@ -106,27 +129,16 @@ impl CreateObj { obj.write_stream(&mut writer) .map_err(|err| anyhow::anyhow!(err.to_string()))?; writer.flush()?; - { - let mut writer = BufWriter::new(File::create("/tmp/main_obj.o")?); - obj.write_stream(&mut writer) - .map_err(|err| anyhow::anyhow!(err.to_string()))?; - writer.flush()?; - } - let mut writer = BufWriter::new(File::create("func.c")?); + let mut writer = BufWriter::new(File::create(&header_output_path)?); writer.write_all(header_file_src.as_bytes())?; writer.flush()?; - { - let mut writer = BufWriter::new(File::create("/tmp/func.c")?); - writer.write_all(header_file_src.as_bytes())?; - writer.flush()?; - } - //link(output_path.clone(), std::path::Path::new("func.c").into())?; } } eprintln!( - "✔ Object compiled successfully to `{}`.", + "✔ Object compiled successfully to `{}` and the header file was generated at `{}`.", self.output.display(), + header_output.display(), ); Ok(()) diff --git a/lib/cli/src/commands/wasmer_create_exe.h b/lib/cli/src/commands/wasmer_create_exe.h new file mode 100644 index 00000000000..98705d4fc6f --- /dev/null +++ b/lib/cli/src/commands/wasmer_create_exe.h @@ -0,0 +1,25 @@ +#include "wasmer.h" +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern size_t WASMER_MODULE_LENGTH asm("WASMER_MODULE_LENGTH"); +extern char WASMER_MODULE_DATA asm("WASMER_MODULE_DATA"); + +wasm_module_t* wasmer_module_new(wasm_store_t* store, const char* wasm_name) { + wasm_byte_vec_t module_byte_vec = { + .size = WASMER_MODULE_LENGTH, + .data = (const char*)&WASMER_MODULE_DATA, + }; + wasm_module_t* module = wasm_module_deserialize(store, &module_byte_vec); + + return module; +} + +#ifdef __cplusplus +} +#endif diff --git a/lib/types/src/serialize.rs b/lib/types/src/serialize.rs index da7e5b9e4aa..abffcfc782e 100644 --- a/lib/types/src/serialize.rs +++ b/lib/types/src/serialize.rs @@ -205,7 +205,7 @@ impl MetadataHeader { /// Parses the header and returns the length of the metadata following it. pub fn parse(bytes: &[u8]) -> Result { - if bytes.as_ptr() as usize % 16 != 0 { + if bytes.as_ptr() as usize % 8 != 0 { return Err(DeserializeError::CorruptedBinary( "misaligned metadata".to_string(), ));