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