2222#![ allow( dead_code,  unused_macros) ]  
2323#![ forbid( unsafe_op_in_unsafe_fn) ]  
2424
25- use  crate :: ffi:: CStr ; 
26- use  crate :: marker:: PhantomData ; 
27- use  crate :: sync:: atomic:: { self ,   Atomic ,  AtomicPtr ,  Ordering } ; 
25+ use  crate :: ffi:: { CStr ,  c_char ,  c_void } ; 
26+ use  crate :: marker:: { FnPtr ,   PhantomData } ; 
27+ use  crate :: sync:: atomic:: { Atomic ,  AtomicPtr ,  Ordering } ; 
2828use  crate :: { mem,  ptr} ; 
2929
30+ // We currently only test `dlsym!`, but that doesn't work on all platforms, so 
31+ // we gate the tests to only the platforms where it is actually used. 
32+ // 
33+ // FIXME(joboet): add more tests, reorganise the whole module and get rid of 
34+ //                `#[allow(dead_code, unused_macros)]`. 
35+ #[ cfg( any(  
36+     target_vendor = "apple" ,  
37+     all( target_os = "linux" ,  target_env = "gnu" ) ,  
38+     target_os = "freebsd" ,  
39+ ) ) ] 
40+ #[ cfg( test) ]  
41+ mod  tests; 
42+ 
3043// We can use true weak linkage on ELF targets. 
3144#[ cfg( all( unix,  not( target_vendor = "apple" ) ) ) ]  
3245pub ( crate )  macro weak { 
@@ -64,7 +77,7 @@ impl<F: Copy> ExternWeak<F> {
6477
6578pub ( crate )  macro dlsym { 
6679    ( fn  $name: ident( $( $param: ident :  $t: ty) , *  $( , ) ?)  -> $ret: ty; )  => ( 
67-           dlsym ! ( 
80+         dlsym ! ( 
6881            #[ link_name = stringify!( $name) ] 
6982            fn  $name( $( $param :  $t) , * )  -> $ret; 
7083        ) ; 
@@ -73,84 +86,99 @@ pub(crate) macro dlsym {
7386        #[ link_name = $sym: expr] 
7487        fn  $name: ident( $( $param: ident :  $t: ty) , *  $( , ) ?)  -> $ret: ty ; 
7588    )  => ( 
76-         static  DLSYM :  DlsymWeak < unsafe  extern  "C"  fn ( $( $t) , * )  -> $ret>  =
77-             DlsymWeak :: new ( concat ! ( $sym,  '\0' ) ) ; 
89+         static  DLSYM :  DlsymWeak < unsafe  extern  "C"  fn ( $( $t) , * )  -> $ret>  = { 
90+             let  Ok ( name )  = CStr :: from_bytes_with_nul ( concat ! ( $sym,  '\0' ) . as_bytes( ) )  else  { 
91+                 panic ! ( "symbol name may not contain NUL" ) 
92+             } ; 
93+ 
94+             // SAFETY: Whoever calls the function pointer returned by `get()` 
95+             // is responsible for ensuring that the signature is correct. Just 
96+             // like with extern blocks, this is syntactically enforced by making 
97+             // the function pointer be unsafe. 
98+             unsafe  {  DlsymWeak :: new ( name )  } 
99+         } ; 
100+ 
78101        let  $name = & DLSYM ; 
79102    ) 
80103} 
104+ 
81105pub ( crate ) struct  DlsymWeak < F >  { 
82-     name :  & ' static  str , 
106+     /// A pointer to the nul-terminated name of the symbol. 
107+ // Use a pointer instead of `&'static CStr` to save space. 
108+     name :  * const  c_char , 
83109    func :  Atomic < * mut  libc:: c_void > , 
84110    _marker :  PhantomData < F > , 
85111} 
86112
87- impl < F >  DlsymWeak < F >  { 
88-     pub ( crate )  const  fn  new ( name :  & ' static  str )  -> Self  { 
113+ impl < F :  FnPtr >  DlsymWeak < F >  { 
114+     /// # Safety 
115+ /// 
116+ /// If the signature of `F` does not match the signature of the symbol (if 
117+ /// it exists), calling the function pointer returned by `get()` is 
118+ /// undefined behaviour. 
119+ pub ( crate )  const  unsafe  fn  new ( name :  & ' static  CStr )  -> Self  { 
89120        DlsymWeak  { 
90-             name, 
121+             name :  name . as_ptr ( ) , 
91122            func :  AtomicPtr :: new ( ptr:: without_provenance_mut ( 1 ) ) , 
92123            _marker :  PhantomData , 
93124        } 
94125    } 
95126
96127    #[ inline]  
97128    pub ( crate )  fn  get ( & self )  -> Option < F >  { 
98-         unsafe   { 
99-              // Relaxed is fine here because we fence before reading through the  
100-              // pointer (see the comment below). 
101-              match   self . func . load ( Ordering :: Relaxed )   { 
102-                 func  if  func . addr ( )  ==  1  =>  self . initialize ( ) , 
103-                 func  if  func . is_null ( )  =>  None , 
104-                 func =>  { 
105-                      let  func = mem :: transmute_copy :: < * mut  libc :: c_void ,   F > ( & func ) ; 
106-                      // The caller is presumably going to read through this value  
107-                      // (by calling the function we've dlsymed). This means we'd  
108-                      // need to have loaded it with at least C11's consume  
109-                      // ordering in order to be guaranteed that  the data we read  
110-                      // from the pointer isn't from before the pointer was  
111-                      // stored. Rust has no equivalent to memory_order_consume,  
112-                      // so we use an acquire fence (sorry, ARM ). 
113-                      // 
114-                      // Now, in practice this likely isn't needed even on CPUs  
115-                      // where relaxed and consume mean different things. The 
116-                      // symbols we're loading are probably present (or not) at 
117-                      // init, and even  if they aren't the runtime dynamic loader 
118-                      // is extremely likely have sufficient barriers internally  
119-                      // (possibly implicitly, for example the ones provided by 
120-                      // invoking `mprotect`). 
121-                      // 
122-                      // That said, none of that's *guaranteed*, and so we fence.  
123-                     atomic :: fence ( Ordering :: Acquire ) ; 
124-                      Some ( func ) 
125-                  } 
126-             } 
129+         // The caller is presumably going to read through this value 
130+         // (by calling the function we've dlsymed). This means we'd  
131+         // need to have loaded it with at least C11's consume 
132+         // ordering in order to be guaranteed that the data we read 
133+         // from the pointer isn't from before the pointer was 
134+         // stored. Rust has no equivalent to memory_order_consume ,
135+         // so we use an acquire load (sorry, ARM). 
136+         // 
137+         // Now, in practice this likely isn't needed even on CPUs  
138+         // where relaxed and consume mean different things. The  
139+         // symbols we're loading are probably present (or not) at  
140+         // init, and even if they aren't  the runtime dynamic loader  
141+         // is extremely likely have sufficient barriers internally  
142+         // (possibly implicitly, for example the ones provided by  
143+         // invoking `mprotect` ). 
144+         // 
145+         // That said, none of that's *guaranteed*, so we use acquire.  
146+         match   self . func . load ( Ordering :: Acquire )   { 
147+             func  if  func . addr ( )  ==  1  =>  self . initialize ( ) , 
148+             func  if  func . is_null ( )  =>  None , 
149+             // SAFETY:  
150+             // `func` is not null and `F` implements `FnPtr`, thus this 
151+             // transmutation is well-defined. It is the responsibility of the 
152+             // creator of this `DlsymWeak` to ensure that calling the resulting 
153+             // function pointer does not result in undefined behaviour (though  
154+             // the `dlsym!` macro delegates this responsibility to the caller 
155+             // of the function by using `unsafe` function pointers). 
156+             // FIXME: use `transmute` once it stops complaining about generics. 
157+             func =>  Some ( unsafe   {  mem :: transmute_copy :: < * mut   c_void ,   F > ( & func )   } ) , 
127158        } 
128159    } 
129160
130161    // Cold because it should only happen during first-time initialization. 
131162    #[ cold]  
132-     unsafe   fn  initialize ( & self )  -> Option < F >  { 
133-         assert_eq ! ( size_of :: < F > ( ) ,  size_of :: < * mut  libc :: c_void> ( ) ) ; 
134- 
135-         let  val = unsafe  {  fetch ( self . name )  } ; 
136-         // This synchronizes with the acquire fence  in `get`. 
163+     fn  initialize ( & self )  -> Option < F >  { 
164+         // SAFETY: `self.name` was created from a `&'static CStr` and is 
165+          // therefore a valid C string pointer. 
166+         let  val = unsafe  {  libc :: dlsym ( libc :: RTLD_DEFAULT ,   self . name )  } ; 
167+         // This synchronizes with the acquire load  in `get`. 
137168        self . func . store ( val,  Ordering :: Release ) ; 
138169
139170        if  val. is_null ( )  { 
140171            None 
141172        }  else  { 
173+             // SAFETY: see the comment in `get`. 
174+             // FIXME: use `transmute` once it stops complaining about generics. 
142175            Some ( unsafe  {  mem:: transmute_copy :: < * mut  libc:: c_void ,  F > ( & val)  } ) 
143176        } 
144177    } 
145178} 
146179
147- unsafe  fn  fetch ( name :  & str )  -> * mut  libc:: c_void  { 
148-     let  name = match  CStr :: from_bytes_with_nul ( name. as_bytes ( ) )  { 
149-         Ok ( cstr)  => cstr, 
150-         Err ( ..)  => return  ptr:: null_mut ( ) , 
151-     } ; 
152-     unsafe  {  libc:: dlsym ( libc:: RTLD_DEFAULT ,  name. as_ptr ( ) )  } 
153- } 
180+ unsafe  impl < F >  Send  for  DlsymWeak < F >  { } 
181+ unsafe  impl < F >  Sync  for  DlsymWeak < F >  { } 
154182
155183#[ cfg( not( any( target_os = "linux" ,  target_os = "android" ) ) ) ]  
156184pub ( crate )  macro syscall { 
0 commit comments