1 //! 32-bit x86 Linux system calls.
2 //!
3 //! There are two forms; `indirect_*` which take a callee, which allow calling
4 //! through the vDSO when possible, and plain forms, which use the `int 0x80`
5 //! instruction.
6 //!
7 //! Most `rustix` syscalls use the vsyscall mechanism rather than going using
8 //! `int 0x80` sequences, as vsyscall is much faster.
9 //!
10 //! Syscalls made with `int 0x80` preserve the flags register, while syscalls
11 //! made using vsyscall do not.
12
13 #![allow(dead_code)]
14
15 use crate::backend::reg::{
16 ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0,
17 };
18 use crate::backend::vdso_wrappers::SyscallType;
19 use core::arch::asm;
20
21 #[inline]
indirect_syscall0( callee: SyscallType, nr: SyscallNumber<'_>, ) -> RetReg<R0>22 pub(in crate::backend) unsafe fn indirect_syscall0(
23 callee: SyscallType,
24 nr: SyscallNumber<'_>,
25 ) -> RetReg<R0> {
26 let r0;
27 asm!(
28 "call {callee}",
29 callee = in(reg) callee,
30 inlateout("eax") nr.to_asm() => r0,
31 );
32 FromAsm::from_asm(r0)
33 }
34
35 #[inline]
indirect_syscall1( callee: SyscallType, nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, ) -> RetReg<R0>36 pub(in crate::backend) unsafe fn indirect_syscall1(
37 callee: SyscallType,
38 nr: SyscallNumber<'_>,
39 a0: ArgReg<'_, A0>,
40 ) -> RetReg<R0> {
41 let r0;
42 asm!(
43 "call {callee}",
44 callee = in(reg) callee,
45 inlateout("eax") nr.to_asm() => r0,
46 in("ebx") a0.to_asm(),
47 );
48 FromAsm::from_asm(r0)
49 }
50
51 #[inline]
indirect_syscall1_noreturn( callee: SyscallType, nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, ) -> !52 pub(in crate::backend) unsafe fn indirect_syscall1_noreturn(
53 callee: SyscallType,
54 nr: SyscallNumber<'_>,
55 a0: ArgReg<'_, A0>,
56 ) -> ! {
57 asm!(
58 "call {callee}",
59 callee = in(reg) callee,
60 in("eax") nr.to_asm(),
61 in("ebx") a0.to_asm(),
62 options(noreturn)
63 )
64 }
65
66 #[inline]
indirect_syscall2( callee: SyscallType, nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, ) -> RetReg<R0>67 pub(in crate::backend) unsafe fn indirect_syscall2(
68 callee: SyscallType,
69 nr: SyscallNumber<'_>,
70 a0: ArgReg<'_, A0>,
71 a1: ArgReg<'_, A1>,
72 ) -> RetReg<R0> {
73 let r0;
74 asm!(
75 "call {callee}",
76 callee = in(reg) callee,
77 inlateout("eax") nr.to_asm() => r0,
78 in("ebx") a0.to_asm(),
79 in("ecx") a1.to_asm(),
80 );
81 FromAsm::from_asm(r0)
82 }
83
84 #[inline]
indirect_syscall3( callee: SyscallType, nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, ) -> RetReg<R0>85 pub(in crate::backend) unsafe fn indirect_syscall3(
86 callee: SyscallType,
87 nr: SyscallNumber<'_>,
88 a0: ArgReg<'_, A0>,
89 a1: ArgReg<'_, A1>,
90 a2: ArgReg<'_, A2>,
91 ) -> RetReg<R0> {
92 let r0;
93 asm!(
94 "call {callee}",
95 callee = in(reg) callee,
96 inlateout("eax") nr.to_asm() => r0,
97 in("ebx") a0.to_asm(),
98 in("ecx") a1.to_asm(),
99 in("edx") a2.to_asm(),
100 );
101 FromAsm::from_asm(r0)
102 }
103
104 #[inline]
indirect_syscall4( callee: SyscallType, nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, a3: ArgReg<'_, A3>, ) -> RetReg<R0>105 pub(in crate::backend) unsafe fn indirect_syscall4(
106 callee: SyscallType,
107 nr: SyscallNumber<'_>,
108 a0: ArgReg<'_, A0>,
109 a1: ArgReg<'_, A1>,
110 a2: ArgReg<'_, A2>,
111 a3: ArgReg<'_, A3>,
112 ) -> RetReg<R0> {
113 let r0;
114 // a3 should go in esi, but `asm!` won't let us use it as an operand.
115 // temporarily swap it into place, and then swap it back afterward.
116 //
117 // We hard-code the callee operand to use edi instead of `in(reg)` because
118 // even though we can't name esi as an operand, the compiler can use esi to
119 // satisfy `in(reg)`.
120 asm!(
121 "xchg esi, {a3}",
122 "call edi",
123 "xchg esi, {a3}",
124 a3 = in(reg) a3.to_asm(),
125 in("edi") callee,
126 inlateout("eax") nr.to_asm() => r0,
127 in("ebx") a0.to_asm(),
128 in("ecx") a1.to_asm(),
129 in("edx") a2.to_asm(),
130 );
131 FromAsm::from_asm(r0)
132 }
133
134 #[inline]
indirect_syscall5( callee: SyscallType, nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, a3: ArgReg<'_, A3>, a4: ArgReg<'_, A4>, ) -> RetReg<R0>135 pub(in crate::backend) unsafe fn indirect_syscall5(
136 callee: SyscallType,
137 nr: SyscallNumber<'_>,
138 a0: ArgReg<'_, A0>,
139 a1: ArgReg<'_, A1>,
140 a2: ArgReg<'_, A2>,
141 a3: ArgReg<'_, A3>,
142 a4: ArgReg<'_, A4>,
143 ) -> RetReg<R0> {
144 let r0;
145 // Oof. a3 should go in esi, and `asm!` won't let us use that register as
146 // an operand. And we can't request stack slots. And there are no other
147 // registers free. Use eax as a temporary pointer to a slice, since it
148 // gets clobbered as the return value anyway.
149 asm!(
150 "push esi",
151 "push DWORD PTR [eax + 0]",
152 "mov esi, DWORD PTR [eax + 4]",
153 "mov eax, DWORD PTR [eax + 8]",
154 "call DWORD PTR [esp]",
155 "pop esi",
156 "pop esi",
157 inout("eax") &[callee as _, a3.to_asm(), nr.to_asm()] => r0,
158 in("ebx") a0.to_asm(),
159 in("ecx") a1.to_asm(),
160 in("edx") a2.to_asm(),
161 in("edi") a4.to_asm(),
162 );
163 FromAsm::from_asm(r0)
164 }
165
166 #[allow(clippy::too_many_arguments)]
167 #[inline]
indirect_syscall6( callee: SyscallType, nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, a3: ArgReg<'_, A3>, a4: ArgReg<'_, A4>, a5: ArgReg<'_, A5>, ) -> RetReg<R0>168 pub(in crate::backend) unsafe fn indirect_syscall6(
169 callee: SyscallType,
170 nr: SyscallNumber<'_>,
171 a0: ArgReg<'_, A0>,
172 a1: ArgReg<'_, A1>,
173 a2: ArgReg<'_, A2>,
174 a3: ArgReg<'_, A3>,
175 a4: ArgReg<'_, A4>,
176 a5: ArgReg<'_, A5>,
177 ) -> RetReg<R0> {
178 let r0;
179 // Oof again. a3 should go in esi, and a5 should go in ebp, and `asm!`
180 // won't let us use either of those registers as operands. And we can't
181 // request stack slots. And there are no other registers free. Use eax as a
182 // temporary pointer to a slice, since it gets clobbered as the return
183 // value anyway.
184 //
185 // This is another reason that syscalls should be compiler intrinsics
186 // rather than inline asm.
187 asm!(
188 "push ebp",
189 "push esi",
190 "push DWORD PTR [eax + 0]",
191 "mov esi, DWORD PTR [eax + 4]",
192 "mov ebp, DWORD PTR [eax + 8]",
193 "mov eax, DWORD PTR [eax + 12]",
194 "call DWORD PTR [esp]",
195 "pop esi",
196 "pop esi",
197 "pop ebp",
198 inout("eax") &[callee as _, a3.to_asm(), a5.to_asm(), nr.to_asm()] => r0,
199 in("ebx") a0.to_asm(),
200 in("ecx") a1.to_asm(),
201 in("edx") a2.to_asm(),
202 in("edi") a4.to_asm(),
203 );
204 FromAsm::from_asm(r0)
205 }
206
207 #[inline]
syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0>208 pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
209 let r0;
210 asm!(
211 "int $$0x80",
212 inlateout("eax") nr.to_asm() => r0,
213 options(nostack, preserves_flags, readonly)
214 );
215 FromAsm::from_asm(r0)
216 }
217
218 #[inline]
syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0>219 pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
220 let r0;
221 asm!(
222 "int $$0x80",
223 inlateout("eax") nr.to_asm() => r0,
224 in("ebx") a0.to_asm(),
225 options(nostack, preserves_flags)
226 );
227 FromAsm::from_asm(r0)
228 }
229
230 #[inline]
syscall1_readonly( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, ) -> RetReg<R0>231 pub(in crate::backend) unsafe fn syscall1_readonly(
232 nr: SyscallNumber<'_>,
233 a0: ArgReg<'_, A0>,
234 ) -> RetReg<R0> {
235 let r0;
236 asm!(
237 "int $$0x80",
238 inlateout("eax") nr.to_asm() => r0,
239 in("ebx") a0.to_asm(),
240 options(nostack, preserves_flags, readonly)
241 );
242 FromAsm::from_asm(r0)
243 }
244
245 #[inline]
syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> !246 pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
247 asm!(
248 "int $$0x80",
249 in("eax") nr.to_asm(),
250 in("ebx") a0.to_asm(),
251 options(noreturn)
252 )
253 }
254
255 #[inline]
syscall2( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, ) -> RetReg<R0>256 pub(in crate::backend) unsafe fn syscall2(
257 nr: SyscallNumber<'_>,
258 a0: ArgReg<'_, A0>,
259 a1: ArgReg<'_, A1>,
260 ) -> RetReg<R0> {
261 let r0;
262 asm!(
263 "int $$0x80",
264 inlateout("eax") nr.to_asm() => r0,
265 in("ebx") a0.to_asm(),
266 in("ecx") a1.to_asm(),
267 options(nostack, preserves_flags)
268 );
269 FromAsm::from_asm(r0)
270 }
271
272 #[inline]
syscall2_readonly( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, ) -> RetReg<R0>273 pub(in crate::backend) unsafe fn syscall2_readonly(
274 nr: SyscallNumber<'_>,
275 a0: ArgReg<'_, A0>,
276 a1: ArgReg<'_, A1>,
277 ) -> RetReg<R0> {
278 let r0;
279 asm!(
280 "int $$0x80",
281 inlateout("eax") nr.to_asm() => r0,
282 in("ebx") a0.to_asm(),
283 in("ecx") a1.to_asm(),
284 options(nostack, preserves_flags, readonly)
285 );
286 FromAsm::from_asm(r0)
287 }
288
289 #[inline]
syscall3( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, ) -> RetReg<R0>290 pub(in crate::backend) unsafe fn syscall3(
291 nr: SyscallNumber<'_>,
292 a0: ArgReg<'_, A0>,
293 a1: ArgReg<'_, A1>,
294 a2: ArgReg<'_, A2>,
295 ) -> RetReg<R0> {
296 let r0;
297 asm!(
298 "int $$0x80",
299 inlateout("eax") nr.to_asm() => r0,
300 in("ebx") a0.to_asm(),
301 in("ecx") a1.to_asm(),
302 in("edx") a2.to_asm(),
303 options(nostack, preserves_flags)
304 );
305 FromAsm::from_asm(r0)
306 }
307
308 #[inline]
syscall3_readonly( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, ) -> RetReg<R0>309 pub(in crate::backend) unsafe fn syscall3_readonly(
310 nr: SyscallNumber<'_>,
311 a0: ArgReg<'_, A0>,
312 a1: ArgReg<'_, A1>,
313 a2: ArgReg<'_, A2>,
314 ) -> RetReg<R0> {
315 let r0;
316 asm!(
317 "int $$0x80",
318 inlateout("eax") nr.to_asm() => r0,
319 in("ebx") a0.to_asm(),
320 in("ecx") a1.to_asm(),
321 in("edx") a2.to_asm(),
322 options(nostack, preserves_flags, readonly)
323 );
324 FromAsm::from_asm(r0)
325 }
326
327 #[inline]
syscall4( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, a3: ArgReg<'_, A3>, ) -> RetReg<R0>328 pub(in crate::backend) unsafe fn syscall4(
329 nr: SyscallNumber<'_>,
330 a0: ArgReg<'_, A0>,
331 a1: ArgReg<'_, A1>,
332 a2: ArgReg<'_, A2>,
333 a3: ArgReg<'_, A3>,
334 ) -> RetReg<R0> {
335 let r0;
336 // a3 should go in esi, but `asm!` won't let us use it as an operand.
337 // Temporarily swap it into place, and then swap it back afterward.
338 asm!(
339 "xchg esi, {a3}",
340 "int $$0x80",
341 "xchg esi, {a3}",
342 a3 = in(reg) a3.to_asm(),
343 inlateout("eax") nr.to_asm() => r0,
344 in("ebx") a0.to_asm(),
345 in("ecx") a1.to_asm(),
346 in("edx") a2.to_asm(),
347 options(nostack, preserves_flags)
348 );
349 FromAsm::from_asm(r0)
350 }
351
352 #[inline]
syscall4_readonly( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, a3: ArgReg<'_, A3>, ) -> RetReg<R0>353 pub(in crate::backend) unsafe fn syscall4_readonly(
354 nr: SyscallNumber<'_>,
355 a0: ArgReg<'_, A0>,
356 a1: ArgReg<'_, A1>,
357 a2: ArgReg<'_, A2>,
358 a3: ArgReg<'_, A3>,
359 ) -> RetReg<R0> {
360 let r0;
361 asm!(
362 "xchg esi, {a3}",
363 "int $$0x80",
364 "xchg esi, {a3}",
365 a3 = in(reg) a3.to_asm(),
366 inlateout("eax") nr.to_asm() => r0,
367 in("ebx") a0.to_asm(),
368 in("ecx") a1.to_asm(),
369 in("edx") a2.to_asm(),
370 options(nostack, preserves_flags, readonly)
371 );
372 FromAsm::from_asm(r0)
373 }
374
375 #[inline]
syscall5( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, a3: ArgReg<'_, A3>, a4: ArgReg<'_, A4>, ) -> RetReg<R0>376 pub(in crate::backend) unsafe fn syscall5(
377 nr: SyscallNumber<'_>,
378 a0: ArgReg<'_, A0>,
379 a1: ArgReg<'_, A1>,
380 a2: ArgReg<'_, A2>,
381 a3: ArgReg<'_, A3>,
382 a4: ArgReg<'_, A4>,
383 ) -> RetReg<R0> {
384 let r0;
385 // As in `syscall4`, use xchg to handle a3. a4 should go in edi, and we can
386 // use that register as an operand. Unlike in `indirect_syscall5`, we don't
387 // have a `callee` operand taking up a register, so we have enough
388 // registers and don't need to use a slice.
389 asm!(
390 "xchg esi, {a3}",
391 "int $$0x80",
392 "xchg esi, {a3}",
393 a3 = in(reg) a3.to_asm(),
394 inlateout("eax") nr.to_asm() => r0,
395 in("ebx") a0.to_asm(),
396 in("ecx") a1.to_asm(),
397 in("edx") a2.to_asm(),
398 in("edi") a4.to_asm(),
399 options(nostack, preserves_flags)
400 );
401 FromAsm::from_asm(r0)
402 }
403
404 #[inline]
syscall5_readonly( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, a3: ArgReg<'_, A3>, a4: ArgReg<'_, A4>, ) -> RetReg<R0>405 pub(in crate::backend) unsafe fn syscall5_readonly(
406 nr: SyscallNumber<'_>,
407 a0: ArgReg<'_, A0>,
408 a1: ArgReg<'_, A1>,
409 a2: ArgReg<'_, A2>,
410 a3: ArgReg<'_, A3>,
411 a4: ArgReg<'_, A4>,
412 ) -> RetReg<R0> {
413 let r0;
414 // See the comments in `syscall5`.
415 asm!(
416 "xchg esi, {a3}",
417 "int $$0x80",
418 "xchg esi, {a3}",
419 a3 = in(reg) a3.to_asm(),
420 inlateout("eax") nr.to_asm() => r0,
421 in("ebx") a0.to_asm(),
422 in("ecx") a1.to_asm(),
423 in("edx") a2.to_asm(),
424 in("edi") a4.to_asm(),
425 options(nostack, preserves_flags, readonly)
426 );
427 FromAsm::from_asm(r0)
428 }
429
430 #[inline]
syscall6( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, a3: ArgReg<'_, A3>, a4: ArgReg<'_, A4>, a5: ArgReg<'_, A5>, ) -> RetReg<R0>431 pub(in crate::backend) unsafe fn syscall6(
432 nr: SyscallNumber<'_>,
433 a0: ArgReg<'_, A0>,
434 a1: ArgReg<'_, A1>,
435 a2: ArgReg<'_, A2>,
436 a3: ArgReg<'_, A3>,
437 a4: ArgReg<'_, A4>,
438 a5: ArgReg<'_, A5>,
439 ) -> RetReg<R0> {
440 let r0;
441 // See the comments in `indirect_syscall6`.
442 asm!(
443 "push ebp",
444 "push esi",
445 "mov esi, DWORD PTR [eax + 0]",
446 "mov ebp, DWORD PTR [eax + 4]",
447 "mov eax, DWORD PTR [eax + 8]",
448 "int $$0x80",
449 "pop esi",
450 "pop ebp",
451 inout("eax") &[a3.to_asm(), a5.to_asm(), nr.to_asm()] => r0,
452 in("ebx") a0.to_asm(),
453 in("ecx") a1.to_asm(),
454 in("edx") a2.to_asm(),
455 in("edi") a4.to_asm(),
456 options(preserves_flags)
457 );
458 FromAsm::from_asm(r0)
459 }
460
461 #[inline]
syscall6_readonly( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, a3: ArgReg<'_, A3>, a4: ArgReg<'_, A4>, a5: ArgReg<'_, A5>, ) -> RetReg<R0>462 pub(in crate::backend) unsafe fn syscall6_readonly(
463 nr: SyscallNumber<'_>,
464 a0: ArgReg<'_, A0>,
465 a1: ArgReg<'_, A1>,
466 a2: ArgReg<'_, A2>,
467 a3: ArgReg<'_, A3>,
468 a4: ArgReg<'_, A4>,
469 a5: ArgReg<'_, A5>,
470 ) -> RetReg<R0> {
471 let r0;
472 // See the comments in `indirect_syscall6`.
473 asm!(
474 "push ebp",
475 "push esi",
476 "mov esi, DWORD PTR [eax + 0]",
477 "mov ebp, DWORD PTR [eax + 4]",
478 "mov eax, DWORD PTR [eax + 8]",
479 "int $$0x80",
480 "pop esi",
481 "pop ebp",
482 inout("eax") &[a3.to_asm(), a5.to_asm(), nr.to_asm()] => r0,
483 in("ebx") a0.to_asm(),
484 in("ecx") a1.to_asm(),
485 in("edx") a2.to_asm(),
486 in("edi") a4.to_asm(),
487 options(preserves_flags, readonly)
488 );
489 FromAsm::from_asm(r0)
490 }
491