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