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 gets
148 // clobbered as the return value anyway.
149 asm!(
150 "push esi",
151 "push [eax + 0]",
152 "mov esi, [eax + 4]",
153 "mov eax, [eax + 8]",
154 "call [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 #[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>167 pub(in crate::backend) unsafe fn indirect_syscall6(
168 callee: SyscallType,
169 nr: SyscallNumber<'_>,
170 a0: ArgReg<'_, A0>,
171 a1: ArgReg<'_, A1>,
172 a2: ArgReg<'_, A2>,
173 a3: ArgReg<'_, A3>,
174 a4: ArgReg<'_, A4>,
175 a5: ArgReg<'_, A5>,
176 ) -> RetReg<R0> {
177 let r0;
178 // Oof again. a3 should go in esi, and a5 should go in ebp, and `asm!`
179 // won't let us use either of those registers as operands. And we can't
180 // request stack slots. And there are no other registers free. Use eax as a
181 // temporary pointer to a slice, since it gets clobbered as the return
182 // value anyway.
183 //
184 // This is another reason that syscalls should be compiler intrinsics
185 // rather than inline asm.
186 asm!(
187 "push ebp",
188 "push esi",
189 "push [eax + 0]",
190 "mov esi, [eax + 4]",
191 "mov ebp, [eax + 8]",
192 "mov eax, [eax + 12]",
193 "call [esp]",
194 "pop esi",
195 "pop esi",
196 "pop ebp",
197 inout("eax") &[callee as _, a3.to_asm(), a5.to_asm(), nr.to_asm()] => r0,
198 in("ebx") a0.to_asm(),
199 in("ecx") a1.to_asm(),
200 in("edx") a2.to_asm(),
201 in("edi") a4.to_asm(),
202 );
203 FromAsm::from_asm(r0)
204 }
205
206 #[inline]
syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0>207 pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
208 let r0;
209 asm!(
210 "int $$0x80",
211 inlateout("eax") nr.to_asm() => r0,
212 options(nostack, preserves_flags, readonly)
213 );
214 FromAsm::from_asm(r0)
215 }
216
217 #[inline]
syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0>218 pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
219 let r0;
220 asm!(
221 "int $$0x80",
222 inlateout("eax") nr.to_asm() => r0,
223 in("ebx") a0.to_asm(),
224 options(nostack, preserves_flags)
225 );
226 FromAsm::from_asm(r0)
227 }
228
229 #[inline]
syscall1_readonly( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, ) -> RetReg<R0>230 pub(in crate::backend) unsafe fn syscall1_readonly(
231 nr: SyscallNumber<'_>,
232 a0: ArgReg<'_, A0>,
233 ) -> RetReg<R0> {
234 let r0;
235 asm!(
236 "int $$0x80",
237 inlateout("eax") nr.to_asm() => r0,
238 in("ebx") a0.to_asm(),
239 options(nostack, preserves_flags, readonly)
240 );
241 FromAsm::from_asm(r0)
242 }
243
244 #[inline]
syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> !245 pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
246 asm!(
247 "int $$0x80",
248 in("eax") nr.to_asm(),
249 in("ebx") a0.to_asm(),
250 options(nostack, noreturn)
251 )
252 }
253
254 #[inline]
syscall2( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, ) -> RetReg<R0>255 pub(in crate::backend) unsafe fn syscall2(
256 nr: SyscallNumber<'_>,
257 a0: ArgReg<'_, A0>,
258 a1: ArgReg<'_, A1>,
259 ) -> RetReg<R0> {
260 let r0;
261 asm!(
262 "int $$0x80",
263 inlateout("eax") nr.to_asm() => r0,
264 in("ebx") a0.to_asm(),
265 in("ecx") a1.to_asm(),
266 options(nostack, preserves_flags)
267 );
268 FromAsm::from_asm(r0)
269 }
270
271 #[inline]
syscall2_readonly( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, ) -> RetReg<R0>272 pub(in crate::backend) unsafe fn syscall2_readonly(
273 nr: SyscallNumber<'_>,
274 a0: ArgReg<'_, A0>,
275 a1: ArgReg<'_, A1>,
276 ) -> RetReg<R0> {
277 let r0;
278 asm!(
279 "int $$0x80",
280 inlateout("eax") nr.to_asm() => r0,
281 in("ebx") a0.to_asm(),
282 in("ecx") a1.to_asm(),
283 options(nostack, preserves_flags, readonly)
284 );
285 FromAsm::from_asm(r0)
286 }
287
288 #[inline]
syscall3( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, ) -> RetReg<R0>289 pub(in crate::backend) unsafe fn syscall3(
290 nr: SyscallNumber<'_>,
291 a0: ArgReg<'_, A0>,
292 a1: ArgReg<'_, A1>,
293 a2: ArgReg<'_, A2>,
294 ) -> RetReg<R0> {
295 let r0;
296 asm!(
297 "int $$0x80",
298 inlateout("eax") nr.to_asm() => r0,
299 in("ebx") a0.to_asm(),
300 in("ecx") a1.to_asm(),
301 in("edx") a2.to_asm(),
302 options(nostack, preserves_flags)
303 );
304 FromAsm::from_asm(r0)
305 }
306
307 #[inline]
syscall3_readonly( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, ) -> RetReg<R0>308 pub(in crate::backend) unsafe fn syscall3_readonly(
309 nr: SyscallNumber<'_>,
310 a0: ArgReg<'_, A0>,
311 a1: ArgReg<'_, A1>,
312 a2: ArgReg<'_, A2>,
313 ) -> RetReg<R0> {
314 let r0;
315 asm!(
316 "int $$0x80",
317 inlateout("eax") nr.to_asm() => r0,
318 in("ebx") a0.to_asm(),
319 in("ecx") a1.to_asm(),
320 in("edx") a2.to_asm(),
321 options(nostack, preserves_flags, readonly)
322 );
323 FromAsm::from_asm(r0)
324 }
325
326 #[inline]
syscall4( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, a3: ArgReg<'_, A3>, ) -> RetReg<R0>327 pub(in crate::backend) unsafe fn syscall4(
328 nr: SyscallNumber<'_>,
329 a0: ArgReg<'_, A0>,
330 a1: ArgReg<'_, A1>,
331 a2: ArgReg<'_, A2>,
332 a3: ArgReg<'_, A3>,
333 ) -> RetReg<R0> {
334 let r0;
335 // a3 should go in esi, but `asm!` won't let us use it as an operand.
336 // Temporarily swap it into place, and then swap it back afterward.
337 asm!(
338 "xchg esi, {a3}",
339 "int $$0x80",
340 "xchg esi, {a3}",
341 a3 = in(reg) a3.to_asm(),
342 inlateout("eax") nr.to_asm() => r0,
343 in("ebx") a0.to_asm(),
344 in("ecx") a1.to_asm(),
345 in("edx") a2.to_asm(),
346 options(nostack, preserves_flags)
347 );
348 FromAsm::from_asm(r0)
349 }
350
351 #[inline]
syscall4_readonly( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, a3: ArgReg<'_, A3>, ) -> RetReg<R0>352 pub(in crate::backend) unsafe fn syscall4_readonly(
353 nr: SyscallNumber<'_>,
354 a0: ArgReg<'_, A0>,
355 a1: ArgReg<'_, A1>,
356 a2: ArgReg<'_, A2>,
357 a3: ArgReg<'_, A3>,
358 ) -> RetReg<R0> {
359 let r0;
360 asm!(
361 "xchg esi, {a3}",
362 "int $$0x80",
363 "xchg esi, {a3}",
364 a3 = in(reg) a3.to_asm(),
365 inlateout("eax") nr.to_asm() => r0,
366 in("ebx") a0.to_asm(),
367 in("ecx") a1.to_asm(),
368 in("edx") a2.to_asm(),
369 options(nostack, preserves_flags, readonly)
370 );
371 FromAsm::from_asm(r0)
372 }
373
374 #[inline]
syscall5( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, a3: ArgReg<'_, A3>, a4: ArgReg<'_, A4>, ) -> RetReg<R0>375 pub(in crate::backend) unsafe fn syscall5(
376 nr: SyscallNumber<'_>,
377 a0: ArgReg<'_, A0>,
378 a1: ArgReg<'_, A1>,
379 a2: ArgReg<'_, A2>,
380 a3: ArgReg<'_, A3>,
381 a4: ArgReg<'_, A4>,
382 ) -> RetReg<R0> {
383 let r0;
384 // As in `syscall4`, use xchg to handle a3. a4 should go in edi, and we can
385 // use that register as an operand. Unlike in `indirect_syscall5`, we don't
386 // have a `callee` operand taking up a register, so we have enough
387 // registers and don't need to use a slice.
388 asm!(
389 "xchg esi, {a3}",
390 "int $$0x80",
391 "xchg esi, {a3}",
392 a3 = in(reg) a3.to_asm(),
393 inlateout("eax") nr.to_asm() => r0,
394 in("ebx") a0.to_asm(),
395 in("ecx") a1.to_asm(),
396 in("edx") a2.to_asm(),
397 in("edi") a4.to_asm(),
398 options(nostack, preserves_flags)
399 );
400 FromAsm::from_asm(r0)
401 }
402
403 #[inline]
syscall5_readonly( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, a3: ArgReg<'_, A3>, a4: ArgReg<'_, A4>, ) -> RetReg<R0>404 pub(in crate::backend) unsafe fn syscall5_readonly(
405 nr: SyscallNumber<'_>,
406 a0: ArgReg<'_, A0>,
407 a1: ArgReg<'_, A1>,
408 a2: ArgReg<'_, A2>,
409 a3: ArgReg<'_, A3>,
410 a4: ArgReg<'_, A4>,
411 ) -> RetReg<R0> {
412 let r0;
413 // See the comments in `syscall5`.
414 asm!(
415 "xchg esi, {a3}",
416 "int $$0x80",
417 "xchg esi, {a3}",
418 a3 = in(reg) a3.to_asm(),
419 inlateout("eax") nr.to_asm() => r0,
420 in("ebx") a0.to_asm(),
421 in("ecx") a1.to_asm(),
422 in("edx") a2.to_asm(),
423 in("edi") a4.to_asm(),
424 options(nostack, preserves_flags, readonly)
425 );
426 FromAsm::from_asm(r0)
427 }
428
429 #[inline]
syscall6( nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>, a1: ArgReg<'_, A1>, a2: ArgReg<'_, A2>, a3: ArgReg<'_, A3>, a4: ArgReg<'_, A4>, a5: ArgReg<'_, A5>, ) -> RetReg<R0>430 pub(in crate::backend) unsafe fn syscall6(
431 nr: SyscallNumber<'_>,
432 a0: ArgReg<'_, A0>,
433 a1: ArgReg<'_, A1>,
434 a2: ArgReg<'_, A2>,
435 a3: ArgReg<'_, A3>,
436 a4: ArgReg<'_, A4>,
437 a5: ArgReg<'_, A5>,
438 ) -> RetReg<R0> {
439 let r0;
440 // See the comments in `indirect_syscall6`.
441 asm!(
442 "push ebp",
443 "push esi",
444 "mov esi, [eax + 0]",
445 "mov ebp, [eax + 4]",
446 "mov eax, [eax + 8]",
447 "int $$0x80",
448 "pop esi",
449 "pop ebp",
450 inout("eax") &[a3.to_asm(), a5.to_asm(), nr.to_asm()] => r0,
451 in("ebx") a0.to_asm(),
452 in("ecx") a1.to_asm(),
453 in("edx") a2.to_asm(),
454 in("edi") a4.to_asm(),
455 options(preserves_flags)
456 );
457 FromAsm::from_asm(r0)
458 }
459
460 #[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>461 pub(in crate::backend) unsafe fn syscall6_readonly(
462 nr: SyscallNumber<'_>,
463 a0: ArgReg<'_, A0>,
464 a1: ArgReg<'_, A1>,
465 a2: ArgReg<'_, A2>,
466 a3: ArgReg<'_, A3>,
467 a4: ArgReg<'_, A4>,
468 a5: ArgReg<'_, A5>,
469 ) -> RetReg<R0> {
470 let r0;
471 // See the comments in `indirect_syscall6`.
472 asm!(
473 "push ebp",
474 "push esi",
475 "mov esi, [eax + 0]",
476 "mov ebp, [eax + 4]",
477 "mov eax, [eax + 8]",
478 "int $$0x80",
479 "pop esi",
480 "pop ebp",
481 inout("eax") &[a3.to_asm(), a5.to_asm(), nr.to_asm()] => r0,
482 in("ebx") a0.to_asm(),
483 in("ecx") a1.to_asm(),
484 in("edx") a2.to_asm(),
485 in("edi") a4.to_asm(),
486 options(preserves_flags, readonly)
487 );
488 FromAsm::from_asm(r0)
489 }
490