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 | |