1 //! Architecture-specific syscall code.
2 //!
3 //! This module also has a `choose` submodule which chooses a scheme and is
4 //! what most of the `rustix` syscalls use.
5 //!
6 //! Compilers should really have intrinsics for making system calls. They're
7 //! much like regular calls, with custom calling conventions, and calling
8 //! conventions are otherwise the compiler's job. But for now, use inline asm.
9 //!
10 //! The calling conventions for Linux syscalls are [documented here].
11 //!
12 //! [documented here]: https://man7.org/linux/man-pages/man2/syscall.2.html
13 //!
14 //! # Safety
15 //!
16 //! This contains the inline `asm` statements performing the syscall
17 //! instructions.
18 
19 #![allow(unsafe_code)]
20 #![cfg_attr(not(feature = "all-apis"), allow(unused_imports))]
21 // We'll use as many arguments as syscalls need.
22 #![allow(clippy::too_many_arguments)]
23 
24 // These functions always use the machine's syscall instruction, even when it
25 // isn't the fastest option available.
26 #[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")]
27 #[cfg_attr(all(target_arch = "arm", not(thumb_mode)), path = "arm.rs")]
28 #[cfg_attr(all(target_arch = "arm", thumb_mode), path = "thumb.rs")]
29 #[cfg_attr(target_arch = "mips", path = "mips.rs")]
30 #[cfg_attr(target_arch = "mips32r6", path = "mips32r6.rs")]
31 #[cfg_attr(target_arch = "mips64", path = "mips64.rs")]
32 #[cfg_attr(target_arch = "mips64r6", path = "mips64r6.rs")]
33 #[cfg_attr(target_arch = "powerpc64", path = "powerpc64.rs")]
34 #[cfg_attr(target_arch = "riscv64", path = "riscv64.rs")]
35 #[cfg_attr(target_arch = "x86", path = "x86.rs")]
36 #[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
37 pub(in crate::backend) mod asm;
38 
39 // On most architectures, the architecture syscall instruction is fast, so use
40 // it directly.
41 #[cfg(any(
42     target_arch = "arm",
43     target_arch = "aarch64",
44     target_arch = "mips",
45     target_arch = "mips32r6",
46     target_arch = "mips64",
47     target_arch = "mips64r6",
48     target_arch = "powerpc64",
49     target_arch = "riscv64",
50     target_arch = "x86_64",
51 ))]
52 pub(in crate::backend) use self::asm as choose;
53 
54 // On 32-bit x86, use vDSO wrappers for all syscalls. We could use the
55 // architecture syscall instruction (`int 0x80`), but the vDSO kernel_vsyscall
56 // mechanism is much faster.
57 #[cfg(target_arch = "x86")]
58 pub(in crate::backend) use super::vdso_wrappers::x86_via_vdso as choose;
59 
60 // This would be the code for always using `int 0x80` on 32-bit x86.
61 //#[cfg(target_arch = "x86")]
62 //pub(in crate::backend) use self::asm as choose;
63 
64 // Macros for invoking system calls.
65 //
66 // These factor out:
67 //  - Calling `nr` on the syscall number to convert it into `SyscallNumber`.
68 //  - Calling `.into()` on each of the arguments to convert them into `ArgReg`.
69 //  - Qualifying the `syscall*` and `__NR_*` identifiers.
70 //  - Counting the number of arguments.
71 macro_rules! syscall {
72     ($nr:ident) => {
73         $crate::backend::arch::choose::syscall0($crate::backend::reg::nr(
74             linux_raw_sys::general::$nr,
75         ))
76     };
77 
78     ($nr:ident, $a0:expr) => {
79         $crate::backend::arch::choose::syscall1(
80             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
81             $a0.into(),
82         )
83     };
84 
85     ($nr:ident, $a0:expr, $a1:expr) => {
86         $crate::backend::arch::choose::syscall2(
87             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
88             $a0.into(),
89             $a1.into(),
90         )
91     };
92 
93     ($nr:ident, $a0:expr, $a1:expr, $a2:expr) => {
94         $crate::backend::arch::choose::syscall3(
95             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
96             $a0.into(),
97             $a1.into(),
98             $a2.into(),
99         )
100     };
101 
102     ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {
103         $crate::backend::arch::choose::syscall4(
104             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
105             $a0.into(),
106             $a1.into(),
107             $a2.into(),
108             $a3.into(),
109         )
110     };
111 
112     ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
113         $crate::backend::arch::choose::syscall5(
114             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
115             $a0.into(),
116             $a1.into(),
117             $a2.into(),
118             $a3.into(),
119             $a4.into(),
120         )
121     };
122 
123     ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => {
124         $crate::backend::arch::choose::syscall6(
125             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
126             $a0.into(),
127             $a1.into(),
128             $a2.into(),
129             $a3.into(),
130             $a4.into(),
131             $a5.into(),
132         )
133     };
134 
135     ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => {
136         $crate::backend::arch::choose::syscall7(
137             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
138             $a0.into(),
139             $a1.into(),
140             $a2.into(),
141             $a3.into(),
142             $a4.into(),
143             $a5.into(),
144             $a6.into(),
145         )
146     };
147 }
148 
149 // Macro to invoke a syscall that always uses direct assembly, rather than the
150 // vDSO. Useful when still finding the vDSO.
151 #[allow(unused_macros)]
152 macro_rules! syscall_always_asm {
153     ($nr:ident) => {
154         $crate::backend::arch::asm::syscall0($crate::backend::reg::nr(linux_raw_sys::general::$nr))
155     };
156 
157     ($nr:ident, $a0:expr) => {
158         $crate::backend::arch::asm::syscall1(
159             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
160             $a0.into(),
161         )
162     };
163 
164     ($nr:ident, $a0:expr, $a1:expr) => {
165         $crate::backend::arch::asm::syscall2(
166             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
167             $a0.into(),
168             $a1.into(),
169         )
170     };
171 
172     ($nr:ident, $a0:expr, $a1:expr, $a2:expr) => {
173         $crate::backend::arch::asm::syscall3(
174             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
175             $a0.into(),
176             $a1.into(),
177             $a2.into(),
178         )
179     };
180 
181     ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {
182         $crate::backend::arch::asm::syscall4(
183             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
184             $a0.into(),
185             $a1.into(),
186             $a2.into(),
187             $a3.into(),
188         )
189     };
190 
191     ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
192         $crate::backend::arch::asm::syscall5(
193             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
194             $a0.into(),
195             $a1.into(),
196             $a2.into(),
197             $a3.into(),
198             $a4.into(),
199         )
200     };
201 
202     ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => {
203         $crate::backend::arch::asm::syscall6(
204             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
205             $a0.into(),
206             $a1.into(),
207             $a2.into(),
208             $a3.into(),
209             $a4.into(),
210             $a5.into(),
211         )
212     };
213 
214     ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => {
215         $crate::backend::arch::asm::syscall7(
216             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
217             $a0.into(),
218             $a1.into(),
219             $a2.into(),
220             $a3.into(),
221             $a4.into(),
222             $a5.into(),
223             $a6.into(),
224         )
225     };
226 }
227 
228 /// Like `syscall`, but adds the `readonly` attribute to the inline asm, which
229 /// indicates that the syscall does not mutate any memory.
230 macro_rules! syscall_readonly {
231     ($nr:ident) => {
232         $crate::backend::arch::choose::syscall0_readonly($crate::backend::reg::nr(
233             linux_raw_sys::general::$nr,
234         ))
235     };
236 
237     ($nr:ident, $a0:expr) => {
238         $crate::backend::arch::choose::syscall1_readonly(
239             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
240             $a0.into(),
241         )
242     };
243 
244     ($nr:ident, $a0:expr, $a1:expr) => {
245         $crate::backend::arch::choose::syscall2_readonly(
246             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
247             $a0.into(),
248             $a1.into(),
249         )
250     };
251 
252     ($nr:ident, $a0:expr, $a1:expr, $a2:expr) => {
253         $crate::backend::arch::choose::syscall3_readonly(
254             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
255             $a0.into(),
256             $a1.into(),
257             $a2.into(),
258         )
259     };
260 
261     ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {
262         $crate::backend::arch::choose::syscall4_readonly(
263             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
264             $a0.into(),
265             $a1.into(),
266             $a2.into(),
267             $a3.into(),
268         )
269     };
270 
271     ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
272         $crate::backend::arch::choose::syscall5_readonly(
273             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
274             $a0.into(),
275             $a1.into(),
276             $a2.into(),
277             $a3.into(),
278             $a4.into(),
279         )
280     };
281 
282     ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => {
283         $crate::backend::arch::choose::syscall6_readonly(
284             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
285             $a0.into(),
286             $a1.into(),
287             $a2.into(),
288             $a3.into(),
289             $a4.into(),
290             $a5.into(),
291         )
292     };
293 
294     ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => {
295         $crate::backend::arch::choose::syscall7_readonly(
296             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
297             $a0.into(),
298             $a1.into(),
299             $a2.into(),
300             $a3.into(),
301             $a4.into(),
302             $a5.into(),
303             $a6.into(),
304         )
305     };
306 }
307 
308 /// Like `syscall`, but indicates that the syscall does not return.
309 #[cfg(feature = "runtime")]
310 macro_rules! syscall_noreturn {
311     ($nr:ident, $a0:expr) => {
312         $crate::backend::arch::choose::syscall1_noreturn(
313             $crate::backend::reg::nr(linux_raw_sys::general::$nr),
314             $a0.into(),
315         )
316     };
317 }
318