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