Import the wasm2obj experiment and minimally update it.
This commit is contained in:
@@ -12,12 +12,18 @@ publish = false
|
|||||||
name = "wasmstandalone"
|
name = "wasmstandalone"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "wasm2obj"
|
||||||
|
path = "src/wasm2obj.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cretonne = { path = "/home/sunfish/rust/cretonne/lib/cretonne" }
|
cretonne = { path = "/home/sunfish/rust/cretonne/lib/cretonne" }
|
||||||
|
cretonne-frontend = { path = "/home/sunfish/rust/cretonne/lib/frontend" }
|
||||||
cretonne-reader = { path = "/home/sunfish/rust/cretonne/lib/reader" }
|
cretonne-reader = { path = "/home/sunfish/rust/cretonne/lib/reader" }
|
||||||
cretonne-wasm = { path = "/home/sunfish/rust/cretonne/lib/wasm" }
|
cretonne-wasm = { path = "/home/sunfish/rust/cretonne/lib/wasm" }
|
||||||
cretonne-native = { path = "/home/sunfish/rust/cretonne/lib/native" }
|
cretonne-native = { path = "/home/sunfish/rust/cretonne/lib/native" }
|
||||||
wasmstandalone = { path = "lib/wasmstandalone" }
|
wasmstandalone = { path = "lib/wasmstandalone" }
|
||||||
|
wasm2obj = { path = "lib/wasm2obj" }
|
||||||
wasmparser = "0.8.2"
|
wasmparser = "0.8.2"
|
||||||
wasmtext = { git = "https://github.com/yurydelendik/wasmtext" }
|
wasmtext = { git = "https://github.com/yurydelendik/wasmtext" }
|
||||||
filecheck = "0.0.1"
|
filecheck = "0.0.1"
|
||||||
@@ -27,5 +33,6 @@ serde_derive = "1.0.8"
|
|||||||
num_cpus = "1.5.1"
|
num_cpus = "1.5.1"
|
||||||
term = "0.4.6"
|
term = "0.4.6"
|
||||||
tempdir = "*"
|
tempdir = "*"
|
||||||
|
faerie = { git = "https://github.com/m4b/faerie" }
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|||||||
3
lib/wasm2obj/.gitignore
vendored
Normal file
3
lib/wasm2obj/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
target/
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
||||||
11
lib/wasm2obj/Cargo.toml
Normal file
11
lib/wasm2obj/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "wasm2obj"
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["The Cretonne Project Developers"]
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cretonne = { path = "/home/sunfish/rust/cretonne/lib/cretonne" }
|
||||||
|
cretonne-frontend = { path = "/home/sunfish/rust/cretonne/lib/frontend" }
|
||||||
|
cretonne-wasm = { path = "/home/sunfish/rust/cretonne/lib/wasm" }
|
||||||
|
faerie = { git = "https://github.com/m4b/faerie" }
|
||||||
116
lib/wasm2obj/src/emit_module.rs
Normal file
116
lib/wasm2obj/src/emit_module.rs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
use cretonne::Context;
|
||||||
|
use cretonne::settings;
|
||||||
|
use cretonne::isa::TargetIsa;
|
||||||
|
use cretonne::verify_function;
|
||||||
|
use cretonne::verifier;
|
||||||
|
use cretonne::settings::Configurable;
|
||||||
|
use cretonne::result::CtonError;
|
||||||
|
use cretonne::ir::entities::AnyEntity;
|
||||||
|
use cretonne::ir::{self, Ebb, FuncRef, JumpTable, Function};
|
||||||
|
use cretonne::binemit::{RelocSink, Reloc, CodeOffset};
|
||||||
|
use cton_wasm::TranslationResult;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Write;
|
||||||
|
use faerie::Artifact;
|
||||||
|
|
||||||
|
type RelocRef = u16;
|
||||||
|
|
||||||
|
// Implementation of a relocation sink that just saves all the information for later
|
||||||
|
struct FaerieRelocSink {
|
||||||
|
ebbs: HashMap<RelocRef, (Ebb, CodeOffset)>,
|
||||||
|
funcs: HashMap<RelocRef, (FuncRef, CodeOffset)>,
|
||||||
|
jts: HashMap<RelocRef, (JumpTable, CodeOffset)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelocSink for FaerieRelocSink {
|
||||||
|
fn reloc_ebb(&mut self, offset: CodeOffset, reloc: Reloc, ebb: Ebb) {
|
||||||
|
self.ebbs.insert(reloc.0, (ebb, offset));
|
||||||
|
}
|
||||||
|
fn reloc_func(&mut self, offset: CodeOffset, reloc: Reloc, func: FuncRef) {
|
||||||
|
self.funcs.insert(reloc.0, (func, offset));
|
||||||
|
}
|
||||||
|
fn reloc_jt(&mut self, offset: CodeOffset, reloc: Reloc, jt: JumpTable) {
|
||||||
|
self.jts.insert(reloc.0, (jt, offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FaerieRelocSink {
|
||||||
|
fn new() -> FaerieRelocSink {
|
||||||
|
FaerieRelocSink {
|
||||||
|
ebbs: HashMap::new(),
|
||||||
|
funcs: HashMap::new(),
|
||||||
|
jts: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emits a module that has been emitted with the `WasmRuntime` runtime
|
||||||
|
/// implementation to a native object file.
|
||||||
|
pub fn emit_module(
|
||||||
|
trans_result: &TranslationResult,
|
||||||
|
obj: &mut Artifact,
|
||||||
|
isa: &TargetIsa,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
debug_assert!(
|
||||||
|
trans_result.start_index.is_none() ||
|
||||||
|
trans_result.start_index.unwrap() >= trans_result.function_imports_count,
|
||||||
|
"imported start functions not supported yet"
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut shared_builder = settings::builder();
|
||||||
|
shared_builder.enable("enable_verifier").expect(
|
||||||
|
"Missing enable_verifier setting",
|
||||||
|
);
|
||||||
|
|
||||||
|
for function in &trans_result.functions {
|
||||||
|
let mut context = Context::new();
|
||||||
|
verify_function(function, isa).unwrap();
|
||||||
|
context.func = function.clone(); // TODO: Avoid this clone.
|
||||||
|
let code_size = context.compile(&*isa).map_err(|e| {
|
||||||
|
pretty_error(&context.func, Some(isa), e)
|
||||||
|
})? as usize;
|
||||||
|
if code_size == 0 {
|
||||||
|
return Err(String::from("no code generated by Cretonne"));
|
||||||
|
}
|
||||||
|
let mut code_buf: Vec<u8> = Vec::with_capacity(code_size);
|
||||||
|
code_buf.resize(code_size, 0);
|
||||||
|
let mut relocsink = FaerieRelocSink::new();
|
||||||
|
context.emit_to_memory(code_buf.as_mut_ptr(), &mut relocsink, &*isa);
|
||||||
|
|
||||||
|
// FIXME: get the real linkage name of the function
|
||||||
|
obj.add_code("the_function_name", code_buf);
|
||||||
|
|
||||||
|
assert!(relocsink.jts.is_empty(), "jump tables not yet implemented");
|
||||||
|
assert!(relocsink.ebbs.is_empty(), "ebb relocs not yet implemented");
|
||||||
|
assert!(
|
||||||
|
relocsink.funcs.is_empty(),
|
||||||
|
"function relocs not yet implemented"
|
||||||
|
);
|
||||||
|
|
||||||
|
// FIXME: handle imports
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pretty-print a verifier error.
|
||||||
|
fn pretty_verifier_error(func: &Function, isa: Option<&TargetIsa>, err: verifier::Error) -> String {
|
||||||
|
let mut msg = err.to_string();
|
||||||
|
match err.location {
|
||||||
|
AnyEntity::Inst(inst) => {
|
||||||
|
write!(msg, "\n{}: {}\n\n", inst, func.dfg.display_inst(inst, isa)).unwrap()
|
||||||
|
}
|
||||||
|
_ => msg.push('\n'),
|
||||||
|
}
|
||||||
|
write!(msg, "{}", func.display(isa)).unwrap();
|
||||||
|
msg
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pretty-print a Cretonne error.
|
||||||
|
fn pretty_error(func: &ir::Function, isa: Option<&TargetIsa>, err: CtonError) -> String {
|
||||||
|
if let CtonError::Verifier(e) = err {
|
||||||
|
pretty_verifier_error(func, isa, e)
|
||||||
|
} else {
|
||||||
|
err.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
7
lib/wasm2obj/src/lib.rs
Normal file
7
lib/wasm2obj/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
extern crate cretonne;
|
||||||
|
extern crate cton_wasm;
|
||||||
|
extern crate faerie;
|
||||||
|
|
||||||
|
mod emit_module;
|
||||||
|
|
||||||
|
pub use emit_module::emit_module;
|
||||||
@@ -16,7 +16,7 @@ use std::ptr::copy_nonoverlapping;
|
|||||||
use std::ptr::write;
|
use std::ptr::write;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum TableElement {
|
pub enum TableElement {
|
||||||
Trap(),
|
Trap(),
|
||||||
Function(FunctionIndex),
|
Function(FunctionIndex),
|
||||||
}
|
}
|
||||||
@@ -31,15 +31,22 @@ struct GlobalsData {
|
|||||||
info: Vec<GlobalInfo>,
|
info: Vec<GlobalInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TableData {
|
/// A description of a WebAssembly table.
|
||||||
data: Vec<usize>,
|
pub struct TableData {
|
||||||
elements: Vec<TableElement>,
|
/// The data stored in the table.
|
||||||
info: Table,
|
pub data: Vec<u8>,
|
||||||
|
/// Function indices to be stored in the table.
|
||||||
|
pub elements: Vec<TableElement>,
|
||||||
|
/// The description of the table.
|
||||||
|
pub info: Table,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MemoryData {
|
/// A description of a WebAssembly linear memory.
|
||||||
data: Vec<u8>,
|
pub struct MemoryData {
|
||||||
info: Memory,
|
/// The data stored in the memory.
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
/// The description of the memory.
|
||||||
|
pub info: Memory,
|
||||||
}
|
}
|
||||||
|
|
||||||
const PAGE_SIZE: usize = 65536;
|
const PAGE_SIZE: usize = 65536;
|
||||||
@@ -58,8 +65,10 @@ pub struct StandaloneRuntime {
|
|||||||
imported_funcs: Vec<ir::FunctionName>,
|
imported_funcs: Vec<ir::FunctionName>,
|
||||||
|
|
||||||
globals: GlobalsData,
|
globals: GlobalsData,
|
||||||
tables: Vec<TableData>,
|
/// WebAssembly tables.
|
||||||
memories: Vec<MemoryData>,
|
pub tables: Vec<TableData>,
|
||||||
|
/// WebAssembly linear memories.
|
||||||
|
pub memories: Vec<MemoryData>,
|
||||||
instantiated: bool,
|
instantiated: bool,
|
||||||
|
|
||||||
has_current_memory: Option<FuncRef>,
|
has_current_memory: Option<FuncRef>,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//! CLI tool to use the functions provided by crates [wasm2cretonne](../wasm2cretonne/index.html)
|
//! CLI tool to use the functions provided by the [wasmstandalone](../wasmstandalone/index.html)
|
||||||
//! and [wasmstandalone](../wasmstandalone/index.html).
|
//! crate.
|
||||||
//!
|
//!
|
||||||
//! Reads Wasm binary files (one Wasm module per file), translates the functions' code to Cretonne
|
//! Reads Wasm binary files (one Wasm module per file), translates the functions' code to Cretonne
|
||||||
//! IL. Can also executes the `start` function of the module by laying out the memories, globals
|
//! IL. Can also executes the `start` function of the module by laying out the memories, globals
|
||||||
|
|||||||
134
src/wasm2obj.rs
Normal file
134
src/wasm2obj.rs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
//! Translation from wasm to native object files.
|
||||||
|
//!
|
||||||
|
//! Reads a Wasm binary file, translates the functions' code to Cretonne
|
||||||
|
//! IL, then translates it to native code, and writes it out to a native
|
||||||
|
//! object file with relocations.
|
||||||
|
|
||||||
|
extern crate cton_wasm;
|
||||||
|
extern crate wasm2obj;
|
||||||
|
extern crate cretonne;
|
||||||
|
extern crate cton_native;
|
||||||
|
extern crate docopt;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
extern crate wasmstandalone;
|
||||||
|
extern crate faerie;
|
||||||
|
|
||||||
|
use cton_wasm::translate_module;
|
||||||
|
use cretonne::settings;
|
||||||
|
use wasm2obj::emit_module;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use std::io::BufReader;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use docopt::Docopt;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process;
|
||||||
|
use std::fmt::format;
|
||||||
|
use faerie::{Artifact, Elf, Target};
|
||||||
|
|
||||||
|
const USAGE: &str = "
|
||||||
|
Wasm to native object translation utility.
|
||||||
|
Takes a binary WebAssembly module into a native object file.
|
||||||
|
The translation is dependent on the runtime chosen.
|
||||||
|
The default is a dummy runtime that produces placeholder values.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
wasm2obj <file> -o <output>
|
||||||
|
wasm2obj --help | --version
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-v, --verbose displays the module and translated functions
|
||||||
|
-h, --help print this help message
|
||||||
|
--version print the Cretonne version
|
||||||
|
";
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
struct Args {
|
||||||
|
arg_file: String,
|
||||||
|
arg_output: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_wasm_file(path: PathBuf) -> Result<Vec<u8>, io::Error> {
|
||||||
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
|
let file = File::open(path)?;
|
||||||
|
let mut buf_reader = BufReader::new(file);
|
||||||
|
buf_reader.read_to_end(&mut buf)?;
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args: Args = Docopt::new(USAGE)
|
||||||
|
.and_then(|d| {
|
||||||
|
d.help(true)
|
||||||
|
.version(Some(String::from("0.0.0")))
|
||||||
|
.deserialize()
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|e| e.exit());
|
||||||
|
|
||||||
|
let path = Path::new(&args.arg_file);
|
||||||
|
match handle_module(path.to_path_buf(), &args.arg_output) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(message) => {
|
||||||
|
println!(" error: {}", message);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_module(path: PathBuf, output: &str) -> Result<(), String> {
|
||||||
|
let data = match read_wasm_file(path) {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(String::from(err.description()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (flag_builder, isa_builder) = cton_native::builders().unwrap_or_else(|_| {
|
||||||
|
panic!("host machine is not a supported target");
|
||||||
|
});
|
||||||
|
let isa = isa_builder.finish(settings::Flags::new(&flag_builder));
|
||||||
|
|
||||||
|
let mut runtime = wasmstandalone::StandaloneRuntime::with_flags(isa.flags().clone());
|
||||||
|
|
||||||
|
let translation = {
|
||||||
|
match translate_module(&data, &mut runtime) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(string) => {
|
||||||
|
return Err(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: Make the target a parameter.
|
||||||
|
// FIXME: Make the output filename a parameter.
|
||||||
|
let mut obj = Artifact::new(Target::X86_64, Some(String::from(output)));
|
||||||
|
|
||||||
|
emit_module(&translation, &mut obj, &*isa)?;
|
||||||
|
|
||||||
|
if !runtime.tables.is_empty() {
|
||||||
|
if runtime.tables.len() > 1 {
|
||||||
|
return Err(String::from("multiple tables not supported yet"));
|
||||||
|
}
|
||||||
|
obj.add_data("table", runtime.tables[0].data.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !runtime.memories.is_empty() {
|
||||||
|
if runtime.memories.len() > 1 {
|
||||||
|
return Err(String::from("multiple memories not supported yet"));
|
||||||
|
}
|
||||||
|
obj.add_data("memory", runtime.memories[0].data.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Make the format a parameter.
|
||||||
|
let file = ::std::fs::File::create(Path::new(output)).map_err(|x| {
|
||||||
|
format(format_args!("{}", x))
|
||||||
|
})?;
|
||||||
|
obj.write::<Elf>(file).map_err(
|
||||||
|
|x| format(format_args!("{}", x)),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user