panda/
abi.rs

1#![allow(unused_macros, unused_imports)]
2use crate::prelude::*;
3use crate::regs::Reg::{self, *};
4
5use crate::mem::{virtual_memory_read, virtual_memory_write};
6use crate::regs;
7
8use std::convert::TryInto;
9use std::sync::atomic::{AtomicBool, Ordering};
10
11static IS_SYSENTER: AtomicBool = AtomicBool::new(false);
12
13#[allow(dead_code)]
14pub(crate) fn set_is_sysenter(is_sysenter: bool) {
15    IS_SYSENTER.store(is_sysenter, Ordering::SeqCst);
16}
17
18fn is_sysenter() -> bool {
19    IS_SYSENTER.load(Ordering::SeqCst)
20}
21
22struct Stack;
23
24pub mod syscall {
25    use super::*;
26
27    macro_rules! reg_or_stack {
28        (Stack) => {
29            StorageLocation::StackOffset(0)
30        };
31        ($reg:ident) => {
32            StorageLocation::Reg($reg)
33        };
34    }
35
36    macro_rules! syscall_regs {
37        {
38            const { $syscall_args:ident, $syscall_ret:ident, $syscall_num_reg:ident, $syscall_args_len:ident };
39            $(
40                #[cfg($(arch = $arch:literal),+)] {
41                    args$(: $size:literal)? = [$( $args:ident $(@ $offset:literal)? ),*];
42                    return = $ret:ident;
43                    syscall_number = $sys_num:ident;
44                }
45            )*
46        } => {
47            $(
48                /// Number of syscall arguments
49                #[cfg(any($(feature = $arch),*))]
50                pub const $syscall_args_len: usize = 6 $(+ ($size - 6))?;
51
52                /// Argument registers for performing syscalls
53                #[cfg(any($(feature = $arch),*))]
54                pub const $syscall_args: [StorageLocation; $syscall_args_len] = [$(
55                    reg_or_stack!($args) $(.with_offset($offset))?
56                ),*];
57
58                /// Register where syscall return value is stored on syscall exit
59                #[cfg(any($(feature = $arch),*))]
60                pub const $syscall_ret: Reg = $ret;
61
62                /// Register where the syscall number is stored on syscall enter
63                #[cfg(any($(feature = $arch),*))]
64                pub const $syscall_num_reg: Reg = $sys_num;
65            )*
66        }
67    }
68
69    syscall_regs! {
70        const {SYSCALL_ARGS, SYSCALL_RET, SYSCALL_NUM_REG, SYSCALL_ARGS_LEN};
71
72        #[cfg(arch = "x86_64")] {
73            args = [RDI, RSI, RDX, R10, R8, R9];
74            return = RAX;
75            syscall_number = RAX;
76        }
77
78        #[cfg(arch = "i386")] {
79            args = [EBX, ECX @ 0x8, EDX @ 0x4, ESI, EDI, EBP @ 0x0];
80            return = EAX;
81            syscall_number = EAX;
82        }
83
84        // we primarily support EABI systems, but this might work for OABI too
85        #[cfg(arch = "arm")] {
86            args = [R0, R1, R2, R3, R4, R5];
87            return = R0;
88            syscall_number = R7;
89        }
90
91        #[cfg(arch = "aarch64")] {
92            args = [X0, X1, X2, X3, X4, X5];
93            return = X0;
94            syscall_number = X8;
95        }
96
97        // we "only" "support" the n32 ABI (syscalls2 supports configuring o32 ABI at
98        // compile-time, other things probably(?) don't)
99        #[cfg(arch = "mips", arch = "mipsel")] {
100            // n32 ABI
101            // args = [A0, A1, A2, A3, T0, T1];
102
103            // o32 ABI
104            args: 8 = [A0, A1, A2, A3, Stack@0x10, Stack@0x14, Stack@0x18, Stack@0x1c];
105            return = V0;
106            syscall_number = V0;
107        }
108
109        #[cfg(arch = "mips64", arch = "mips64el")] {
110            // n32 ABI
111            args = [A0, A1, A2, A3, T0, T1];
112            return = V0;
113            syscall_number = V0;
114        }
115    }
116}
117
118#[derive(Clone, Copy, PartialEq, Debug)]
119pub enum StorageLocation {
120    Reg(Reg),
121    StackOffset(target_ulong),
122    StackReg(Reg, target_ulong),
123}
124
125impl From<Reg> for StorageLocation {
126    fn from(reg: Reg) -> Self {
127        Self::Reg(reg)
128    }
129}
130
131impl From<(Reg, target_ulong)> for StorageLocation {
132    fn from((reg, offset): (Reg, target_ulong)) -> Self {
133        Self::StackReg(reg, offset)
134    }
135}
136
137impl From<Stack> for StorageLocation {
138    fn from(_: Stack) -> Self {
139        Self::StackOffset(0)
140    }
141}
142
143const REG_SIZE: usize = std::mem::size_of::<target_ulong>();
144
145fn is_little_endian() -> bool {
146    crate::ARCH_ENDIAN == crate::enums::Endian::Little
147}
148
149impl StorageLocation {
150    #[allow(dead_code)]
151    pub(crate) const fn with_offset(self, offset: target_ulong) -> Self {
152        if let Self::Reg(reg) | Self::StackReg(reg, _) = self {
153            Self::StackReg(reg, offset)
154        } else {
155            Self::StackOffset(offset)
156        }
157    }
158
159    fn is_stack(&self) -> bool {
160        matches!(self, Self::StackOffset(_)) || is_sysenter()
161    }
162
163    pub fn read(self, cpu: &mut CPUState) -> target_ulong {
164        match self {
165            Self::StackReg(_, offset) | Self::StackOffset(offset) if self.is_stack() => {
166                let sp = regs::get_reg(cpu, regs::reg_sp());
167
168                let bytes = virtual_memory_read(cpu, sp + offset, REG_SIZE)
169                    .expect("Failed to read syscall argument from stack")
170                    .try_into()
171                    .unwrap();
172
173                if is_little_endian() {
174                    target_ulong::from_le_bytes(bytes)
175                } else {
176                    target_ulong::from_be_bytes(bytes)
177                }
178            }
179            Self::StackOffset(_offset) => unreachable!(),
180            Self::Reg(reg) | Self::StackReg(reg, _) => regs::get_reg(cpu, reg),
181        }
182    }
183
184    pub fn write(self, cpu: &mut CPUState, val: target_ulong) {
185        match self {
186            Self::StackReg(reg, offset) if is_sysenter() => {
187                let sp = regs::get_reg(cpu, regs::reg_sp());
188
189                virtual_memory_write(cpu, sp + offset, &val.to_le_bytes());
190
191                #[cfg(feature = "i386")]
192                if reg == Reg::EBP {
193                    return;
194                }
195
196                regs::set_reg(cpu, reg, val);
197            }
198            Self::StackOffset(offset) => {
199                let sp = regs::get_reg(cpu, regs::reg_sp());
200
201                let bytes = if is_little_endian() {
202                    val.to_le_bytes()
203                } else {
204                    val.to_be_bytes()
205                };
206
207                virtual_memory_write(cpu, sp + offset, &bytes);
208            }
209            Self::Reg(reg) | Self::StackReg(reg, _) => regs::set_reg(cpu, reg, val),
210        }
211    }
212}