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