summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poveda Ruiz <31802960+pvdrz@users.noreply.github.com>2022-11-02 15:30:34 -0500
committerGitHub <noreply@github.com>2022-11-02 15:30:34 -0500
commit9c32b460481903d90c6ac5df277bfa853a0558d8 (patch)
tree6ee9ed2f853a78942314fb71df3868b89125cc86
parenta673a6bc9b83675a7468379e79dcf5d5659c4623 (diff)
Add the `--override-abi` option (#2329)
* Add the `--override-abi` option. This option can be used from the CLI with the <abi>:<regex> syntax and it overrides the ABI of a function if it matches <regex>. Fixes #2257
-rw-r--r--CHANGELOG.md2
-rw-r--r--bindgen-cli/options.rs18
-rw-r--r--bindgen-tests/tests/expectations/tests/abi-override.rs16
-rw-r--r--bindgen-tests/tests/headers/abi-override.h5
-rw-r--r--bindgen/codegen/dyngen.rs6
-rw-r--r--bindgen/codegen/mod.rs46
-rw-r--r--bindgen/ir/function.rs148
-rw-r--r--bindgen/lib.rs25
8 files changed, 190 insertions, 76 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 560ce38b..baa5cb20 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 5c3960e9..c186cfd9 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 <regex>. The <override> value must be of the shape <abi>:<regex> where <abi> 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 00000000..49693692
--- /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 00000000..cee72cae
--- /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 26cfe5cc..d02c51e4 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<proc_macro2::TokenStream>,
diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
index a59265ab..4e90102d 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;
@@ -2474,9 +2476,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,
};
@@ -3988,14 +3994,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");
@@ -4099,22 +4107,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
@@ -4512,7 +4522,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;
@@ -4973,10 +4983,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<Abi>,
+ call_conv: Option<ClangAbi>,
) -> bool {
// If the mangled name and the canonical name are the same then no
// mangling can have happened between the two versions.
@@ -4989,13 +4999,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 f488b384..8a70d60a 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<Self, Self::Err> {
+ 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<String>, 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 5e04acfe..10410734 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<T: Into<String>>(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<Abi, RegexSet>,
}
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);
}
}
@@ -2280,6 +2300,7 @@ impl Default for BindgenOptions {
vtable_generation: false,
sort_semantically: false,
merge_extern_blocks: false,
+ abi_overrides: Default::default(),
}
}
}