1 //! Experimental low-level implementation details for libc-like runtime
2 //! libraries such as [Origin].
3 //!
4 //! Do not use the functions in this module unless you've read all of their
5 //! code. They don't always behave the same way as functions with similar names
6 //! in `libc`. Sometimes information about the differences is included in the
7 //! Linux documentation under “C library/kernel differences” sections. And, if
8 //! there is a libc in the process, these functions may have surprising
9 //! interactions with it.
10 //!
11 //! These functions are for implementing thread-local storage (TLS), managing
12 //! threads, loaded libraries, and other process-wide resources. Most of
13 //! `rustix` doesn't care about what other libraries are linked into the
14 //! program or what they're doing, but the features in this module generally
15 //! can only be used by one entity within a process.
16 //!
17 //! The API for these functions is not stable, and this module is
18 //! `doc(hidden)`.
19 //!
20 //! [Origin]: https://github.com/sunfishcode/origin#readme
21 //!
22 //! # Safety
23 //!
24 //! This module is intended to be used for implementing a runtime library such
25 //! as libc. Use of these features for any other purpose is likely to create
26 //! serious problems.
27 #![allow(unsafe_code)]
28 
29 use crate::backend;
30 #[cfg(linux_raw)]
31 use crate::ffi::CStr;
32 #[cfg(linux_raw)]
33 #[cfg(feature = "fs")]
34 use crate::fs::AtFlags;
35 #[cfg(linux_raw)]
36 use crate::io;
37 #[cfg(linux_raw)]
38 use crate::pid::Pid;
39 #[cfg(linux_raw)]
40 #[cfg(feature = "fs")]
41 use backend::fd::AsFd;
42 #[cfg(linux_raw)]
43 use core::ffi::c_void;
44 
45 #[cfg(linux_raw)]
46 pub use crate::signal::Signal;
47 
48 /// `sigaction`
49 #[cfg(linux_raw)]
50 pub type Sigaction = linux_raw_sys::general::kernel_sigaction;
51 
52 /// `stack_t`
53 #[cfg(linux_raw)]
54 pub type Stack = linux_raw_sys::general::stack_t;
55 
56 /// `sigset_t`
57 #[cfg(linux_raw)]
58 pub type Sigset = linux_raw_sys::general::kernel_sigset_t;
59 
60 /// `siginfo_t`
61 #[cfg(linux_raw)]
62 pub type Siginfo = linux_raw_sys::general::siginfo_t;
63 
64 pub use crate::timespec::{Nsecs, Secs, Timespec};
65 
66 /// `SIG_*` constants for use with [`sigprocmask`].
67 #[cfg(linux_raw)]
68 #[repr(u32)]
69 pub enum How {
70     /// `SIG_BLOCK`
71     BLOCK = linux_raw_sys::general::SIG_BLOCK,
72 
73     /// `SIG_UNBLOCK`
74     UNBLOCK = linux_raw_sys::general::SIG_UNBLOCK,
75 
76     /// `SIG_SETMASK`
77     SETMASK = linux_raw_sys::general::SIG_SETMASK,
78 }
79 
80 #[cfg(target_arch = "x86")]
81 #[inline]
set_thread_area(u_info: &mut UserDesc) -> io::Result<()>82 pub unsafe fn set_thread_area(u_info: &mut UserDesc) -> io::Result<()> {
83     backend::runtime::syscalls::tls::set_thread_area(u_info)
84 }
85 
86 #[cfg(target_arch = "arm")]
87 #[inline]
arm_set_tls(data: *mut c_void) -> io::Result<()>88 pub unsafe fn arm_set_tls(data: *mut c_void) -> io::Result<()> {
89     backend::runtime::syscalls::tls::arm_set_tls(data)
90 }
91 
92 /// `prctl(PR_SET_FS, data)`—Set the x86-64 `fs` register.
93 ///
94 /// # Safety
95 ///
96 /// This is a very low-level feature for implementing threading libraries.
97 /// See the references links above.
98 #[cfg(target_arch = "x86_64")]
99 #[inline]
set_fs(data: *mut c_void)100 pub unsafe fn set_fs(data: *mut c_void) {
101     backend::runtime::syscalls::tls::set_fs(data)
102 }
103 
104 /// Set the x86-64 thread ID address.
105 ///
106 /// # Safety
107 ///
108 /// This is a very low-level feature for implementing threading libraries.
109 /// See the references links above.
110 #[inline]
set_tid_address(data: *mut c_void) -> Pid111 pub unsafe fn set_tid_address(data: *mut c_void) -> Pid {
112     backend::runtime::syscalls::tls::set_tid_address(data)
113 }
114 
115 #[cfg(linux_raw)]
116 #[cfg(target_arch = "x86")]
117 pub use backend::runtime::tls::UserDesc;
118 
119 /// `syscall(SYS_exit, status)`—Exit the current thread.
120 ///
121 /// # Safety
122 ///
123 /// This is a very low-level feature for implementing threading libraries.
124 #[inline]
exit_thread(status: i32) -> !125 pub unsafe fn exit_thread(status: i32) -> ! {
126     backend::runtime::syscalls::tls::exit_thread(status)
127 }
128 
129 /// Exit all the threads in the current process' thread group.
130 ///
131 /// This is equivalent to `_exit` and `_Exit` in libc.
132 ///
133 /// This does not call any `__cxa_atexit`, `atexit`, or any other destructors.
134 /// Most programs should use [`std::process::exit`] instead of calling this
135 /// directly.
136 ///
137 /// # References
138 ///  - [POSIX `_Exit`]
139 ///  - [Linux `exit_group`]
140 ///  - [Linux `_Exit`]
141 ///
142 /// [POSIX `_Exit`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/_Exit.html
143 /// [Linux `exit_group`]: https://man7.org/linux/man-pages/man2/exit_group.2.html
144 /// [Linux `_Exit`]: https://man7.org/linux/man-pages/man2/_Exit.2.html
145 #[doc(alias = "_exit")]
146 #[doc(alias = "_Exit")]
147 #[inline]
exit_group(status: i32) -> !148 pub fn exit_group(status: i32) -> ! {
149     backend::runtime::syscalls::exit_group(status)
150 }
151 
152 /// `EXIT_SUCCESS` for use with [`exit_group`].
153 ///
154 /// # References
155 ///  - [POSIX]
156 ///  - [Linux]
157 ///
158 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/stdlib.h.html
159 /// [Linux]: https://man7.org/linux/man-pages/man3/exit.3.html
160 pub const EXIT_SUCCESS: i32 = backend::c::EXIT_SUCCESS;
161 
162 /// `EXIT_FAILURE` for use with [`exit_group`].
163 ///
164 /// # References
165 ///  - [POSIX]
166 ///  - [Linux]
167 ///
168 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/stdlib.h.html
169 /// [Linux]: https://man7.org/linux/man-pages/man3/exit.3.html
170 pub const EXIT_FAILURE: i32 = backend::c::EXIT_FAILURE;
171 
172 /// Return fields from the main executable segment headers ("phdrs") relevant
173 /// to initializing TLS provided to the program at startup.
174 ///
175 /// `addr` will always be non-null, even when the TLS data is absent, so that
176 /// the `addr` and `file_size` parameters are suitable for creating a slice
177 /// with `slice::from_raw_parts`.
178 #[inline]
startup_tls_info() -> StartupTlsInfo179 pub fn startup_tls_info() -> StartupTlsInfo {
180     backend::runtime::tls::startup_tls_info()
181 }
182 
183 /// `(getauxval(AT_PHDR), getauxval(AT_PHENT), getauxval(AT_PHNUM))`—Returns
184 /// the address, ELF segment header size, and number of ELF segment headers for
185 /// the main executable.
186 ///
187 /// # References
188 ///  - [Linux]
189 ///
190 /// [Linux]: https://man7.org/linux/man-pages/man3/getauxval.3.html
191 #[inline]
exe_phdrs() -> (*const c_void, usize, usize)192 pub fn exe_phdrs() -> (*const c_void, usize, usize) {
193     backend::param::auxv::exe_phdrs()
194 }
195 
196 /// `getauxval(AT_ENTRY)`—Returns the address of the program entrypoint.
197 ///
198 /// Most code interested in the program entrypoint address should instead use a
199 /// symbol reference to `_start`. That will be properly PC-relative or
200 /// relocated if needed, and will come with appropriate pointer type and
201 /// pointer provenance.
202 ///
203 /// This function is intended only for use in code that implements those
204 /// relocations, to compute the ASLR offset. It has type `usize`, so it doesn't
205 /// carry any provenance, and it shouldn't be used to dereference memory.
206 ///
207 /// # References
208 ///  - [Linux]
209 ///
210 /// [Linux]: https://man7.org/linux/man-pages/man3/getauxval.3.html
211 #[inline]
entry() -> usize212 pub fn entry() -> usize {
213     backend::param::auxv::entry()
214 }
215 
216 /// `getauxval(AT_RANDOM)`—Returns the address of 16 pseudorandom bytes.
217 ///
218 /// These bytes are for use by libc. For anything else, use the `rand` crate.
219 ///
220 /// # References
221 ///  - [Linux]
222 ///
223 /// [Linux]: https://man7.org/linux/man-pages/man3/getauxval.3.html
224 #[inline]
random() -> *const [u8; 16]225 pub fn random() -> *const [u8; 16] {
226     backend::param::auxv::random()
227 }
228 
229 #[cfg(linux_raw)]
230 pub use backend::runtime::tls::StartupTlsInfo;
231 
232 /// `fork()`—Creates a new process by duplicating the calling process.
233 ///
234 /// On success, the pid of the child process is returned in the parent, and
235 /// `None` is returned in the child.
236 ///
237 /// Unlike its POSIX and libc counterparts, this `fork` does not invoke any
238 /// handlers (such as those registered with `pthread_atfork`).
239 ///
240 /// The program environment in the child after a `fork` and before an `execve`
241 /// is very special. All code that executes in this environment must avoid:
242 ///
243 ///  - Acquiring any other locks that are held in other threads on the parent
244 ///    at the time of the `fork`, as the child only contains one thread, and
245 ///    attempting to acquire such locks will deadlock (though this is [not
246 ///    considered unsafe]).
247 ///
248 ///  - Performing any dynamic allocation using the global allocator, since
249 ///    global allocators may use locks to ensure thread safety, and their locks
250 ///    may not be released in the child process, so attempts to allocate may
251 ///    deadlock (as described in the previous point).
252 ///
253 ///  - Accessing any external state which the parent assumes it has exclusive
254 ///    access to, such as a file protected by a file lock, as this could
255 ///    corrupt the external state.
256 ///
257 ///  - Accessing any random-number-generator state inherited from the parent,
258 ///    as the parent may have the same state and generate the same random
259 ///    numbers, which may violate security invariants.
260 ///
261 ///  - Accessing any thread runtime state, since this function does not update
262 ///    the thread id in the thread runtime, so thread runtime functions could
263 ///    cause undefined behavior.
264 ///
265 ///  - Accessing any memory shared with the parent, such as a [`MAP_SHARED`]
266 ///    mapping, even with anonymous or [`memfd_create`] mappings, as this could
267 ///    cause undefined behavior.
268 ///
269 ///  - Calling any C function which isn't known to be [async-signal-safe], as
270 ///    that could cause undefined behavior. The extent to which this also
271 ///    applies to Rust functions is unclear at this time.
272 ///
273 ///  - And more.
274 ///
275 /// # Safety
276 ///
277 /// The child must avoid accessing any memory shared with the parent in a
278 /// way that invokes undefined behavior. It must avoid accessing any threading
279 /// runtime functions in a way that invokes undefined behavior. And it must
280 /// avoid invoking any undefined behavior through any function that is not
281 /// guaranteed to be async-signal-safe. But, what does async-signal-safe even
282 /// mean in a Rust program? This documentation does not have all the answers.
283 ///
284 /// So you're on your own. And on top of all the troubles with `fork` in
285 /// general, this wrapper implementation is highly experimental.
286 ///
287 /// # References
288 ///  - [POSIX]
289 ///  - [Linux]
290 ///
291 /// # Literary interlude
292 ///
293 /// > Do not jump on ancient uncles.
294 /// > Do not yell at average mice.
295 /// > Do not wear a broom to breakfast.
296 /// > Do not ask a snake’s advice.
297 /// > Do not bathe in chocolate pudding.
298 /// > Do not talk to bearded bears.
299 /// > Do not smoke cigars on sofas.
300 /// > Do not dance on velvet chairs.
301 /// > Do not take a whale to visit
302 /// > Russell’s mother’s cousin’s yacht.
303 /// > And whatever else you do do
304 /// > It is better you
305 /// > Do not.
306 ///
307 /// - “Rules”, by Karla Kuskin
308 ///
309 /// [`MAP_SHARED`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/mmap.html
310 /// [not considered unsafe]: https://doc.rust-lang.org/reference/behavior-not-considered-unsafe.html#deadlocks
311 /// [`memfd_create`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
312 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html
313 /// [Linux]: https://man7.org/linux/man-pages/man2/fork.2.html
314 /// [async-signal-safe]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03
fork() -> io::Result<Fork>315 pub unsafe fn fork() -> io::Result<Fork> {
316     backend::runtime::syscalls::fork()
317 }
318 
319 /// Regular Unix `fork` doesn't tell the child its own PID because it assumes
320 /// the child can just do `getpid`. That's true, but it's more fun if it
321 /// doesn't have to.
322 pub enum Fork {
323     Child(Pid),
324     Parent(Pid),
325 }
326 
327 /// `execveat(dirfd, path.as_c_str(), argv, envp, flags)`—Execute a new
328 /// command using the current process.
329 ///
330 /// # Safety
331 ///
332 /// The `argv` and `envp` pointers must point to NUL-terminated arrays, and
333 /// their contents must be pointers to NUL-terminated byte arrays.
334 ///
335 /// # References
336 ///  - [Linux]
337 ///
338 /// [Linux]: https://man7.org/linux/man-pages/man2/execveat.2.html
339 #[inline]
340 #[cfg(feature = "fs")]
341 #[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))]
execveat<Fd: AsFd>( dirfd: Fd, path: &CStr, argv: *const *const u8, envp: *const *const u8, flags: AtFlags, ) -> io::Errno342 pub unsafe fn execveat<Fd: AsFd>(
343     dirfd: Fd,
344     path: &CStr,
345     argv: *const *const u8,
346     envp: *const *const u8,
347     flags: AtFlags,
348 ) -> io::Errno {
349     backend::runtime::syscalls::execveat(dirfd.as_fd(), path, argv, envp, flags)
350 }
351 
352 /// `execve(path.as_c_str(), argv, envp)`—Execute a new command using the
353 /// current process.
354 ///
355 /// # Safety
356 ///
357 /// The `argv` and `envp` pointers must point to NUL-terminated arrays, and
358 /// their contents must be pointers to NUL-terminated byte arrays.
359 ///
360 /// # References
361 ///  - [Linux]
362 ///
363 /// [Linux]: https://man7.org/linux/man-pages/man2/execve.2.html
364 #[inline]
execve(path: &CStr, argv: *const *const u8, envp: *const *const u8) -> io::Errno365 pub unsafe fn execve(path: &CStr, argv: *const *const u8, envp: *const *const u8) -> io::Errno {
366     backend::runtime::syscalls::execve(path, argv, envp)
367 }
368 
369 /// `sigaction(signal, &new, &old)`—Modify or query a signal handler.
370 ///
371 /// # Safety
372 ///
373 /// You're on your own. And on top of all the troubles with signal handlers,
374 /// this implementation is highly experimental. Even further, it differs from
375 /// the libc `sigaction` in several non-obvious and unsafe ways.
376 ///
377 /// # References
378 ///  - [POSIX]
379 ///  - [Linux]
380 ///
381 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html
382 /// [Linux]: https://man7.org/linux/man-pages/man2/sigaction.2.html
383 #[inline]
sigaction(signal: Signal, new: Option<Sigaction>) -> io::Result<Sigaction>384 pub unsafe fn sigaction(signal: Signal, new: Option<Sigaction>) -> io::Result<Sigaction> {
385     backend::runtime::syscalls::sigaction(signal, new)
386 }
387 
388 /// `sigaltstack(new, old)`—Modify or query a signal stack.
389 ///
390 /// # Safety
391 ///
392 /// You're on your own. And on top of all the troubles with signal handlers,
393 /// this implementation is highly experimental.
394 ///
395 /// # References
396 ///  - [POSIX]
397 ///  - [Linux]
398 ///
399 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaltstack.html
400 /// [Linux]: https://man7.org/linux/man-pages/man2/sigaltstack.2.html
401 #[inline]
sigaltstack(new: Option<Stack>) -> io::Result<Stack>402 pub unsafe fn sigaltstack(new: Option<Stack>) -> io::Result<Stack> {
403     backend::runtime::syscalls::sigaltstack(new)
404 }
405 
406 /// `tkill(tid, sig)`—Send a signal to a thread.
407 ///
408 /// # Safety
409 ///
410 /// You're on your own. And on top of all the troubles with signal handlers,
411 /// this implementation is highly experimental. The warning about the hazard
412 /// of recycled thread ID's applies.
413 ///
414 /// # References
415 ///  - [Linux]
416 ///
417 /// [Linux]: https://man7.org/linux/man-pages/man2/tkill.2.html
418 #[inline]
tkill(tid: Pid, sig: Signal) -> io::Result<()>419 pub unsafe fn tkill(tid: Pid, sig: Signal) -> io::Result<()> {
420     backend::runtime::syscalls::tkill(tid, sig)
421 }
422 
423 /// `sigprocmask(how, set, oldset)`—Adjust the process signal mask.
424 ///
425 /// # Safety
426 ///
427 /// You're on your own. And on top of all the troubles with signal handlers,
428 /// this implementation is highly experimental. Even further, it differs from
429 /// the libc `sigprocmask` in several non-obvious and unsafe ways.
430 ///
431 /// # References
432 ///  - [Linux `sigprocmask`]
433 ///  - [Linux `pthread_sigmask`]
434 ///
435 /// [Linux `sigprocmask`]: https://man7.org/linux/man-pages/man2/sigprocmask.2.html
436 /// [Linux `pthread_sigmask`]: https://man7.org/linux/man-pages/man3/pthread_sigmask.3.html
437 #[inline]
438 #[doc(alias = "pthread_sigmask")]
sigprocmask(how: How, set: Option<&Sigset>) -> io::Result<Sigset>439 pub unsafe fn sigprocmask(how: How, set: Option<&Sigset>) -> io::Result<Sigset> {
440     backend::runtime::syscalls::sigprocmask(how, set)
441 }
442 
443 /// `sigpending()`—Query the pending signals.
444 ///
445 /// # References
446 ///  - [Linux `sigpending`]
447 ///
448 /// [Linux `sigpending`]: https://man7.org/linux/man-pages/man2/sigpending.2.html
449 #[inline]
sigpending() -> Sigset450 pub fn sigpending() -> Sigset {
451     backend::runtime::syscalls::sigpending()
452 }
453 
454 /// `sigsuspend(set)`—Suspend the calling thread and wait for signals.
455 ///
456 /// # References
457 ///  - [Linux `sigsuspend`]
458 ///
459 /// [Linux `sigsuspend`]: https://man7.org/linux/man-pages/man2/sigsuspend.2.html
460 #[inline]
sigsuspend(set: &Sigset) -> io::Result<()>461 pub fn sigsuspend(set: &Sigset) -> io::Result<()> {
462     backend::runtime::syscalls::sigsuspend(set)
463 }
464 
465 /// `sigwait(set)`—Wait for signals.
466 ///
467 /// # Safety
468 ///
469 /// If code elsewhere in the process is depending on delivery of a signal to
470 /// prevent it from executing some code, this could cause it to miss that
471 /// signal and execute that code.
472 ///
473 /// # References
474 ///  - [Linux]
475 ///
476 /// [Linux]: https://man7.org/linux/man-pages/man3/sigwait.3.html
477 #[inline]
sigwait(set: &Sigset) -> io::Result<Signal>478 pub unsafe fn sigwait(set: &Sigset) -> io::Result<Signal> {
479     backend::runtime::syscalls::sigwait(set)
480 }
481 
482 /// `sigwaitinfo(set)`—Wait for signals, returning a [`Siginfo`].
483 ///
484 /// # Safety
485 ///
486 /// If code elsewhere in the process is depending on delivery of a signal to
487 /// prevent it from executing some code, this could cause it to miss that
488 /// signal and execute that code.
489 ///
490 /// # References
491 ///  - [Linux]
492 ///
493 /// [Linux]: https://man7.org/linux/man-pages/man2/sigwaitinfo.2.html
494 #[inline]
sigwaitinfo(set: &Sigset) -> io::Result<Siginfo>495 pub unsafe fn sigwaitinfo(set: &Sigset) -> io::Result<Siginfo> {
496     backend::runtime::syscalls::sigwaitinfo(set)
497 }
498 
499 /// `sigtimedwait(set)`—Wait for signals, optionally with a timeout.
500 ///
501 /// # Safety
502 ///
503 /// If code elsewhere in the process is depending on delivery of a signal to
504 /// prevent it from executing some code, this could cause it to miss that
505 /// signal and execute that code.
506 ///
507 /// # References
508 ///  - [Linux]
509 ///
510 /// [Linux]: https://man7.org/linux/man-pages/man2/sigtimedwait.2.html
511 #[inline]
sigtimedwait(set: &Sigset, timeout: Option<Timespec>) -> io::Result<Siginfo>512 pub unsafe fn sigtimedwait(set: &Sigset, timeout: Option<Timespec>) -> io::Result<Siginfo> {
513     backend::runtime::syscalls::sigtimedwait(set, timeout)
514 }
515 
516 /// `getauxval(AT_SECURE)`—Returns the Linux “secure execution” mode.
517 ///
518 /// Return a boolean value indicating whether “secure execution” mode was
519 /// requested, due to the process having elevated privileges. This includes
520 /// whether the `AT_SECURE` AUX value is set, and whether the initial real UID
521 /// and GID differ from the initial effective UID and GID.
522 ///
523 /// The meaning of “secure execution” mode is beyond the scope of this
524 /// comment.
525 ///
526 /// # References
527 ///  - [Linux]
528 ///
529 /// [Linux]: https://man7.org/linux/man-pages/man3/getauxval.3.html
530 #[cfg(any(
531     linux_raw,
532     any(
533         all(target_os = "android", target_pointer_width = "64"),
534         target_os = "linux",
535     )
536 ))]
537 #[inline]
linux_secure() -> bool538 pub fn linux_secure() -> bool {
539     backend::param::auxv::linux_secure()
540 }
541 
542 /// `brk(addr)`—Change the location of the “program break”.
543 ///
544 /// # Safety
545 ///
546 /// This is not identical to `brk` in libc. libc `brk` may have bookkeeping
547 /// that needs to be kept up to date that this doesn't keep up to date, so
548 /// don't use it unless you are implementing libc.
549 #[cfg(linux_raw)]
550 #[inline]
brk(addr: *mut c_void) -> io::Result<*mut c_void>551 pub unsafe fn brk(addr: *mut c_void) -> io::Result<*mut c_void> {
552     backend::runtime::syscalls::brk(addr)
553 }
554 
555 /// `__SIGRTMIN`—The start of the realtime signal range.
556 ///
557 /// This is the raw `SIGRTMIN` value from the OS, which is not the same as the
558 /// `SIGRTMIN` macro provided by libc. Don't use this unless you are
559 /// implementing libc.
560 #[cfg(linux_raw)]
561 pub const SIGRTMIN: u32 = linux_raw_sys::general::SIGRTMIN;
562 
563 /// `__SIGRTMAX`—The last of the realtime signal range.
564 ///
565 /// This is the raw `SIGRTMAX` value from the OS, which is not the same as the
566 /// `SIGRTMAX` macro provided by libc. Don't use this unless you are
567 /// implementing libc.
568 #[cfg(linux_raw)]
569 pub const SIGRTMAX: u32 = {
570     // Use the actual `SIGRTMAX` value on platforms which define it.
571     #[cfg(not(any(target_arch = "arm", target_arch = "x86", target_arch = "x86_64")))]
572     {
573         linux_raw_sys::general::SIGRTMAX
574     }
575 
576     // On platfoms that don't, derive it from `_NSIG`.
577     #[cfg(any(target_arch = "arm", target_arch = "x86", target_arch = "x86_64"))]
578     {
579         linux_raw_sys::general::_NSIG - 1
580     }
581 };
582