2323#![ forbid( unsafe_op_in_unsafe_fn) ]
2424
2525use crate :: ffi:: { CStr , c_char, c_void} ;
26- use crate :: marker:: PhantomData ;
26+ use crate :: marker:: { FnPtr , PhantomData } ;
2727use crate :: sync:: atomic:: { Atomic , AtomicPtr , Ordering } ;
2828use crate :: { mem, ptr} ;
2929
@@ -76,8 +76,18 @@ pub(crate) macro dlsym {
7676 #[ link_name = $sym: expr]
7777 fn $name: ident( $( $param: ident : $t: ty) , * $( , ) ?) -> $ret: ty ;
7878 ) => (
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+
8191 let $name = & DLSYM ;
8292 )
8393}
@@ -90,12 +100,13 @@ pub(crate) struct DlsymWeak<F> {
90100 _marker : PhantomData < F > ,
91101}
92102
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 {
99110 DlsymWeak {
100111 name : name. as_ptr ( ) ,
101112 func : AtomicPtr :: new ( ptr:: without_provenance_mut ( 1 ) ) ,
@@ -125,26 +136,32 @@ impl<F> DlsymWeak<F> {
125136 match self . func . load ( Ordering :: Acquire ) {
126137 func if func. addr ( ) == 1 => self . initialize ( ) ,
127138 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.
128147 func => Some ( unsafe { mem:: transmute_copy :: < * mut c_void , F > ( & func) } ) ,
129148 }
130149 }
131150
132151 // Cold because it should only happen during first-time initialization.
133152 #[ cold]
134153 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.
141156 let val = unsafe { libc:: dlsym ( libc:: RTLD_DEFAULT , self . name ) } ;
142157 // This synchronizes with the acquire load in `get`.
143158 self . func . store ( val, Ordering :: Release ) ;
144159
145160 if val. is_null ( ) {
146161 None
147162 } else {
163+ // SAFETY: see the comment in `get`.
164+ // FIXME: use `transmute` once it stops complaining about generics.
148165 Some ( unsafe { mem:: transmute_copy :: < * mut libc:: c_void , F > ( & val) } )
149166 }
150167 }
0 commit comments