11//! Implementation for NetBSD
2- use crate :: {
3- util_libc:: { sys_fill_exact, Weak } ,
4- Error ,
2+ use crate :: { util_libc:: sys_fill_exact, Error } ;
3+ use core:: {
4+ ffi:: c_void,
5+ mem:: MaybeUninit ,
6+ ptr,
7+ ptr:: NonNull ,
8+ sync:: atomic:: { fence, AtomicPtr , Ordering } ,
59} ;
6- use core:: { mem:: MaybeUninit , ptr} ;
710
811fn kern_arnd ( buf : & mut [ MaybeUninit < u8 > ] ) -> libc:: ssize_t {
912 static MIB : [ libc:: c_int ; 2 ] = [ libc:: CTL_KERN , libc:: KERN_ARND ] ;
@@ -29,7 +32,7 @@ type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) ->
2932
3033pub fn getrandom_inner ( dest : & mut [ MaybeUninit < u8 > ] ) -> Result < ( ) , Error > {
3134 // getrandom(2) was introduced in NetBSD 10.0
32- static GETRANDOM : Weak = unsafe { Weak :: new ( "getrandom \0 " ) } ;
35+ static GETRANDOM : WeakGetrandom = WeakGetrandom :: new ( ) ;
3336 if let Some ( fptr) = GETRANDOM . ptr ( ) {
3437 let func: GetRandomFn = unsafe { core:: mem:: transmute ( fptr) } ;
3538 return sys_fill_exact ( dest, |buf| unsafe {
@@ -44,3 +47,57 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
4447 }
4548 Ok ( ( ) )
4649}
50+
51+ // A "weak" binding to the `getrandom` function that may or may not be present at runtime.
52+ // Used for supporting newer OS features while still building on older systems.
53+ // Based off of the DlsymWeak struct in libstd:
54+ // https://github.com/rust-lang/rust/blob/1.61.0/library/std/src/sys/unix/weak.rs#L84
55+ // except that the caller must manually cast self.ptr() to a function pointer.
56+ pub struct WeakGetrandom {
57+ addr : AtomicPtr < c_void > ,
58+ }
59+
60+ impl WeakGetrandom {
61+ // A non-null pointer value which indicates we are uninitialized. This
62+ // constant should ideally not be a valid address of a function pointer.
63+ // However, if by chance libc::dlsym does return UNINIT, there will not
64+ // be undefined behavior. libc::dlsym will just be called each time ptr()
65+ // is called. This would be inefficient, but correct.
66+ // TODO: Replace with core::ptr::invalid_mut(1) when that is stable.
67+ const UNINIT : * mut c_void = 1 as * mut c_void ;
68+
69+ // Construct a weak binding to the `getrandom` function.
70+ pub const fn new ( ) -> Self {
71+ Self {
72+ addr : AtomicPtr :: new ( Self :: UNINIT ) ,
73+ }
74+ }
75+
76+ // Return the address of a function if present at runtime. Otherwise,
77+ // return None. Multiple callers can call ptr() concurrently. It will
78+ // always return _some_ value returned by libc::dlsym. However, the
79+ // dlsym function may be called multiple times.
80+ pub fn ptr ( & self ) -> Option < NonNull < c_void > > {
81+ // Despite having only a single atomic variable (self.addr), we still
82+ // cannot always use Ordering::Relaxed, as we need to make sure a
83+ // successful call to dlsym() is "ordered before" any data read through
84+ // the returned pointer (which occurs when the function is called).
85+ // Our implementation mirrors that of the one in libstd, meaning that
86+ // the use of non-Relaxed operations is probably unnecessary.
87+ match self . addr . load ( Ordering :: Relaxed ) {
88+ Self :: UNINIT => {
89+ let name = b"getrandom\0 " ;
90+ assert_eq ! ( name[ name. len( ) - 1 ] , 0 ) ;
91+ let addr = unsafe { libc:: dlsym ( libc:: RTLD_DEFAULT , name. as_ptr ( ) as * const _ ) } ;
92+ // Synchronizes with the Acquire fence below
93+ self . addr . store ( addr, Ordering :: Release ) ;
94+ NonNull :: new ( addr)
95+ }
96+ addr => {
97+ let func = NonNull :: new ( addr) ?;
98+ fence ( Ordering :: Acquire ) ;
99+ Some ( func)
100+ }
101+ }
102+ }
103+ }
0 commit comments