mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-12 20:41:24 +00:00
Create the wasm-bindgen-wasm-conventions
crate
This tiny crate provides utilities for working with Wasm codegen conventions (typically established by LLVM or lld) such as getting the shadow stack pointer. It also de-duplicates all the places in the codebase where we were implementing these conventions in one-off ways.
This commit is contained in:
16
crates/wasm-conventions/Cargo.toml
Normal file
16
crates/wasm-conventions/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "wasm-bindgen-wasm-conventions"
|
||||
version = "0.2.50"
|
||||
authors = ["The wasm-bindgen developers"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/wasm-conventions"
|
||||
homepage = "https://rustwasm.github.io/wasm-bindgen/"
|
||||
documentation = "https://docs.rs/wasm-bindgen-wasm-conventions"
|
||||
description = "Utilities for working with Wasm codegen conventions (usually established by LLVM/lld)"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
walrus = "0.12.0"
|
||||
failure = "0.1.5"
|
95
crates/wasm-conventions/src/lib.rs
Executable file
95
crates/wasm-conventions/src/lib.rs
Executable file
@ -0,0 +1,95 @@
|
||||
//! A tiny crate of utilities for working with implicit Wasm codegen conventions
|
||||
//! (often established by LLVM and lld).
|
||||
//!
|
||||
//! Examples conventions include:
|
||||
//!
|
||||
//! * The shadow stack pointer
|
||||
//! * The canonical linear memory that contains the shadow stack
|
||||
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
|
||||
use failure::{bail, format_err, Error};
|
||||
use walrus::{GlobalId, GlobalKind, MemoryId, Module, ValType};
|
||||
|
||||
/// Get a Wasm module's canonical linear memory.
|
||||
pub fn get_memory(module: &Module) -> Result<MemoryId, Error> {
|
||||
let mut memories = module.memories.iter().map(|m| m.id());
|
||||
let memory = memories.next();
|
||||
if memories.next().is_some() {
|
||||
bail!(
|
||||
"expected a single memory, found multiple; multiple memories \
|
||||
currently not supported"
|
||||
);
|
||||
}
|
||||
memory.ok_or_else(|| {
|
||||
format_err!(
|
||||
"module does not have a memory; must have a memory \
|
||||
to transform return pointers into Wasm multi-value"
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Discover the shadow stack pointer and add it to the module's exports as
|
||||
/// `__shadow_stack_pointer`.
|
||||
///
|
||||
/// Adding it to the exports is useful for making sure it doesn't get GC'd.
|
||||
pub fn export_shadow_stack_pointer(module: &mut Module) -> Result<(), Error> {
|
||||
let candidates = module
|
||||
.globals
|
||||
.iter()
|
||||
.filter(|g| g.ty == ValType::I32)
|
||||
.filter(|g| g.mutable)
|
||||
.filter(|g| match g.kind {
|
||||
GlobalKind::Local(_) => true,
|
||||
GlobalKind::Import(_) => false,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let ssp = match candidates.len() {
|
||||
0 => bail!("could not find the shadow stack pointer for the module"),
|
||||
// If we've got two mutable globals then we're in a pretty standard
|
||||
// situation for threaded code where one is the stack pointer and one is the
|
||||
// TLS base offset. We need to figure out which is which, and we basically
|
||||
// assume LLVM's current codegen where the first is the stack pointer.
|
||||
//
|
||||
// TODO: have an actual check here.
|
||||
1 | 2 => candidates[0].id(),
|
||||
_ => bail!("too many mutable globals to infer which is the shadow stack pointer"),
|
||||
};
|
||||
|
||||
module.exports.add("__shadow_stack_pointer", ssp);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unexport the shadow stack pointer that was previously added to the module's
|
||||
/// exports as `__shadow_stack_pointer`.
|
||||
pub fn unexport_shadow_stack_pointer(module: &mut Module) -> Result<(), Error> {
|
||||
let e = module
|
||||
.exports
|
||||
.iter()
|
||||
.find(|e| e.name == "__shadow_stack_pointer")
|
||||
.map(|e| e.id())
|
||||
.ok_or_else(|| {
|
||||
format_err!("did not find the `__shadow_stack_pointer` export in the module")
|
||||
})?;
|
||||
module.exports.delete(e);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the `__shadow_stack_pointer`.
|
||||
///
|
||||
/// It must have been previously added to the module's exports via
|
||||
/// `export_shadow_stack_pointer`.
|
||||
pub fn get_shadow_stack_pointer(module: &Module) -> Result<GlobalId, Error> {
|
||||
module
|
||||
.exports
|
||||
.iter()
|
||||
.find(|e| e.name == "__shadow_stack_pointer")
|
||||
.ok_or_else(|| {
|
||||
format_err!("did not find the `__shadow_stack_pointer` export in the module")
|
||||
})
|
||||
.and_then(|e| match e.item {
|
||||
walrus::ExportItem::Global(g) => Ok(g),
|
||||
_ => bail!("`__shadow_stack_pointer` export is wrong kind"),
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user