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}