]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | //! Linux auxv support. |
2 | //! | |
3 | //! # Safety | |
4 | //! | |
5 | //! This uses raw pointers to locate and read the kernel-provided auxv array. | |
6 | #![allow(unsafe_code)] | |
7 | ||
8 | use super::super::c; | |
9 | use super::super::elf::{Elf_Ehdr, Elf_Phdr}; | |
10 | #[cfg(feature = "param")] | |
11 | use crate::ffi::CStr; | |
12 | use core::mem::size_of; | |
13 | use core::ptr::null; | |
14 | #[cfg(feature = "runtime")] | |
15 | use core::slice; | |
16 | use linux_raw_sys::general::{ | |
17 | AT_CLKTCK, AT_EXECFN, AT_HWCAP, AT_HWCAP2, AT_NULL, AT_PAGESZ, AT_PHDR, AT_PHENT, AT_PHNUM, | |
18 | AT_SYSINFO_EHDR, | |
19 | }; | |
20 | ||
21 | #[cfg(feature = "param")] | |
22 | #[inline] | |
23 | pub(crate) fn page_size() -> usize { | |
24 | auxv().page_size | |
25 | } | |
26 | ||
27 | #[cfg(feature = "param")] | |
28 | #[inline] | |
29 | pub(crate) fn clock_ticks_per_second() -> u64 { | |
30 | auxv().clock_ticks_per_second as u64 | |
31 | } | |
32 | ||
33 | #[cfg(feature = "param")] | |
34 | #[inline] | |
35 | pub(crate) fn linux_hwcap() -> (usize, usize) { | |
36 | let auxv = auxv(); | |
37 | (auxv.hwcap, auxv.hwcap2) | |
38 | } | |
39 | ||
40 | #[cfg(feature = "param")] | |
41 | #[inline] | |
42 | pub(crate) fn linux_execfn() -> &'static CStr { | |
43 | let execfn = auxv().execfn; | |
44 | ||
45 | // Safety: We assume the `AT_EXECFN` value provided by the kernel is a | |
46 | // valid pointer to a valid NUL-terminated array of bytes. | |
47 | unsafe { CStr::from_ptr(execfn.cast()) } | |
48 | } | |
49 | ||
50 | #[cfg(feature = "runtime")] | |
51 | #[inline] | |
52 | pub(crate) fn exe_phdrs() -> (*const c::c_void, usize) { | |
53 | let auxv = auxv(); | |
54 | (auxv.phdr.cast(), auxv.phnum) | |
55 | } | |
56 | ||
57 | #[cfg(feature = "runtime")] | |
58 | #[inline] | |
59 | pub(in super::super) fn exe_phdrs_slice() -> &'static [Elf_Phdr] { | |
60 | let auxv = auxv(); | |
61 | ||
62 | // Safety: We assume the `AT_PHDR` and `AT_PHNUM` values provided by the | |
63 | // kernel form a valid slice. | |
64 | unsafe { slice::from_raw_parts(auxv.phdr, auxv.phnum) } | |
65 | } | |
66 | ||
67 | #[inline] | |
68 | pub(in super::super) fn sysinfo_ehdr() -> *const Elf_Ehdr { | |
69 | auxv().sysinfo_ehdr | |
70 | } | |
71 | ||
72 | #[inline] | |
73 | fn auxv() -> &'static Auxv { | |
74 | // Safety: `AUXV` is initialized from the `.init_array`, and we never | |
75 | // mutate it thereafter, so it's effectively initialized read-only in all | |
76 | // other code. | |
77 | unsafe { | |
78 | // Assert that the initialization has happened. On glibc and musl, this | |
79 | // is handled automatically by `.init_array` functions. Otherwise, | |
80 | // `rustix::process::init` must be called explicitly. | |
81 | debug_assert_ne!(AUXV.page_size, 0); | |
82 | ||
83 | &AUXV | |
84 | } | |
85 | } | |
86 | ||
87 | /// A struct for holding fields obtained from the kernel-provided auxv array. | |
88 | struct Auxv { | |
89 | page_size: usize, | |
90 | clock_ticks_per_second: usize, | |
91 | hwcap: usize, | |
92 | hwcap2: usize, | |
93 | sysinfo_ehdr: *const Elf_Ehdr, | |
94 | phdr: *const Elf_Phdr, | |
95 | phnum: usize, | |
96 | execfn: *const c::c_char, | |
97 | } | |
98 | ||
99 | /// Data obtained from the kernel-provided auxv array. This is initialized at | |
100 | /// program startup below. | |
101 | static mut AUXV: Auxv = Auxv { | |
102 | page_size: 0, | |
103 | clock_ticks_per_second: 0, | |
104 | hwcap: 0, | |
105 | hwcap2: 0, | |
106 | sysinfo_ehdr: null(), | |
107 | phdr: null(), | |
108 | phnum: 0, | |
109 | execfn: null(), | |
110 | }; | |
111 | ||
112 | /// GLIBC passes argc, argv, and envp to functions in .init_array, as a | |
113 | /// non-standard extension. Use priority 99 so that we run before any | |
114 | /// normal user-defined constructor functions. | |
115 | #[cfg(all(target_env = "gnu", not(target_vendor = "mustang")))] | |
116 | #[used] | |
117 | #[link_section = ".init_array.00099"] | |
118 | static INIT_ARRAY: unsafe extern "C" fn(c::c_int, *mut *mut u8, *mut *mut u8) = { | |
119 | unsafe extern "C" fn function(_argc: c::c_int, _argv: *mut *mut u8, envp: *mut *mut u8) { | |
120 | init_from_envp(envp); | |
121 | } | |
122 | function | |
123 | }; | |
124 | ||
125 | /// For musl, assume that `__environ` is available and points to the original | |
126 | /// environment from the kernel, so we can find the auxv array in memory after | |
127 | /// it. Use priority 99 so that we run before any normal user-defined | |
128 | /// constructor functions. | |
129 | /// | |
130 | /// <https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/baselib---environ.html> | |
131 | #[cfg(all(target_env = "musl", not(target_vendor = "mustang")))] | |
132 | #[used] | |
133 | #[link_section = ".init_array.00099"] | |
134 | static INIT_ARRAY: unsafe extern "C" fn() = { | |
135 | unsafe extern "C" fn function() { | |
136 | extern "C" { | |
137 | static __environ: *mut *mut u8; | |
138 | } | |
139 | ||
140 | init_from_envp(__environ) | |
141 | } | |
142 | function | |
143 | }; | |
144 | ||
145 | /// On mustang or any non-musl non-glibic platform where we don't know that we | |
146 | /// have `.init_array`, we export a function to be called during | |
147 | /// initialization, and passed a pointer to the original environment variable | |
148 | /// block set up by the OS. | |
149 | #[cfg(any( | |
150 | target_vendor = "mustang", | |
151 | not(any(target_env = "gnu", target_env = "musl")), | |
152 | ))] | |
153 | #[inline] | |
154 | pub(crate) unsafe fn init(envp: *mut *mut u8) { | |
155 | init_from_envp(envp); | |
156 | } | |
157 | ||
158 | /// # Safety | |
159 | /// | |
160 | /// This must be passed a pointer to the environment variable buffer | |
161 | /// provided by the kernel, which is followed in memory by the auxv array. | |
162 | unsafe fn init_from_envp(mut envp: *mut *mut u8) { | |
163 | while !(*envp).is_null() { | |
164 | envp = envp.add(1); | |
165 | } | |
166 | init_from_auxp(envp.add(1).cast()) | |
167 | } | |
168 | ||
169 | /// # Safety | |
170 | /// | |
171 | /// This must be passed a pointer to the auxv array provided by the kernel. | |
172 | unsafe fn init_from_auxp(mut auxp: *const Elf_auxv_t) { | |
173 | loop { | |
174 | let Elf_auxv_t { a_type, a_val } = *auxp; | |
175 | match a_type as _ { | |
176 | AT_PAGESZ => AUXV.page_size = a_val as usize, | |
177 | AT_CLKTCK => AUXV.clock_ticks_per_second = a_val as usize, | |
178 | AT_HWCAP => AUXV.hwcap = a_val as usize, | |
179 | AT_HWCAP2 => AUXV.hwcap2 = a_val as usize, | |
180 | AT_SYSINFO_EHDR => AUXV.sysinfo_ehdr = a_val.cast(), | |
181 | AT_PHDR => AUXV.phdr = a_val.cast(), | |
182 | AT_PHNUM => AUXV.phnum = a_val as usize, | |
183 | AT_PHENT => assert_eq!(a_val as usize, size_of::<Elf_Phdr>()), | |
184 | AT_EXECFN => AUXV.execfn = a_val.cast(), | |
185 | AT_NULL => break, | |
186 | _ => (), | |
187 | } | |
188 | auxp = auxp.add(1); | |
189 | } | |
190 | } | |
191 | ||
192 | // ELF ABI | |
193 | ||
194 | #[repr(C)] | |
195 | #[derive(Copy, Clone)] | |
196 | struct Elf_auxv_t { | |
197 | a_type: usize, | |
198 | ||
199 | // Some of the values in the auxv array are pointers, so we make `a_val` a | |
200 | // pointer, in order to preserve their provenance. For the values which are | |
201 | // integers, we cast this to `usize`. | |
202 | a_val: *const (), | |
203 | } |