• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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