From: Alice Ryhl <aliceryhl@google.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Miguel Ojeda <ojeda@kernel.org>
Cc: "Petr Mladek" <pmladek@suse.com>,
"Steven Rostedt" <rostedt@goodmis.org>,
"Andy Shevchenko" <andriy.shevchenko@linux.intel.com>,
"Rasmus Villemoes" <linux@rasmusvillemoes.dk>,
"Sergey Senozhatsky" <senozhatsky@chromium.org>,
"Andrew Morton" <akpm@linux-foundation.org>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <benno.lossin@proton.me>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Trevor Gross" <tmgross@umich.edu>,
"Maarten Lankhorst" <maarten.lankhorst@linux.intel.com>,
"Maxime Ripard" <mripard@kernel.org>,
"Thomas Zimmermann" <tzimmermann@suse.de>,
"David Airlie" <airlied@gmail.com>,
"Simona Vetter" <simona@ffwll.ch>,
linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
dri-devel@lists.freedesktop.org,
"Alice Ryhl" <aliceryhl@google.com>
Subject: [PATCH 2/4] rust: add #[export] macro
Date: Thu, 27 Feb 2025 17:02:00 +0000 [thread overview]
Message-ID: <20250227-export-macro-v1-2-948775fc37aa@google.com> (raw)
In-Reply-To: <20250227-export-macro-v1-0-948775fc37aa@google.com>
This macro behaves like #[no_mangle], but also performs an assertion
that the Rust function has the same signature as what is declared in the
C header.
If the signatures don't match, you will get errors that look like this:
error[E0308]: `if` and `else` have incompatible types
--> <linux>/rust/kernel/print.rs:22:22
|
21 | #[export]
| --------- expected because of this
22 | unsafe extern "C" fn rust_fmt_argument(
| ^^^^^^^^^^^^^^^^^ expected `u8`, found `i8`
|
= note: expected fn item `unsafe extern "C" fn(*mut u8, *mut u8, *mut c_void) -> *mut u8 {bindings::rust_fmt_argument}`
found fn item `unsafe extern "C" fn(*mut i8, *mut i8, *const c_void) -> *mut i8 {print::rust_fmt_argument}`
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
rust/kernel/prelude.rs | 2 +-
rust/macros/export.rs | 25 +++++++++++++++++++++++++
rust/macros/helpers.rs | 19 ++++++++++++++++++-
rust/macros/lib.rs | 18 ++++++++++++++++++
rust/macros/quote.rs | 21 +++++++++++++++++++--
5 files changed, 81 insertions(+), 4 deletions(-)
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index dde2e0649790..889102f5a81e 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -17,7 +17,7 @@
pub use crate::alloc::{flags::*, Box, KBox, KVBox, KVVec, KVec, VBox, VVec, Vec};
#[doc(no_inline)]
-pub use macros::{module, pin_data, pinned_drop, vtable, Zeroable};
+pub use macros::{export, module, pin_data, pinned_drop, vtable, Zeroable};
pub use super::{build_assert, build_error};
diff --git a/rust/macros/export.rs b/rust/macros/export.rs
new file mode 100644
index 000000000000..3398e1655124
--- /dev/null
+++ b/rust/macros/export.rs
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use crate::helpers::function_name;
+use proc_macro::TokenStream;
+
+pub(crate) fn export(_attr: TokenStream, ts: TokenStream) -> TokenStream {
+ let Some(name) = function_name(ts.clone()) else {
+ return "::core::compile_error!(\"The #[export] attribute must be used on a function.\");"
+ .parse::<TokenStream>()
+ .unwrap();
+ };
+
+ let signature_check = quote!(
+ const _: () = {
+ if true {
+ ::kernel::bindings::#name
+ } else {
+ #name
+ };
+ };
+ );
+
+ let no_mangle = "#[no_mangle]".parse::<TokenStream>().unwrap();
+ TokenStream::from_iter([signature_check, no_mangle, ts])
+}
diff --git a/rust/macros/helpers.rs b/rust/macros/helpers.rs
index 563dcd2b7ace..3e04f8ecfc74 100644
--- a/rust/macros/helpers.rs
+++ b/rust/macros/helpers.rs
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
-use proc_macro::{token_stream, Group, TokenStream, TokenTree};
+use proc_macro::{token_stream, Group, Ident, TokenStream, TokenTree};
pub(crate) fn try_ident(it: &mut token_stream::IntoIter) -> Option<String> {
if let Some(TokenTree::Ident(ident)) = it.next() {
@@ -215,3 +215,20 @@ pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec<TokenTree>) {
rest,
)
}
+
+/// Given a function declaration, finds the name of the function.
+pub(crate) fn function_name(input: TokenStream) -> Option<Ident> {
+ let mut input = input.into_iter();
+ while let Some(token) = input.next() {
+ match token {
+ TokenTree::Ident(i) if i.to_string() == "fn" => {
+ if let Some(TokenTree::Ident(i)) = input.next() {
+ return Some(i);
+ }
+ return None;
+ }
+ _ => continue,
+ }
+ }
+ None
+}
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index d61bc6a56425..3cbf7705c4c1 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -9,6 +9,7 @@
#[macro_use]
mod quote;
mod concat_idents;
+mod export;
mod helpers;
mod module;
mod paste;
@@ -174,6 +175,23 @@ pub fn vtable(attr: TokenStream, ts: TokenStream) -> TokenStream {
vtable::vtable(attr, ts)
}
+/// Export a function so that C code can call it.
+///
+/// This macro has the following effect:
+///
+/// * Disables name mangling for this function.
+/// * Verifies at compile-time that the function signature matches what's in the header file.
+///
+/// This macro requires that the function is mentioned in a C header file, and that the header file
+/// is included in `rust/bindings/bindings_helper.h`.
+///
+/// This macro is *not* the same as the C macro `EXPORT_SYMBOL*`, since all Rust symbols are
+/// currently automatically exported with `EXPORT_SYMBOL_GPL`.
+#[proc_macro_attribute]
+pub fn export(attr: TokenStream, ts: TokenStream) -> TokenStream {
+ export::export(attr, ts)
+}
+
/// Concatenate two identifiers.
///
/// This is useful in macros that need to declare or reference items with names
diff --git a/rust/macros/quote.rs b/rust/macros/quote.rs
index 33a199e4f176..c18960a91082 100644
--- a/rust/macros/quote.rs
+++ b/rust/macros/quote.rs
@@ -20,6 +20,12 @@ fn to_tokens(&self, tokens: &mut TokenStream) {
}
}
+impl ToTokens for proc_macro::Ident {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.extend([TokenTree::from(self.clone())]);
+ }
+}
+
impl ToTokens for TokenTree {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend([self.clone()]);
@@ -40,7 +46,7 @@ fn to_tokens(&self, tokens: &mut TokenStream) {
/// `quote` crate but provides only just enough functionality needed by the current `macros` crate.
macro_rules! quote_spanned {
($span:expr => $($tt:tt)*) => {{
- let mut tokens;
+ let mut tokens: ::std::vec::Vec<::proc_macro::TokenTree>;
#[allow(clippy::vec_init_then_push)]
{
tokens = ::std::vec::Vec::new();
@@ -65,7 +71,8 @@ macro_rules! quote_spanned {
quote_spanned!(@proc $v $span $($tt)*);
};
(@proc $v:ident $span:ident ( $($inner:tt)* ) $($tt:tt)*) => {
- let mut tokens = ::std::vec::Vec::new();
+ #[allow(unused_mut)]
+ let mut tokens = ::std::vec::Vec::<::proc_macro::TokenTree>::new();
quote_spanned!(@proc tokens $span $($inner)*);
$v.push(::proc_macro::TokenTree::Group(::proc_macro::Group::new(
::proc_macro::Delimiter::Parenthesis,
@@ -136,6 +143,16 @@ macro_rules! quote_spanned {
));
quote_spanned!(@proc $v $span $($tt)*);
};
+ (@proc $v:ident $span:ident = $($tt:tt)*) => {
+ $v.push(::proc_macro::TokenTree::Punct(
+ ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Alone)
+ ));
+ quote_spanned!(@proc $v $span $($tt)*);
+ };
+ (@proc $v:ident $span:ident _ $($tt:tt)*) => {
+ $v.push(::proc_macro::TokenTree::Ident(::proc_macro::Ident::new("_", $span)));
+ quote_spanned!(@proc $v $span $($tt)*);
+ };
(@proc $v:ident $span:ident $id:ident $($tt:tt)*) => {
$v.push(::proc_macro::TokenTree::Ident(::proc_macro::Ident::new(stringify!($id), $span)));
quote_spanned!(@proc $v $span $($tt)*);
--
2.48.1.658.g4767266eb4-goog
next prev parent reply other threads:[~2025-02-27 17:02 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <UXTosjUFv_CKOV-K4oqNGBhMEv64tds9NgXWhHEYdCHuKM2qSPFrpBnTqhFGkcbv5_KXYERykIXhn-sYnEeuUg==@protonmail.internalid>
2025-02-27 17:01 ` [PATCH 0/4] Check Rust signatures at compile time Alice Ryhl
2025-02-27 17:01 ` [PATCH 1/4] rust: fix signature of rust_fmt_argument Alice Ryhl
2025-02-28 8:13 ` Andreas Hindborg
2025-02-28 8:44 ` Alice Ryhl
2025-02-28 9:17 ` Andreas Hindborg
2025-02-27 17:02 ` Alice Ryhl [this message]
2025-02-28 8:12 ` [PATCH 2/4] rust: add #[export] macro Andreas Hindborg
2025-02-28 9:04 ` Alice Ryhl
2025-02-28 9:24 ` Andreas Hindborg
2025-02-27 17:02 ` [PATCH 3/4] print: use new #[export] macro for rust_fmt_argument Alice Ryhl
2025-02-28 8:15 ` Andreas Hindborg
2025-02-27 17:02 ` [PATCH 4/4] panic_qr: use new #[export] macro Alice Ryhl
2025-02-27 21:28 ` Boqun Feng
2025-02-27 23:01 ` Alice Ryhl
2025-02-28 8:19 ` Andreas Hindborg
2025-02-27 19:30 ` [PATCH 0/4] Check Rust signatures at compile time Greg Kroah-Hartman
2025-02-27 23:03 ` Alice Ryhl
2025-02-28 8:47 ` Alice Ryhl
2025-02-28 7:19 ` Andreas Hindborg
2025-02-28 8:46 ` Alice Ryhl
2025-02-28 9:18 ` Andreas Hindborg
2025-02-28 12:27 ` Andy Shevchenko
2025-02-28 12:26 ` Andy Shevchenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250227-export-macro-v1-2-948775fc37aa@google.com \
--to=aliceryhl@google.com \
--cc=a.hindborg@kernel.org \
--cc=airlied@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=andriy.shevchenko@linux.intel.com \
--cc=benno.lossin@proton.me \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@rasmusvillemoes.dk \
--cc=maarten.lankhorst@linux.intel.com \
--cc=mripard@kernel.org \
--cc=ojeda@kernel.org \
--cc=pmladek@suse.com \
--cc=rostedt@goodmis.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=senozhatsky@chromium.org \
--cc=simona@ffwll.ch \
--cc=tmgross@umich.edu \
--cc=tzimmermann@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.