diff options
author | Dr. Chat <arkolbed@gmail.com> | 2021-03-26 17:12:23 -0500 |
---|---|---|
committer | Emilio Cobos Álvarez <emilio@crisal.io> | 2021-04-03 12:24:15 +0200 |
commit | 2c5a1ea6b1bd56085d726dac347a31c49c95be76 (patch) | |
tree | a15de55e4d75073c218877d105fb478d83f6bc15 | |
parent | e0157a648f87c4f86fe1a1e6d5bd0502b5eb265a (diff) |
Add a flag to ensure all symbols are resolved when a library is loaded
-rw-r--r-- | src/codegen/dyngen.rs | 65 | ||||
-rw-r--r-- | src/codegen/mod.rs | 1 | ||||
-rw-r--r-- | src/lib.rs | 18 | ||||
-rw-r--r-- | src/options.rs | 7 | ||||
-rw-r--r-- | tests/expectations/tests/dynamic_loading_required.rs | 59 | ||||
-rw-r--r-- | tests/expectations/tests/dynamic_loading_simple.rs | 8 | ||||
-rw-r--r-- | tests/expectations/tests/dynamic_loading_template.rs | 8 | ||||
-rw-r--r-- | tests/expectations/tests/dynamic_loading_with_allowlist.rs | 8 | ||||
-rw-r--r-- | tests/expectations/tests/dynamic_loading_with_blocklist.rs | 8 | ||||
-rw-r--r-- | tests/expectations/tests/dynamic_loading_with_class.rs | 8 | ||||
-rw-r--r-- | tests/headers/dynamic_loading_required.h | 5 |
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, @@ -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(); |