summaryrefslogtreecommitdiffstats
path: root/vendor/rustix/src/imp/linux_raw/param/auxv.rs
blob: beed8613c3501a5c116c5287031cb39e0d52ab89 (plain)
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//! Linux auxv support.
//!
//! # Safety
//!
//! This uses raw pointers to locate and read the kernel-provided auxv array.
#![allow(unsafe_code)]

use super::super::c;
use super::super::elf::{Elf_Ehdr, Elf_Phdr};
#[cfg(feature = "param")]
use crate::ffi::CStr;
use core::mem::size_of;
use core::ptr::null;
#[cfg(feature = "runtime")]
use core::slice;
use linux_raw_sys::general::{
    AT_CLKTCK, AT_EXECFN, AT_HWCAP, AT_HWCAP2, AT_NULL, AT_PAGESZ, AT_PHDR, AT_PHENT, AT_PHNUM,
    AT_SYSINFO_EHDR,
};

#[cfg(feature = "param")]
#[inline]
pub(crate) fn page_size() -> usize {
    auxv().page_size
}

#[cfg(feature = "param")]
#[inline]
pub(crate) fn clock_ticks_per_second() -> u64 {
    auxv().clock_ticks_per_second as u64
}

#[cfg(feature = "param")]
#[inline]
pub(crate) fn linux_hwcap() -> (usize, usize) {
    let auxv = auxv();
    (auxv.hwcap, auxv.hwcap2)
}

#[cfg(feature = "param")]
#[inline]
pub(crate) fn linux_execfn() -> &'static CStr {
    let execfn = auxv().execfn;

    // Safety: We assume the `AT_EXECFN` value provided by the kernel is a
    // valid pointer to a valid NUL-terminated array of bytes.
    unsafe { CStr::from_ptr(execfn.cast()) }
}

#[cfg(feature = "runtime")]
#[inline]
pub(crate) fn exe_phdrs() -> (*const c::c_void, usize) {
    let auxv = auxv();
    (auxv.phdr.cast(), auxv.phnum)
}

#[cfg(feature = "runtime")]
#[inline]
pub(in super::super) fn exe_phdrs_slice() -> &'static [Elf_Phdr] {
    let auxv = auxv();

    // Safety: We assume the `AT_PHDR` and `AT_PHNUM` values provided by the
    // kernel form a valid slice.
    unsafe { slice::from_raw_parts(auxv.phdr, auxv.phnum) }
}

#[inline]
pub(in super::super) fn sysinfo_ehdr() -> *const Elf_Ehdr {
    auxv().sysinfo_ehdr
}

#[inline]
fn auxv() -> &'static Auxv {
    // Safety: `AUXV` is initialized from the `.init_array`, and we never
    // mutate it thereafter, so it's effectively initialized read-only in all
    // other code.
    unsafe {
        // Assert that the initialization has happened. On glibc and musl, this
        // is handled automatically by `.init_array` functions. Otherwise,
        // `rustix::process::init` must be called explicitly.
        debug_assert_ne!(AUXV.page_size, 0);

        &AUXV
    }
}

/// A struct for holding fields obtained from the kernel-provided auxv array.
struct Auxv {
    page_size: usize,
    clock_ticks_per_second: usize,
    hwcap: usize,
    hwcap2: usize,
    sysinfo_ehdr: *const Elf_Ehdr,
    phdr: *const Elf_Phdr,
    phnum: usize,
    execfn: *const c::c_char,
}

/// Data obtained from the kernel-provided auxv array. This is initialized at
/// program startup below.
static mut AUXV: Auxv = Auxv {
    page_size: 0,
    clock_ticks_per_second: 0,
    hwcap: 0,
    hwcap2: 0,
    sysinfo_ehdr: null(),
    phdr: null(),
    phnum: 0,
    execfn: null(),
};

/// GLIBC passes argc, argv, and envp to functions in .init_array, as a
/// non-standard extension. Use priority 99 so that we run before any
/// normal user-defined constructor functions.
#[cfg(all(target_env = "gnu", not(target_vendor = "mustang")))]
#[used]
#[link_section = ".init_array.00099"]
static INIT_ARRAY: unsafe extern "C" fn(c::c_int, *mut *mut u8, *mut *mut u8) = {
    unsafe extern "C" fn function(_argc: c::c_int, _argv: *mut *mut u8, envp: *mut *mut u8) {
        init_from_envp(envp);
    }
    function
};

/// For musl, assume that `__environ` is available and points to the original
/// environment from the kernel, so we can find the auxv array in memory after
/// it. Use priority 99 so that we run before any normal user-defined
/// constructor functions.
///
/// <https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/baselib---environ.html>
#[cfg(all(target_env = "musl", not(target_vendor = "mustang")))]
#[used]
#[link_section = ".init_array.00099"]
static INIT_ARRAY: unsafe extern "C" fn() = {
    unsafe extern "C" fn function() {
        extern "C" {
            static __environ: *mut *mut u8;
        }

        init_from_envp(__environ)
    }
    function
};

/// On mustang or any non-musl non-glibic platform where we don't know that we
/// have `.init_array`, we export a function to be called during
/// initialization, and passed a pointer to the original environment variable
/// block set up by the OS.
#[cfg(any(
    target_vendor = "mustang",
    not(any(target_env = "gnu", target_env = "musl")),
))]
#[inline]
pub(crate) unsafe fn init(envp: *mut *mut u8) {
    init_from_envp(envp);
}

/// # Safety
///
/// This must be passed a pointer to the environment variable buffer
/// provided by the kernel, which is followed in memory by the auxv array.
unsafe fn init_from_envp(mut envp: *mut *mut u8) {
    while !(*envp).is_null() {
        envp = envp.add(1);
    }
    init_from_auxp(envp.add(1).cast())
}

/// # Safety
///
/// This must be passed a pointer to the auxv array provided by the kernel.
unsafe fn init_from_auxp(mut auxp: *const Elf_auxv_t) {
    loop {
        let Elf_auxv_t { a_type, a_val } = *auxp;
        match a_type as _ {
            AT_PAGESZ => AUXV.page_size = a_val as usize,
            AT_CLKTCK => AUXV.clock_ticks_per_second = a_val as usize,
            AT_HWCAP => AUXV.hwcap = a_val as usize,
            AT_HWCAP2 => AUXV.hwcap2 = a_val as usize,
            AT_SYSINFO_EHDR => AUXV.sysinfo_ehdr = a_val.cast(),
            AT_PHDR => AUXV.phdr = a_val.cast(),
            AT_PHNUM => AUXV.phnum = a_val as usize,
            AT_PHENT => assert_eq!(a_val as usize, size_of::<Elf_Phdr>()),
            AT_EXECFN => AUXV.execfn = a_val.cast(),
            AT_NULL => break,
            _ => (),
        }
        auxp = auxp.add(1);
    }
}

// ELF ABI

#[repr(C)]
#[derive(Copy, Clone)]
struct Elf_auxv_t {
    a_type: usize,

    // Some of the values in the auxv array are pointers, so we make `a_val` a
    // pointer, in order to preserve their provenance. For the values which are
    // integers, we cast this to `usize`.
    a_val: *const (),
}