23
23
#![ forbid( unsafe_op_in_unsafe_fn) ]
24
24
25
25
use crate :: ffi:: { CStr , c_char, c_void} ;
26
- use crate :: marker:: PhantomData ;
26
+ use crate :: marker:: { FnPtr , PhantomData } ;
27
27
use crate :: sync:: atomic:: { Atomic , AtomicPtr , Ordering } ;
28
28
use crate :: { mem, ptr} ;
29
29
@@ -76,8 +76,18 @@ pub(crate) macro dlsym {
76
76
#[ link_name = $sym: expr]
77
77
fn $name: ident( $( $param: ident : $t: ty) , * $( , ) ?) -> $ret: ty ;
78
78
) => (
79
- static DLSYM : DlsymWeak < unsafe extern "C" fn ( $( $t) , * ) -> $ret> =
80
- DlsymWeak :: new ( concat ! ( $sym, '0円' ) ) ;
79
+ static DLSYM : DlsymWeak < unsafe extern "C" fn ( $( $t) , * ) -> $ret> = {
80
+ let Ok ( name ) = CStr :: from_bytes_with_nul ( concat ! ( $sym, '0円' ) . as_bytes( ) ) else {
81
+ panic ! ( "symbol name may not contain NUL" )
82
+ } ;
83
+
84
+ // SAFETY: Whoever calls the function pointer returned by `get()`
85
+ // is responsible for ensuring that the signature is correct. Just
86
+ // like with extern blocks, this is syntactically enforced by making
87
+ // the function pointer be unsafe.
88
+ unsafe { DlsymWeak :: new ( name ) }
89
+ } ;
90
+
81
91
let $name = & DLSYM ;
82
92
)
83
93
}
@@ -90,12 +100,13 @@ pub(crate) struct DlsymWeak<F> {
90
100
_marker : PhantomData < F > ,
91
101
}
92
102
93
- impl < F > DlsymWeak < F > {
94
- pub ( crate ) const fn new ( name : & ' static str ) -> Self {
95
- let Ok ( name) = CStr :: from_bytes_with_nul ( name. as_bytes ( ) ) else {
96
- panic ! ( "not a nul-terminated string" )
97
- } ;
98
-
103
+ impl < F : FnPtr > DlsymWeak < F > {
104
+ /// # Safety
105
+ ///
106
+ /// If the signature of `F` does not match the signature of the symbol (if
107
+ /// it exists), calling the function pointer returned by `get()` is
108
+ /// undefined behaviour.
109
+ pub ( crate ) const unsafe fn new ( name : & ' static CStr ) -> Self {
99
110
DlsymWeak {
100
111
name : name. as_ptr ( ) ,
101
112
func : AtomicPtr :: new ( ptr:: without_provenance_mut ( 1 ) ) ,
@@ -125,26 +136,32 @@ impl<F> DlsymWeak<F> {
125
136
match self . func . load ( Ordering :: Acquire ) {
126
137
func if func. addr ( ) == 1 => self . initialize ( ) ,
127
138
func if func. is_null ( ) => None ,
139
+ // SAFETY:
140
+ // `func` is not null and `F` implements `FnPtr`, thus this
141
+ // transmutation is well-defined. It is the responsibility of the
142
+ // creator of this `DlsymWeak` to ensure that calling the resulting
143
+ // function pointer does not result in undefined behaviour (though
144
+ // the `dlsym!` macro delegates this responsibility to the caller
145
+ // of the function by using `unsafe` function pointers).
146
+ // FIXME: use `transmute` once it stops complaining about generics.
128
147
func => Some ( unsafe { mem:: transmute_copy :: < * mut c_void , F > ( & func) } ) ,
129
148
}
130
149
}
131
150
132
151
// Cold because it should only happen during first-time initialization.
133
152
#[ cold]
134
153
fn initialize ( & self ) -> Option < F > {
135
- const {
136
- if size_of :: < F > ( ) != size_of :: < * mut libc:: c_void > ( ) {
137
- panic ! ( "not a function pointer" )
138
- }
139
- }
140
-
154
+ // SAFETY: `self.name` was created from a `&'static CStr` and is
155
+ // therefore a valid C string pointer.
141
156
let val = unsafe { libc:: dlsym ( libc:: RTLD_DEFAULT , self . name ) } ;
142
157
// This synchronizes with the acquire load in `get`.
143
158
self . func . store ( val, Ordering :: Release ) ;
144
159
145
160
if val. is_null ( ) {
146
161
None
147
162
} else {
163
+ // SAFETY: see the comment in `get`.
164
+ // FIXME: use `transmute` once it stops complaining about generics.
148
165
Some ( unsafe { mem:: transmute_copy :: < * mut libc:: c_void , F > ( & val) } )
149
166
}
150
167
}
0 commit comments