linux_libc_auxv/aux_var/typ.rs
1/*
2MIT License
3
4Copyright (c) 2025 Philipp Schuster
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24use core::cmp::Ordering;
25
26#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, thiserror::Error)]
27#[error("invalid aux var type: {0}")]
28pub struct ParseAuxVarTypeError(usize);
29
30/// Rust-style representation of the auxiliary variable's type.
31///
32/// Also see [`AuxVar`].
33///
34/// - `0-17` are architecture independent
35/// - `>=32` are for `x86_64`.
36/// - `>=40` are for power PC.
37///
38/// ## More Info
39/// * <https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/auxvec.h>
40///
41/// [`AuxVar`]: crate::AuxVar
42#[derive(Copy, Clone, Debug, PartialEq, Eq)]
43#[repr(usize)]
44pub enum AuxVarType {
45 // ### architecture neutral
46 /// end of vector
47 Null = 0,
48 /// entry should be ignored
49 Ignore = 1,
50 /// file descriptor of program
51 ExecFd = 2,
52 /// program headers for program
53 Phdr = 3,
54 /// size of program header entry
55 Phent = 4,
56 /// number of program headers
57 Phnum = 5,
58 /// system page size
59 Pagesz = 6,
60 /// The base address of the program interpreter (usually, the
61 /// dynamic linker).
62 Base = 7,
63 /// Flags that apply on the whole auxiliary vector. See [`crate::AuxVarFlags`].
64 Flags = 8,
65 /// entry point of program
66 Entry = 9,
67 /// program is not ELF
68 NotElf = 10,
69 /// real uid
70 Uid = 11,
71 /// effective uid
72 EUid = 12,
73 /// real gid
74 Gid = 13,
75 /// effective gid
76 EGid = 14,
77 /// string identifying CPU for optimizations
78 Platform = 15,
79 /// Arch dependent hints at CPU capabilities.
80 /// On x86_64 these are the CPUID features.
81 HwCap = 16,
82 /// frequency at which times() increments
83 Clktck = 17,
84 /// secure mode boolean
85 Secure = 23,
86 /// string identifying real platform, may differ from AtPlatform.
87 BasePlatform = 24,
88 /// address of 16 random bytes
89 Random = 25,
90 /// extension of AtHwcap
91 HwCap2 = 26,
92 /// filename of program, for example "./my_executable\0"
93 ExecFn = 31,
94
95 // ### according to Linux code: from here: x86_64
96 /// The entry point to the system call function in the vDSO.
97 /// Not present/needed on all architectures (e.g., absent on
98 /// x86-64).
99 Sysinfo = 32,
100 /// The address of a page containing the virtual Dynamic
101 /// Shared Object (vDSO) that the kernel creates in order to
102 /// provide fast implementations of certain system calls.
103 SysinfoEhdr = 33,
104
105 // ### according to Linux code: from here: PowerPC
106 /// L1 instruction cache size
107 L1iCacheSize = 40,
108 /// L1 instruction cache geometry
109 L1iCacheGeometry = 41,
110 /// L1 cache geometry
111 L1dCacheSize = 42,
112 /// L1 cache size
113 L1dCacheGeometry = 43,
114 /// L2 cache size
115 L2CacheSize = 44,
116 /// L2 cache geometry
117 L2CacheGeometry = 45,
118 /// L3 cache size
119 L3CacheSize = 46,
120 /// L3 cache geometry
121 L3CacheGeometry = 47,
122
123 /// Minimal stack size for signal delivery.
124 MinSigStkSz = 51,
125}
126
127impl AuxVarType {
128 /// Returns an array with all variants.
129 #[must_use]
130 pub const fn variants() -> &'static [Self] {
131 &[
132 Self::Null,
133 Self::Ignore,
134 Self::ExecFd,
135 Self::Phdr,
136 Self::Phent,
137 Self::Phnum,
138 Self::Pagesz,
139 Self::Base,
140 Self::Flags,
141 Self::Entry,
142 Self::NotElf,
143 Self::Uid,
144 Self::EUid,
145 Self::Gid,
146 Self::EGid,
147 Self::Platform,
148 Self::HwCap,
149 Self::Clktck,
150 Self::Secure,
151 Self::BasePlatform,
152 Self::Random,
153 Self::HwCap2,
154 Self::ExecFn,
155 Self::Sysinfo,
156 Self::SysinfoEhdr,
157 Self::L1iCacheSize,
158 Self::L1iCacheGeometry,
159 Self::L1dCacheSize,
160 Self::L1dCacheGeometry,
161 Self::L2CacheSize,
162 Self::L2CacheGeometry,
163 Self::L3CacheSize,
164 Self::L3CacheGeometry,
165 Self::MinSigStkSz,
166 ]
167 }
168
169 /// Returns the underlying ABI-compatible integer value.
170 #[must_use]
171 pub const fn val(self) -> usize {
172 self as _
173 }
174
175 /// If this is true, the value of the key should be interpreted as pointer
176 /// into the aux vector data area. Otherwise, the value of the key is an
177 /// immediate value/integer.
178 #[must_use]
179 pub const fn value_in_data_area(self) -> bool {
180 // this info can be found here:
181 // https://elixir.bootlin.com/linux/latest/source/fs/binfmt_elf.c#L259
182 match self {
183 Self::Null => false,
184 Self::Ignore => false,
185 Self::ExecFd => false,
186 Self::Phdr => false,
187 Self::Phent => false,
188 Self::Phnum => false,
189 Self::Pagesz => false,
190 Self::Base => false,
191 Self::Flags => false,
192 Self::Entry => false,
193 Self::NotElf => false,
194 Self::Uid => false,
195 Self::EUid => false,
196 Self::Gid => false,
197 Self::EGid => false,
198 // references C-str
199 Self::Platform => true,
200 Self::HwCap => false,
201 Self::Clktck => false,
202 Self::Secure => false,
203 // references C-str
204 Self::BasePlatform => true,
205 // references random bytes
206 Self::Random => true,
207 Self::HwCap2 => false,
208 // references C-str
209 Self::ExecFn => true,
210 Self::SysinfoEhdr => false,
211 Self::Sysinfo => false,
212 Self::L1iCacheSize => false,
213 Self::L1iCacheGeometry => false,
214 Self::L1dCacheSize => false,
215 Self::L1dCacheGeometry => false,
216 Self::L2CacheSize => false,
217 Self::L2CacheGeometry => false,
218 Self::L3CacheSize => false,
219 Self::L3CacheGeometry => false,
220 Self::MinSigStkSz => false,
221 }
222 }
223
224 /// The payload of entries where this returns true represents a
225 /// null-terminated C-string.
226 #[must_use]
227 pub fn value_is_cstr(self) -> bool {
228 self.value_in_data_area()
229 && [Self::Platform, Self::BasePlatform, Self::ExecFn].contains(&self)
230 }
231
232 /// The payload of some [`AuxVarType`] is stored in the aux var data area.
233 /// Most of these payloads are variable-length and null-terminated. If they
234 /// have a fixed size, then this function returns it.
235 #[must_use]
236 pub const fn data_area_val_size_hint(self) -> Option<usize> {
237 match self {
238 Self::Random => Some(16),
239 _ => None,
240 }
241 }
242}
243
244impl From<AuxVarType> for usize {
245 fn from(value: AuxVarType) -> Self {
246 value.val()
247 }
248}
249
250impl TryFrom<usize> for AuxVarType {
251 type Error = ParseAuxVarTypeError;
252
253 fn try_from(value: usize) -> Result<Self, Self::Error> {
254 for variant in Self::variants() {
255 if variant.val() == value {
256 return Ok(*variant);
257 }
258 }
259 Err(ParseAuxVarTypeError(value))
260 }
261}
262
263impl PartialOrd for AuxVarType {
264 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
265 Some(self.cmp(other))
266 }
267}
268
269impl Ord for AuxVarType {
270 fn cmp(&self, other: &Self) -> Ordering {
271 if matches!(self, Self::Null) && !matches!(other, Self::Null) {
272 Ordering::Greater
273 } else {
274 self.val().cmp(&other.val())
275 }
276 }
277}
278
279#[cfg(test)]
280mod tests {
281 use super::*;
282 use std::collections::BTreeSet;
283
284 #[test]
285 fn test_variants_are_sorted() {
286 let mut variants = AuxVarType::variants().to_vec();
287 variants.sort();
288 assert_eq!(AuxVarType::variants(), variants.as_slice());
289 }
290
291 /// Tests that the ATNull entry always comes last in an ordered collection.
292 /// This enables us to easily write all AT-VARs at once but keep the
293 /// terminating null entry at the end.
294 #[test]
295 fn test_aux_var_key_order() {
296 let mut set = BTreeSet::new();
297 set.insert(AuxVarType::ExecFn);
298 set.insert(AuxVarType::Platform);
299 set.insert(AuxVarType::Null);
300 set.insert(AuxVarType::Clktck);
301 set.insert(AuxVarType::ExecFn);
302 assert_eq!(set.last(), Some(&AuxVarType::Null));
303 }
304}