Skip to main content

taproot_c_scape/
lib.rs

1#![doc = include_str!("../README.md")]
2// c-scape does not use std; features that depend on std are in c-gull instead.
3#![no_std]
4// taproot fork: forbid LLVM from lowering our libc function bodies into calls to
5// themselves (loop-idiom recognition). Without this, strcpy/strcat compile down
6// to `mov %rdi,%rax; ret` - the copy is eliminated - in a cdylib/LTO build.
7#![no_builtins]
8// Nightly Rust features that we depend on. On x86_64 there are none, on
9// any feature set: every variadic entry point is fixed arity or a va.rs
10// walker shim, and the printf family formats through the vendored
11// printf_impl port. Other architectures don't have the va.rs walker, so
12// stdio.rs/exec.rs fall back to nightly variadics there.
13#![cfg_attr(not(target_arch = "x86_64"), feature(c_variadic))]
14// Disable some common warnings.
15#![allow(unexpected_cfgs)]
16// Don't warn if `try_into()` is fallible on some targets.
17#![allow(unreachable_patterns)]
18// Don't warn if `try_into()` is fallible on some targets.
19#![allow(irrefutable_let_patterns)]
20
21// Check that our features were used as we intend.
22#[cfg(all(feature = "coexist-with-libc", feature = "take-charge"))]
23compile_error!("Enable only one of \"coexist-with-libc\" and \"take-charge\".");
24#[cfg(all(not(feature = "coexist-with-libc"), not(feature = "take-charge")))]
25compile_error!("Enable one \"coexist-with-libc\" and \"take-charge\".");
26
27extern crate alloc;
28
29// Re-export the libc crate's API. This allows users to depend on the c-scape
30// crate in place of libc.
31pub use libc::*;
32
33#[macro_use]
34mod use_libc;
35
36#[cfg(not(target_os = "wasi"))]
37mod at_fork;
38mod error_str;
39pub mod sync_cell;
40mod sync_ptr;
41
42// Selected libc-compatible interfaces.
43//
44// The goal here isn't necessarily to build a complete libc; it's primarily
45// to provide things that `std` and possibly popular crates are currently
46// using.
47//
48// This effectively undoes the work that `rustix` does: it calls `rustix` and
49// translates it back into a C-like ABI. Ideally, Rust code should just call
50// the `rustix` APIs directly, which are safer, more ergonomic, and skip this
51// whole layer.
52
53#[cfg(feature = "take-charge")]
54use core::ptr::addr_of;
55use errno::{set_errno, Errno};
56
57mod arpa_inet;
58mod atoi;
59mod base64;
60mod brk;
61mod ctype;
62#[cfg(any(target_os = "android", target_os = "linux"))]
63mod dl;
64mod env;
65mod errno_;
66mod error;
67mod exec;
68#[cfg(feature = "take-charge")]
69mod exit;
70mod fs;
71mod glibc_versioning;
72mod int;
73mod io;
74mod jmp;
75mod locale;
76#[cfg(feature = "take-charge")]
77mod malloc;
78mod math;
79mod mem;
80mod mkostemps;
81#[cfg(not(target_os = "wasi"))]
82mod mm;
83mod net;
84mod nss;
85mod path;
86mod pause;
87mod posix_spawn;
88#[cfg(target_arch = "x86_64")]
89pub mod printf_impl;
90#[cfg(not(target_os = "wasi"))]
91mod process;
92mod process_;
93mod pty;
94mod rand;
95mod rand48;
96mod rand_;
97mod regex;
98mod shm;
99#[cfg(not(target_os = "wasi"))]
100#[cfg(feature = "take-charge")]
101mod signal;
102mod sort;
103#[cfg(feature = "take-charge")]
104mod stdio;
105mod strtod;
106mod strtol;
107mod syscall;
108mod syslog;
109mod system;
110mod termios_;
111#[cfg(feature = "thread")]
112#[cfg(feature = "take-charge")]
113mod thread;
114mod glibc_extras;
115mod time;
116#[cfg(target_arch = "x86_64")]
117pub mod va;
118
119#[cfg(feature = "deprecated-and-unimplemented")]
120mod deprecated;
121#[cfg(feature = "todo")]
122mod todo;
123
124/// An ABI-conforming `__dso_handle`.
125#[cfg(feature = "take-charge")]
126#[no_mangle]
127static __dso_handle: UnsafeSendSyncVoidStar =
128    UnsafeSendSyncVoidStar(addr_of!(__dso_handle) as *const _);
129
130/// A type for `__dso_handle`.
131///
132/// `*const c_void` isn't `Send` or `Sync` because a raw pointer could point to
133/// arbitrary data which isn't thread-safe, however `__dso_handle` is used as
134/// an opaque cookie value, and it always points to itself.
135///
136/// Note that in C, `__dso_handle`'s type is usually `void *` which would
137/// correspond to `*mut c_void`, however we can assume the pointee is never
138/// actually mutated.
139#[repr(transparent)]
140#[cfg(feature = "take-charge")]
141struct UnsafeSendSyncVoidStar(*const core::ffi::c_void);
142#[cfg(feature = "take-charge")]
143unsafe impl Send for UnsafeSendSyncVoidStar {}
144#[cfg(feature = "take-charge")]
145unsafe impl Sync for UnsafeSendSyncVoidStar {}
146
147/// This function is called by Origin.
148///
149/// SAFETY: `argc`, `argv`, and `envp` describe incoming program
150/// command-line arguments and environment variables.
151#[cfg(feature = "take-charge")]
152#[cfg(feature = "call-main")]
153#[no_mangle]
154unsafe fn origin_main(argc: usize, argv: *mut *mut u8, envp: *mut *mut u8) -> i32 {
155    extern "C" {
156        fn main(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32;
157    }
158    main(argc as _, argv as _, envp as _)
159}
160
161// utilities
162
163/// Convert a rustix `Result` into an `Option` with the error stored
164/// in `errno`.
165fn convert_res<T>(result: Result<T, rustix::io::Errno>) -> Option<T> {
166    result
167        .map_err(|err| set_errno(Errno(err.raw_os_error())))
168        .ok()
169}
170
171/// A type that implements `lock_api::GetThreadId` for use with
172/// `lock_api::RawReentrantMutex`.
173#[cfg(feature = "thread")]
174#[cfg(feature = "take-charge")]
175pub(crate) struct GetThreadId;
176
177#[cfg(feature = "thread")]
178#[cfg(feature = "take-charge")]
179unsafe impl rustix_futex_sync::lock_api::GetThreadId for GetThreadId {
180    const INIT: Self = Self;
181
182    #[inline]
183    fn nonzero_thread_id(&self) -> core::num::NonZeroUsize {
184        // Use the current thread "raw" value, which origin guarantees uniquely
185        // identifies a thread. `thread::current_id` would also work, but would
186        // be slightly slower on some architectures.
187        origin::thread::current().to_raw_non_null().addr()
188    }
189}
190
191/// If requested, define the global allocator.
192#[cfg(feature = "global-allocator")]
193#[global_allocator]
194static GLOBAL_ALLOCATOR: rustix_dlmalloc::GlobalDlmalloc = rustix_dlmalloc::GlobalDlmalloc;
195
196/// Convert a `KernelSigSet` into a `libc::sigset_t`.
197#[cfg(feature = "take-charge")]
198pub(crate) fn expand_sigset(set: rustix::runtime::KernelSigSet) -> libc::sigset_t {
199    let mut lc = core::mem::MaybeUninit::<libc::sigset_t>::uninit();
200    unsafe {
201        // First create an empty `sigset_t`.
202        let r = libc::sigemptyset(lc.as_mut_ptr());
203        assert_eq!(r, 0);
204        // Then write a `KernelSigSet` into the beginning of it. It's
205        // guaranteed to have a subset of the layout.
206        lc.as_mut_ptr()
207            .cast::<rustix::runtime::KernelSigSet>()
208            .write(set);
209        lc.assume_init()
210    }
211}