blob: 4af64d6c4794e28f7ba0fa23317fa509e6d42d6b [file] [log] [blame]
//! Syscall wrappers for platforms which pass the syscall number specially.
//!
//! Rustix aims to minimize the amount of assembly code it needs. To that end,
//! this code reorders syscall arguments as close as feasible to the actual
//! syscall convention before calling the assembly functions.
//!
//! Many architectures use a convention where the syscall number is passed in a
//! special register, with the regular syscall arguments passed in either the
//! same or similar registers as the platform C convention. This code
//! approximates that order by passing the regular syscall arguments first, and
//! the syscall number last. That way, the outline assembly code typically just
//! needs to move the syscall number to its special register, and leave the
//! other arguments mostly as they are.
#[cfg(target_arch = "mips")]
use crate::backend::reg::A6;
use crate::backend::reg::{ArgReg, RetReg, SyscallNumber, A0, A1, A2, A3, A4, A5, R0};
// First we declare the actual assembly routines with `*_nr_last` names and
// reordered arguments. If the signatures or calling conventions are ever
// changed, the symbol names should also be updated accordingly, to avoid
// collisions with other versions of this crate.
//
// We don't define `_readonly` versions of these because we have no way to tell
// Rust that calls to our outline assembly are readonly.
extern "C" {
fn rustix_syscall0_nr_last(nr: SyscallNumber<'_>) -> RetReg<R0>;
fn rustix_syscall1_nr_last(a0: ArgReg<'_, A0>, nr: SyscallNumber<'_>) -> RetReg<R0>;
fn rustix_syscall1_noreturn_nr_last(a0: ArgReg<'_, A0>, nr: SyscallNumber<'_>) -> !;
fn rustix_syscall2_nr_last(
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
fn rustix_syscall3_nr_last(
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
fn rustix_syscall4_nr_last(
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
fn rustix_syscall5_nr_last(
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
fn rustix_syscall6_nr_last(
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
#[cfg(target_arch = "mips")]
fn rustix_syscall7_nr_last(
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
a6: ArgReg<'_, A6>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
}
// Then we define inline wrapper functions that do the reordering.
#[inline]
pub(in crate::backend) unsafe fn syscall0(nr: SyscallNumber<'_>) -> RetReg<R0> {
rustix_syscall0_nr_last(nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
rustix_syscall1_nr_last(a0, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
rustix_syscall1_noreturn_nr_last(a0, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
rustix_syscall2_nr_last(a0, a1, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
rustix_syscall3_nr_last(a0, a1, a2, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
rustix_syscall4_nr_last(a0, a1, a2, a3, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
rustix_syscall5_nr_last(a0, a1, a2, a3, a4, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
rustix_syscall6_nr_last(a0, a1, a2, a3, a4, a5, nr)
}
#[cfg(target_arch = "mips")]
#[inline]
pub(in crate::backend) unsafe fn syscall7(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
a6: ArgReg<'_, A6>,
) -> RetReg<R0> {
rustix_syscall7_nr_last(a0, a1, a2, a3, a4, a5, a6, nr)
}
// Then we define the `_readonly` versions of the wrappers. We don't have
// separate `_readonly` implementations, so these can just be aliases to
// their non-`_readonly` counterparts.
#[cfg(target_arch = "mips")]
pub(in crate::backend) use syscall7 as syscall7_readonly;
pub(in crate::backend) use {
syscall0 as syscall0_readonly, syscall1 as syscall1_readonly, syscall2 as syscall2_readonly,
syscall3 as syscall3_readonly, syscall4 as syscall4_readonly, syscall5 as syscall5_readonly,
syscall6 as syscall6_readonly,
};