summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/codegen/dyngen.rs65
-rw-r--r--src/codegen/mod.rs1
-rw-r--r--src/lib.rs18
-rw-r--r--src/options.rs7
-rw-r--r--tests/expectations/tests/dynamic_loading_required.rs59
-rw-r--r--tests/expectations/tests/dynamic_loading_simple.rs8
-rw-r--r--tests/expectations/tests/dynamic_loading_template.rs8
-rw-r--r--tests/expectations/tests/dynamic_loading_with_allowlist.rs8
-rw-r--r--tests/expectations/tests/dynamic_loading_with_blocklist.rs8
-rw-r--r--tests/expectations/tests/dynamic_loading_with_class.rs8
-rw-r--r--tests/headers/dynamic_loading_required.h5
11 files changed, 161 insertions, 34 deletions
diff --git a/src/codegen/dyngen.rs b/src/codegen/dyngen.rs
index 768fb02e..de9e0f98 100644
--- a/src/codegen/dyngen.rs
+++ b/src/codegen/dyngen.rs
@@ -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! {
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 1fba8316..95162204 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -3869,6 +3869,7 @@ impl CodeGenerator for Function {
ident,
abi,
signature.is_variadic(),
+ ctx.options().dynamic_link_require_all,
args,
args_identifiers,
ret,
diff --git a/src/lib.rs b/src/lib.rs
index 3eeb0735..0c207a2f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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,
}
diff --git a/src/options.rs b/src/options.rs
index 4f75424a..36267321 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -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);
}
diff --git a/tests/expectations/tests/dynamic_loading_required.rs b/tests/expectations/tests/dynamic_loading_required.rs
new file mode 100644
index 00000000..3600eace
--- /dev/null
+++ b/tests/expectations/tests/dynamic_loading_required.rs
@@ -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()
+ }
+}
diff --git a/tests/expectations/tests/dynamic_loading_simple.rs b/tests/expectations/tests/dynamic_loading_simple.rs
index c9c4ba15..3d2e3d2c 100644
--- a/tests/expectations/tests/dynamic_loading_simple.rs
+++ b/tests/expectations/tests/dynamic_loading_simple.rs
@@ -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,
diff --git a/tests/expectations/tests/dynamic_loading_template.rs b/tests/expectations/tests/dynamic_loading_template.rs
index a46253c6..de231434 100644
--- a/tests/expectations/tests/dynamic_loading_template.rs
+++ b/tests/expectations/tests/dynamic_loading_template.rs
@@ -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,
diff --git a/tests/expectations/tests/dynamic_loading_with_allowlist.rs b/tests/expectations/tests/dynamic_loading_with_allowlist.rs
index 678cd340..83e4a541 100644
--- a/tests/expectations/tests/dynamic_loading_with_allowlist.rs
+++ b/tests/expectations/tests/dynamic_loading_with_allowlist.rs
@@ -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,
diff --git a/tests/expectations/tests/dynamic_loading_with_blocklist.rs b/tests/expectations/tests/dynamic_loading_with_blocklist.rs
index 930876a4..598780b2 100644
--- a/tests/expectations/tests/dynamic_loading_with_blocklist.rs
+++ b/tests/expectations/tests/dynamic_loading_with_blocklist.rs
@@ -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,
diff --git a/tests/expectations/tests/dynamic_loading_with_class.rs b/tests/expectations/tests/dynamic_loading_with_class.rs
index 81a045b5..ba0defaa 100644
--- a/tests/expectations/tests/dynamic_loading_with_class.rs
+++ b/tests/expectations/tests/dynamic_loading_with_class.rs
@@ -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,
diff --git a/tests/headers/dynamic_loading_required.h b/tests/headers/dynamic_loading_required.h
new file mode 100644
index 00000000..f9861e84
--- /dev/null
+++ b/tests/headers/dynamic_loading_required.h
@@ -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();