From 83873fca4714be6ceb264d6d1ca7d56b4fda8150 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Wed, 16 Jan 2019 13:43:07 +0100 Subject: ffi-macros: New crate. - This crate contains macros for Sequoia's FFI crate(s). Having it in a separate crate means that we can share it when we split the FFI crate into two. - More importantly, we need a separate crate if we want to create procedural macros. - As first macro, this patch adds ffi_catch_abort that wraps a function's body in a catch_unwind block, aborting on panics. --- ffi-macros/Cargo.toml | 29 +++++++++++++++++ ffi-macros/README.md | 1 + ffi-macros/src/lib.rs | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 ffi-macros/Cargo.toml create mode 100644 ffi-macros/README.md create mode 100644 ffi-macros/src/lib.rs (limited to 'ffi-macros') diff --git a/ffi-macros/Cargo.toml b/ffi-macros/Cargo.toml new file mode 100644 index 00000000..152ced79 --- /dev/null +++ b/ffi-macros/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "sequoia-ffi-macros" +description = "Macros for writing the C API for Sequoia" +version = "0.3.0" +authors = [ + "Justus Winter ", + "Kai Michaelis ", + "Neal H. Walfield ", +] +documentation = "https://docs.sequoia-pgp.org/sequoia_ffi" +homepage = "https://sequoia-pgp.org/" +repository = "https://gitlab.com/sequoia-pgp/sequoia" +readme = "README.md" +license = "GPL-3.0" + +[badges] +gitlab = { repository = "sequoia-pgp/sequoia" } +maintenance = { status = "actively-developed" } + +[dependencies] +proc-macro2 = "0.4" +quote = "0.6" + +[dependencies.syn] +version = "0.15" +features = ["full"] + +[lib] +proc-macro = true diff --git a/ffi-macros/README.md b/ffi-macros/README.md new file mode 100644 index 00000000..f7f818a2 --- /dev/null +++ b/ffi-macros/README.md @@ -0,0 +1 @@ +This is a set of macros to help with the FFI glue for Sequoia. diff --git a/ffi-macros/src/lib.rs b/ffi-macros/src/lib.rs new file mode 100644 index 00000000..255c56c8 --- /dev/null +++ b/ffi-macros/src/lib.rs @@ -0,0 +1,87 @@ +//! Common macros for Sequoia's FFI crates. + +extern crate syn; +extern crate quote; +extern crate proc_macro; +extern crate proc_macro2; + +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; + +use quote::{quote, ToTokens}; + +/// Wraps a function's body in a catch_unwind block, aborting on +/// panics. +/// +/// Unwinding the stack across the FFI boundary is [undefined +/// behavior]. We therefore need to wrap every FFI function's body +/// with [catch_unwind]. This macro does that in an unobtrusive +/// manner. +/// +/// [undefined behavior]: https://doc.rust-lang.org/nomicon/unwinding.html +/// [catch_unwind]: https://doc.rust-lang.org/std/panic/fn.catch_unwind.html +/// +/// # Example +/// +/// ```rust,ignore +/// #[ffi_catch_abort] +/// #[no_mangle] +/// pub extern "system" fn sahnetorte() { +/// assert_eq!(2 * 3, 4); // See what happens... +/// } +/// ``` +#[proc_macro_attribute] +pub fn ffi_catch_abort(_attr: TokenStream, item: TokenStream) -> TokenStream { + // Parse tokens into a function declaration. + let fun = syn::parse_macro_input!(item as syn::ItemFn); + + // Extract all information from the parsed function that we need + // to compose the new function. + let attrs = fun.attrs.iter() + .fold(TokenStream2::new(), + |mut acc, attr| { + acc.extend(attr.clone().into_token_stream()); + acc + }); + let vis = &fun.vis; + let constness = &fun.constness; + let unsafety = &fun.unsafety; + let asyncness = &fun.asyncness; + let abi = &fun.abi; + let ident = &fun.ident; + + let decl = &fun.decl; + let fn_token = &decl.fn_token; + let fn_generics = &decl.generics; + let fn_out = &decl.output; + let fn_in = &decl.inputs; + + let block = &fun.block; + + // We wrap the functions body into an catch_unwind, asserting that + // all variables captured by the closure are unwind safe. This is + // safe because we terminate the process on panics, therefore no + // inconsistencies can be observed. + let expanded = quote! { + #attrs #vis #constness #unsafety #asyncness #abi + #fn_token #ident #fn_generics ( #fn_in ) #fn_out + { + match ::std::panic::catch_unwind( + ::std::panic::AssertUnwindSafe(|| #fn_out #block)) + { + Ok(v) => v, + Err(p) => { + unsafe { + ::libc::abort(); + } + }, + } + } + }; + + // To debug problems with the generated code, just eprintln it: + // + // eprintln!("{}", expanded); + + expanded.into() +} -- cgit v1.2.3