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")]
39pub(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))]
56pub(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")]
62pub(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.
75macro_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)]
156macro_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.
234macro_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")]
314macro_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