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
This commit is contained in:
Christian Poveda Ruiz 2022-11-02 15:30:34 -05:00 committed by GitHub
parent a673a6bc9b
commit 9c32b46048
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 193 additions and 79 deletions

View File

@ -148,6 +148,8 @@
# Unreleased
## Added
* new feature: `--override-abi` flag to override the ABI used by functions
matching a regular expression.
## Changed

View File

@ -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))
}

View File

@ -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();
}

View File

@ -0,0 +1,5 @@
// bindgen-flags: --override-abi=foo=fastcall --override-abi=bar=stdcall
void foo();
void bar();
void baz();

View File

@ -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>,

View File

@ -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

View File

@ -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,
/// An unknown or invalid ABI.
Unknown(CXCallingConv),
}
impl Abi {
/// Returns whether this Abi is known or not.
fn is_unknown(&self) -> bool {
matches!(*self, Abi::Unknown(..))
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) {
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!(
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 ClangAbi {
/// Returns whether this Abi is known or not.
fn is_unknown(&self) -> bool {
matches!(*self, ClangAbi::Unknown(..))
}
}
impl quote::ToTokens for ClangAbi {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
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 {

View File

@ -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(),
}
}
}