Add feature=pirita_file

This commit is contained in:
Felix Schütt
2022-10-26 17:32:57 +02:00
parent 184445f090
commit 1e2617ca18
14 changed files with 2223 additions and 406 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,26 @@
//! Create a compiled standalone object file for a given Wasm file.
#![allow(dead_code)]
//! Create a standalone native executable for a given Wasm file.
use super::ObjectFormat;
use crate::{commands::PrefixerFn, store::CompilerOptions};
use crate::{
commands::{CrossCompile, PrefixerFn},
store::CompilerOptions,
};
use anyhow::{Context, Result};
use clap::Parser;
#[cfg(feature = "pirita_file")]
use pirita::{ParseOptions, PiritaFileMmap};
use std::env;
use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufWriter;
use std::path::PathBuf;
use std::process::Command;
use wasmer::*;
use wasmer_object::{emit_serialized, get_object_for_target};
const WASMER_SERIALIZED_HEADER: &[u8] = include_bytes!("wasmer_deserialize_module.h");
const WASMER_SERIALIZED_HEADER: &[u8] = include_bytes!("wasmer_create_exe.h");
#[derive(Debug, Parser)]
/// The options for the `wasmer create-exe` subcommand
@@ -83,16 +90,27 @@ impl CreateObj {
Target::new(target_triple.clone(), features)
})
.unwrap_or_default();
let (store, compiler_type) = self.compiler.get_store_for_target(target.clone())?;
let starting_cd = env::current_dir()?;
let wasm_module_path = starting_cd.join(&self.path);
let output_path = starting_cd.join(&self.output);
let object_format = self.object_format.unwrap_or(ObjectFormat::Symbols);
#[cfg(feature = "pirita_file")]
{
if let Ok(pirita) =
PiritaFileMmap::parse(wasm_module_path.clone(), &ParseOptions::default())
{
return self.execute_pirita(&pirita, target, output_path, object_format);
}
}
let (store, compiler_type) = self.compiler.get_store_for_target(target.clone())?;
println!("Compiler: {}", compiler_type.to_string());
println!("Target: {}", target.triple());
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");
@@ -100,7 +118,6 @@ impl CreateObj {
});
let header_output_path = starting_cd.join(&header_output);
let wasm_module_path = starting_cd.join(&self.path);
match object_format {
ObjectFormat::Serialized => {
@@ -108,7 +125,7 @@ impl CreateObj {
.context("failed to compile Wasm")?;
let bytes = module.serialize()?;
let mut obj = get_object_for_target(target.triple())?;
emit_serialized(&mut obj, &bytes, target.triple())?;
emit_serialized(&mut obj, &bytes, target.triple(), "WASMER_MODULE")?;
let mut writer = BufWriter::new(File::create(&output_path)?);
obj.write_stream(&mut writer)
.map_err(|err| anyhow::anyhow!(err.to_string()))?;
@@ -150,17 +167,98 @@ impl CreateObj {
self.output.display(),
header_output.display(),
);
eprintln!("\n---\n");
eprintln!(
r#"To use, link the object file to your executable and call the `wasmer_object_module_new` function defined in the header file. For example, in the C language:
#include "{}"
wasm_module_t *module = wasmer_object_module_new(store, "my_module_name");
"#,
header_output.display(),
);
Ok(())
}
#[cfg(feature = "pirita_file")]
fn execute_pirita(
&self,
file: &PiritaFileMmap,
target: Target,
output_path: PathBuf,
object_format: ObjectFormat,
) -> Result<()> {
use wasmer_object::Object;
if output_path.exists() {
if output_path.is_dir() {
wapm_targz_to_pirita::nuke_dir(&output_path)?;
}
} else {
let _ = std::fs::create_dir_all(&output_path)?;
}
println!(
"outputting create-obj to directory {}",
output_path.display()
);
let (store, _) = self.compiler.get_store_for_target(target.clone())?;
crate::commands::create_exe::CreateExe::create_objs_pirita(
&store,
file,
&target,
&output_path,
object_format,
)?;
Ok(())
}
}
fn link(
output_path: PathBuf,
object_path: PathBuf,
header_code_path: PathBuf,
) -> anyhow::Result<()> {
let libwasmer_path = get_libwasmer_path()?
.canonicalize()
.context("Failed to find libwasmer")?;
println!(
"link output {:?}",
Command::new("cc")
.arg(&header_code_path)
.arg(&format!("-L{}", libwasmer_path.display()))
//.arg(&format!("-I{}", header_code_path.display()))
.arg("-pie")
.arg("-o")
.arg("header_obj.o")
.output()?
);
//ld -relocatable a.o b.o -o c.o
println!(
"link output {:?}",
Command::new("ld")
.arg("-relocatable")
.arg(&object_path)
.arg("header_obj.o")
.arg("-o")
.arg(&output_path)
.output()?
);
Ok(())
}
/// path to the static libwasmer
fn get_libwasmer_path() -> anyhow::Result<PathBuf> {
let mut path = get_wasmer_dir()?;
path.push("lib");
// TODO: prefer headless Wasmer if/when it's a separate library.
#[cfg(not(windows))]
path.push("libwasmer.a");
#[cfg(windows)]
path.push("wasmer.lib");
Ok(path)
}
fn get_wasmer_dir() -> anyhow::Result<PathBuf> {
Ok(PathBuf::from(
env::var("WASMER_DIR")
.or_else(|e| {
option_env!("WASMER_INSTALL_PREFIX")
.map(str::to_string)
.ok_or(e)
})
.context("Trying to read env var `WASMER_DIR`")?,
))
}

View File

@@ -216,6 +216,14 @@ impl Run {
}
fn inner_execute(&self) -> Result<()> {
#[cfg(feature = "pirita_file")]
{
if let Some(pf) = pirita::PiritaContainer::load_mmap(self.path.clone()) {
return pf
.run(&self.command_name.clone().unwrap_or_default())
.map_err(|e| anyhow!("Could not run PiritaFile: {e}"));
}
}
let (mut store, module) = self.get_store_module()?;
#[cfg(feature = "emscripten")]
{

View File

@@ -0,0 +1,25 @@
#include "wasmer.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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

View File

@@ -1,16 +1,24 @@
#include "wasmer.h"
#include "static_defs.h"
//#include "my_wasm.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define own
// TODO: make this define templated so that the Rust code can toggle it on/off
#define WASI
extern wasm_module_t* wasmer_object_module_new(wasm_store_t* store, const char* module_name) asm("wasmer_object_module_new");
#ifdef WASI_PIRITA
extern size_t VOLUMES_LENGTH asm("VOLUMES_LENGTH");
extern char VOLUMES_DATA asm("VOLUMES_DATA");
// DECLARE_MODULES
#else
extern size_t WASMER_MODULE_LENGTH asm("WASMER_MODULE_LENGTH");
extern char WASMER_MODULE_DATA asm("WASMER_MODULE_DATA");
#endif
static void print_wasmer_error() {
int error_len = wasmer_last_error_length();
@@ -93,18 +101,54 @@ int main(int argc, char *argv[]) {
wasm_engine_t *engine = wasm_engine_new_with_config(config);
wasm_store_t *store = wasm_store_new(engine);
wasm_module_t *module = wasmer_object_module_new(store, "module");
#ifdef WASI_PIRITA
// INSTANTIATE_MODULES
#else
wasm_byte_vec_t module_byte_vec = {
.size = WASMER_MODULE_LENGTH,
.data = &WASMER_MODULE_DATA,
};
wasm_module_t *module = wasm_module_deserialize(store, &module_byte_vec);
if (!module) {
fprintf(stderr, "Failed to create module\n");
print_wasmer_error();
return -1;
}
#endif
// We have now finished the memory buffer book keeping and we have a valid
// Module.
#ifdef WASI
#ifdef WASI_PIRITA
wasi_config_t *wasi_config = wasi_config_new(argv[0]);
handle_arguments(wasi_config, argc, argv);
wasm_byte_vec_t volume_bytes = {
.size = VOLUMES_LENGTH,
.data = &VOLUMES_DATA,
};
wasi_filesystem_t* filesystem = wasi_filesystem_init_static_memory(&volume_bytes);
if (!filesystem) {
printf("Error parsing filesystem from bytes\n");
return 1;
}
wasm_extern_vec_t imports;
wasi_env_t* wasi_env = wasi_env_with_filesystem(
wasi_config,
store,
module,
filesystem,
&imports,
"##atom-name##"
);
if (!wasi_env) {
printf("Error setting filesystem\n");
return 1;
}
#else
wasi_config_t *wasi_config = wasi_config_new(argv[0]);
handle_arguments(wasi_config, argc, argv);
@@ -114,7 +158,6 @@ int main(int argc, char *argv[]) {
print_wasmer_error();
return 1;
}
#endif
wasm_importtype_vec_t import_types;
wasm_module_imports(module, &import_types);
@@ -132,6 +175,7 @@ int main(int argc, char *argv[]) {
return 1;
}
#endif
#endif
wasm_instance_t *instance = wasm_instance_new(store, module, &imports, NULL);
@@ -179,6 +223,9 @@ int main(int argc, char *argv[]) {
// TODO: handle non-WASI start (maybe with invoke?)
#ifdef WASI_PIRITA
wasi_filesystem_delete(filesystem);
#endif
#ifdef WASI
wasi_env_delete(wasi_env);
wasm_extern_vec_delete(&exports);

View File

@@ -10,7 +10,7 @@ extern "C" {
extern size_t WASMER_MODULE_LENGTH asm("WASMER_MODULE_LENGTH");
extern char WASMER_MODULE_DATA asm("WASMER_MODULE_DATA");
wasm_module_t* wasmer_object_module_new(wasm_store_t* store, const char* module_name) {
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,

View File

@@ -0,0 +1,232 @@
#include "wasmer.h"
#include "static_defs.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define own
// TODO: make this define templated so that the Rust code can toggle it on/off
#define WASI
#ifdef WASI_PIRITA
extern size_t VOLUMES_LENGTH asm("VOLUMES_LENGTH");
extern char VOLUMES_DATA asm("VOLUMES_DATA");
#endif
extern wasm_module_t* wasmer_module_new(wasm_store_t* store) asm("wasmer_module_new");
extern wasm_module_t* wasmer_static_module_new(wasm_store_t* store,const char* wasm_name) asm("wasmer_static_module_new");
static void print_wasmer_error() {
int error_len = wasmer_last_error_length();
printf("Error len: `%d`\n", error_len);
char *error_str = (char *)malloc(error_len);
wasmer_last_error_message(error_str, error_len);
printf("%s\n", error_str);
free(error_str);
}
#ifdef WASI
static void pass_mapdir_arg(wasi_config_t *wasi_config, char *mapdir) {
int colon_location = strchr(mapdir, ':') - mapdir;
if (colon_location == 0) {
// error malformed argument
fprintf(stderr, "Expected mapdir argument of the form alias:directory\n");
exit(-1);
}
char *alias = (char *)malloc(colon_location + 1);
memcpy(alias, mapdir, colon_location);
alias[colon_location] = '\0';
int dir_len = strlen(mapdir) - colon_location;
char *dir = (char *)malloc(dir_len + 1);
memcpy(dir, &mapdir[colon_location + 1], dir_len);
dir[dir_len] = '\0';
wasi_config_mapdir(wasi_config, alias, dir);
free(alias);
free(dir);
}
// We try to parse out `--dir` and `--mapdir` ahead of time and process those
// specially. All other arguments are passed to the guest program.
static void handle_arguments(wasi_config_t *wasi_config, int argc,
char *argv[]) {
for (int i = 1; i < argc; ++i) {
// We probably want special args like `--dir` and `--mapdir` to not be
// passed directly
if (strcmp(argv[i], "--dir") == 0) {
// next arg is a preopen directory
if ((i + 1) < argc) {
i++;
wasi_config_preopen_dir(wasi_config, argv[i]);
} else {
fprintf(stderr, "--dir expects a following argument specifying which "
"directory to preopen\n");
exit(-1);
}
} else if (strcmp(argv[i], "--mapdir") == 0) {
// next arg is a mapdir
if ((i + 1) < argc) {
i++;
pass_mapdir_arg(wasi_config, argv[i]);
} else {
fprintf(stderr,
"--mapdir expects a following argument specifying which "
"directory to preopen in the form alias:directory\n");
exit(-1);
}
} else if (strncmp(argv[i], "--dir=", strlen("--dir=")) == 0) {
// this arg is a preopen dir
char *dir = argv[i] + strlen("--dir=");
wasi_config_preopen_dir(wasi_config, dir);
} else if (strncmp(argv[i], "--mapdir=", strlen("--mapdir=")) == 0) {
// this arg is a mapdir
char *mapdir = argv[i] + strlen("--mapdir=");
pass_mapdir_arg(wasi_config, mapdir);
} else {
// guest argument
wasi_config_arg(wasi_config, argv[i]);
}
}
}
#endif
int main(int argc, char *argv[]) {
wasm_config_t *config = wasm_config_new();
wasm_engine_t *engine = wasm_engine_new_with_config(config);
wasm_store_t *store = wasm_store_new(engine);
#ifdef WASI_PIRITA
// INSTANTIATE_MODULES
#else
wasm_module_t *module = wasmer_static_module_new(store, "module");
#endif
if (!module) {
fprintf(stderr, "Failed to create module\n");
print_wasmer_error();
return -1;
}
// We have now finished the memory buffer book keeping and we have a valid
// Module.
#ifdef WASI_PIRITA
wasi_config_t *wasi_config = wasi_config_new(argv[0]);
handle_arguments(wasi_config, argc, argv);
wasm_byte_vec_t volume_bytes = {
.size = VOLUMES_LENGTH,
.data = &VOLUMES_DATA,
};
wasi_filesystem_t* filesystem = wasi_filesystem_init_static_memory(&volume_bytes);
if (!filesystem) {
printf("Error parsing filesystem from bytes\n");
return 1;
}
wasm_extern_vec_t imports;
wasi_env_t* wasi_env = wasi_env_with_filesystem(
wasi_config,
store,
module,
filesystem,
&imports,
"##atom-name##"
);
if (!wasi_env) {
printf("Error setting filesystem\n");
return 1;
}
#else
wasi_config_t *wasi_config = wasi_config_new(argv[0]);
handle_arguments(wasi_config, argc, argv);
wasi_env_t *wasi_env = wasi_env_new(store, wasi_config);
if (!wasi_env) {
fprintf(stderr, "Error building WASI env!\n");
print_wasmer_error();
return 1;
}
wasm_importtype_vec_t import_types;
wasm_module_imports(module, &import_types);
wasm_extern_vec_t imports;
wasm_extern_vec_new_uninitialized(&imports, import_types.size);
wasm_importtype_vec_delete(&import_types);
#ifdef WASI
bool get_imports_result = wasi_get_imports(store, wasi_env, module, &imports);
if (!get_imports_result) {
fprintf(stderr, "Error getting WASI imports!\n");
print_wasmer_error();
return 1;
}
#endif
#endif
wasm_instance_t *instance = wasm_instance_new(store, module, &imports, NULL);
if (!instance) {
fprintf(stderr, "Failed to create instance\n");
print_wasmer_error();
return -1;
}
#ifdef WASI
// Read the exports.
wasm_extern_vec_t exports;
wasm_instance_exports(instance, &exports);
wasm_memory_t* mem = NULL;
for (size_t i = 0; i < exports.size; i++) {
mem = wasm_extern_as_memory(exports.data[i]);
if (mem) {
break;
}
}
if (!mem) {
fprintf(stderr, "Failed to create instance: Could not find memory in exports\n");
print_wasmer_error();
return -1;
}
wasi_env_set_memory(wasi_env, mem);
own wasm_func_t *start_function = wasi_get_start_function(instance);
if (!start_function) {
fprintf(stderr, "`_start` function not found\n");
print_wasmer_error();
return -1;
}
wasm_val_vec_t args = WASM_EMPTY_VEC;
wasm_val_vec_t results = WASM_EMPTY_VEC;
own wasm_trap_t *trap = wasm_func_call(start_function, &args, &results);
if (trap) {
fprintf(stderr, "Trap is not NULL: TODO:\n");
return -1;
}
#endif
// TODO: handle non-WASI start (maybe with invoke?)
#ifdef WASI_PIRITA
wasi_filesystem_delete(filesystem);
#endif
#ifdef WASI
wasi_env_delete(wasi_env);
wasm_extern_vec_delete(&exports);
#endif
wasm_instance_delete(instance);
wasm_module_delete(module);
wasm_store_delete(store);
wasm_engine_delete(engine);
return 0;
}