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")]
37pub(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))]
52pub(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")]
58pub(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.
71macro_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)]
152macro_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.
230macro_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")]
310macro_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