Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only emit #[link_name] attribute if necessary. #1558

Merged
merged 3 commits into from
May 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 94 additions & 13 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,10 +558,15 @@ impl CodeGenerator for Var {
}
} else {
let mut attrs = vec![];
if let Some(mangled) = self.mangled_name() {
attrs.push(attributes::link_name(mangled));
} else if canonical_name != self.name() {
attrs.push(attributes::link_name(self.name()));

// If necessary, apply a `#[link_name]` attribute
let link_name = self.mangled_name().unwrap_or(self.name());
if !utils::names_will_be_identical_after_mangling(
&canonical_name,
link_name,
None,
) {
attrs.push(attributes::link_name(link_name));
}

let maybe_mut = if self.is_const() {
Expand Down Expand Up @@ -3390,14 +3395,6 @@ impl CodeGenerator for Function {
write!(&mut canonical_name, "{}", times_seen).unwrap();
}

if let Some(mangled) = mangled_name {
if canonical_name != mangled {
attributes.push(attributes::link_name(mangled));
}
} else if name != canonical_name {
attributes.push(attributes::link_name(name));
}

let abi = match signature.abi() {
Abi::ThisCall if !ctx.options().rust_features().thiscall_abi => {
warn!("Skipping function with thiscall ABI that isn't supported by the configured Rust target");
Expand All @@ -3418,6 +3415,15 @@ impl CodeGenerator for Function {
abi => abi,
};

let link_name = mangled_name.unwrap_or(name);
if !utils::names_will_be_identical_after_mangling(
&canonical_name,
link_name,
Some(abi),
) {
attributes.push(attributes::link_name(link_name));
}

let ident = ctx.rust_ident(canonical_name);
let tokens = quote!( extern #abi {
#(#attributes)*
Expand Down Expand Up @@ -3581,7 +3587,7 @@ pub(crate) fn codegen(context: BindgenContext) -> (Vec<proc_macro2::TokenStream>
mod utils {
use super::{ToRustTyOrOpaque, error};
use ir::context::BindgenContext;
use ir::function::FunctionSig;
use ir::function::{Abi, FunctionSig};
use ir::item::{Item, ItemCanonicalPath};
use ir::ty::TypeKind;
use proc_macro2;
Expand Down Expand Up @@ -3965,4 +3971,79 @@ mod utils {
*const ::block::Block<(#(#args,)*), #ret_ty>
}
}

// 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(
canonical_name: &str,
mangled_name: &str,
call_conv: Option<Abi>,
) -> bool {
// If the mangled name and the canonical name are the same then no
// mangling can have happened between the two versions.
if canonical_name == mangled_name {
return true;
}

// Working with &[u8] makes indexing simpler than with &str
let canonical_name = canonical_name.as_bytes();
let mangled_name = mangled_name.as_bytes();

let (mangling_prefix, expect_suffix) = match call_conv {
Some(Abi::C) |
// None is the case for global variables
None => {
(b'_', false)
}
Some(Abi::Stdcall) => (b'_', true),
Some(Abi::Fastcall) => (b'@', true),

// This is something we don't recognize, stay on the safe side
// by emitting the `#[link_name]` attribute
Some(_) => return false,
};

// Check that the mangled name is long enough to at least contain the
// canonical name plus the expected prefix.
if mangled_name.len() < canonical_name.len() + 1 {
return false;
}

// Return if the mangled name does not start with the prefix expected
// for the given calling convention.
if mangled_name[0] != mangling_prefix {
return false;
}

// Check that the mangled name contains the canonical name after the
// prefix
if &mangled_name[1..canonical_name.len() + 1] != canonical_name {
return false;
}

// If the given calling convention also prescribes a suffix, check that
// it exists too
if expect_suffix {
let suffix = &mangled_name[canonical_name.len() + 1..];

// The shortest suffix is "@0"
if suffix.len() < 2 {
return false;
}

// Check that the suffix starts with '@' and is all ASCII decimals
// after that.
if suffix[0] != b'@' || !suffix[1..].iter().all(u8::is_ascii_digit)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be good to have tests for this. Do we have them? I see call-conv-field has a change for @0, do we have one for longer suffixes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add one with a longer suffix.

{
return false;
}
} else if mangled_name.len() != canonical_name.len() + 1 {
// If we don't expect a prefix but there is one, we need the
// #[link_name] attribute
return false;
}

true
}
}
1 change: 0 additions & 1 deletion tests/expectations/tests/bitfield_large_overflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,5 @@ fn bindgen_test_layout__bindgen_ty_1() {
);
}
extern "C" {
#[link_name = "\u{1}a"]
pub static mut a: _bindgen_ty_1;
}
2 changes: 0 additions & 2 deletions tests/expectations/tests/class_nested.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ fn bindgen_test_layout_A_C() {
);
}
extern "C" {
#[link_name = "\u{1}var"]
pub static mut var: A_B;
}
#[test]
Expand All @@ -118,7 +117,6 @@ fn __bindgen_test_layout_A_D_open0_int_close0_instantiation() {
);
}
extern "C" {
#[link_name = "\u{1}baz"]
pub static mut baz: A_D<::std::os::raw::c_int>;
}
#[repr(C)]
Expand Down
3 changes: 0 additions & 3 deletions tests/expectations/tests/complex_global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@ pub struct __BindgenComplex<T> {
pub im: T,
}
extern "C" {
#[link_name = "\u{1}globalValueFloat"]
pub static mut globalValueFloat: __BindgenComplex<f32>;
}
extern "C" {
#[link_name = "\u{1}globalValueDouble"]
pub static mut globalValueDouble: __BindgenComplex<f64>;
}
extern "C" {
#[link_name = "\u{1}globalValueLongDouble"]
pub static mut globalValueLongDouble: __BindgenComplex<f64>;
}
1 change: 0 additions & 1 deletion tests/expectations/tests/decl_extern_int_twice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@


extern "C" {
#[link_name = "\u{1}foo"]
pub static mut foo: ::std::os::raw::c_int;
}
1 change: 0 additions & 1 deletion tests/expectations/tests/decl_ptr_to_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@


extern "C" {
#[link_name = "\u{1}foo"]
pub static mut foo: *mut [::std::os::raw::c_int; 1usize];
}
1 change: 0 additions & 1 deletion tests/expectations/tests/extern-const-struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,5 @@ impl Default for nsFoo {
}
}
extern "C" {
#[link_name = "\u{1}gDetails"]
pub static gDetails: nsFoo;
}
1 change: 0 additions & 1 deletion tests/expectations/tests/func_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@


extern "C" {
#[link_name = "\u{1}foo"]
pub static mut foo:
::std::option::Option<
unsafe extern "C" fn(x: ::std::os::raw::c_int, y: ::std::os::raw::c_int)
Expand Down
4 changes: 0 additions & 4 deletions tests/expectations/tests/issue-511.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@
)]

extern "C" {
#[link_name = "\u{1}a"]
pub static mut a: *mut ::std::os::raw::c_char;
}
extern "C" {
#[link_name = "\u{1}b"]
pub static mut b: *const ::std::os::raw::c_char;
}
extern "C" {
#[link_name = "\u{1}c"]
pub static c: *mut ::std::os::raw::c_char;
}
extern "C" {
#[link_name = "\u{1}d"]
pub static d: *const ::std::os::raw::c_char;
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ fn bindgen_test_layout__bindgen_ty_1() {
);
}
extern "C" {
#[link_name = "\u{1}AutoIdVector"]
pub static mut AutoIdVector: _bindgen_ty_1;
}
#[test]
Expand Down
12 changes: 0 additions & 12 deletions tests/expectations/tests/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,39 @@


extern "C" {
#[link_name = "\u{1}u8"]
pub static mut u8: ::std::os::raw::c_int;
}
extern "C" {
#[link_name = "\u{1}u16"]
pub static mut u16: ::std::os::raw::c_int;
}
extern "C" {
#[link_name = "\u{1}u32"]
pub static mut u32: ::std::os::raw::c_int;
}
extern "C" {
#[link_name = "\u{1}u64"]
pub static mut u64: ::std::os::raw::c_int;
}
extern "C" {
#[link_name = "\u{1}i8"]
pub static mut i8: ::std::os::raw::c_int;
}
extern "C" {
#[link_name = "\u{1}i16"]
pub static mut i16: ::std::os::raw::c_int;
}
extern "C" {
#[link_name = "\u{1}i32"]
pub static mut i32: ::std::os::raw::c_int;
}
extern "C" {
#[link_name = "\u{1}i64"]
pub static mut i64: ::std::os::raw::c_int;
}
extern "C" {
#[link_name = "\u{1}f32"]
pub static mut f32: ::std::os::raw::c_int;
}
extern "C" {
#[link_name = "\u{1}f64"]
pub static mut f64: ::std::os::raw::c_int;
}
extern "C" {
#[link_name = "\u{1}usize"]
pub static mut usize: ::std::os::raw::c_int;
}
extern "C" {
#[link_name = "\u{1}isize"]
pub static mut isize: ::std::os::raw::c_int;
}
extern "C" {
Expand Down
8 changes: 0 additions & 8 deletions tests/expectations/tests/libclang-3.8/constant-evaluate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,26 @@ pub enum _bindgen_ty_1 { foo = 4, bar = 8, }
pub type EasyToOverflow = ::std::os::raw::c_ulonglong;
pub const k: EasyToOverflow = 2147483648;
extern "C" {
#[link_name = "\u{1}k_expr"]
pub static k_expr: EasyToOverflow;
}
extern "C" {
#[link_name = "\u{1}wow"]
pub static wow: EasyToOverflow;
}
extern "C" {
#[link_name = "\u{1}BAZ"]
pub static BAZ: ::std::os::raw::c_longlong;
}
extern "C" {
#[link_name = "\u{1}fuzz"]
pub static fuzz: f64;
}
extern "C" {
#[link_name = "\u{1}BAZZ"]
pub static BAZZ: ::std::os::raw::c_char;
}
extern "C" {
#[link_name = "\u{1}WAT"]
pub static WAT: ::std::os::raw::c_char;
}
extern "C" {
#[link_name = "\u{1}bytestring"]
pub static mut bytestring: *const ::std::os::raw::c_char;
}
extern "C" {
#[link_name = "\u{1}NOT_UTF8"]
pub static mut NOT_UTF8: *const ::std::os::raw::c_char;
}
55 changes: 55 additions & 0 deletions tests/expectations/tests/libclang-3.8/mangling-win32.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* automatically generated by rust-bindgen */


#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]


extern "C" {
pub fn foo();
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct Foo {
pub _address: u8,
}
extern "C" {
#[link_name = "\u{1}?sBar@Foo@@2_NA"]
pub static mut Foo_sBar: bool;
}
#[test]
fn bindgen_test_layout_Foo() {
assert_eq!(
::std::mem::size_of::<Foo>(),
1usize,
concat!("Size of: ", stringify!(Foo))
);
assert_eq!(
::std::mem::align_of::<Foo>(),
1usize,
concat!("Alignment of ", stringify!(Foo))
);
}
extern "C" {
#[link_name = "\u{1}@fast_call_func_no_args@0"]
pub fn fast_call_func_no_args() -> ::std::os::raw::c_int;
}
extern "C" {
#[link_name = "\u{1}@fast_call_func_many_args@12"]
pub fn fast_call_func_many_args(
arg1: ::std::os::raw::c_int,
arg2: ::std::os::raw::c_int,
arg3: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[link_name = "\u{1}_std_call_func_no_args@0"]
pub fn std_call_func_no_args() -> ::std::os::raw::c_int;
}
extern "C" {
#[link_name = "\u{1}_std_call_func_many_args@12"]
pub fn std_call_func_many_args(
arg1: ::std::os::raw::c_int,
arg2: ::std::os::raw::c_int,
arg3: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int;
}
1 change: 0 additions & 1 deletion tests/expectations/tests/libclang-3.9/call-conv-field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,5 @@ fn bindgen_test_layout_JNINativeInterface_() {
);
}
extern "stdcall" {
#[link_name = "\u{1}_bar@0"]
pub fn bar();
}
1 change: 0 additions & 1 deletion tests/expectations/tests/libclang-3.9/constant-evaluate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ pub type EasyToOverflow = ::std::os::raw::c_ulonglong;
pub const k: EasyToOverflow = 2147483648;
pub const k_expr: EasyToOverflow = 0;
extern "C" {
#[link_name = "\u{1}wow"]
pub static wow: EasyToOverflow;
}
pub const BAZ: ::std::os::raw::c_longlong = 24;
Expand Down
Loading