diff --git a/CHANGELOG.md b/CHANGELOG.md index de08d829e1..8174ccd85c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -148,6 +148,8 @@ # Unreleased ## Added + * new feature: `--override-abi` flag to override the ABI used by functions + matching a regular expression. ## Changed diff --git a/bindgen-cli/options.rs b/bindgen-cli/options.rs index 5c3960e92b..c186cfd92c 100644 --- a/bindgen-cli/options.rs +++ b/bindgen-cli/options.rs @@ -568,6 +568,12 @@ where Arg::new("merge-extern-blocks") .long("merge-extern-blocks") .help("Deduplicates extern blocks."), + Arg::new("override-abi") + .long("override-abi") + .help("Overrides the ABI of functions matching . The value must be of the shape : where can be one of C, stdcall, fastcall, thiscall, aapcs or win64.") + .value_name("override") + .multiple_occurrences(true) + .number_of_values(1), Arg::new("V") .long("version") .help("Prints the version, and exits"), @@ -1088,5 +1094,17 @@ where builder = builder.merge_extern_blocks(true); } + if let Some(abi_overrides) = matches.values_of("override-abi") { + for abi_override in abi_overrides { + let (regex, abi_str) = abi_override + .rsplit_once("=") + .expect("Invalid ABI override: Missing `=`"); + let abi = abi_str + .parse() + .unwrap_or_else(|err| panic!("Invalid ABI override: {}", err)); + builder = builder.override_abi(abi, regex); + } + } + Ok((builder, output, verbose)) } diff --git a/bindgen-tests/tests/expectations/tests/abi-override.rs b/bindgen-tests/tests/expectations/tests/abi-override.rs new file mode 100644 index 0000000000..49693692e4 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/abi-override.rs @@ -0,0 +1,16 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +extern "fastcall" { + pub fn foo(); +} +extern "stdcall" { + pub fn bar(); +} +extern "C" { + pub fn baz(); +} diff --git a/bindgen-tests/tests/headers/abi-override.h b/bindgen-tests/tests/headers/abi-override.h new file mode 100644 index 0000000000..cee72caeef --- /dev/null +++ b/bindgen-tests/tests/headers/abi-override.h @@ -0,0 +1,5 @@ +// bindgen-flags: --override-abi=foo=fastcall --override-abi=bar=stdcall + +void foo(); +void bar(); +void baz(); diff --git a/bindgen/codegen/dyngen.rs b/bindgen/codegen/dyngen.rs index 26cfe5cc9e..d02c51e420 100644 --- a/bindgen/codegen/dyngen.rs +++ b/bindgen/codegen/dyngen.rs @@ -1,5 +1,5 @@ use crate::codegen; -use crate::ir::function::Abi; +use crate::ir::function::ClangAbi; use proc_macro2::Ident; /// Used to build the output tokens for dynamic bindings. @@ -113,10 +113,10 @@ impl DynamicItems { } #[allow(clippy::too_many_arguments)] - pub fn push( + pub(crate) fn push( &mut self, ident: Ident, - abi: Abi, + abi: ClangAbi, is_variadic: bool, is_required: bool, args: Vec, diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 7e0d7aa0c9..1c959ffdb6 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -32,7 +32,9 @@ use crate::ir::derive::{ }; use crate::ir::dot; use crate::ir::enum_ty::{Enum, EnumVariant, EnumVariantValue}; -use crate::ir::function::{Abi, Function, FunctionKind, FunctionSig, Linkage}; +use crate::ir::function::{ + Abi, ClangAbi, Function, FunctionKind, FunctionSig, Linkage, +}; use crate::ir::int::IntKind; use crate::ir::item::{IsOpaque, Item, ItemCanonicalName, ItemCanonicalPath}; use crate::ir::item_kind::ItemKind; @@ -2475,9 +2477,13 @@ impl MethodCodegen for Method { _ => panic!("How in the world?"), }; - let supported_abi = match signature.abi() { - Abi::ThisCall => ctx.options().rust_features().thiscall_abi, - Abi::Vectorcall => ctx.options().rust_features().vectorcall_abi, + let supported_abi = match signature.abi(ctx, Some(&*name)) { + ClangAbi::Known(Abi::ThisCall) => { + ctx.options().rust_features().thiscall_abi + } + ClangAbi::Known(Abi::Vectorcall) => { + ctx.options().rust_features().vectorcall_abi + } _ => true, }; @@ -3991,14 +3997,16 @@ impl TryToRustTy for FunctionSig { // TODO: we might want to consider ignoring the reference return value. let ret = utils::fnsig_return_ty(ctx, self); let arguments = utils::fnsig_arguments(ctx, self); - let abi = self.abi(); + let abi = self.abi(ctx, None); match abi { - Abi::ThisCall if !ctx.options().rust_features().thiscall_abi => { + ClangAbi::Known(Abi::ThisCall) + if !ctx.options().rust_features().thiscall_abi => + { warn!("Skipping function with thiscall ABI that isn't supported by the configured Rust target"); Ok(proc_macro2::TokenStream::new()) } - Abi::Vectorcall + ClangAbi::Known(Abi::Vectorcall) if !ctx.options().rust_features().vectorcall_abi => { warn!("Skipping function with vectorcall ABI that isn't supported by the configured Rust target"); @@ -4102,22 +4110,24 @@ impl CodeGenerator for Function { attributes.push(attributes::doc(comment)); } - let abi = match signature.abi() { - Abi::ThisCall if !ctx.options().rust_features().thiscall_abi => { + let abi = match signature.abi(ctx, Some(name)) { + ClangAbi::Known(Abi::ThisCall) + if !ctx.options().rust_features().thiscall_abi => + { warn!("Skipping function with thiscall ABI that isn't supported by the configured Rust target"); return None; } - Abi::Vectorcall + ClangAbi::Known(Abi::Vectorcall) if !ctx.options().rust_features().vectorcall_abi => { warn!("Skipping function with vectorcall ABI that isn't supported by the configured Rust target"); return None; } - Abi::Win64 if signature.is_variadic() => { + ClangAbi::Known(Abi::Win64) if signature.is_variadic() => { warn!("Skipping variadic function with Win64 ABI that isn't supported"); return None; } - Abi::Unknown(unknown_abi) => { + ClangAbi::Unknown(unknown_abi) => { panic!( "Invalid or unknown abi {:?} for function {:?} ({:?})", unknown_abi, canonical_name, self @@ -4515,7 +4525,7 @@ pub(crate) fn codegen( pub mod utils { use super::{error, ToRustTyOrOpaque}; use crate::ir::context::BindgenContext; - use crate::ir::function::{Abi, FunctionSig}; + use crate::ir::function::{Abi, ClangAbi, FunctionSig}; use crate::ir::item::{Item, ItemCanonicalPath}; use crate::ir::ty::TypeKind; use proc_macro2; @@ -4976,10 +4986,10 @@ pub mod utils { // Returns true if `canonical_name` will end up as `mangled_name` at the // machine code level, i.e. after LLVM has applied any target specific // mangling. - pub fn names_will_be_identical_after_mangling( + pub(crate) fn names_will_be_identical_after_mangling( canonical_name: &str, mangled_name: &str, - call_conv: Option, + call_conv: Option, ) -> bool { // If the mangled name and the canonical name are the same then no // mangling can have happened between the two versions. @@ -4992,13 +5002,13 @@ pub mod utils { let mangled_name = mangled_name.as_bytes(); let (mangling_prefix, expect_suffix) = match call_conv { - Some(Abi::C) | + Some(ClangAbi::Known(Abi::C)) | // None is the case for global variables None => { (b'_', false) } - Some(Abi::Stdcall) => (b'_', true), - Some(Abi::Fastcall) => (b'@', true), + Some(ClangAbi::Known(Abi::Stdcall)) => (b'_', true), + Some(ClangAbi::Known(Abi::Fastcall)) => (b'@', true), // This is something we don't recognize, stay on the safe side // by emitting the `#[link_name]` attribute diff --git a/bindgen/ir/function.rs b/bindgen/ir/function.rs index c160ed8149..cd59b594f7 100644 --- a/bindgen/ir/function.rs +++ b/bindgen/ir/function.rs @@ -15,6 +15,7 @@ use proc_macro2; use quote; use quote::TokenStreamExt; use std::io; +use std::str::FromStr; const RUST_DERIVE_FUNPTR_LIMIT: usize = 12; @@ -170,8 +171,8 @@ impl DotAttributes for Function { } } -/// An ABI extracted from a clang cursor. -#[derive(Debug, Copy, Clone)] +/// A valid rust ABI. +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] pub enum Abi { /// The default C ABI. C, @@ -187,32 +188,72 @@ pub enum Abi { Aapcs, /// The "win64" ABI. Win64, +} + +impl FromStr for Abi { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "C" => Ok(Self::C), + "stdcall" => Ok(Self::Stdcall), + "fastcall" => Ok(Self::Fastcall), + "thiscall" => Ok(Self::ThisCall), + "vectorcall" => Ok(Self::Vectorcall), + "aapcs" => Ok(Self::Aapcs), + "win64" => Ok(Self::Win64), + _ => Err(format!("Invalid or unknown ABI {:?}", s)), + } + } +} + +impl std::fmt::Display for Abi { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match *self { + Self::C => "C", + Self::Stdcall => "stdcall", + Self::Fastcall => "fastcall", + Self::ThisCall => "thiscall", + Self::Vectorcall => "vectorcall", + Self::Aapcs => "aapcs", + Self::Win64 => "win64", + }; + + s.fmt(f) + } +} + +impl quote::ToTokens for Abi { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let abi = self.to_string(); + tokens.append_all(quote! { #abi }); + } +} + +/// An ABI extracted from a clang cursor. +#[derive(Debug, Copy, Clone)] +pub(crate) enum ClangAbi { + Known(Abi), /// An unknown or invalid ABI. Unknown(CXCallingConv), } -impl Abi { +impl ClangAbi { /// Returns whether this Abi is known or not. fn is_unknown(&self) -> bool { - matches!(*self, Abi::Unknown(..)) + matches!(*self, ClangAbi::Unknown(..)) } } -impl quote::ToTokens for Abi { +impl quote::ToTokens for ClangAbi { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - tokens.append_all(match *self { - Abi::C => quote! { "C" }, - Abi::Stdcall => quote! { "stdcall" }, - Abi::Fastcall => quote! { "fastcall" }, - Abi::ThisCall => quote! { "thiscall" }, - Abi::Vectorcall => quote! { "vectorcall" }, - Abi::Aapcs => quote! { "aapcs" }, - Abi::Win64 => quote! { "win64" }, - Abi::Unknown(cc) => panic!( + match *self { + Self::Known(abi) => abi.to_tokens(tokens), + Self::Unknown(cc) => panic!( "Cannot turn unknown calling convention to tokens: {:?}", cc ), - }); + } } } @@ -234,21 +275,21 @@ pub struct FunctionSig { must_use: bool, /// The ABI of this function. - abi: Abi, + abi: ClangAbi, } -fn get_abi(cc: CXCallingConv) -> Abi { +fn get_abi(cc: CXCallingConv) -> ClangAbi { use clang_sys::*; match cc { - CXCallingConv_Default => Abi::C, - CXCallingConv_C => Abi::C, - CXCallingConv_X86StdCall => Abi::Stdcall, - CXCallingConv_X86FastCall => Abi::Fastcall, - CXCallingConv_X86ThisCall => Abi::ThisCall, - CXCallingConv_X86VectorCall => Abi::Vectorcall, - CXCallingConv_AAPCS => Abi::Aapcs, - CXCallingConv_X86_64Win64 => Abi::Win64, - other => Abi::Unknown(other), + CXCallingConv_Default => ClangAbi::Known(Abi::C), + CXCallingConv_C => ClangAbi::Known(Abi::C), + CXCallingConv_X86StdCall => ClangAbi::Known(Abi::Stdcall), + CXCallingConv_X86FastCall => ClangAbi::Known(Abi::Fastcall), + CXCallingConv_X86ThisCall => ClangAbi::Known(Abi::ThisCall), + CXCallingConv_X86VectorCall => ClangAbi::Known(Abi::Vectorcall), + CXCallingConv_AAPCS => ClangAbi::Known(Abi::Aapcs), + CXCallingConv_X86_64Win64 => ClangAbi::Known(Abi::Win64), + other => ClangAbi::Unknown(other), } } @@ -354,25 +395,6 @@ fn args_from_ty_and_cursor( } impl FunctionSig { - /// Construct a new function signature. - pub fn new( - return_type: TypeId, - argument_types: Vec<(Option, TypeId)>, - is_variadic: bool, - is_divergent: bool, - must_use: bool, - abi: Abi, - ) -> Self { - FunctionSig { - return_type, - argument_types, - is_variadic, - is_divergent, - must_use, - abi, - } - } - /// Construct a new function signature from the given Clang type. pub fn from_ty( ty: &clang::Type, @@ -540,20 +562,21 @@ impl FunctionSig { call_conv = cursor_call_conv; } } + let abi = get_abi(call_conv); if abi.is_unknown() { warn!("Unknown calling convention: {:?}", call_conv); } - Ok(Self::new( - ret, - args, - ty.is_variadic(), + Ok(FunctionSig { + return_type: ret, + argument_types: args, + is_variadic: ty.is_variadic(), is_divergent, must_use, abi, - )) + }) } /// Get this function signature's return type. @@ -567,8 +590,27 @@ impl FunctionSig { } /// Get this function signature's ABI. - pub fn abi(&self) -> Abi { - self.abi + pub(crate) fn abi( + &self, + ctx: &BindgenContext, + name: Option<&str>, + ) -> ClangAbi { + // FIXME (pvdrz): Try to do this check lazily instead. Maybe store the ABI inside `ctx` + // instead?. + if let Some(name) = name { + if let Some((abi, _)) = ctx + .options() + .abi_overrides + .iter() + .find(|(_, regex_set)| regex_set.matches(name)) + { + ClangAbi::Known(*abi) + } else { + self.abi + } + } else { + self.abi + } } /// Is this function signature variadic? @@ -598,7 +640,7 @@ impl FunctionSig { return false; } - matches!(self.abi, Abi::C | Abi::Unknown(..)) + matches!(self.abi, ClangAbi::Known(Abi::C) | ClangAbi::Unknown(..)) } pub(crate) fn is_divergent(&self) -> bool { diff --git a/bindgen/lib.rs b/bindgen/lib.rs index 3551ecbc5f..721c460644 100644 --- a/bindgen/lib.rs +++ b/bindgen/lib.rs @@ -74,6 +74,7 @@ pub use crate::features::{ RustTarget, LATEST_STABLE_RUST, RUST_TARGET_STRINGS, }; use crate::ir::context::{BindgenContext, ItemId}; +pub use crate::ir::function::Abi; use crate::ir::item::Item; use crate::parse::{ClangItemParser, ParseError}; use crate::regex_set::RegexSet; @@ -364,6 +365,13 @@ impl Builder { } } + for (abi, set) in &self.options.abi_overrides { + for item in set.get_items() { + output_vector.push("--override-abi".to_owned()); + output_vector.push(format!("{}={}", item, abi)); + } + } + if !self.options.layout_tests { output_vector.push("--no-layout-tests".into()); } @@ -1774,6 +1782,16 @@ impl Builder { self.options.c_naming = doit; self } + + /// Override the ABI of a given function. Regular expressions are supported. + pub fn override_abi>(mut self, abi: Abi, arg: T) -> Self { + self.options + .abi_overrides + .entry(abi) + .or_default() + .insert(arg.into()); + self + } } /// Configuration options for generated bindings. @@ -2109,11 +2127,13 @@ struct BindgenOptions { /// Deduplicate `extern` blocks. merge_extern_blocks: bool, + + abi_overrides: HashMap, } impl BindgenOptions { fn build(&mut self) { - let mut regex_sets = [ + let regex_sets = [ &mut self.allowlisted_vars, &mut self.allowlisted_types, &mut self.allowlisted_functions, @@ -2143,7 +2163,7 @@ impl BindgenOptions { &mut self.must_use_types, ]; let record_matches = self.record_matches; - for regex_set in &mut regex_sets { + for regex_set in self.abi_overrides.values_mut().chain(regex_sets) { regex_set.build(record_matches); } } @@ -2260,6 +2280,7 @@ impl Default for BindgenOptions { vtable_generation: false, sort_semantically: false, merge_extern_blocks: false, + abi_overrides: Default::default(), } } }