linux_syscall/
linux-syscall.rs

1// Copyright (c) 2022 John Millikin <john@john-millikin.com>
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted.
5//
6// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
7// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
8// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
9// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
10// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
11// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
12// PERFORMANCE OF THIS SOFTWARE.
13//
14// SPDX-License-Identifier: 0BSD
15
16//! This library defines syscall numbers and a [`syscall!`] macro for directly
17//! invoking Linux system calls.
18//!
19//! The [`arch`] modules document available syscall numbers for all supported
20//! architectures, and the top-level module re-exports syscall numbers for the
21//! current target platform.
22//!
23//! Syscall results may be inspected with the [`Result*` traits](#traits).
24//!
25//! # Example
26//!
27//! ```
28//! # #[macro_use] extern crate linux_syscall;
29//! # use linux_syscall::*;
30//! # fn main() -> core::result::Result<(), linux_errno::Error> {
31//! let stdout: i32 = 1;
32//! let hello = "Hello, world!\n\0";
33//! let rc = unsafe {
34//! 	syscall!(SYS_write, stdout, hello.as_ptr(), hello.len())
35//! };
36//! rc.check()?;
37//! # Ok(())
38//! # }
39//! ```
40//!
41//! # Safety
42//!
43//! Very unsafe.
44//!
45//! Linux syscalls are low-level primitives that operate without any notion
46//! of borrow checking or type safety. It is the caller's responsibility to
47//! ensure parameters have types, values, and lifetimes appropriate to the
48//! syscall being invoked.
49//!
50//! * The kernel cannot distinguish `*const T` and `*mut T`.
51//! * Many syscalls accept complex parameters as pointers to a `struct`. The
52//!   caller must ensure such parameters have appropriate layout and alignment.
53//! * Syscalls vary between architectures. The same syscall name may have
54//!   a completely different signature even on similar targets, for example
55//!   `SYS_mmap` on `x86` and `x86_64`.
56
57#![no_std]
58
59use linux_errno::Error;
60
61/// An architecture-specific syscall number.
62#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
63pub struct Syscall {
64	nr: u32,
65}
66
67impl Syscall {
68	#[inline]
69	pub const fn from_u32(nr: u32) -> Syscall {
70		Syscall { nr }
71	}
72}
73
74impl From<u32> for Syscall {
75	#[inline]
76	fn from(nr: u32) -> Syscall {
77		Syscall { nr }
78	}
79}
80
81impl From<Syscall> for u32 {
82	#[inline]
83	fn from(syscall: Syscall) -> u32 {
84		syscall.nr
85	}
86}
87
88/// Check whether a syscall succeeded or failed.
89pub trait Result {
90	fn check(&self) -> core::result::Result<(), Error>;
91}
92
93/// Interpret a syscall result as a 32-bit integer.
94pub trait Result32: Result {
95	fn try_i32(&self) -> core::result::Result<i32, Error>;
96
97	fn try_u32(&self) -> core::result::Result<u32, Error>;
98}
99
100/// Interpret a syscall result as a 64-bit integer.
101pub trait Result64: Result {
102	fn try_i64(&self) -> core::result::Result<i64, Error>;
103
104	fn try_u64(&self) -> core::result::Result<u64, Error>;
105}
106
107/// Interpret a syscall result as a pointer.
108pub trait ResultPtr: Result {
109	fn try_ptr(&self) -> core::result::Result<*const (), Error>;
110
111	fn try_ptr_mut(&self) -> core::result::Result<*mut (), Error>;
112}
113
114/// Interpret a syscall result as a pointer-sized integer.
115pub trait ResultSize: Result {
116	fn try_isize(&self) -> core::result::Result<isize, Error>;
117
118	fn try_usize(&self) -> core::result::Result<usize, Error>;
119}
120
121macro_rules! single_register_result {
122	($arch_result:ty) => {
123		use linux_errno::Error;
124
125		impl $crate::Result for $arch_result {
126			#[inline]
127			fn check(&self) -> core::result::Result<(), Error> {
128				if self.0 >= self::MAX_ERRNO {
129					return Err(new_err(self.0 as u16));
130				}
131				Ok(())
132			}
133		}
134
135		#[inline]
136		#[cold]
137		const fn new_err(truncated_register: u16) -> Error {
138			let (err, _) = truncated_register.overflowing_neg();
139			unsafe { Error::new_unchecked(err) }
140		}
141
142		impl $crate::ResultPtr for $arch_result {
143			#[inline]
144			fn try_ptr(&self) -> core::result::Result<*const (), Error> {
145				$crate::Result::check(self)?;
146				Ok(self.0 as *const ())
147			}
148
149			#[inline]
150			fn try_ptr_mut(&self) -> core::result::Result<*mut (), Error> {
151				$crate::Result::check(self)?;
152				Ok(self.0 as *mut ())
153			}
154		}
155
156		impl<T> core::convert::TryFrom<$arch_result> for *const T {
157			type Error = Error;
158
159			#[inline]
160			fn try_from(rc: $arch_result) -> core::result::Result<Self, Error> {
161				$crate::ResultPtr::try_ptr(&rc).map(|p| p.cast())
162			}
163		}
164
165		impl<T> core::convert::TryFrom<$arch_result> for *mut T {
166			type Error = Error;
167
168			#[inline]
169			fn try_from(rc: $arch_result) -> core::result::Result<Self, Error> {
170				$crate::ResultPtr::try_ptr_mut(&rc).map(|p| p.cast())
171			}
172		}
173
174		impl $crate::ResultSize for $arch_result {
175			#[inline]
176			fn try_isize(&self) -> core::result::Result<isize, Error> {
177				$crate::Result::check(self)?;
178				Ok(self.0 as isize)
179			}
180
181			#[inline]
182			fn try_usize(&self) -> core::result::Result<usize, Error> {
183				$crate::Result::check(self)?;
184				Ok(self.0 as usize)
185			}
186		}
187
188		impl core::convert::TryFrom<$arch_result> for isize {
189			type Error = Error;
190
191			#[inline]
192			fn try_from(rc: $arch_result) -> core::result::Result<Self, Error> {
193				$crate::ResultSize::try_isize(&rc)
194			}
195		}
196
197		impl core::convert::TryFrom<$arch_result> for usize {
198			type Error = Error;
199
200			#[inline]
201			fn try_from(rc: $arch_result) -> core::result::Result<Self, Error> {
202				$crate::ResultSize::try_usize(&rc)
203			}
204		}
205	};
206}
207
208#[cfg(any(target_pointer_width = "32", doc))]
209macro_rules! single_register_result32 {
210	($arch_result:ty) => {
211		single_register_result!($arch_result);
212
213		const MAX_ERRNO: u32 = (-4095i32) as u32;
214
215		impl $crate::Result32 for $arch_result {
216			#[inline]
217			fn try_i32(&self) -> core::result::Result<i32, Error> {
218				$crate::Result::check(self)?;
219				Ok(self.0 as i32)
220			}
221
222			#[inline]
223			fn try_u32(&self) -> core::result::Result<u32, Error> {
224				$crate::Result::check(self)?;
225				Ok(self.0)
226			}
227		}
228
229		impl core::convert::TryFrom<$arch_result> for i32 {
230			type Error = Error;
231
232			#[inline]
233			fn try_from(rc: $arch_result) -> core::result::Result<Self, Error> {
234				$crate::Result32::try_i32(&rc)
235			}
236		}
237
238		impl core::convert::TryFrom<$arch_result> for u32 {
239			type Error = Error;
240
241			#[inline]
242			fn try_from(rc: $arch_result) -> core::result::Result<Self, Error> {
243				$crate::Result32::try_u32(&rc)
244			}
245		}
246	};
247}
248
249#[cfg(any(target_pointer_width = "64", doc))]
250macro_rules! single_register_result64 {
251	($arch_result:ty) => {
252		single_register_result!($arch_result);
253
254		const MAX_ERRNO: u64 = (-4095i64) as u64;
255
256		impl $crate::Result64 for $arch_result {
257			#[inline]
258			fn try_i64(&self) -> core::result::Result<i64, Error> {
259				$crate::Result::check(self)?;
260				Ok(self.0 as i64)
261			}
262
263			#[inline]
264			fn try_u64(&self) -> core::result::Result<u64, Error> {
265				$crate::Result::check(self)?;
266				Ok(self.0)
267			}
268		}
269
270		impl core::convert::TryFrom<$arch_result> for i64 {
271			type Error = Error;
272
273			#[inline]
274			fn try_from(rc: $arch_result) -> core::result::Result<Self, Error> {
275				$crate::Result64::try_i64(&rc)
276			}
277		}
278
279		impl core::convert::TryFrom<$arch_result> for u64 {
280			type Error = Error;
281
282			#[inline]
283			fn try_from(rc: $arch_result) -> core::result::Result<Self, Error> {
284				$crate::Result64::try_u64(&rc)
285			}
286		}
287	};
288}
289
290macro_rules! syscall_constants {
291	( $( $name:ident = $value:literal , )+ ) => {
292		use $crate::Syscall;
293		$(
294			pub const $name: Syscall = Syscall::from_u32($value);
295		)*
296	};
297}
298
299/// Linux syscall numbers for specific target architectures.
300pub mod arch {
301	/// Linux syscall numbers for the `aarch64` architecture.
302	#[cfg(any(target_arch = "aarch64", doc))]
303	pub mod aarch64 {
304		mod syscall_asm;
305		pub use self::syscall_asm::Result;
306
307		pub(crate) mod syscall_tbl;
308		pub use self::syscall_tbl::*;
309	}
310
311	/// Linux syscall numbers for the `arm` architecture.
312	#[cfg(any(target_arch = "arm", doc))]
313	pub mod arm {
314		mod syscall_asm;
315		pub use self::syscall_asm::Result;
316
317		pub(crate) mod syscall_tbl;
318		pub use self::syscall_tbl::*;
319	}
320
321	/// Linux syscall numbers for the `riscv64` architecture.
322	#[cfg(any(target_arch = "riscv64", doc))]
323	pub mod riscv64 {
324		mod syscall_asm;
325		pub use self::syscall_asm::Result;
326
327		pub(crate) mod syscall_tbl;
328		pub use self::syscall_tbl::*;
329	}
330
331	/// Linux syscall numbers for the `s390x` architecture.
332	#[cfg(any(target_arch = "s390x", doc))]
333	pub mod s390x {
334		mod syscall_asm;
335		pub use self::syscall_asm::Result;
336
337		pub(crate) mod syscall_tbl;
338		pub use self::syscall_tbl::*;
339	}
340
341	/// Linux syscall numbers for the `x86` architecture.
342	#[cfg(any(target_arch = "x86", doc))]
343	pub mod x86 {
344		mod syscall_asm;
345		pub use self::syscall_asm::Result;
346
347		pub(crate) mod syscall_tbl;
348		pub use self::syscall_tbl::*;
349	}
350
351	/// Linux syscall numbers for the `x86_64` architecture.
352	#[cfg(any(target_arch = "x86_64", doc))]
353	pub mod x86_64 {
354		mod syscall_asm;
355		pub use self::syscall_asm::Result;
356
357		pub(crate) mod syscall_tbl;
358		pub use self::syscall_tbl::*;
359	}
360}
361
362#[cfg(target_arch = "arm")]
363pub use crate::arch::arm::syscall_tbl::*;
364
365#[cfg(target_arch = "aarch64")]
366pub use crate::arch::aarch64::syscall_tbl::*;
367
368#[cfg(target_arch = "riscv64")]
369pub use crate::arch::riscv64::syscall_tbl::*;
370
371#[cfg(target_arch = "s390x")]
372pub use crate::arch::s390x::syscall_tbl::*;
373
374#[cfg(target_arch = "x86")]
375pub use crate::arch::x86::syscall_tbl::*;
376
377#[cfg(target_arch = "x86_64")]
378pub use crate::arch::x86_64::syscall_tbl::*;
379
380/// Invokes a Linux syscall.
381///
382/// `$syscall` must be a value that implements [`Into<Syscall>`](Syscall).
383/// Other arguments must be valid [`asm!`](core::arch::asm!) input operands,
384/// such as integers or pointers.
385///
386/// The returned value is an architecture-specific implementation of [`Result`].
387///
388/// Additional traits implemented by syscall results vary by architecture.
389/// For all architectures currently supported by this library:
390///
391/// * The [`ResultSize`] and [`ResultPtr`] traits are implemented.
392/// * One of the [`Result32`] or [`Result64`] traits is implemented, according
393///   to the native word size.
394///
395/// # Example
396///
397/// ```
398/// # #[macro_use] extern crate linux_syscall;
399/// # use linux_syscall::*;
400/// # fn main() -> core::result::Result<(), linux_errno::Error> {
401/// let stdout: i32 = 1;
402/// let hello = "Hello, world!\n\0";
403/// let rc = unsafe {
404/// 	syscall!(SYS_write, stdout, hello.as_ptr(), hello.len())
405/// };
406/// rc.check()?;
407/// # Ok(())
408/// # }
409/// ```
410///
411/// # Safety
412///
413/// Very unsafe. See the [module documentation](self) for details.
414#[cfg(doc)]
415#[macro_export]
416macro_rules! syscall {
417	($syscall:expr $(,)?) => {};
418	($syscall:expr, $a1:expr $(,)?) => {};
419	($syscall:expr, $a1:expr, $a2:expr $(,)?) => {};
420	($syscall:expr, $a1:expr, $a2:expr, $a3:expr $(,)?) => {};
421	($syscall:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr $(,)?) => {};
422	($syscall:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr $(,)?) => {};
423	($syscall:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr $(,)?) => {};
424}