From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f73.google.com (mail-wm1-f73.google.com [209.85.128.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AB3EB26D5D1 for ; Fri, 28 Feb 2025 12:40:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.73 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740746415; cv=none; b=dRMvA6yejYkaFv29nUdgMVkK2KvqzWrPTrFajdmJF5hJVIo5juIXaXTs49DpuAehk0bGks7jgmc8o18B/a5XnfW+Q79b3AVMWrFAv2hmfmYvUblNho9VtFn86d6JEFebBgJn2Va2ldUFAMBa17UdQOn6qN9d2h329sINk3C+AeU= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740746415; c=relaxed/simple; bh=5WVJDDoOMWVTToGZqS6EzXIOd2Gz+/3HoUezV/SekI8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=rM2HKx91Ge3Oaidt8+XpMNN39KGfGOgMfvPR2/NILPrG0l0jFxfKSy92tUYND589s01TdKbYBv4KLEv1SwI1FvispT/o5lwzro7bOi5xeI1izgnLI0AqRbmyV9YRNdGn0Ll5tb7G+uN3Ae4ogegqfaDFsw7BTqnCnEhAxCS5q7k= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=4AleCqwK; arc=none smtp.client-ip=209.85.128.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="4AleCqwK" Received: by mail-wm1-f73.google.com with SMTP id 5b1f17b1804b1-4399d2a1331so10801265e9.1 for ; Fri, 28 Feb 2025 04:40:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1740746412; x=1741351212; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=l3jVWPGGKqLCvuenpCukdw2BdIHWqKd1uEI4muVogSg=; b=4AleCqwKB7Y7bB84UAMNwT1q9frjP5D35i4DJlBuE7R3gSrljybVEGj5smGpCeIwCq dvnK90oWZTOZe35XcuYogmZoC8qTo2lZw4N5jgixb2E6rGTdpll65u9Z1Id1gznxzCbd Io160W4IdZxqH+TH4J/rDptSJjTY18QDEJ3rbCV/O4iucLpP/FpyE5epyKH/vD51DYEF x4P17MnjGwfztUcTCnEW+/ahE5AeT0ZhftkcM/ux4eF/2P4nT6rx10QcsCyCTzsyvd5K TzgxyXr51h6SgykiR4sdcipw+dK57Nucko4izif2AtPShla/GijKJsgy3Qa8/W/1wpJb Yxhw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740746412; x=1741351212; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=l3jVWPGGKqLCvuenpCukdw2BdIHWqKd1uEI4muVogSg=; b=M2ERApAzRhJYFmKi33xZEYLTbHvskNX8wwVuYzR0IORtgj29P+/1L86ah4yr4rjunm YYBCANQSaVkwla68iyF+d+7HiTxHQBY72vdjoCBQyEpLcyk+3MWdIsES7ORrRy9F3t7F wwCJFRo88Gd7UGv/rIRuWLXxz5RNJIfI881gr3RvJkNMzbjVYg0LaYlhzxqPkXdwuE1x +cnZqQuqlWFG1ohnQhg+I5nEHTdSw34ccM+QV4KNCI6R7TD8TMSrMGCGXuOf0EOFlvQp vhZTKBQkMcVPQPpm6McBUxAyRYIfWkQmiT/tC6lJO8aJwwLKCl2iMVmoO4LstdVKD8QS sr3w== X-Forwarded-Encrypted: i=1; AJvYcCVdiwCjszedXOos2GpzJyjQf+OJPHiCoM3VBjFH9P30Hbd4MIWHAqu/fVDMW/5XvhJHa7qlEAr4s1Rfpgq7Qg==@vger.kernel.org X-Gm-Message-State: AOJu0YyCDoKBgMX3nv9YXCSqWf2wt/fEf8Wj6+miAvunTUv2cSjpAn/U DbFmoUIuRqBJnZhGG66gLlkUoP6neXIsa1h1oA0BjcbctflJn4XGI1MgtPo06Yowwjjjdvo0wv+ 1losIN5qnr91Tug== X-Google-Smtp-Source: AGHT+IGiO67Lsqlqu2zH4k6evB04PeH28TLpEEsqPRIVHE93/W85JAQLdE41QCH630255Ni8glO+u11+ewCTl8g= X-Received: from wmsd10.prod.google.com ([2002:a05:600c:3aca:b0:439:9541:1cf5]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:1c9f:b0:434:9e17:190c with SMTP id 5b1f17b1804b1-43ba6188e94mr26636865e9.0.1740746412195; Fri, 28 Feb 2025 04:40:12 -0800 (PST) Date: Fri, 28 Feb 2025 12:39:32 +0000 In-Reply-To: <20250228-export-macro-v2-0-569cc7e8926c@google.com> Precedence: bulk X-Mailing-List: rust-for-linux@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250228-export-macro-v2-0-569cc7e8926c@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=6541; i=aliceryhl@google.com; h=from:subject:message-id; bh=5WVJDDoOMWVTToGZqS6EzXIOd2Gz+/3HoUezV/SekI8=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBnwa6hUITYWJl/dPCaT3DkAHUJoqZpyVu6b2hRy /7EqajnYoyJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZ8GuoQAKCRAEWL7uWMY5 Rn6GD/9qthSYpENpEq926mPspcUYP5BVOc1VoMWtdTSk2Gbc6ytG9cgiVWr1qh6h0pis4Rlv6GE iCys28XgoAwtyPwUJyouL5vLN69zhjQ9A1bG5/7KdlWu+PQKpeQijeufFnt7VTAOceVE0soo3bm 3UDNjaK/RMuBntw1pkOn7e2gXLdrlsGQEiMGQZTDaoWzKOYjtC7aUCzyJ53a2K0L64Zluk2+5in akMOnB5ywQbnvsWaf9ax7ZC7JA2k2jJxUq32ECwTd7qu4uoHqFaY7Shxryvxp8GCCioblbZPgo9 NNOqSrDW4++cffxUog3Zfq0j81QgGUkio4nl6k5kJwsjdW38+79IkwkM3HgS+Rz8SeMvDvquLFQ XvD6OLEgRXVbnBfURzwDufAhpHElGq10OMT1txFrxc7RgQgpES+HqNZ33JsoMir2ROLhMgn0SAA 2cAHKLJwlunm84rd7ZcPt9o+Glm/Ar0oUksZpbEOI5z+HNsgYF2jE/gg9fLzq+VkzWxKLnEzLGx ahs9Rlp4WfyhfriizEC0Dp1oHst/0VsBpOEJL7olDIl+WCAZ8hnlG6ABHeJ4B0sY12z31QFzICL 73Mg4qh8RVEd0TlTS+ZKthzzkWyJx+bKVkddrJeRGkruXRN6cXuYeZO+Wpo4FXMpQxXzItHZuFa Mav3YeJON9IyQuQ== X-Mailer: b4 0.14.1 Message-ID: <20250228-export-macro-v2-3-569cc7e8926c@google.com> Subject: [PATCH v2 3/5] rust: add #[export] macro From: Alice Ryhl To: Greg Kroah-Hartman , Miguel Ojeda Cc: Petr Mladek , Steven Rostedt , Andy Shevchenko , Rasmus Villemoes , Sergey Senozhatsky , Andrew Morton , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Benno Lossin , Andreas Hindborg , Trevor Gross , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Rust has two different tools for generating function declarations to call across the FFI boundary: * bindgen. Generates Rust declarations from a C header. * cbindgen. Generates C headers from Rust declarations. In the kernel, we only use bindgen. This is because cbindgen assumes a cargo-based buildsystem, so it is not compatible with the kernel's build system. This means that when C code calls a Rust function by name, its signature must be duplicated in both Rust code and a C header, and the signature needs to be kept in sync manually. To eliminate this manual checking, introduce a new macro that verifies at compile time that the two function declarations use the same signature. The idea is to run the C declaration through bindgen, and then have rustc verify that the function pointers have the same type. The signature must still be written twice, but at least you can no longer get it wrong. If the signatures don't match, you will get errors that look like this: error[E0308]: `if` and `else` have incompatible types --> /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}` It is unfortunate that the error message starts out by saying "`if` and `else` have incompatible types", but I believe the rest of the error message is reasonably clear and not too confusing. Signed-off-by: Alice Ryhl --- rust/kernel/prelude.rs | 2 +- rust/macros/export.rs | 28 ++++++++++++++++++++++++++++ rust/macros/helpers.rs | 19 ++++++++++++++++++- rust/macros/lib.rs | 24 ++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 2 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..c5ec75f2b07f --- /dev/null +++ b/rust/macros/export.rs @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 + +use crate::helpers::function_name; +use proc_macro::TokenStream; + +/// Please see [`crate::export`] for documentation. +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::() + .unwrap(); + }; + + // This verifies that the function has the same signature as the declaration generated by + // bindgen. It makes use of the fact that all branches of an if/else must have the same type. + let signature_check = quote!( + const _: () = { + if true { + ::kernel::bindings::#name + } else { + #name + }; + }; + ); + + let no_mangle = "#[no_mangle]".parse::().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 { if let Some(TokenTree::Ident(ident)) = it.next() { @@ -215,3 +215,20 @@ pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec) { rest, ) } + +/// Given a function declaration, finds the name of the function. +pub(crate) fn function_name(input: TokenStream) -> Option { + 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..fbb2860e991f 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,29 @@ pub fn vtable(attr: TokenStream, ts: TokenStream) -> TokenStream { vtable::vtable(attr, ts) } +/// Export a function so that C code can call it via a header file. +/// +/// Functions exported using this macro can be called from C code using the declaration in the +/// appropriate header file. It should only be used in cases where C calls the function through a +/// header file; cases where C calls into Rust via a function pointer in a vtable (such as +/// `file_operations`) should not use this macro. +/// +/// This macro has the following effect: +/// +/// * Disables name mangling for this function. +/// * Verifies at compile-time that the function signature matches the declaration in the header +/// file. +/// +/// You must declare the signature of the Rust function in a header file that is included by +/// `rust/bindings/bindings_helper.h`. +/// +/// This macro is *not* the same as the C macros `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 -- 2.48.1.711.g2feabab25a-goog