From 9e66ad5515f88951fb8b2fb69e2b62d994e0c600 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Thu, 21 Nov 2024 16:05:02 +0000 Subject: [PATCH] lisa._assets.kmodules.lisa: Improve Rust bindings --- .../lisa/rust/lisakmod-macros/Cargo.lock | 84 ++++ .../rust/lisakmod-macros/macros/Cargo.lock | 77 ++++ .../lisakmod-macros/macros/src/inlinec.rs | 374 ++++++++++++++---- .../rust/lisakmod-macros/macros/src/lib.rs | 9 +- .../lisa/rust/lisakmod-macros/src/inlinec.rs | 89 +++-- .../rust/lisakmod-macros/tests/inlinec.rs | 4 +- .../kmodules/lisa/rust/lisakmod/Cargo.lock | 16 +- .../kmodules/lisa/rust/lisakmod/src/lib.rs | 1 + .../lisa/rust/lisakmod/src/prelude.rs | 2 +- .../lisa/rust/lisakmod/src/runtime/alloc.rs | 9 +- .../lisa/rust/lisakmod/src/runtime/module.rs | 4 +- .../lisa/rust/lisakmod/src/runtime/sync.rs | 47 ++- .../kmodules/lisa/rust/lisakmod/src/tests.rs | 2 +- 13 files changed, 567 insertions(+), 151 deletions(-) create mode 100644 lisa/_assets/kmodules/lisa/rust/lisakmod-macros/Cargo.lock create mode 100644 lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/Cargo.lock diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/Cargo.lock b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/Cargo.lock new file mode 100644 index 000000000..23e920bb5 --- /dev/null +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/Cargo.lock @@ -0,0 +1,84 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" + +[[package]] +name = "lisakmod_macros" +version = "0.1.0" +dependencies = [ + "lisakmod_macros_proc", +] + +[[package]] +name = "lisakmod_macros_proc" +version = "0.1.0" +dependencies = [ + "getrandom", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/Cargo.lock b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/Cargo.lock new file mode 100644 index 000000000..4745fa07a --- /dev/null +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/Cargo.lock @@ -0,0 +1,77 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" + +[[package]] +name = "lisakmod_macros_proc" +version = "0.1.0" +dependencies = [ + "getrandom", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/inlinec.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/inlinec.rs index d3ad106b7..920fab601 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/inlinec.rs +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/inlinec.rs @@ -6,8 +6,8 @@ use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; use syn::{ parse::Parse, parse_quote, punctuated::Punctuated, spanned::Spanned, token, Abi, Attribute, - Error, Expr, ExprGroup, ExprLit, ExprTuple, FnArg, Generics, Ident, ItemFn, Lit, LitStr, Meta, - Pat, ReturnType, Stmt, StmtMacro, Token, Type, Visibility, + Error, Expr, ExprGroup, ExprLit, ExprTuple, FnArg, Generics, Ident, ItemFn, ItemStatic, Lit, + LitStr, Meta, Pat, Path, ReturnType, Stmt, StmtMacro, Token, Type, Visibility, }; use crate::misc::{concatcp, get_random}; @@ -85,21 +85,21 @@ impl Parse for CFuncInput { snippets.push(quote! {#snippet}); } - let c_code = match snippets.len() { - 1 => Ok(( + let c_code = match &snippets[..] { + [code] => Ok(( None, - Some(snippets[0].clone()), + Some(code.clone()), None, )), - 2 => Ok(( - Some(snippets[0].clone()), - Some(snippets[1].clone()), + [pre, code] => Ok(( + Some(pre.clone()), + Some(code.clone()), None, )), - 3 => Ok(( - Some(snippets[0].clone()), - Some(snippets[1].clone()), - Some(snippets[2].clone()), + [pre, code, post] => Ok(( + Some(pre.clone()), + Some(code.clone()), + Some(post.clone()), )), _ => Err(Error::new(span, "An inline C function must contain either a single string expression with the C code in it or two string expressions (one output before the function and one for the body).")) }?; @@ -134,26 +134,13 @@ fn make_c_func( let (c_out, c_header_out, rust_out) = _make_c_func(rust_name, c_name, f_generics, f_args, f_ret_ty, c_code)?; - let outs = [c_out, c_header_out]; - let sections = [ - format!(".binstore.c.code.{}", c_name), - format!(".binstore.c.header.{}", c_name), + let c_out = [ + make_c_out(c_out, &format!(".binstore.c.code.{}", c_name))?, + make_c_out(c_header_out, &format!(".binstore.c.header.{}", c_name))?, ]; Ok(quote! { - // Store the C function in a section of the binary, that will be extracted by the - // module Makefile and compiled separately as C code. - #( - const _: () = { - const CODE_SLICE: &[u8] = #outs.as_bytes(); - const CODE_LEN: usize = CODE_SLICE.len(); - - #[link_section = #sections ] - #[used] - static CODE: [u8; CODE_LEN] = ::lisakmod_macros::private::misc::slice_to_array::<{CODE_LEN}>(CODE_SLICE); - }; - )* - + #(#c_out)* #rust_out }) } @@ -311,7 +298,7 @@ fn _make_c_func( let rust_out = match rust_name { Some(rust_name) => quote! { - extern "C" { + unsafe extern "C" { #[link_name = #c_name_str] fn #rust_name #f_generics(#rust_extern_args) -> <#f_ret_ty as ::lisakmod_macros::inlinec::FfiType>::FfiType #f_where; } @@ -375,7 +362,7 @@ pub fn cfunc(_attrs: TokenStream, code: TokenStream) -> Result Result { +pub fn cconstant(args: TokenStream) -> Result { let span = Span::call_site(); let args = syn::parse::Parser::parse2(Punctuated::::parse_terminated, args)?; @@ -402,16 +389,15 @@ pub fn c_constant(args: TokenStream) -> Result { .map(parse_arg) .collect::, _>>()?; - let (headers, expr, default) = if args.len() == 3 { - Ok((args[0].clone(), args[1].clone(), args[2].clone())) - } else { - Err(Error::new( + let (headers, expr) = match &args[..] { + [headers, expr] => Ok((headers, expr)), + _ => Err(Error::new( span, - "Expected 3 string literals: the header #includes, the expression, and a default value (as a C expression string) when no C toolchain is available (e.g. when running cargo check).", + "Expected 2 string literals: the header #includes and the C constant expression evaluate.", )) }?; - let out = match std::env::var("LISA_EVAL_C") { + let (out, static_assert) = match std::env::var("LISA_EVAL_C") { Ok(cmd) => { use std::process::Command; let mut cmd = Command::new(cmd); @@ -425,27 +411,29 @@ pub fn c_constant(args: TokenStream) -> Result { let out = std::str::from_utf8(&out.stdout) .map_err(|_| Error::new(span, "Could not decode toolchain output"))?; - out.trim().to_owned() + let assert_cond = concatcp(quote! {"(", #expr, ") == (", #out, ")"})?; + let out = syn::parse_str::(out.trim())?; + ( + quote! {Some(#out)}, + quote! { + // Ensure that the constant value we got from the compiler matches what we will get in + // the real world when running the code. + ::lisakmod_macros::inlinec::c_static_assert!( + #headers, + #assert_cond + ); + }, + ) } - Err(_) => default.to_owned(), + Err(_) => (quote! {None}, quote! {}), }; - let out_expr = syn::parse_str::(&out)?; - let assert_cond = concatcp(quote! { - "(", #expr, ") == (", #out, ")" - })?; - let out = quote! { + Ok(quote! { { - // Ensure that the constant value we got from the compiler matches what we will get in - // the real world when running the code. - ::lisakmod_macros::inlinec::c_static_assert!( - #headers, - #assert_cond - ); - #out_expr + #static_assert + #out } - }; - Ok(out) + }) } struct CExportInput { @@ -459,39 +447,98 @@ impl Parse for CExportInput { } } -struct CExportAttrs { - link_name: Option, +struct CAttrs { + export_name: Option, + no_mangle: bool, +} + +fn path_to_string(path: &Path) -> String { + path.segments + .iter() + .map(|seg| seg.ident.to_string()) + .collect::>() + .join("::") } -impl Parse for CExportAttrs { +impl Parse for CAttrs { fn parse(input: syn::parse::ParseStream) -> syn::Result { let attrs = Punctuated::::parse_terminated(input)?; - let mut link_name = None; + let unknown = |span| Err(Error::new(span, "Unknown argument".to_string())); + + let mut export_name = None; + let mut no_mangle = false; for attr in attrs { match attr { - Meta::List(ml) => match ml.path.get_ident() { - Some(ident) => match &*ident.to_string() { - "link_name" => { - link_name = Some(syn::parse2::(ml.tokens)?.value()); - } - name => { - return Err(Error::new(ml.span(), format!("Unknown argument: {name}"))) + // Meta::List(ml) => match ml.path.get_ident() { + // Some(ident) => match &*ident.to_string() { + // "export_name" => { + // export_name = Some(syn::parse2::(ml.tokens)?.value()); + // } + // _ => return unknown(ml.span()), + // }, + // _ => return unknown(ml.span()), + // }, + Meta::List(ml) => return unknown(ml.span()), + Meta::Path(path) => { + let span = path.span(); + let path = path_to_string(&path); + match &*path { + "no_mangle" => { + no_mangle = true; } - }, - _ => return Err(Error::new(ml.span(), "Invalid argument name".to_string())), - }, - attr => return Err(Error::new(attr.span(), "Unknown argument".to_string())), + _ => return unknown(span), + } + } + + Meta::NameValue(name_value) => { + let path = path_to_string(&name_value.path); + let span = name_value.span(); + let expr = name_value.value; + match &*path { + "export_name" => match expr { + Expr::Lit(lit) => match lit.lit { + Lit::Str(lit) => { + export_name = Some(lit.value()); + } + lit => { + return Err(Error::new( + lit.span(), + "Expected string literal".to_string(), + )) + } + }, + expr => { + return Err(Error::new( + expr.span(), + "Expected string literal".to_string(), + )) + } + }, + _ => return unknown(span), + }; + } } } - Ok(CExportAttrs { link_name }) + if export_name.is_some() && no_mangle { + Err(Error::new( + input.span(), + "\"no_mangle\" and \"export_name\" attributes cannot be specified at the same time" + .to_string(), + )) + } else { + Ok(CAttrs { + export_name, + no_mangle, + }) + } } } pub fn cexport(attrs: TokenStream, code: TokenStream) -> Result { let input = syn::parse2::(code)?; - let attrs = syn::parse2::(attrs)?; + let attrs = syn::parse2::(attrs)?; let mut item_fn = input.item_fn; let f_args = get_f_args(&item_fn)?; let f_ret_ty = get_f_ret_ty(&item_fn)?; @@ -567,10 +614,13 @@ pub fn cexport(attrs: TokenStream, code: TokenStream) -> Result format!("__lisa_c_shim_{name}_{}", get_random()), + let c_name = match attrs.export_name { + None => match attrs.no_mangle { + true => name.to_string(), + false => format!("__lisa_c_shim_{name}_{}", get_random()), + }, Some(name) => { export_markers.push(_export_symbol(format_ident!("{name}"))?); name @@ -600,8 +650,9 @@ pub fn cexport(attrs: TokenStream, code: TokenStream) -> Result Result { Ok(quote! { const _:() = { #[used] - #[no_mangle] + #[unsafe(no_mangle)] static #marker: () = (); }; }) } + +fn make_c_out(c_code: TokenStream, section: &str) -> Result { + Ok(quote! { + const _: () = { + const CODE_SLICE: &[u8] = #c_code.as_bytes(); + const CODE_LEN: usize = CODE_SLICE.len(); + + // Store the C function in a section of the binary, that will be extracted by the + // module Makefile and compiled separately as C code. + #[link_section = #section ] + #[used] + static CODE: [u8; CODE_LEN] = ::lisakmod_macros::private::misc::slice_to_array::<{CODE_LEN}>(CODE_SLICE); + }; + }) +} + +struct CStaticInput { + name: Ident, + c_code: ( + Option, + Option, + Option, + ), + static_attrs: Vec, + ty: Type, + vis: Visibility, +} + +impl Parse for CStaticInput { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let item_static: ItemStatic = input.parse()?; + let span = item_static.span(); + let static_attrs = item_static.attrs; + let name = item_static.ident; + let ty = *item_static.ty; + let vis = item_static.vis; + + let snippets = match *item_static.expr { + Expr::Tuple(ExprTuple { elems, .. }) => { + let mut snippets = Vec::new(); + for item in elems { + snippets.push(match item { + Expr::Lit(lit) => match lit.lit { + Lit::Str(lit) => Ok(quote! {#lit}), + lit => Err(Error::new( + lit.span(), + "Expected string literal or macro invocation", + )), + }, + Expr::Macro(mac) => Ok(quote! {#mac}), + expr => Err(Error::new( + expr.span(), + "Expected string literal or macro invocation", + )), + }?); + } + Ok(snippets) + } + expr => Err(Error::new( + expr.span(), + "C static must be defined by a tuple of string constants", + )), + }?; + + let c_code = match &snippets[..] { + [code] => Ok(( + None, + Some(code.clone()), + None, + )), + [pre, code] => Ok(( + Some(pre.clone()), + Some(code.clone()), + None, + )), + [pre, code, post] => Ok(( + Some(pre.clone()), + Some(code.clone()), + Some(post.clone()), + )), + _ => Err(Error::new(span, "An inline C static must contain either a single string expression with the C code in it or two string expressions (one output before the function and one for the definition). The C macro STATIC_VARIABLE must be used to refer to the name of the C variable.")) + }?; + + Ok(Self { + name, + c_code, + static_attrs, + ty, + vis, + }) + } +} + +pub fn cstatic(attrs: TokenStream, code: TokenStream) -> Result { + let input = syn::parse2::(code)?; + let attrs = syn::parse2::(attrs)?; + + let name = input.name; + let static_attrs = input.static_attrs; + let ty = input.ty; + let vis = input.vis; + let (pre_c_code, c_code, post_c_code) = input.c_code; + + let empty = quote! {""}; + let (pre_c_code, pre_c_code_line) = match &pre_c_code { + Some(tokens) => (tokens, tokens.span().start().line), + None => (&empty, 0), + }; + + let c_code_line = match &c_code { + Some(tokens) => tokens.span().start().line, + None => pre_c_code_line, + }; + let (post_c_code, post_c_code_line) = match &post_c_code { + Some(tokens) => (tokens, tokens.span().start().line), + None => (&empty, c_code_line), + }; + + let pre_c_code_line = pre_c_code_line.to_string(); + let c_code_line = c_code_line.to_string(); + let post_c_code_line = post_c_code_line.to_string(); + + let c_name = match attrs.export_name { + None => match attrs.no_mangle { + true => name.to_string(), + false => format!("__lisa_c_shim_{name}_{}", get_random()), + }, + Some(name) => name, + }; + + let section = format!(".binstore.c.code.{}", c_name); + let c_out = concatcp(quote! { + "#define STATIC_VARIABLE ", #c_name, "\n", + "\n#line ", #pre_c_code_line, " \"", file!(), "\"\n", + #pre_c_code, + "\n#line ", #c_code_line, " \"", file!(), "\"\n", + #c_code, + + // Check that the variable created by the user has the correct type by taking its address + // in a pointer to the type we expect. + "\n#define DECLARATOR PTR_TY_DECL(", #c_name, "_type_check)\n", + "static __attribute__ ((unused)) ", <#ty as ::lisakmod_macros::inlinec::FfiType>::C_DECL, " = &", #c_name, ";\n", + "#undef DECLARATOR", + + "\n#line ", #post_c_code_line, " \"", file!(), "\"\n", + #post_c_code, + "#undef STATIC_VARIABLE\n" + })?; + + let c_out = make_c_out(c_out, §ion)?; + + Ok(quote! { + #c_out + + const _: () = { + const fn check_ffi_type() + where + T: ::lisakmod_macros::inlinec::FfiType, + T: ::lisakmod_macros::inlinec::FromFfi, + T: ::lisakmod_macros::inlinec::IntoFfi + {} + check_ffi_type::<#ty>(); + }; + + unsafe extern "C" { + #[link_name = #c_name] + #(#static_attrs)* + #vis static #name: #ty; + } + }) +} diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/lib.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/lib.rs index 31f2c2560..231e497c3 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/lib.rs +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/lib.rs @@ -24,8 +24,8 @@ pub fn cexport(attrs: TokenStream, code: TokenStream) -> TokenStream { } #[proc_macro] -pub fn c_constant(args: TokenStream) -> TokenStream { - convert(inlinec::c_constant(args.into())) +pub fn cconstant(args: TokenStream) -> TokenStream { + convert(inlinec::cconstant(args.into())) } #[proc_macro] @@ -37,3 +37,8 @@ pub fn export_symbol(args: TokenStream) -> TokenStream { pub fn concatcp(args: TokenStream) -> TokenStream { convert(misc::concatcp(args.into())) } + +#[proc_macro_attribute] +pub fn cstatic(attrs: TokenStream, code: TokenStream) -> TokenStream { + convert(inlinec::cstatic(attrs.into(), code.into())) +} diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/src/inlinec.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/src/inlinec.rs index eda33651e..5ede5f376 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/src/inlinec.rs +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/src/inlinec.rs @@ -12,7 +12,7 @@ use core::{ ptr::{null, null_mut, NonNull}, }; -pub use lisakmod_macros_proc::{c_constant, cexport, cfunc}; +pub use lisakmod_macros_proc::{cconstant, cexport, cfunc, cstatic}; pub trait FfiType { // TODO: if and when Rust gains const trait methods, we can just define a const function to @@ -817,22 +817,25 @@ impl FromFfi for Result<(), Infallible> { } } -pub struct Align(::Aligned) -where - Self: GetAligned; - -pub trait GetAligned { - type Aligned; +// Combine both alignment and size so that the resulting type can be used in a repr(C) parent +// struct. Otherwise, we would end up having to either: +// * Use a ZST for alignment, which cannot be repr(C) as of 25/11/2024 +// * Use repr(transparent) instead of repr(C), but then we have more than one field with non-zero +// size or non-1 alignment. +pub trait GetAlignedData { + type AlignedData; } macro_rules! make_getaligned { ($($align:tt),*) => { $( const _:() = { + #[repr(C)] #[repr(align($align))] - pub struct Aligned; - impl GetAligned for Align<$align> { - type Aligned = Aligned; + pub struct AlignedData([u8; SIZE]); + + impl GetAlignedData for () { + type AlignedData = AlignedData; } }; )* @@ -906,29 +909,40 @@ macro_rules! __internal_opaque_type { ($vis:vis struct $name:ident, $c_name:literal, $c_header:literal) => { // Model opaque types as recommended in the Rustonomicon: // https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs - #[repr(C)] + // On top of that recipe, we add: + // * A way to get the correct alignment using C compile-time reflection. + // * repr(transparent), so that the FFI-safe warning is satisfied in all use cases. This + // way, we guarantee that the struct has only one non-ZST member, and its ABI is that of + // this member. The member in question is an array of u8, which is FFI-safe. + #[repr(transparent)] $vis struct $name { - _data: [ - u8; - $crate::inlinec::c_constant!( - ("#include <", $c_header, ">"), - ("sizeof (", $c_name, ")"), - "1" - ) - ], // Since we cannot make Opaque types aligned with a simple attribute // (#[repr(align(my_macro!()))] is rejected since my_macro!() is not an integer // literal), we add a zero-sized member that allows specifying the alignment as a // generic const parameter. - _align: $crate::inlinec::Align<{ - $crate::inlinec::c_constant!( - ("#include <", $c_header, ">"), - ("_Alignof (", $c_name, ")"), - "1" - ) - }>, - _marker: - ::core::marker::PhantomData<(*mut u8, ::core::marker::PhantomPinned)>, + _data: < + () as $crate::inlinec::GetAlignedData< + { + match $crate::inlinec::cconstant!( + ("#include <", $c_header, ">"), + ("sizeof (", $c_name, ")"), + ) { + Some(x) => x, + None => 1, + } + }, + { + match $crate::inlinec::cconstant!( + ("#include <", $c_header, ">"), + ("_Alignof (", $c_name, ")"), + ) { + Some(x) => x, + None => 1, + } + } + > + >::AlignedData, + _marker: ::core::marker::PhantomData<(*mut u8, ::core::marker::PhantomPinned)>, } // Double check that the we did not fumble the Rust struct layout somehow. @@ -940,8 +954,7 @@ macro_rules! __internal_opaque_type { ::core::mem::align_of::(), ) } - let (size, _): (usize, usize) = member_layout(|x: &$name| &x._data); - let (_, align): (usize, usize) = member_layout(|x: &$name| &x._align); + let (size, align): (usize, usize) = member_layout(|x: &$name| &x._data); // Check alignment first as a wrong alignment will likely impact the overal size as // well due to different padding. assert!( @@ -958,7 +971,23 @@ macro_rules! __internal_opaque_type { use $crate::inlinec::Opaque as _; impl $crate::inlinec::Opaque for $name {} + impl $crate::inlinec::FfiType for $name { + type FfiType = $name; + const C_DECL: &'static str = $crate::misc::concatcp!("BUILTIN_TY_DECL(", $c_name, ", DECLARATOR)"); + } + impl $crate::inlinec::FromFfi for $name { + #[inline] + unsafe fn from_ffi(x: Self::FfiType) -> Self { + x + } + } + impl $crate::inlinec::IntoFfi for $name { + #[inline] + fn into_ffi(self) -> Self::FfiType { + self + } + } }; } // Since the macro is tagged with #[macro_export], it will be exposed in the crate namespace diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/tests/inlinec.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/tests/inlinec.rs index 49ceeda59..76768f11c 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/tests/inlinec.rs +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/tests/inlinec.rs @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: Apache-2.0 */ -use lisakmod_macros::inlinec::c_constant; +use lisakmod_macros::inlinec::cconstant; // #[cfunc] // fn myfunc(x: u64, y: u64) -> u8 { @@ -55,7 +55,7 @@ fn test_cfunc() { macro_rules! mymac { ($header:literal, $ty:literal) => { - c_constant!(("#include <", $header, ">"), ("sizeof(", $ty, ")"), "8") + cconstant!(("#include <", $header, ">"), ("sizeof(", $ty, ")")).unwrap_or(8) }; } assert_eq!(mymac!("linux/kobject.h", "int"), 8); diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod/Cargo.lock b/lisa/_assets/kmodules/lisa/rust/lisakmod/Cargo.lock index d62f138ed..9f12d666d 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod/Cargo.lock +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod/Cargo.lock @@ -21,9 +21,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.161" +version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" [[package]] name = "lisakmod" @@ -51,9 +51,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -69,9 +69,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.87" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "wasi" diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod/src/lib.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/lib.rs index da009096e..5fdf53118 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod/src/lib.rs +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/lib.rs @@ -2,6 +2,7 @@ #![no_std] #![no_builtins] #![feature(gen_blocks)] +#![feature(const_pin)] extern crate alloc; diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod/src/prelude.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/prelude.rs index f2bb322d1..058c2b1d8 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod/src/prelude.rs +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/prelude.rs @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ pub use crate::{ - inlinec::{cexport, cfunc}, + inlinec::{cexport, cfunc, cstatic, cconstant}, runtime::kbox::KBox, }; diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod/src/runtime/alloc.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/runtime/alloc.rs index 5c71287d6..bdd142e55 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod/src/runtime/alloc.rs +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/runtime/alloc.rs @@ -7,7 +7,7 @@ use core::{ }; use crate::{ - inlinec::{c_eval, cfunc}, + inlinec::{cconstant, cfunc}, runtime::printk::pr_err, }; @@ -15,21 +15,20 @@ struct KernelAllocator; #[inline] fn with_size *mut u8>(layout: Layout, f: F) -> *mut u8 { - let minalign = || c_eval!("linux/slab.h", "ARCH_KMALLOC_MINALIGN", usize); + let minalign: usize = cconstant!("#include ", "ARCH_KMALLOC_MINALIGN").unwrap_or(1); let size = layout.size(); let align = layout.align(); // For sizes which are a power of two, the kmalloc() alignment is also guaranteed to be at // least the respective size. - if (size.is_power_of_two() && align <= size) || (align <= minalign()) { + if (size.is_power_of_two() && align <= size) || (align <= minalign) { let ptr = f(layout.size()); assert_eq!((ptr as usize % align), 0); ptr } else { // Do not panic as this would create UB pr_err!( - "Rust: cannot allocate memory with alignment > {minalign} and a size {size} that is not a power of two", - minalign = minalign() + "Rust: cannot allocate memory with alignment > {minalign} and a size {size} that is not a power of two" ); core::ptr::null_mut() } diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod/src/runtime/module.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/runtime/module.rs index fe1650c3d..2e75b352e 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod/src/runtime/module.rs +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/runtime/module.rs @@ -13,7 +13,7 @@ new_static_mutex!( None ); -#[cexport(link_name("rust_mod_init"))] +#[cexport(export_name = "rust_mod_init")] pub fn rust_mod_init() -> c_int { let mut iterator = Box::new(module_main()); let ret = iterator.next().expect("Main iterator did not yield once"); @@ -30,7 +30,7 @@ pub fn rust_mod_init() -> c_int { } } -#[cexport(link_name("rust_mod_exit"))] +#[cexport(export_name = "rust_mod_exit")] pub fn rust_mod_exit() { let mut iterator = MAIN_ITERATOR.lock(); let iterator = iterator diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod/src/runtime/sync.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/runtime/sync.rs index 0eb23c6a4..ee8604172 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod/src/runtime/sync.rs +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/runtime/sync.rs @@ -190,16 +190,7 @@ opaque_type!(pub struct CMutex, "struct mutex", "linux/mutex.h"); enum AllocatedCMutex { KBox(Pin>>), - // FIXME: introduce #[cstatic] to allow defining a static global with C code, the same way we - // have #[cfunc]. This would be restricted to types that are FfiType + IntoFfi + - // FromFfi - - // Mutexes in static variables have to be defined in C code using the DEFINE_MUTEX() macro. - // Unfortunately, that means we can't get their address directly in Rust, as that would require - // having an equivalent of #[cfunc] for other values than functions, which would be doable but - // extra work. Instead, we can just make a C function that declares a static mutex inside it - // and returns its address. - Static(fn() -> Pin<&'static UnsafeCell>), + Static(Pin<&'static UnsafeCell>), } impl AllocatedCMutex { @@ -207,7 +198,7 @@ impl AllocatedCMutex { fn as_pin_ref(&self) -> Pin<&UnsafeCell> { match self { AllocatedCMutex::KBox(c_mutex) => c_mutex.as_ref(), - AllocatedCMutex::Static(f) => f(), + AllocatedCMutex::Static(c_mutex) => *c_mutex, } } } @@ -216,18 +207,26 @@ macro_rules! new_static_mutex { ($vis:vis $name:ident, $ty:ty, $data:expr) => { $vis static $name: $crate::runtime::sync::Mutex<$ty> = $crate::runtime::sync::Mutex::__internal_from_ref($data, { - #[$crate::inlinec::cfunc] - fn get_static_mutex() -> ::core::pin::Pin< - &'static ::core::cell::UnsafeCell<$crate::runtime::sync::CMutex>, - > { - "#include "; - - r#" - static DEFINE_MUTEX(mutex); - return &mutex; - "# + #[$crate::inlinec::cstatic] + static STATIC_MUTEX: $crate::runtime::sync::CMutex = ( + "#include ", + "DEFINE_MUTEX(STATIC_VARIABLE);" + ); + unsafe { + ::core::pin::Pin::new_unchecked( + // SAFETY: Similarly to UnsafeCell::from_mut(), we convert &T to &UnsafeCell. + // UnsafeCell::from_mut() requires a &mut, which is not possible for a + // static variable in Rust 2024 edition however. Requiring an &mut provides + // the assurance that no other reference is floating around, which is + // necessary for the operation to be sound. Here, it is guaranteed as + // nothing else has access to our static variable and we do not leak any + // &T. + // + // Also, UnsafeCell and T have the same memory layout, and no &T or &mut + // T can escape here as STATIC_MUTEX is only reachable here. + &*( &STATIC_MUTEX as *const _ as *const ::core::cell::UnsafeCell<_>) + ) } - get_static_mutex }); }; } @@ -265,9 +264,9 @@ impl Mutex { } #[inline] - pub const fn __internal_from_ref(x: T, f: fn() -> Pin<&'static UnsafeCell>) -> Self { + pub const fn __internal_from_ref(x: T, c_mutex: Pin<&'static UnsafeCell>) -> Self { Mutex { - c_mutex: AllocatedCMutex::Static(f), + c_mutex: AllocatedCMutex::Static(c_mutex), data: UnsafeCell::new(x), } } diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod/src/tests.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/tests.rs index de6df46dc..2c67a6ac5 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod/src/tests.rs +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/tests.rs @@ -56,7 +56,7 @@ fn test_4() { let minalign = c_eval!("linux/slab.h", "ARCH_KMALLOC_MINALIGN", usize); // Check we don't get any C compilation error with duplicated code. - let minalign2 = c_eval!("linux/slab.h", "ARCH_KMALLOC_MINALIGN", usize); + let minalign2: usize = cconstant!("#include ", "ARCH_KMALLOC_MINALIGN").unwrap(); assert_eq!(minalign, minalign2); assert!(minalign >= 8); } -- GitLab