va_list/
impl-x86_64-elf.rs

1// x86_64 ELF - Aka the Itanium ABI
2//
3use ::core::{mem, ptr};
4use ::core::ffi::c_void;
5use super::VaPrimitive;	// Note: Uses `super` for testing purposes
6
7#[repr(transparent)]
8pub struct VaList<'a>(&'a mut VaListInner);
9
10#[repr(C)]
11#[derive(Debug)]
12#[doc(hidden)]
13pub struct VaListInner {
14    gp_offset: u32,
15    fp_offset: u32,
16    overflow_arg_area: *const c_void,
17    reg_save_area: *const c_void,
18}
19
20impl<'a> VaList<'a> {
21    fn inner(&mut self) -> &mut VaListInner {
22        &mut *self.0
23    }
24}
25
26#[doc(hidden)]
27impl VaListInner {
28    fn check_space(&self, num_gp: u32, num_fp: u32) -> bool {
29        !(self.gp_offset > 48 - num_gp * 8 || self.fp_offset > 304 - num_fp * 16)
30    }
31
32    unsafe fn get_gp<T>(&mut self) -> T {
33        let n_gp = (mem::size_of::<T>() + 7) / 8;
34        assert!(self.check_space(n_gp as u32, 0));
35        let rv = ptr::read((self.reg_save_area as usize + self.gp_offset as usize) as *const _);
36        self.gp_offset += (8 * n_gp) as u32;
37        rv
38    }
39
40    unsafe fn get_overflow<T>(&mut self) -> T {
41        let align = mem::align_of::<T>();
42        // 7. Align overflow_reg_area upwards to a 16-byte boundary if alignment
43        //    needed by T exceeds 8 bytes
44        let addr = self.overflow_arg_area as usize;
45        if align > 8 {
46            if addr % 16 != 0 {
47                self.overflow_arg_area = ((addr + 15) & !(16 - 1)) as *const _;
48            }
49        } else {
50            if addr % 8 != 0 {
51                self.overflow_arg_area = ((addr + 7) & !(8 - 1)) as *const _;
52            }
53        }
54        // 8. Fetch from overflow areay
55        let rv = ptr::read(self.overflow_arg_area as *const _);
56        self.overflow_arg_area =
57            ((self.overflow_arg_area as usize) + mem::size_of::<T>()) as *const _;
58        rv
59    }
60}
61
62
63impl<T: 'static> VaPrimitive for *const T {
64    unsafe fn get(list: &mut VaList) -> Self {
65        <usize>::get(list) as *const T
66    }
67}
68
69macro_rules! impl_va_prim {
70    ($u: ty, $s: ty) => {
71        impl VaPrimitive for $u {
72            unsafe fn get(list: &mut VaList) -> Self {
73                let inner = list.inner();
74                // See the ELF AMD64 ABI document for a description of how this should act
75                if !inner.check_space(1, 0) {
76                    inner.get_overflow()
77                } else {
78                    inner.get_gp()
79                }
80            }
81        }
82        impl VaPrimitive for $s {
83            unsafe fn get(list: &mut VaList) -> Self {
84                mem::transmute(<$u>::get(list))
85            }
86        }
87    };
88}
89
90impl_va_prim!{ usize, isize }
91impl_va_prim!{ u64, i64 }
92impl_va_prim!{ u32, i32 }
93//impl_va_prim!{ u16, i16 }
94//impl_va_prim!{ u8, i8 }
95