Add a flag to ensure all symbols are resolved when a library is loaded

This commit is contained in:
Dr. Chat 2021-03-26 17:12:23 -05:00 committed by Emilio Cobos Álvarez
parent e0157a648f
commit 2c5a1ea6b1
11 changed files with 161 additions and 34 deletions

View File

@ -5,6 +5,11 @@ use proc_macro2::Ident;
/// Used to build the output tokens for dynamic bindings.
#[derive(Default)]
pub struct DynamicItems {
/// Tracks whether or not we contain any required symbols.
/// If so, the signature of the generated `from_library` function
/// will be altered to return a `Result<Self, ::libloading::Error>`
has_required: bool,
/// Tracks the tokens that will appears inside the library struct -- e.g.:
/// ```ignore
/// struct Lib {
@ -77,6 +82,11 @@ impl DynamicItems {
let constructor_inits = &self.constructor_inits;
let init_fields = &self.init_fields;
let struct_implementation = &self.struct_implementation;
// FIXME: Is there a better way to lay this out? Conditional in the quote
// macro?
// If we have any required symbols, we must alter the signature of `from_library`
// so that it can return a failure code.
quote! {
extern crate libloading;
@ -91,19 +101,19 @@ impl DynamicItems {
) -> Result<Self, ::libloading::Error>
where P: AsRef<::std::ffi::OsStr> {
let library = ::libloading::Library::new(path)?;
Ok(Self::from_library(library))
Self::from_library(library)
}
pub unsafe fn from_library<L>(
library: L
) -> Self
) -> Result<Self, ::libloading::Error>
where L: Into<::libloading::Library> {
let __library = library.into();
#( #constructor_inits )*
#lib_ident {
Ok(#lib_ident {
__library,
#( #init_fields ),*
}
})
}
#( #struct_implementation )*
@ -116,6 +126,7 @@ impl DynamicItems {
ident: Ident,
abi: Abi,
is_variadic: bool,
is_required: bool,
args: Vec<proc_macro2::TokenStream>,
args_identifiers: Vec<proc_macro2::TokenStream>,
ret: proc_macro2::TokenStream,
@ -125,24 +136,50 @@ impl DynamicItems {
assert_eq!(args.len(), args_identifiers.len());
}
self.struct_members.push(quote! {
pub #ident: Result<unsafe extern #abi fn ( #( #args ),* ) #ret, ::libloading::Error>,
});
self.has_required |= is_required;
self.struct_members.push(
if is_required {
quote! {
pub #ident: unsafe extern #abi fn ( #( #args),* ) #ret,
}
} else {
quote! {
pub #ident: Result<unsafe extern #abi fn ( #( #args ),* ) #ret, ::libloading::Error>,
}
}
);
// We can't implement variadic functions from C easily, so we allow to
// access the function pointer so that the user can call it just fine.
if !is_variadic {
self.struct_implementation.push(quote! {
pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty {
let sym = self.#ident.as_ref().expect("Expected function, got error.");
(sym)(#( #args_identifiers ),*)
self.struct_implementation.push(
if is_required {
quote! {
pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty {
self.#ident(#( #args_identifiers ),*)
}
}
} else {
quote! {
pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty {
let sym = self.#ident.as_ref().expect("Expected function, got error.");
(sym)(#( #args_identifiers ),*)
}
}
}
});
);
}
let ident_str = codegen::helpers::ast_ty::cstr_expr(ident.to_string());
self.constructor_inits.push(quote! {
let #ident = __library.get(#ident_str).map(|sym| *sym);
self.constructor_inits.push(if is_required {
quote! {
let #ident = __library.get(#ident_str).map(|sym| *sym)?;
}
} else {
quote! {
let #ident = __library.get(#ident_str).map(|sym| *sym);
}
});
self.init_fields.push(quote! {

View File

@ -3869,6 +3869,7 @@ impl CodeGenerator for Function {
ident,
abi,
signature.is_variadic(),
ctx.options().dynamic_link_require_all,
args,
args_identifiers,
ret,

View File

@ -545,6 +545,10 @@ impl Builder {
output_vector.push(name.clone());
}
if self.options.dynamic_link_require_all {
output_vector.push("--dynamic-link-require-all".into());
}
if self.options.respect_cxx_access_specs {
output_vector.push("--respect-cxx-access-specs".into());
}
@ -1567,6 +1571,14 @@ impl Builder {
self
}
/// Require successful linkage for all routines in a shared library.
/// This allows us to optimize function calls by being able to safely assume function pointers
/// are valid.
pub fn dynamic_link_require_all(mut self, req: bool) -> Self {
self.options.dynamic_link_require_all = req;
self
}
/// Generate bindings as `pub` only if the bound item is publically accessible by C++.
pub fn respect_cxx_access_specs(mut self, doit: bool) -> Self {
self.options.respect_cxx_access_specs = doit;
@ -1870,6 +1882,11 @@ struct BindgenOptions {
/// this is None, no dynamic bindings are created.
dynamic_library_name: Option<String>,
/// Require successful linkage for all routines in a shared library.
/// This allows us to optimize function calls by being able to safely assume function pointers
/// are valid. No effect if `dynamic_library_name` is None.
dynamic_link_require_all: bool,
/// Only make generated bindings `pub` if the items would be publically accessible
/// by C++.
respect_cxx_access_specs: bool,
@ -2012,6 +2029,7 @@ impl Default for BindgenOptions {
array_pointers_in_arguments: false,
wasm_import_module_name: None,
dynamic_library_name: None,
dynamic_link_require_all: false,
respect_cxx_access_specs: false,
translate_enum_integer_types: false,
}

View File

@ -500,6 +500,9 @@ where
.long("dynamic-loading")
.takes_value(true)
.help("Use dynamic loading mode with the given library name."),
Arg::with_name("dynamic-link-require-all")
.long("dynamic-link-require-all")
.help("Require successful linkage to all functions in the library."),
Arg::with_name("respect-cxx-access-specs")
.long("respect-cxx-access-specs")
.help("Makes generated bindings `pub` only for items if the items are publically accessible in C++."),
@ -928,6 +931,10 @@ where
builder = builder.dynamic_library_name(dynamic_library_name);
}
if matches.is_present("dynamic-link-require-all") {
builder = builder.dynamic_link_require_all(true);
}
if matches.is_present("respect-cxx-access-specs") {
builder = builder.respect_cxx_access_specs(true);
}

View File

@ -0,0 +1,59 @@
#![allow(
dead_code,
non_snake_case,
non_camel_case_types,
non_upper_case_globals
)]
extern crate libloading;
pub struct TestLib {
__library: ::libloading::Library,
pub foo: unsafe extern "C" fn(
x: ::std::os::raw::c_int,
y: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
pub bar: unsafe extern "C" fn(
x: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int,
pub baz: unsafe extern "C" fn() -> ::std::os::raw::c_int,
}
impl TestLib {
pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
where
P: AsRef<::std::ffi::OsStr>,
{
let library = ::libloading::Library::new(path)?;
Self::from_library(library)
}
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
where
L: Into<::libloading::Library>,
{
let __library = library.into();
let foo = __library.get(b"foo\0").map(|sym| *sym)?;
let bar = __library.get(b"bar\0").map(|sym| *sym)?;
let baz = __library.get(b"baz\0").map(|sym| *sym)?;
Ok(TestLib {
__library,
foo,
bar,
baz,
})
}
pub unsafe fn foo(
&self,
x: ::std::os::raw::c_int,
y: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int {
self.foo(x, y)
}
pub unsafe fn bar(
&self,
x: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int {
self.bar(x)
}
pub unsafe fn baz(&self) -> ::std::os::raw::c_int {
self.baz()
}
}

View File

@ -32,9 +32,9 @@ impl TestLib {
P: AsRef<::std::ffi::OsStr>,
{
let library = ::libloading::Library::new(path)?;
Ok(Self::from_library(library))
Self::from_library(library)
}
pub unsafe fn from_library<L>(library: L) -> Self
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
where
L: Into<::libloading::Library>,
{
@ -42,12 +42,12 @@ impl TestLib {
let foo = __library.get(b"foo\0").map(|sym| *sym);
let bar = __library.get(b"bar\0").map(|sym| *sym);
let baz = __library.get(b"baz\0").map(|sym| *sym);
TestLib {
Ok(TestLib {
__library,
foo,
bar,
baz,
}
})
}
pub unsafe fn foo(
&self,

View File

@ -20,20 +20,20 @@ impl TestLib {
P: AsRef<::std::ffi::OsStr>,
{
let library = ::libloading::Library::new(path)?;
Ok(Self::from_library(library))
Self::from_library(library)
}
pub unsafe fn from_library<L>(library: L) -> Self
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
where
L: Into<::libloading::Library>,
{
let __library = library.into();
let foo = __library.get(b"foo\0").map(|sym| *sym);
let foo1 = __library.get(b"foo1\0").map(|sym| *sym);
TestLib {
Ok(TestLib {
__library,
foo,
foo1,
}
})
}
pub unsafe fn foo(
&self,

View File

@ -34,9 +34,9 @@ impl TestLib {
P: AsRef<::std::ffi::OsStr>,
{
let library = ::libloading::Library::new(path)?;
Ok(Self::from_library(library))
Self::from_library(library)
}
pub unsafe fn from_library<L>(library: L) -> Self
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
where
L: Into<::libloading::Library>,
{
@ -44,12 +44,12 @@ impl TestLib {
let foo = __library.get(b"foo\0").map(|sym| *sym);
let baz = __library.get(b"baz\0").map(|sym| *sym);
let bazz = __library.get(b"bazz\0").map(|sym| *sym);
TestLib {
Ok(TestLib {
__library,
foo,
baz,
bazz,
}
})
}
pub unsafe fn foo(
&self,

View File

@ -78,20 +78,20 @@ impl TestLib {
P: AsRef<::std::ffi::OsStr>,
{
let library = ::libloading::Library::new(path)?;
Ok(Self::from_library(library))
Self::from_library(library)
}
pub unsafe fn from_library<L>(library: L) -> Self
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
where
L: Into<::libloading::Library>,
{
let __library = library.into();
let foo = __library.get(b"foo\0").map(|sym| *sym);
let bar = __library.get(b"bar\0").map(|sym| *sym);
TestLib {
Ok(TestLib {
__library,
foo,
bar,
}
})
}
pub unsafe fn foo(
&self,

View File

@ -73,20 +73,20 @@ impl TestLib {
P: AsRef<::std::ffi::OsStr>,
{
let library = ::libloading::Library::new(path)?;
Ok(Self::from_library(library))
Self::from_library(library)
}
pub unsafe fn from_library<L>(library: L) -> Self
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
where
L: Into<::libloading::Library>,
{
let __library = library.into();
let foo = __library.get(b"foo\0").map(|sym| *sym);
let bar = __library.get(b"bar\0").map(|sym| *sym);
TestLib {
Ok(TestLib {
__library,
foo,
bar,
}
})
}
pub unsafe fn foo(
&self,

View File

@ -0,0 +1,5 @@
// bindgen-flags: --dynamic-loading TestLib --dynamic-link-require-all
int foo(int x, int y);
int bar(void *x);
int baz();