linux_libc_auxv/aux_var/
mod.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*/
24
25mod serialized;
26mod typ;
27
28pub use serialized::*;
29pub use typ::*;
30
31use crate::util::count_bytes_until_null;
32use core::cmp::Ordering;
33use core::ffi::CStr;
34use core::fmt::{Debug, Display, Formatter};
35#[cfg(feature = "alloc")]
36use {alloc::borrow::ToOwned, alloc::ffi::CString, alloc::string::String, alloc::string::ToString};
37bitflags::bitflags! {
38    /// Flags for the auxiliary vector. See <https://elixir.bootlin.com/linux/v5.15.5/source/include/uapi/linux/binfmts.h#L23>.
39    #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)]
40    pub struct AuxVarFlags: usize {
41        /// Opposite of [`Self::PRESERVE_ARGV0`].
42        const NOT_PRESERVE_ARGV0 = 0;
43        /// Preserve argv0 for the interpreter.
44        const PRESERVE_ARGV0 = 1;
45    }
46}
47
48/// Possible string payload variants of an [`AuxVar`].
49///
50/// Due to the diverse variants, is not guaranteed that
51/// - a terminating NUL byte is present, and
52/// - that no interim NUL bytes are present.
53///
54/// When constructing these variants, adding a terminating NUL byte is not
55/// necessary. Interim NUL bytes are prohibited.
56///
57/// This type can be easily construct using `::from()` respectively `.into()`.
58#[derive(Debug, Eq, PartialEq, Clone)]
59pub enum AuxVarString<'a> {
60    #[cfg(feature = "alloc")]
61    String(String),
62    #[cfg(feature = "alloc")]
63    CString(CString),
64    Str(&'a str),
65    CStr(&'a CStr),
66}
67
68impl<'a> AuxVarString<'a> {
69    /// Returns the bytes of the underlying type.
70    ///
71    /// Due to the diverse variants, is not guaranteed that
72    /// - a terminating NUL byte is present, and
73    /// - that no interim NUL bytes are present.
74    pub fn as_bytes(&self) -> &[u8] {
75        match self {
76            #[cfg(feature = "alloc")]
77            AuxVarString::String(str) => str.as_bytes(),
78            #[cfg(feature = "alloc")]
79            AuxVarString::CString(cstr) => cstr.to_bytes_with_nul(),
80            AuxVarString::Str(str) => str.as_bytes(),
81            AuxVarString::CStr(cstr) => cstr.to_bytes_with_nul(),
82        }
83    }
84
85    /// Returns the number of bytes until the first NUL, excluding the NUL.
86    pub fn count_bytes(&self) -> usize {
87        count_bytes_until_null(self.as_bytes()).unwrap_or(self.as_bytes().len())
88    }
89
90    /// Upgrades the underlying reference to an owned variant.
91    ///
92    /// This is a no-op if the variant already owns the value.
93    #[cfg(feature = "alloc")]
94    pub fn upgrade_to_owned(self) -> Self {
95        match self {
96            AuxVarString::Str(str) => Self::String(str.to_owned()),
97            AuxVarString::CStr(cstr) => Self::CString(cstr.to_owned()),
98            o => o,
99        }
100    }
101
102    /// Transforms the inner value into a owned Rust [`String`].
103    #[cfg(feature = "alloc")]
104    pub fn into_string(self) -> String {
105        match self {
106            AuxVarString::String(str) => str,
107            AuxVarString::CString(str) => str.to_str().unwrap().to_string(),
108            AuxVarString::Str(str) => str.to_string(),
109            AuxVarString::CStr(str) => str.to_str().unwrap().to_string(),
110        }
111    }
112}
113
114impl Display for AuxVarString<'_> {
115    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
116        match self {
117            #[cfg(feature = "alloc")]
118            AuxVarString::String(v) => Display::fmt(v, f),
119            #[cfg(feature = "alloc")]
120            AuxVarString::CString(v) => Debug::fmt(&v.to_str(), f),
121            AuxVarString::Str(v) => Display::fmt(v, f),
122            AuxVarString::CStr(v) => Debug::fmt(&v.to_str(), f),
123        }
124    }
125}
126
127impl<'a> From<&'a str> for AuxVarString<'a> {
128    fn from(str: &'a str) -> Self {
129        Self::Str(str)
130    }
131}
132
133impl<'a> From<&'a CStr> for AuxVarString<'a> {
134    fn from(str: &'a CStr) -> Self {
135        Self::CStr(str)
136    }
137}
138
139#[cfg(feature = "alloc")]
140impl<'a> From<String> for AuxVarString<'a> {
141    fn from(str: String) -> Self {
142        Self::String(str)
143    }
144}
145
146#[cfg(feature = "alloc")]
147impl<'a> From<CString> for AuxVarString<'a> {
148    fn from(str: CString) -> Self {
149        Self::CString(str)
150    }
151}
152
153/// High-level version of an auxiliary vector (`auxv`) entry. Also called
154/// _Auxiliary Variable_ or _AT Variable_.
155///
156/// The data/payload is either an immediate value embedded into the enum variant
157/// or a pointer into the `auxv` data area. The enum variant's payload does not
158/// necessarily correspond to the ABI.
159///
160/// ## More Info
161/// * <https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/auxvec.h>
162/// * <https://elixir.bootlin.com/linux/latest/source/fs/binfmt_elf.c#L259>
163/// * <https://man7.org/linux/man-pages/man3/getauxval.3.html>
164/// * <https://lwn.net/Articles/631631/>
165#[derive(Debug, Eq, PartialEq, Clone)]
166pub enum AuxVar<'a> {
167    /// Entry with payload for type [`AuxVarType::Null`].
168    Null,
169    /// Entry with payload for type [`AuxVarType::Ignore`].
170    Ignore,
171    /// Entry with payload for type [`AuxVarType::ExecFd`].
172    ExecFd(usize),
173    /// Entry with payload for type [`AuxVarType::Phdr`].
174    Phdr(*const u8),
175    /// Entry with payload for type [`AuxVarType::Phent`].
176    Phent(usize),
177    /// Entry with payload for type [`AuxVarType::Phnum`].
178    Phnum(usize),
179    /// Entry with payload for type [`AuxVarType::Pagesz`].
180    Pagesz(usize),
181    /// Entry with payload for type [`AuxVarType::Base`].
182    Base(*const u8),
183    /// Entry with payload for type [`AuxVarType::Flags`].
184    Flags(AuxVarFlags),
185    /// Entry with payload for type [`AuxVarType::Entry`].
186    Entry(*const u8),
187    /// Entry with payload for type [`AuxVarType::NotElf`].
188    NotElf(bool),
189    /// Entry with payload for type [`AuxVarType::Uid`].
190    Uid(usize),
191    /// Entry with payload for type [`AuxVarType::EUid`].
192    EUid(usize),
193    /// Entry with payload for type [`AuxVarType::Gid`].
194    Gid(usize),
195    /// Entry with payload for type [`AuxVarType::EGid`].
196    EGid(usize),
197    /// Entry with payload for type [`AuxVarType::Platform`].
198    Platform(AuxVarString<'a>),
199    /// Entry with payload for type [`AuxVarType::HwCap`].
200    HwCap(usize),
201    /// Entry with payload for type [`AuxVarType::Clktck`].
202    Clktck(usize),
203    /// Entry with payload for type [`AuxVarType::Secure`].
204    Secure(bool),
205    /// Entry with payload for type [`AuxVarType::BasePlatform`].
206    BasePlatform(AuxVarString<'a>),
207    /// Entry with payload for type [`AuxVarType::Random`].
208    Random(/* ABI: raw ptr to data area */ [u8; 16]),
209    /// Entry with payload for type [`AuxVarType::HwCap2`].
210    HwCap2(usize),
211    /// Entry with payload for type [`AuxVarType::ExecFn`].
212    ExecFn(AuxVarString<'a>),
213    /// Entry with payload for type [`AuxVarType::Sysinfo`].
214    Sysinfo(*const u8),
215    /// Entry with payload for type [`AuxVarType::SysinfoEhdr`].
216    SysinfoEhdr(*const u8),
217    /// Entry with payload for type [`AuxVarType::L1iCacheSize`].
218    L1iCacheSize(usize),
219    /// Entry with payload for type [`AuxVarType::L1iCacheGeometry`].
220    L1iCacheGeometry(usize),
221    /// Entry with payload for type [`AuxVarType::L1dCacheSize`].
222    L1dCacheSize(usize),
223    /// Entry with payload for type [`AuxVarType::L1dCacheGeometry`].
224    L1dCacheGeometry(usize),
225    /// Entry with payload for type [`AuxVarType::L2CacheSize`].
226    L2CacheSize(usize),
227    /// Entry with payload for type [`AuxVarType::L2CacheGeometry`].
228    L2CacheGeometry(usize),
229    /// Entry with payload for type [`AuxVarType::L3CacheSize`].
230    L3CacheSize(usize),
231    /// Entry with payload for type [`AuxVarType::L3CacheGeometry`].
232    L3CacheGeometry(usize),
233    /// Entry with payload for type [`AuxVarType::MinSigStkSz`].
234    MinSigStkSz(usize),
235}
236
237impl<'a> AuxVar<'a> {
238    /// Creates a [`CStr`] reference from a underlying buffer.
239    ///
240    /// The string starts at the beginning and ends at the first NUL byte.
241    ///
242    /// # Arguments
243    /// - `buffer`: Buffer containing the whole structure, also the data
244    ///   that some auxiliary variables point to.
245    ///
246    fn _from_raw_to_cstr(ptr: usize, buffer: &[u8]) -> &CStr {
247        let begin_index = ptr - buffer.as_ptr() as usize;
248
249        let bytes = &buffer[begin_index..];
250        CStr::from_bytes_until_nul(bytes).unwrap()
251    }
252
253    /// Creates the corresponding enum variant from a [`AuxVarRaw`].
254    ///
255    /// # Arguments
256    /// - `serialized`: Raw value read from memory
257    /// - `buffer`: Buffer containing the whole structure, also the data
258    ///   that some auxiliary variables point to.
259    ///
260    /// # Safety
261    /// This function creates undefined behavior or might even crash if the
262    /// value is an invalid pointer or a pointer pointing to invalid memory.
263    pub(crate) unsafe fn from_raw(serialized: &AuxVarRaw, buffer: &'a [u8]) -> Self {
264        let key = serialized.key().unwrap();
265
266        match key {
267            AuxVarType::Platform => {
268                Self::Platform(Self::_from_raw_to_cstr(serialized.value(), buffer).into())
269            }
270            AuxVarType::BasePlatform => {
271                Self::BasePlatform(Self::_from_raw_to_cstr(serialized.value(), buffer).into())
272            }
273            AuxVarType::ExecFn => {
274                Self::ExecFn(Self::_from_raw_to_cstr(serialized.value(), buffer).into())
275            }
276            AuxVarType::Random => {
277                let begin_index = serialized.value() - buffer.as_ptr() as usize;
278                let end_index = begin_index + 16 /* 16 bytes of randomness */;
279                assert!(end_index < buffer.len());
280
281                let mut bytes = [0; 16];
282                bytes.copy_from_slice(&buffer[begin_index..end_index]);
283
284                Self::Random(bytes)
285            }
286            AuxVarType::Null => Self::Null,
287            AuxVarType::Ignore => Self::Ignore,
288            AuxVarType::ExecFd => Self::ExecFd(serialized.value()),
289            AuxVarType::Phdr => Self::Phdr(serialized.value() as *const u8),
290            AuxVarType::Phent => Self::Phent(serialized.value()),
291            AuxVarType::Phnum => Self::Phnum(serialized.value()),
292            AuxVarType::Pagesz => Self::Pagesz(serialized.value()),
293            AuxVarType::Base => Self::Base(serialized.value() as *const u8),
294            AuxVarType::Flags => Self::Flags(AuxVarFlags::from_bits_truncate(serialized.value())),
295            AuxVarType::Entry => Self::Entry(serialized.value() as *const u8),
296            AuxVarType::NotElf => Self::NotElf(serialized.value() == 1),
297            AuxVarType::Uid => Self::Uid(serialized.value()),
298            AuxVarType::EUid => Self::EUid(serialized.value()),
299            AuxVarType::Gid => Self::Gid(serialized.value()),
300            AuxVarType::EGid => Self::EGid(serialized.value()),
301            // AuxVarType::Platform =>
302            AuxVarType::HwCap => Self::HwCap(serialized.value()),
303            AuxVarType::Clktck => Self::Clktck(serialized.value()),
304            AuxVarType::Secure => Self::Secure(serialized.value() == 1),
305            // AuxVarType::BasePlatform =>
306            // AuxVarType::Random =>
307            AuxVarType::HwCap2 => Self::HwCap2(serialized.value()),
308            //AuxVarType::ExecFn => Self::ExecFn(serialized.value()),
309            AuxVarType::Sysinfo => Self::Sysinfo(serialized.value() as *const u8),
310            AuxVarType::SysinfoEhdr => Self::SysinfoEhdr(serialized.value() as *const u8),
311            AuxVarType::L1iCacheSize => Self::L1iCacheSize(serialized.value()),
312            AuxVarType::L1iCacheGeometry => Self::L1iCacheGeometry(serialized.value()),
313            AuxVarType::L1dCacheSize => Self::L1dCacheSize(serialized.value()),
314            AuxVarType::L1dCacheGeometry => Self::L1dCacheGeometry(serialized.value()),
315            AuxVarType::L2CacheSize => Self::L2CacheSize(serialized.value()),
316            AuxVarType::L2CacheGeometry => Self::L2CacheGeometry(serialized.value()),
317            AuxVarType::L3CacheSize => Self::L3CacheSize(serialized.value()),
318            AuxVarType::L3CacheGeometry => Self::L3CacheGeometry(serialized.value()),
319            AuxVarType::MinSigStkSz => Self::MinSigStkSz(serialized.value()),
320        }
321    }
322
323    /// Returns the [`AuxVarType`] this aux var corresponds to.
324    #[must_use]
325    pub const fn key(&self) -> AuxVarType {
326        match self {
327            AuxVar::Null => AuxVarType::Null,
328            AuxVar::Ignore => AuxVarType::Ignore,
329            AuxVar::ExecFd(_) => AuxVarType::ExecFd,
330            AuxVar::Phdr(_) => AuxVarType::Phdr,
331            AuxVar::Phent(_) => AuxVarType::Phent,
332            AuxVar::Phnum(_) => AuxVarType::Phnum,
333            AuxVar::Pagesz(_) => AuxVarType::Pagesz,
334            AuxVar::Base(_) => AuxVarType::Base,
335            AuxVar::Flags(_) => AuxVarType::Flags,
336            AuxVar::Entry(_) => AuxVarType::Entry,
337            AuxVar::NotElf(_) => AuxVarType::NotElf,
338            AuxVar::Uid(_) => AuxVarType::Uid,
339            AuxVar::EUid(_) => AuxVarType::EUid,
340            AuxVar::Gid(_) => AuxVarType::Gid,
341            AuxVar::EGid(_) => AuxVarType::EGid,
342            AuxVar::Platform(_) => AuxVarType::Platform,
343            AuxVar::HwCap(_) => AuxVarType::HwCap,
344            AuxVar::Clktck(_) => AuxVarType::Clktck,
345            AuxVar::Secure(_) => AuxVarType::Secure,
346            AuxVar::BasePlatform(_) => AuxVarType::BasePlatform,
347            AuxVar::Random(_) => AuxVarType::Random,
348            AuxVar::HwCap2(_) => AuxVarType::HwCap2,
349            AuxVar::ExecFn(_) => AuxVarType::ExecFn,
350            AuxVar::Sysinfo(_) => AuxVarType::Sysinfo,
351            AuxVar::SysinfoEhdr(_) => AuxVarType::SysinfoEhdr,
352            AuxVar::L1iCacheSize(_) => AuxVarType::L1iCacheSize,
353            AuxVar::L1iCacheGeometry(_) => AuxVarType::L1iCacheGeometry,
354            AuxVar::L1dCacheSize(_) => AuxVarType::L1dCacheSize,
355            AuxVar::L1dCacheGeometry(_) => AuxVarType::L1dCacheGeometry,
356            AuxVar::L2CacheSize(_) => AuxVarType::L2CacheSize,
357            AuxVar::L2CacheGeometry(_) => AuxVarType::L2CacheGeometry,
358            AuxVar::L3CacheSize(_) => AuxVarType::L3CacheSize,
359            AuxVar::L3CacheGeometry(_) => AuxVarType::L3CacheGeometry,
360            AuxVar::MinSigStkSz(_) => AuxVarType::MinSigStkSz,
361        }
362    }
363
364    /// Transforms any inner value into it's corresponding serialized usize
365    /// value.
366    ///
367    /// This only works for variants that do not reference data exceeding the
368    /// size of an `usize`.
369    #[must_use]
370    pub fn value_raw(&self) -> usize {
371        match self {
372            AuxVar::Platform(_)
373            | AuxVar::BasePlatform(_)
374            | AuxVar::Random(_)
375            | AuxVar::ExecFn(_) => todo!("return Result instead"),
376            AuxVar::Null => 0,
377            AuxVar::Ignore => 0,
378            AuxVar::ExecFd(val) => *val,
379            AuxVar::Phdr(val) => *val as _,
380            AuxVar::Phent(val) => *val,
381            AuxVar::Phnum(val) => *val,
382            AuxVar::Pagesz(val) => *val,
383            AuxVar::Base(val) => *val as _,
384            AuxVar::Flags(val) => val.bits(),
385            AuxVar::Entry(val) => *val as _,
386            AuxVar::NotElf(val) => {
387                if *val {
388                    1
389                } else {
390                    0
391                }
392            }
393            AuxVar::Uid(val) => *val,
394            AuxVar::EUid(val) => *val,
395            AuxVar::Gid(val) => *val,
396            AuxVar::EGid(val) => *val,
397            // AuxVar::Platform(val) => val.as_ptr() as _,
398            AuxVar::HwCap(val) => *val,
399            AuxVar::Clktck(val) => *val,
400            AuxVar::Secure(val) => {
401                if *val {
402                    1
403                } else {
404                    0
405                }
406            }
407            // AuxVar::BasePlatform(val) => val.as_ptr() as _,
408            // AuxVar::Random(val) => val.as_ptr() as _,
409            AuxVar::HwCap2(val) => *val,
410            // AuxVar::ExecFn(val) => val.as_ptr() as _,
411            AuxVar::Sysinfo(val) => *val as _,
412            AuxVar::SysinfoEhdr(val) => *val as _,
413            AuxVar::L1iCacheSize(val) => *val,
414            AuxVar::L1iCacheGeometry(val) => *val,
415            AuxVar::L1dCacheSize(val) => *val,
416            AuxVar::L1dCacheGeometry(val) => *val,
417            AuxVar::L2CacheSize(val) => *val,
418            AuxVar::L2CacheGeometry(val) => *val,
419            AuxVar::L3CacheSize(val) => *val,
420            AuxVar::L3CacheGeometry(val) => *val,
421            AuxVar::MinSigStkSz(val) => *val,
422        }
423    }
424
425    /// Returns a value if the corresponding entry corresponds to a basic
426    /// value/integer, and not a pointer, flags, or a boolean.
427    #[must_use]
428    pub const fn value_integer(&self) -> Option<usize> {
429        match self {
430            AuxVar::ExecFd(val) => Some(*val),
431            AuxVar::Phent(val) => Some(*val),
432            AuxVar::Phnum(val) => Some(*val),
433            AuxVar::Pagesz(val) => Some(*val),
434            AuxVar::Uid(val) => Some(*val),
435            AuxVar::EUid(val) => Some(*val),
436            AuxVar::Gid(val) => Some(*val),
437            AuxVar::EGid(val) => Some(*val),
438            AuxVar::HwCap(val) => Some(*val),
439            AuxVar::Clktck(val) => Some(*val),
440            AuxVar::HwCap2(val) => Some(*val),
441            AuxVar::L1iCacheSize(val) => Some(*val),
442            AuxVar::L1iCacheGeometry(val) => Some(*val),
443            AuxVar::L1dCacheSize(val) => Some(*val),
444            AuxVar::L1dCacheGeometry(val) => Some(*val),
445            AuxVar::L2CacheSize(val) => Some(*val),
446            AuxVar::L2CacheGeometry(val) => Some(*val),
447            AuxVar::L3CacheSize(val) => Some(*val),
448            AuxVar::L3CacheGeometry(val) => Some(*val),
449            AuxVar::MinSigStkSz(val) => Some(*val),
450            _ => None,
451        }
452    }
453
454    /// Returns the [`AuxVarFlags`] if the corresponding entry is of type
455    /// [`AuxVarType::Flags`].
456    #[must_use]
457    pub const fn value_flags(&self) -> Option<AuxVarFlags> {
458        match self {
459            AuxVar::Flags(flags) => Some(*flags),
460            _ => None,
461        }
462    }
463
464    /// Returns a value if the corresponding entry corresponds to a boolean,
465    /// and not a pointer, flags, or a basic value/integer.
466    #[must_use]
467    pub const fn value_boolean(&self) -> Option<bool> {
468        match self {
469            AuxVar::NotElf(val) => Some(*val),
470            AuxVar::Secure(val) => Some(*val),
471            _ => None,
472        }
473    }
474
475    /// Returns a value if the corresponding entry corresponds to a pointer,
476    /// and not a boolean, flags, or a basic value/integer.
477    ///
478    /// This only affects entries that point to memory outside the stack layout,
479    /// i.e., the aux vector data area.
480    #[must_use]
481    pub const fn value_ptr(&self) -> Option<*const u8> {
482        match self {
483            AuxVar::Phdr(val) => Some(*val),
484            AuxVar::Base(val) => Some(*val),
485            AuxVar::Entry(val) => Some(*val),
486            AuxVar::Sysinfo(val) => Some(*val),
487            AuxVar::SysinfoEhdr(val) => Some(*val),
488            _ => None,
489        }
490    }
491
492    /// Returns a value, if the corresponding auxiliary vector entry references data in the
493    /// auxiliary vector data area of the data structure.
494    /// This returns only something for [`AuxVarType::Random`].
495    ///
496    /// This function is safe, because the creation during parsing already guarantee memory
497    /// safety (the addresses are accessed).
498    #[must_use]
499    pub fn value_payload_bytes(&'a self) -> Option<&'a [u8]> {
500        match self {
501            AuxVar::Random(bytes) => Some(&bytes[..]),
502            _ => None,
503        }
504    }
505
506    /// Returns a value, if the corresponding auxiliary vector entry references data in the
507    /// auxiliary vector data area of the data structure.
508    /// This returns only something for [`AuxVarType::Random`].
509    ///
510    /// This function is safe, because the creation during parsing already guarantee memory
511    /// safety (the addresses are accessed).
512    #[must_use]
513    pub const fn value_payload_str(&'a self) -> Option<&'a AuxVarString<'a>> {
514        match self {
515            AuxVar::Platform(val) => Some(val),
516            AuxVar::BasePlatform(val) => Some(val),
517            AuxVar::ExecFn(val) => Some(val),
518            _ => None,
519        }
520    }
521}
522
523impl<'a> PartialOrd for AuxVar<'a> {
524    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
525        Some(self.cmp(other))
526    }
527}
528
529impl<'a> Ord for AuxVar<'a> {
530    fn cmp(&self, other: &Self) -> Ordering {
531        self.key().cmp(&other.key())
532    }
533}
534
535#[cfg(test)]
536mod tests {
537    use super::*;
538    use std::collections::BTreeSet;
539
540    /// Tests that the ATNull entry always comes last in an ordered collection. This enables
541    /// us to easily write all AT-VARs at once but keep the terminating null entry at the end.
542    #[test]
543    fn test_aux_var_order() {
544        let mut set = BTreeSet::new();
545        set.insert(AuxVar::ExecFn(c"./executable".into()));
546        set.insert(AuxVar::Platform(c"x86_64".into()));
547        set.insert(AuxVar::Null);
548        set.insert(AuxVar::Clktck(0x1337));
549        set.insert(AuxVar::ExecFn(c"./executable".into()));
550        assert_eq!(set.iter().last().unwrap().key(), AuxVarType::Null);
551    }
552}