1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//! Read auxv entries one at a time via `getauxval`.
//!
//! Because the underlying `getauxval` C function is weakly linked, and only available on Linux,
//! access to it is done via the trait `Getauxval` to provide some indirection. On
//! `target_os="linux"`, the struct `NativeGetauxval` will be available, and that will call through
//! to `getauxval` if it is available and return an appropriate error if it is not. That means it
//! should be safe to try it if you're not sure your glibc has the function, etc.
//!
//! On all OSs, if you want a no-op sort of implementation (for use on non-Linux OSs, etc), you can
//! use `NotAvailableGetauxval`. It (surprise!) always returns the error that indicates that
//! `getauxval` function was not found. Of course, you can also use write your own stub
//! implementation of the trait for testing.

use super::AuxvType;

extern "C" {
    /// Invoke getauxval(3) if available.
    #[cfg(target_os="linux")]
    fn getauxval_wrapper(key: AuxvType, success: *mut AuxvType) -> i32;
}
/// Errors from invoking `getauxval`.
#[derive(Debug, PartialEq)]
pub enum GetauxvalError {
    /// getauxval() is not available at runtime
    FunctionNotAvailable,
    /// getauxval() could not find the requested key
    NotFound,
    /// getauxval() encountered a different error
    UnknownError
}

/// On Linux, you will probably want `NativeGetauxval`. If you're not
/// on Linux but want to use the same `getauxv`-based logic, you could
/// conditionally use `NotAvailableGetauxval` instead.
pub trait Getauxval {
    /// Look up an entry in the auxiliary vector. See getauxval(3) in glibc.
    fn getauxval(&self, key: AuxvType) -> Result<AuxvType, GetauxvalError>;
}

/// A stub implementation that always returns `FunctionNotAvailable`.
/// This can be used when you want to use something reasonable (i.e. won't crash or fail to
/// compile) that's not `NativeGetauxval` on non-Linux systems.
pub struct NotAvailableGetauxval {}

impl Getauxval for NotAvailableGetauxval {
    fn getauxval(&self, _: AuxvType) -> Result<AuxvType, GetauxvalError> {
        Err(GetauxvalError::FunctionNotAvailable)
    }
}

/// Calls through to the underlying glibc or Bionic `getauxval()`.
#[cfg(target_os="linux")]
pub struct NativeGetauxval {}

#[cfg(target_os="linux")]
impl Getauxval for NativeGetauxval {
    fn getauxval(&self, key: AuxvType)
                 -> Result<AuxvType, GetauxvalError> {

        let mut result = 0;
        unsafe {
            return match getauxval_wrapper(key, &mut result) {
                1 => Ok(result),
                0 => Err(GetauxvalError::NotFound),
                -1 => Err(GetauxvalError::FunctionNotAvailable),
                -2 => Err(GetauxvalError::UnknownError),
                x => panic!("getauxval_wrapper returned an unexpected value: {}", x)
            }
        }
    }
}