Skip to content

Commit 2c5a1ea

Browse files
DrChatemilio
authored andcommitted
Add a flag to ensure all symbols are resolved when a library is loaded
1 parent e0157a6 commit 2c5a1ea

11 files changed

+161
-34
lines changed

src/codegen/dyngen.rs

+51-14
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ use proc_macro2::Ident;
55
/// Used to build the output tokens for dynamic bindings.
66
#[derive(Default)]
77
pub struct DynamicItems {
8+
/// Tracks whether or not we contain any required symbols.
9+
/// If so, the signature of the generated `from_library` function
10+
/// will be altered to return a `Result<Self, ::libloading::Error>`
11+
has_required: bool,
12+
813
/// Tracks the tokens that will appears inside the library struct -- e.g.:
914
/// ```ignore
1015
/// struct Lib {
@@ -77,6 +82,11 @@ impl DynamicItems {
7782
let constructor_inits = &self.constructor_inits;
7883
let init_fields = &self.init_fields;
7984
let struct_implementation = &self.struct_implementation;
85+
86+
// FIXME: Is there a better way to lay this out? Conditional in the quote
87+
// macro?
88+
// If we have any required symbols, we must alter the signature of `from_library`
89+
// so that it can return a failure code.
8090
quote! {
8191
extern crate libloading;
8292

@@ -91,19 +101,19 @@ impl DynamicItems {
91101
) -> Result<Self, ::libloading::Error>
92102
where P: AsRef<::std::ffi::OsStr> {
93103
let library = ::libloading::Library::new(path)?;
94-
Ok(Self::from_library(library))
104+
Self::from_library(library)
95105
}
96106

97107
pub unsafe fn from_library<L>(
98108
library: L
99-
) -> Self
109+
) -> Result<Self, ::libloading::Error>
100110
where L: Into<::libloading::Library> {
101111
let __library = library.into();
102112
#( #constructor_inits )*
103-
#lib_ident {
113+
Ok(#lib_ident {
104114
__library,
105115
#( #init_fields ),*
106-
}
116+
})
107117
}
108118

109119
#( #struct_implementation )*
@@ -116,6 +126,7 @@ impl DynamicItems {
116126
ident: Ident,
117127
abi: Abi,
118128
is_variadic: bool,
129+
is_required: bool,
119130
args: Vec<proc_macro2::TokenStream>,
120131
args_identifiers: Vec<proc_macro2::TokenStream>,
121132
ret: proc_macro2::TokenStream,
@@ -125,24 +136,50 @@ impl DynamicItems {
125136
assert_eq!(args.len(), args_identifiers.len());
126137
}
127138

128-
self.struct_members.push(quote! {
129-
pub #ident: Result<unsafe extern #abi fn ( #( #args ),* ) #ret, ::libloading::Error>,
130-
});
139+
self.has_required |= is_required;
140+
141+
self.struct_members.push(
142+
if is_required {
143+
quote! {
144+
pub #ident: unsafe extern #abi fn ( #( #args),* ) #ret,
145+
}
146+
} else {
147+
quote! {
148+
pub #ident: Result<unsafe extern #abi fn ( #( #args ),* ) #ret, ::libloading::Error>,
149+
}
150+
}
151+
);
131152

132153
// We can't implement variadic functions from C easily, so we allow to
133154
// access the function pointer so that the user can call it just fine.
134155
if !is_variadic {
135-
self.struct_implementation.push(quote! {
136-
pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty {
137-
let sym = self.#ident.as_ref().expect("Expected function, got error.");
138-
(sym)(#( #args_identifiers ),*)
156+
self.struct_implementation.push(
157+
if is_required {
158+
quote! {
159+
pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty {
160+
self.#ident(#( #args_identifiers ),*)
161+
}
162+
}
163+
} else {
164+
quote! {
165+
pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty {
166+
let sym = self.#ident.as_ref().expect("Expected function, got error.");
167+
(sym)(#( #args_identifiers ),*)
168+
}
169+
}
139170
}
140-
});
171+
);
141172
}
142173

143174
let ident_str = codegen::helpers::ast_ty::cstr_expr(ident.to_string());
144-
self.constructor_inits.push(quote! {
145-
let #ident = __library.get(#ident_str).map(|sym| *sym);
175+
self.constructor_inits.push(if is_required {
176+
quote! {
177+
let #ident = __library.get(#ident_str).map(|sym| *sym)?;
178+
}
179+
} else {
180+
quote! {
181+
let #ident = __library.get(#ident_str).map(|sym| *sym);
182+
}
146183
});
147184

148185
self.init_fields.push(quote! {

src/codegen/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3869,6 +3869,7 @@ impl CodeGenerator for Function {
38693869
ident,
38703870
abi,
38713871
signature.is_variadic(),
3872+
ctx.options().dynamic_link_require_all,
38723873
args,
38733874
args_identifiers,
38743875
ret,

src/lib.rs

+18
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,10 @@ impl Builder {
545545
output_vector.push(name.clone());
546546
}
547547

548+
if self.options.dynamic_link_require_all {
549+
output_vector.push("--dynamic-link-require-all".into());
550+
}
551+
548552
if self.options.respect_cxx_access_specs {
549553
output_vector.push("--respect-cxx-access-specs".into());
550554
}
@@ -1567,6 +1571,14 @@ impl Builder {
15671571
self
15681572
}
15691573

1574+
/// Require successful linkage for all routines in a shared library.
1575+
/// This allows us to optimize function calls by being able to safely assume function pointers
1576+
/// are valid.
1577+
pub fn dynamic_link_require_all(mut self, req: bool) -> Self {
1578+
self.options.dynamic_link_require_all = req;
1579+
self
1580+
}
1581+
15701582
/// Generate bindings as `pub` only if the bound item is publically accessible by C++.
15711583
pub fn respect_cxx_access_specs(mut self, doit: bool) -> Self {
15721584
self.options.respect_cxx_access_specs = doit;
@@ -1870,6 +1882,11 @@ struct BindgenOptions {
18701882
/// this is None, no dynamic bindings are created.
18711883
dynamic_library_name: Option<String>,
18721884

1885+
/// Require successful linkage for all routines in a shared library.
1886+
/// This allows us to optimize function calls by being able to safely assume function pointers
1887+
/// are valid. No effect if `dynamic_library_name` is None.
1888+
dynamic_link_require_all: bool,
1889+
18731890
/// Only make generated bindings `pub` if the items would be publically accessible
18741891
/// by C++.
18751892
respect_cxx_access_specs: bool,
@@ -2012,6 +2029,7 @@ impl Default for BindgenOptions {
20122029
array_pointers_in_arguments: false,
20132030
wasm_import_module_name: None,
20142031
dynamic_library_name: None,
2032+
dynamic_link_require_all: false,
20152033
respect_cxx_access_specs: false,
20162034
translate_enum_integer_types: false,
20172035
}

src/options.rs

+7
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,9 @@ where
500500
.long("dynamic-loading")
501501
.takes_value(true)
502502
.help("Use dynamic loading mode with the given library name."),
503+
Arg::with_name("dynamic-link-require-all")
504+
.long("dynamic-link-require-all")
505+
.help("Require successful linkage to all functions in the library."),
503506
Arg::with_name("respect-cxx-access-specs")
504507
.long("respect-cxx-access-specs")
505508
.help("Makes generated bindings `pub` only for items if the items are publically accessible in C++."),
@@ -928,6 +931,10 @@ where
928931
builder = builder.dynamic_library_name(dynamic_library_name);
929932
}
930933

934+
if matches.is_present("dynamic-link-require-all") {
935+
builder = builder.dynamic_link_require_all(true);
936+
}
937+
931938
if matches.is_present("respect-cxx-access-specs") {
932939
builder = builder.respect_cxx_access_specs(true);
933940
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#![allow(
2+
dead_code,
3+
non_snake_case,
4+
non_camel_case_types,
5+
non_upper_case_globals
6+
)]
7+
8+
extern crate libloading;
9+
pub struct TestLib {
10+
__library: ::libloading::Library,
11+
pub foo: unsafe extern "C" fn(
12+
x: ::std::os::raw::c_int,
13+
y: ::std::os::raw::c_int,
14+
) -> ::std::os::raw::c_int,
15+
pub bar: unsafe extern "C" fn(
16+
x: *mut ::std::os::raw::c_void,
17+
) -> ::std::os::raw::c_int,
18+
pub baz: unsafe extern "C" fn() -> ::std::os::raw::c_int,
19+
}
20+
impl TestLib {
21+
pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
22+
where
23+
P: AsRef<::std::ffi::OsStr>,
24+
{
25+
let library = ::libloading::Library::new(path)?;
26+
Self::from_library(library)
27+
}
28+
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
29+
where
30+
L: Into<::libloading::Library>,
31+
{
32+
let __library = library.into();
33+
let foo = __library.get(b"foo\0").map(|sym| *sym)?;
34+
let bar = __library.get(b"bar\0").map(|sym| *sym)?;
35+
let baz = __library.get(b"baz\0").map(|sym| *sym)?;
36+
Ok(TestLib {
37+
__library,
38+
foo,
39+
bar,
40+
baz,
41+
})
42+
}
43+
pub unsafe fn foo(
44+
&self,
45+
x: ::std::os::raw::c_int,
46+
y: ::std::os::raw::c_int,
47+
) -> ::std::os::raw::c_int {
48+
self.foo(x, y)
49+
}
50+
pub unsafe fn bar(
51+
&self,
52+
x: *mut ::std::os::raw::c_void,
53+
) -> ::std::os::raw::c_int {
54+
self.bar(x)
55+
}
56+
pub unsafe fn baz(&self) -> ::std::os::raw::c_int {
57+
self.baz()
58+
}
59+
}

tests/expectations/tests/dynamic_loading_simple.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,22 @@ impl TestLib {
3232
P: AsRef<::std::ffi::OsStr>,
3333
{
3434
let library = ::libloading::Library::new(path)?;
35-
Ok(Self::from_library(library))
35+
Self::from_library(library)
3636
}
37-
pub unsafe fn from_library<L>(library: L) -> Self
37+
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
3838
where
3939
L: Into<::libloading::Library>,
4040
{
4141
let __library = library.into();
4242
let foo = __library.get(b"foo\0").map(|sym| *sym);
4343
let bar = __library.get(b"bar\0").map(|sym| *sym);
4444
let baz = __library.get(b"baz\0").map(|sym| *sym);
45-
TestLib {
45+
Ok(TestLib {
4646
__library,
4747
foo,
4848
bar,
4949
baz,
50-
}
50+
})
5151
}
5252
pub unsafe fn foo(
5353
&self,

tests/expectations/tests/dynamic_loading_template.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,20 @@ impl TestLib {
2020
P: AsRef<::std::ffi::OsStr>,
2121
{
2222
let library = ::libloading::Library::new(path)?;
23-
Ok(Self::from_library(library))
23+
Self::from_library(library)
2424
}
25-
pub unsafe fn from_library<L>(library: L) -> Self
25+
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
2626
where
2727
L: Into<::libloading::Library>,
2828
{
2929
let __library = library.into();
3030
let foo = __library.get(b"foo\0").map(|sym| *sym);
3131
let foo1 = __library.get(b"foo1\0").map(|sym| *sym);
32-
TestLib {
32+
Ok(TestLib {
3333
__library,
3434
foo,
3535
foo1,
36-
}
36+
})
3737
}
3838
pub unsafe fn foo(
3939
&self,

tests/expectations/tests/dynamic_loading_with_allowlist.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -34,22 +34,22 @@ impl TestLib {
3434
P: AsRef<::std::ffi::OsStr>,
3535
{
3636
let library = ::libloading::Library::new(path)?;
37-
Ok(Self::from_library(library))
37+
Self::from_library(library)
3838
}
39-
pub unsafe fn from_library<L>(library: L) -> Self
39+
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
4040
where
4141
L: Into<::libloading::Library>,
4242
{
4343
let __library = library.into();
4444
let foo = __library.get(b"foo\0").map(|sym| *sym);
4545
let baz = __library.get(b"baz\0").map(|sym| *sym);
4646
let bazz = __library.get(b"bazz\0").map(|sym| *sym);
47-
TestLib {
47+
Ok(TestLib {
4848
__library,
4949
foo,
5050
baz,
5151
bazz,
52-
}
52+
})
5353
}
5454
pub unsafe fn foo(
5555
&self,

tests/expectations/tests/dynamic_loading_with_blocklist.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -78,20 +78,20 @@ impl TestLib {
7878
P: AsRef<::std::ffi::OsStr>,
7979
{
8080
let library = ::libloading::Library::new(path)?;
81-
Ok(Self::from_library(library))
81+
Self::from_library(library)
8282
}
83-
pub unsafe fn from_library<L>(library: L) -> Self
83+
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
8484
where
8585
L: Into<::libloading::Library>,
8686
{
8787
let __library = library.into();
8888
let foo = __library.get(b"foo\0").map(|sym| *sym);
8989
let bar = __library.get(b"bar\0").map(|sym| *sym);
90-
TestLib {
90+
Ok(TestLib {
9191
__library,
9292
foo,
9393
bar,
94-
}
94+
})
9595
}
9696
pub unsafe fn foo(
9797
&self,

tests/expectations/tests/dynamic_loading_with_class.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -73,20 +73,20 @@ impl TestLib {
7373
P: AsRef<::std::ffi::OsStr>,
7474
{
7575
let library = ::libloading::Library::new(path)?;
76-
Ok(Self::from_library(library))
76+
Self::from_library(library)
7777
}
78-
pub unsafe fn from_library<L>(library: L) -> Self
78+
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
7979
where
8080
L: Into<::libloading::Library>,
8181
{
8282
let __library = library.into();
8383
let foo = __library.get(b"foo\0").map(|sym| *sym);
8484
let bar = __library.get(b"bar\0").map(|sym| *sym);
85-
TestLib {
85+
Ok(TestLib {
8686
__library,
8787
foo,
8888
bar,
89-
}
89+
})
9090
}
9191
pub unsafe fn foo(
9292
&self,
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// bindgen-flags: --dynamic-loading TestLib --dynamic-link-require-all
2+
3+
int foo(int x, int y);
4+
int bar(void *x);
5+
int baz();

0 commit comments

Comments
 (0)