Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/obj/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ mod context;
mod data_segment;
mod function;
mod module;
mod stubs;
mod table;

pub use crate::module::emit_module;
pub use crate::stubs::generate_c_stubs;

/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
219 changes: 219 additions & 0 deletions lib/obj/src/stubs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
use cranelift_codegen::ir;
use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_entity::EntityRef;
use cranelift_wasm::FuncIndex;
use std::io::{Result, Write};
use wasmtime_environ::{DataInitializer, Module, VMOffsets};

/// Generates C code based on module metadata.
pub fn generate_c_stubs(
header: &mut Vec<u8>,
code: &mut Vec<u8>,
name: &str,
module: &Module,
data_initializers: &[DataInitializer],
target_config: &TargetFrontendConfig,
) -> Result<()> {
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module);

let uppercase_name = String::from(name).to_uppercase();
writeln!(header, "#ifndef __{}_H", uppercase_name)?;
writeln!(header, "#define __{}_H", uppercase_name)?;
writeln!(header, "#include <stddef.h>")?;
writeln!(header, "#include <stdint.h>")?;
writeln!(header)?;

if !module.memory_plans.is_empty() {
if module.memory_plans.len() > 1 {
panic!("multiple memories not supported yet");
}
let (_, memory_plan) = module
.memory_plans
.iter()
.next()
.expect("at least one memory");
let size = memory_plan.memory.minimum << 16;
writeln!(header, "#define MEMORY_INIT_SIZE {}", size)?;
}

writeln!(header)?;
writeln!(header, "struct VMContext {{")?;
if ofs.num_signature_ids > 0 {
writeln!(header, "\tuint32_t\tsignatures[{}];", ofs.num_signature_ids)?;
}
if ofs.num_imported_functions > 0 {
writeln!(header, "\tstruct {{")?;
writeln!(header, "\t\tvoid\t*body;")?;
writeln!(header, "\t\tvoid\t*vmctx;")?;
writeln!(
header,
"\t}}\timported_functions[{}];",
ofs.num_imported_functions
)?;
}
if ofs.num_imported_tables > 0 {
writeln!(header, "\tstruct {{")?;
writeln!(header, "\t\tvoid\t*from;")?;
writeln!(header, "\t\tvoid\t*vmctx;")?;
writeln!(
header,
"\t}}\timported_tables[{}];",
ofs.num_imported_tables
)?;
}
if ofs.num_imported_memories > 0 {
writeln!(header, "\tstruct {{")?;
writeln!(header, "\t\tvoid\t*from;")?;
writeln!(header, "\t\tvoid\t*vmctx;")?;
writeln!(
header,
"\t}}\timported_memories[{}];",
ofs.num_imported_memories
)?;
}
if ofs.num_imported_globals > 0 {
writeln!(
header,
"\tvoid\t*imported_globals[{}];",
ofs.num_imported_globals
)?;
}
if ofs.num_defined_tables > 0 {
writeln!(header, "\tstruct {{")?;
writeln!(header, "\t\tvoid\t**base;")?;
writeln!(header, "\t\tsize_t\tcurrent_elements;")?;
writeln!(header, "\t}}\tdefined_tables[{}];", ofs.num_defined_tables)?;
}
if ofs.num_defined_memories > 0 {
writeln!(header, "\tstruct {{")?;
writeln!(header, "\t\tvoid\t*base;")?;
writeln!(header, "\t\tsize_t\tcurrent_length;")?;
writeln!(
header,
"\t}}\tdefined_memories[{}];",
ofs.num_defined_memories
)?;
}
if ofs.num_defined_globals > 0 {
writeln!(header, "\tstruct {{")?;
writeln!(header, "\t\tunion {{")?;
writeln!(header, "\t\t\tuint32_t\tu32;")?;
writeln!(header, "\t\t\tuint64_t\tu64;")?;
writeln!(header, "\t\t\tfloat\tf32;")?;
writeln!(header, "\t\t\tdouble\tf64;")?;
writeln!(header, "\t\t}};")?;
writeln!(
header,
"\t}}\tdefined_globals[{}];",
ofs.num_defined_globals
)?;
}
writeln!(header, "}};")?;
writeln!(header)?;
for i in 0..module.functions.len() {
if i < ofs.num_imported_functions as usize {
continue;
}
let signature = &module.signatures[module.functions[FuncIndex::from_u32(i as u32)]];
let get_type = |x: &ir::AbiParam| -> &str {
match x.value_type {
ir::types::I32 => "uint32_t",
ir::types::I64 => "uint64_t",
_ => panic!("unsupported type"),
}
};
let mut first = true;
for p in signature.returns.iter() {
if p.purpose != ir::ArgumentPurpose::Normal {
continue;
}
assert!(first);
first = false;
write!(header, "{}", get_type(p))?;
}
if first {
write!(header, "void")?;
}
write!(header, " _wasm_function_{}(", i)?;
let mut first = true;
for p in signature.params.iter() {
if first {
first = false;
} else {
write!(header, ", ")?;
}
match p.purpose {
ir::ArgumentPurpose::VMContext => {
write!(header, "struct VMContext*")?;
}
ir::ArgumentPurpose::Normal => {
write!(header, "{}", get_type(p))?;
}
_ => panic!("unsupported param type: {:?}", p.purpose),
}
}
writeln!(header, ");")?
}
if data_initializers.len() > 0 {
writeln!(header)?;
for i in 0..data_initializers.len() {
writeln!(header, "extern void *_memory_{};", i)?;
}
}
writeln!(header)?;
writeln!(
header,
"extern char _vmcontext_init[{}];",
ofs.size_of_vmctx()
)?;
writeln!(header, "void init_vm(struct VMContext *, void *, size_t);")?;
writeln!(header, "#endif // __{}_H", uppercase_name)?;

writeln!(code, "#include \"{}.h\"", name)?;
writeln!(code)?;
writeln!(code, "#include <string.h>")?;
writeln!(code)?;
writeln!(
code,
"void init_vm(struct VMContext *vmctx, void *memory, size_t memory_len) {{"
)?;
writeln!(
code,
"\tmemcpy(vmctx, _vmcontext_init, sizeof(_vmcontext_init));"
)?;
if ofs.num_defined_memories > 0 {
writeln!(code, "\tvmctx->defined_memories[0].base = memory;")?;
writeln!(
code,
"\tvmctx->defined_memories[0].current_length = memory_len;"
)?;
}

if data_initializers.len() > 0 {
writeln!(code)?;
for i in 0..data_initializers.len() {
let data_initializer = &data_initializers[i];
let offset = {
let addend = data_initializer.location.offset;
if let Some(global_index) = data_initializer.location.base {
format!(
"vmctx->defined_globals[{}].u32 + {}",
global_index.index(),
addend
)
} else {
format!("{}", addend)
}
};
let len = data_initializer.data.len();
writeln!(
code,
"\tmemcpy(memory + {}, _memory_{}, {});",
offset, i, len
)?;
}
}

writeln!(code, "}};")?;
Ok(())
}
35 changes: 33 additions & 2 deletions src/wasm2obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ use std::str::FromStr;
use target_lexicon::Triple;
use wasmtime_debug::{emit_debugsections, read_debuginfo};
use wasmtime_environ::{cranelift, ModuleEnvironment, Tunables};
use wasmtime_obj::emit_module;
use wasmtime_obj::{emit_module, generate_c_stubs};

const USAGE: &str = "
Wasm to native object translation utility.
Expand All @@ -59,14 +59,15 @@ The translation is dependent on the environment chosen.
The default is a dummy environment that produces placeholder values.

Usage:
wasm2obj [--target TARGET] [-g] <file> -o <output>
wasm2obj [--target TARGET] [-g] [--generate-c] <file> -o <output>
wasm2obj --help | --version

Options:
-v, --verbose displays the module and translated functions
-h, --help print this help message
--target <TARGET> build for the target triple; default is the host machine
-g generate debug information
-c, --generate-c generate C code/wrapper
--version print the Cranelift version
";

Expand All @@ -76,6 +77,7 @@ struct Args {
arg_output: String,
arg_target: Option<String>,
flag_g: bool,
flag_generate_c: bool,
}

fn read_wasm_file(path: PathBuf) -> Result<Vec<u8>, io::Error> {
Expand All @@ -100,6 +102,7 @@ fn main() {
&args.arg_target,
&args.arg_output,
args.flag_g,
args.flag_generate_c,
) {
Ok(()) => {}
Err(message) => {
Expand All @@ -114,6 +117,7 @@ fn handle_module(
target: &Option<String>,
output: &str,
generate_debug_info: bool,
generate_c: bool,
) -> Result<(), String> {
let data = match read_wasm_file(path) {
Ok(data) => data,
Expand Down Expand Up @@ -187,5 +191,32 @@ fn handle_module(
::std::fs::File::create(Path::new(output)).map_err(|x| format(format_args!("{}", x)))?;
obj.write(file).map_err(|e| e.to_string())?;

if generate_c {
let base_name = Path::new(output)
.file_stem()
.expect("filename")
.to_str()
.expect("name");
let mut c_header = Vec::new();
let mut c_code = Vec::new();
generate_c_stubs(
&mut c_header,
&mut c_code,
&base_name,
&module,
&lazy_data_initializers,
&target_config,
)
.map_err(|e| e.to_string())?;
File::create(Path::new(output).with_extension("h"))
.map_err(|x| format(format_args!("{}", x)))?
.write_all(&c_header)
.map_err(|e| e.to_string())?;
File::create(Path::new(output).with_extension("c"))
.map_err(|x| format(format_args!("{}", x)))?
.write_all(&c_code)
.map_err(|e| e.to_string())?;
}

Ok(())
}