├── .github └── workflows │ └── main.yml ├── .gitignore ├── .rustfmt.toml ├── .vscode └── settings.json ├── CODE_OF_CONDUCT.md ├── COPYRIGHT ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-Apache-2.0_WITH_LLVM-exception ├── LICENSE-MIT ├── ORG_CODE_OF_CONDUCT.md ├── README.md ├── c-gull ├── COPYRIGHT ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-Apache-2.0_WITH_LLVM-exception ├── LICENSE-MIT ├── README.md └── src │ ├── lib.rs │ ├── nss.rs │ ├── resolve.rs │ ├── sysconf.rs │ ├── system.rs │ ├── termios_.rs │ ├── time.rs │ ├── use_libc.rs │ └── utmp.rs ├── c-scape ├── COPYRIGHT ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-Apache-2.0_WITH_LLVM-exception ├── LICENSE-MIT ├── README.md ├── build.rs ├── empty │ ├── README.md │ ├── aarch64 │ │ ├── libc.a │ │ ├── libcrypt.a │ │ ├── libdl.a │ │ ├── libgcc.a │ │ ├── libgcc_s.a │ │ ├── libm.a │ │ ├── libpthread.a │ │ ├── libresolv.a │ │ ├── librt.a │ │ ├── libunwind.a │ │ ├── libutil.a │ │ └── libxnet.a │ ├── arm │ │ ├── libc.a │ │ ├── libcrypt.a │ │ ├── libdl.a │ │ ├── libgcc.a │ │ ├── libgcc_s.a │ │ ├── libm.a │ │ ├── libpthread.a │ │ ├── libresolv.a │ │ ├── librt.a │ │ ├── libunwind.a │ │ ├── libutil.a │ │ └── libxnet.a │ ├── empty.s │ ├── riscv64 │ │ ├── libc.a │ │ ├── libcrypt.a │ │ ├── libdl.a │ │ ├── libgcc.a │ │ ├── libgcc_s.a │ │ ├── libm.a │ │ ├── libpthread.a │ │ ├── libresolv.a │ │ ├── librt.a │ │ ├── libunwind.a │ │ ├── libutil.a │ │ └── libxnet.a │ ├── x86 │ │ ├── libc.a │ │ ├── libcrypt.a │ │ ├── libdl.a │ │ ├── libgcc.a │ │ ├── libgcc_s.a │ │ ├── libm.a │ │ ├── libpthread.a │ │ ├── libresolv.a │ │ ├── librt.a │ │ ├── libunwind.a │ │ ├── libutil.a │ │ └── libxnet.a │ └── x86_64 │ │ ├── libc.a │ │ ├── libcrypt.a │ │ ├── libdl.a │ │ ├── libgcc.a │ │ ├── libgcc_s.a │ │ ├── libm.a │ │ ├── libpthread.a │ │ ├── libresolv.a │ │ ├── librt.a │ │ ├── libunwind.a │ │ ├── libutil.a │ │ └── libxnet.a └── src │ ├── arpa_inet.rs │ ├── at_fork.rs │ ├── atoi.rs │ ├── base64.rs │ ├── brk.rs │ ├── ctype │ ├── lsb_abi │ │ ├── ctype_b_loc.rs │ │ ├── ctype_tolower_loc.rs │ │ ├── ctype_toupper_loc.rs │ │ └── mod.rs │ └── mod.rs │ ├── deprecated.rs │ ├── env │ ├── get.rs │ ├── mod.rs │ ├── set.rs │ └── set_thread.rs │ ├── errno_.rs │ ├── error.rs │ ├── error_str.rs │ ├── exec.rs │ ├── exit.rs │ ├── fs │ ├── access.rs │ ├── chmod.rs │ ├── dir │ │ ├── dirfd.rs │ │ ├── mod.rs │ │ ├── opendir.rs │ │ └── readdir.rs │ ├── fadvise.rs │ ├── fallocate.rs │ ├── fchown.rs │ ├── fcntl.rs │ ├── flock.rs │ ├── inotify.rs │ ├── link.rs │ ├── lseek.rs │ ├── makedev.rs │ ├── memfd.rs │ ├── mkdir.rs │ ├── mknod.rs │ ├── mod.rs │ ├── open.rs │ ├── readlink.rs │ ├── realpath.rs │ ├── remove.rs │ ├── rename.rs │ ├── sendfile.rs │ ├── stat.rs │ ├── statvfs.rs │ ├── symlink.rs │ ├── sync.rs │ ├── truncate.rs │ ├── utime.rs │ └── xattr.rs │ ├── glibc_versioning.rs │ ├── int.rs │ ├── io │ ├── dup.rs │ ├── epoll.rs │ ├── isatty.rs │ ├── mod.rs │ ├── pipe.rs │ ├── poll.rs │ ├── read.rs │ ├── select.rs │ ├── splice.rs │ ├── timerfd.rs │ └── write.rs │ ├── jmp.rs │ ├── lib.rs │ ├── locale.rs │ ├── malloc │ └── mod.rs │ ├── math │ ├── complex.rs │ └── mod.rs │ ├── mem │ ├── chk.rs │ ├── mem.rs │ ├── mod.rs │ └── ntbs.rs │ ├── mkostemps.rs │ ├── mm │ └── mod.rs │ ├── net │ ├── inet.rs │ └── mod.rs │ ├── nss.rs │ ├── path │ └── mod.rs │ ├── pause.rs │ ├── posix_spawn.rs │ ├── process │ ├── chdir.rs │ ├── chroot.rs │ ├── daemon.rs │ ├── egid.rs │ ├── euid.rs │ ├── exec.rs │ ├── getcwd.rs │ ├── gid.rs │ ├── groups.rs │ ├── kill.rs │ ├── mod.rs │ ├── pid.rs │ ├── pidfd.rs │ ├── priority.rs │ ├── rlimit.rs │ ├── sid.rs │ ├── system.rs │ ├── uid.rs │ ├── umask.rs │ └── wait.rs │ ├── process_.rs │ ├── pty.rs │ ├── rand │ └── mod.rs │ ├── rand48.rs │ ├── rand_.rs │ ├── regex.rs │ ├── shm │ └── mod.rs │ ├── signal │ └── mod.rs │ ├── sort │ ├── bsearch.rs │ ├── mod.rs │ └── qsort.rs │ ├── stdio.rs │ ├── stdio │ ├── buf.rs │ └── chk.rs │ ├── strtod.rs │ ├── strtol.rs │ ├── sync_ptr.rs │ ├── syscall.rs │ ├── system │ └── mod.rs │ ├── termios_ │ └── mod.rs │ ├── thread │ ├── key.rs │ ├── mod.rs │ ├── mutex.rs │ ├── once.rs │ ├── rwlock.rs │ └── spinlock.rs │ ├── time │ └── mod.rs │ ├── todo.rs │ ├── todo │ ├── aio.rs │ ├── cat.rs │ ├── dl.rs │ ├── fenv.rs │ ├── jmp.rs │ ├── locale.rs │ ├── long_double.rs │ ├── long_double_complex.rs │ ├── pthread_cancel.rs │ ├── set_id.rs │ ├── sysv.rs │ └── wchar.rs │ └── use_libc.rs ├── ci ├── aarch64-o-largefile.patch ├── getsockopt-timeouts.patch ├── s390x-stat-have-nsec.patch ├── tcgets2-tcsets2.patch └── translate-errno.patch ├── example-crates ├── c-gull-example-panic-abort │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ └── src │ │ └── main.rs ├── c-gull-example │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ └── src │ │ └── main.rs ├── c-gull-lto │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ └── src │ │ └── main.rs ├── c-gull-unwinding │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ └── src │ │ └── main.rs ├── c-scape-example-panic-abort │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ └── src │ │ └── main.rs ├── c-scape-example │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ └── src │ │ └── main.rs ├── c-scape-unwinding │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ └── src │ │ └── main.rs ├── custom-allocator │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ └── src │ │ └── main.rs ├── dns │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ └── src │ │ └── main.rs ├── libc-replacement │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs └── threadsafe-setenv │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ └── src │ └── main.rs ├── rust-toolchain.toml └── tests └── example_crates.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | # This file tells tools we use rustfmt. We use the default settings. 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.features": [ 3 | "todo" 4 | ] 5 | } -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Short version for non-lawyers: 2 | 3 | `c-ward` is triple-licensed under Apache 2.0 with the LLVM Exception, 4 | Apache 2.0, and MIT terms. 5 | 6 | 7 | Longer version: 8 | 9 | Copyrights in the `c-ward` project are retained by their contributors. 10 | No copyright assignment is required to contribute to the `c-ward` 11 | project. 12 | 13 | Some files include code derived from Rust's `libstd`; see the comments in 14 | the code for details. 15 | 16 | Except as otherwise noted (below and/or in individual files), `c-ward` 17 | is licensed under: 18 | 19 | - the Apache License, Version 2.0, with the LLVM Exception 20 | or 21 | 22 | - the Apache License, Version 2.0 23 | or 24 | , 25 | - or the MIT license 26 | or 27 | , 28 | 29 | at your option. 30 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "c-ward" 3 | version = "0.22.1" 4 | authors = [ 5 | "Dan Gohman ", 6 | ] 7 | description = "An implementation of libc written in Rust" 8 | license = "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT" 9 | repository = "https://github.com/sunfishcode/c-ward" 10 | edition = "2021" 11 | exclude = ["/.github", "ci"] 12 | publish = false 13 | 14 | [dev-dependencies] 15 | assert_cmd = "2.0.12" 16 | libc = "0.2.155" 17 | 18 | [workspace] 19 | members = [ 20 | "c-scape", 21 | "c-gull", 22 | ] 23 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

c-ward

3 | 4 |

5 | An implementation of libc written in Rust 6 |

7 | 8 |

9 | Github Actions CI Status 10 | zulip chat 11 | crates.io page 12 | crates.io page 13 | docs.rs docs 14 | docs.rs docs 15 |

16 |
17 | 18 | c-ward is an implementation of the libc ABI written in Rust. 19 | 20 | It consists of two crates: 21 | - [c-scape], which is `no_std`, and 22 | - [c-gull], which pulls in c-scape and additionally provides features 23 | using `std`. 24 | 25 | It is a goal of c-ward to be a C ABI layer on top of Rust-idomatic libraries, 26 | rather than to have significant implementation code of its own. 27 | 28 | In theory c-ward could be extended to be ABI-compatible with different 29 | platforms, however currently it is only known to be ABI-compatible with 30 | \*-unknown-linux-gnu\* platforms. 31 | 32 | The primary way this is used is through [Mustang] and [Eyra], as their libc 33 | implementations. It can also be used as a regular library in 34 | ["coexist-with-libc" mode]. 35 | 36 | ## Runtime requirements 37 | 38 | Resolving users and DNS records requires the execution of `getent` which 39 | prints the entries on stdout. On a regular glibc system the `getent` 40 | binary is provided by it and uses the NSS setup as usual. 41 | Similar, a musl system also provides `getent` (but does not use NSS). 42 | 43 | ## Similar crates 44 | 45 | Another libc implementation is [relibc]. [tinyrlibc] is a very minimal set of 46 | libc functions for bare-metal embedded platforms. 47 | 48 | ## Where's the `#![no_builtins]`? 49 | 50 | Normally, a libc implementation would use `#[no_builtins]` to prevent compilers 51 | from noticing the bodies of libc functions implement the semantics of libc 52 | functions and replacing them with calls, which effectively makes them uselessly 53 | recursive calls to themselves. 54 | 55 | However, `#[no_builtins]` is too pessimistic, because we don't need to disable 56 | all pattern matching, just these specific cases. 57 | 58 | So instead, c-scape and c-gull are just careful to avoid open-coding functions 59 | which are known to get pattern-matched into builtins. 60 | 61 | [c-scape]: https://github.com/sunfishcode/c-ward/tree/main/c-scape#readme 62 | [c-gull]: https://github.com/sunfishcode/c-ward/tree/main/c-gull#readme 63 | [relibc]: https://gitlab.redox-os.org/redox-os/relibc/ 64 | [tinyrlibc]: https://github.com/rust-embedded-community/tinyrlibc 65 | [Mustang]: https://github.com/sunfishcode/mustang#readme 66 | [Eyra]: https://github.com/sunfishcode/eyra#readme 67 | ["coexist-with-libc" mode]: https://github.com/sunfishcode/c-ward/blob/main/example-crates/libc-replacement#readme 68 | -------------------------------------------------------------------------------- /c-gull/COPYRIGHT: -------------------------------------------------------------------------------- 1 | Short version for non-lawyers: 2 | 3 | `c-gull` is triple-licensed under Apache 2.0 with the LLVM Exception, 4 | Apache 2.0, and MIT terms. 5 | 6 | 7 | Longer version: 8 | 9 | Copyrights in the `c-gull` project are retained by their contributors. 10 | No copyright assignment is required to contribute to the `c-gull` 11 | project. 12 | 13 | Some files include code derived from Rust's `libstd`; see the comments in 14 | the code for details. 15 | 16 | Except as otherwise noted (below and/or in individual files), `c-gull` 17 | is licensed under: 18 | 19 | - the Apache License, Version 2.0, with the LLVM Exception 20 | or 21 | 22 | - the Apache License, Version 2.0 23 | or 24 | , 25 | - or the MIT license 26 | or 27 | , 28 | 29 | at your option. 30 | -------------------------------------------------------------------------------- /c-gull/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /c-gull/README.md: -------------------------------------------------------------------------------- 1 |
2 |

c-gull

3 | 4 |

5 | A libc implementation in Rust 6 |

7 | 8 |

9 | Github Actions CI Status 10 | zulip chat 11 | crates.io page 12 | docs.rs docs 13 |

14 |
15 | 16 | c-gull is a libc implementation. It is an implementation of the ABI described 17 | by the [libc] crate. 18 | 19 | It is implemented in terms of crates written in Rust, such as [c-scape], 20 | [rustix], [origin], [libm], [realpath-ext], [tz-rs], [printf-compat], 21 | [num-complex], and [posix-regex]. 22 | 23 | Currently it only supports `*-*-linux-gnu` ABIs, though other ABIs could be 24 | added in the future. And currently this mostly focused on features needed by 25 | Rust programs, so it doesn't have all the C-idiomatic things like `qsort` yet, 26 | but they could be added in the future. 27 | 28 | The goal is to have very little code in c-gull itself, by factoring out all of 29 | the significant functionality into independent crates with more Rust-idiomatic 30 | APIs, with c-gull just wrapping those APIs to implement the C ABIs. 31 | 32 | This is currently highly experimental, incomplete, and some things aren't 33 | optimized. And it depends on Nightly Rust. 34 | 35 | ## c-gull's two modes 36 | 37 | c-gull has two main cargo features: "take-charge" and "coexist-with-libc". One 38 | of these must be enabled. 39 | 40 | In "take-charge" mode, c-gull takes charge of the process, handling program 41 | startup (via Origin) providing `malloc` (via c-scape), and other things. This 42 | requires some additional setup; see the [c-gull-example] example crate for 43 | more details. 44 | 45 | In "coexist-with-libc" mode, c-gull can be used as a drop-in (partial) libc 46 | replacement. To use it, just change your typical libc dependency in Cargo.toml 47 | to this: 48 | 49 | ```toml 50 | libc = { version = "", package = "c-gull", features = ["coexist-with-libc"] } 51 | ``` 52 | 53 | and c-gull will replace as many of the system libc implementation with its own 54 | implementations as it can. In particular, it can't replace `malloc` or any of 55 | the pthread functions in this configuration, but it can replace many other 56 | things. See the [libc-replacement example] for more details. 57 | 58 | [libc-replacement example]: https://github.com/sunfishcode/c-ward/blob/main/test-crates/libc-replacement/README.md 59 | [c-scape]: https://crates.io/crates/c-scape 60 | [rustix]: https://crates.io/crates/rustix 61 | [origin]: https://crates.io/crates/origin 62 | [libm]: https://crates.io/crates/libm 63 | [libc]: https://crates.io/crates/libc 64 | [realpath-ext]: https://crates.io/crates/realpath-ext 65 | [tz-rs]: https://crates.io/crates/tz-rs 66 | [printf-compat]: https://crates.io/crates/printf-compat 67 | [num-complex]: https://crates.io/crates/num-complex 68 | [posix-regex]: https://crates.io/crates/posix-regex 69 | [c-gull-example]: https://github.com/sunfishcode/c-ward/blob/main/example-crates/c-gull-example 70 | -------------------------------------------------------------------------------- /c-gull/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | #![feature(sync_unsafe_cell)] 3 | #![cfg_attr(not(feature = "std"), no_std)] 4 | // Don't warn if `try_into()` is fallible on some targets. 5 | #![allow(unreachable_patterns)] 6 | // Don't warn if `try_into()` is fallible on some targets. 7 | #![allow(irrefutable_let_patterns)] 8 | 9 | #[cfg(feature = "std")] 10 | extern crate alloc; 11 | #[allow(unused_extern_crates)] 12 | extern crate c_scape; 13 | 14 | // Re-export the c_scape crate's API, which includes the libc API. This allows 15 | // users to depend on the c-scape crate in place of libc. 16 | pub use c_scape::*; 17 | 18 | #[macro_use] 19 | mod use_libc; 20 | 21 | #[cfg(feature = "std")] 22 | mod nss; 23 | #[cfg(feature = "std")] 24 | mod resolve; 25 | #[cfg(feature = "std")] 26 | mod sysconf; 27 | #[cfg(feature = "std")] 28 | mod system; 29 | #[cfg(feature = "std")] 30 | mod termios_; 31 | #[cfg(feature = "std")] 32 | mod time; 33 | #[cfg(feature = "std")] 34 | #[cfg(not(target_env = "musl"))] 35 | mod utmp; 36 | 37 | #[cfg(feature = "std")] 38 | #[cold] 39 | #[no_mangle] 40 | unsafe extern "C" fn __assert_fail( 41 | expr: *const c_char, 42 | file: *const c_char, 43 | line: c_int, 44 | func: *const c_char, 45 | ) -> ! { 46 | use std::ffi::CStr; 47 | //libc!(libc::__assert_fail(expr, file, line, func)); 48 | 49 | eprintln!( 50 | "Assertion failed: {:?} ({:?}:{}: {:?})", 51 | CStr::from_ptr(expr), 52 | CStr::from_ptr(file), 53 | line, 54 | CStr::from_ptr(func) 55 | ); 56 | std::process::abort(); 57 | } 58 | 59 | // utilities 60 | 61 | /// Convert a rustix `Result` into an `Option` with the error stored 62 | /// in `errno`. 63 | #[cfg(feature = "std")] 64 | fn convert_res(result: Result) -> Option { 65 | use errno::{set_errno, Errno}; 66 | result 67 | .map_err(|err| set_errno(Errno(err.raw_os_error()))) 68 | .ok() 69 | } 70 | -------------------------------------------------------------------------------- /c-gull/src/sysconf.rs: -------------------------------------------------------------------------------- 1 | use libc::c_int; 2 | 3 | #[no_mangle] 4 | unsafe extern "C" fn get_nprocs_conf() -> c_int { 5 | //libc!(libc::get_nprocs_conf()); 6 | 7 | match std::thread::available_parallelism() { 8 | Ok(n) => n.get().try_into().unwrap_or(c_int::MAX), 9 | Err(_) => 1, 10 | } 11 | } 12 | 13 | #[no_mangle] 14 | unsafe extern "C" fn get_nprocs() -> c_int { 15 | //libc!(libc::get_nprocs()); 16 | 17 | get_nprocs_conf() 18 | } 19 | -------------------------------------------------------------------------------- /c-gull/src/system.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::CStr; 2 | use errno::{set_errno, Errno}; 3 | use libc::{c_char, c_int}; 4 | use std::ffi::OsStr; 5 | use std::os::unix::ffi::OsStrExt; 6 | use std::os::unix::process::ExitStatusExt; 7 | 8 | #[no_mangle] 9 | unsafe extern "C" fn system(command: *const c_char) -> c_int { 10 | libc!(libc::system(command)); 11 | 12 | if command.is_null() { 13 | (_system(OsStr::new("exit 0")) == 0).into() 14 | } else { 15 | _system(OsStr::from_bytes(CStr::from_ptr(command).to_bytes())) 16 | } 17 | } 18 | 19 | fn _system(command: &OsStr) -> c_int { 20 | let mut sh = std::process::Command::new("/bin/sh"); 21 | sh.arg("-c"); 22 | sh.arg(command); 23 | 24 | match sh.status() { 25 | Ok(status) => status.into_raw(), 26 | Err(err) => { 27 | set_errno(Errno(err.raw_os_error().unwrap())); 28 | -1 29 | } 30 | } 31 | } 32 | 33 | #[cfg(test)] 34 | mod tests { 35 | use super::*; 36 | 37 | #[test] 38 | fn test_system() { 39 | unsafe { 40 | assert_eq!(system(core::ptr::null()), 1); 41 | 42 | let t = system(c"/bin/sh -c exit\\ 42".as_ptr()); 43 | assert!(libc::WIFEXITED(t)); 44 | assert_eq!(libc::WEXITSTATUS(t), 42); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /c-gull/src/termios_.rs: -------------------------------------------------------------------------------- 1 | //! Termios APIs 2 | //! 3 | //! Most of the termios functions are in c-scape, but ttyname is in c-gull 4 | //! because it depends on rustix's procfs feature, which depends on std. Rustix 5 | //! could be changed to avoid using std here, if it becomes important. 6 | 7 | use crate::convert_res; 8 | use alloc::ffi::CString; 9 | use core::cell::SyncUnsafeCell; 10 | use core::ptr::{copy_nonoverlapping, null_mut}; 11 | use libc::{c_char, c_int, size_t}; 12 | use rustix::fd::BorrowedFd; 13 | 14 | #[no_mangle] 15 | unsafe extern "C" fn ttyname(fd: c_int) -> *mut c_char { 16 | libc!(libc::ttyname(fd)); 17 | 18 | static STORAGE: SyncUnsafeCell> = SyncUnsafeCell::new(None); 19 | 20 | let storage = SyncUnsafeCell::get(&STORAGE); 21 | let name = match convert_res(rustix::termios::ttyname( 22 | BorrowedFd::borrow_raw(fd), 23 | Vec::new(), 24 | )) { 25 | Some(name) => name, 26 | None => return null_mut(), 27 | }; 28 | (*storage) = Some(name); 29 | 30 | (*storage).as_ref().unwrap().as_ptr().cast_mut() 31 | } 32 | 33 | #[no_mangle] 34 | unsafe extern "C" fn ttyname_r(fd: c_int, buf: *mut c_char, buflen: size_t) -> c_int { 35 | libc!(libc::ttyname_r(fd, buf, buflen)); 36 | 37 | let name = match rustix::termios::ttyname(BorrowedFd::borrow_raw(fd), Vec::new()) { 38 | Ok(name) => name, 39 | Err(err) => return err.raw_os_error(), 40 | }; 41 | 42 | let len = name.as_bytes().len(); 43 | if len >= buflen { 44 | return libc::ERANGE; 45 | } 46 | 47 | copy_nonoverlapping(name.as_ptr(), buf, len); 48 | 49 | 0 50 | } 51 | -------------------------------------------------------------------------------- /c-scape/COPYRIGHT: -------------------------------------------------------------------------------- 1 | Short version for non-lawyers: 2 | 3 | `c-scape` is triple-licensed under Apache 2.0 with the LLVM Exception, 4 | Apache 2.0, and MIT terms. 5 | 6 | 7 | Longer version: 8 | 9 | Copyrights in the `c-scape` project are retained by their contributors. 10 | No copyright assignment is required to contribute to the `c-scape` 11 | project. 12 | 13 | Some files include code derived from Rust's `libstd`; see the comments in 14 | the code for details. 15 | 16 | Except as otherwise noted (below and/or in individual files), `c-scape` 17 | is licensed under: 18 | 19 | - the Apache License, Version 2.0, with the LLVM Exception 20 | or 21 | 22 | - the Apache License, Version 2.0 23 | or 24 | , 25 | - or the MIT license 26 | or 27 | , 28 | 29 | at your option. 30 | -------------------------------------------------------------------------------- /c-scape/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /c-scape/README.md: -------------------------------------------------------------------------------- 1 |
2 |

c-scape

3 | 4 |

5 | A layer underneath c-gull 6 |

7 | 8 |

9 | Github Actions CI Status 10 | zulip chat 11 | crates.io page 12 | docs.rs docs 13 |

14 |
15 | 16 | c-scape is a layer underneath [c-gull]. It provides a subset of libc features, 17 | containing only features that don't require Rust's `std` to implement. This 18 | allows it to be used by `std` itself. 19 | 20 | This is currently highly experimental, incomplete, and some things aren't 21 | optimized. And it depends on Nightly Rust. 22 | 23 | ## c-scape's two modes 24 | 25 | [Similar to c-gull], c-scape has "take-charge" and "coexist-with-libc" modes. 26 | One of these must be enabled. 27 | 28 | In "take-charge" mode, c-scape takes charge of the process, handling program 29 | startup (via Origin) providing `malloc` (via c-scape), and other things. This 30 | requires some additional setup; see the [c-scape-example] example crate for 31 | more details. 32 | 33 | In "coexist-with-libc" mode, c-scape can be used as a drop-in (partial) libc 34 | replacement. To use it, just change your typical libc dependency in Cargo.toml 35 | to this: 36 | 37 | ```toml 38 | libc = { version = "", package = "c-scape", features = ["coexist-with-libc"] } 39 | ``` 40 | 41 | [c-gull]: https://github.com/sunfishcode/c-ward/tree/main/c-gull 42 | [c-scape-example]: https://github.com/sunfishcode/c-ward/blob/main/example-crates/c-scape-example 43 | [Similar to c-gull]: https://github.com/sunfishcode/c-ward/tree/main/c-gull#c-gulls-two-modes 44 | -------------------------------------------------------------------------------- /c-scape/build.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "cc")] 2 | use cc::Build; 3 | use std::env::var; 4 | 5 | fn main() { 6 | let take_charge = var("CARGO_FEATURE_TAKE_CHARGE").is_ok(); 7 | 8 | // In coexist-with-libc builds, do nothing. But in take-charge builds, add 9 | // empty versions of libc.a and other libraries to the linker commandline, 10 | // to prevent the linker from finding and linking in the system versions. 11 | if take_charge { 12 | for name in &[ 13 | "gcc", "gcc_s", "util", "rt", "pthread", "m", "dl", "c", "crypt", "xnet", "resolv", 14 | "unwind", 15 | ] { 16 | link_in_empty(name); 17 | } 18 | } 19 | 20 | let os = var("CARGO_CFG_TARGET_OS").unwrap(); 21 | 22 | if os == "linux" || os == "l4re" || os == "android" || os == "emscripten" { 23 | use_feature("linux_like"); 24 | } 25 | 26 | // Add some additional common target combinations. 27 | 28 | // Android and "regular" Linux both use the Linux kernel. 29 | if os == "android" || os == "linux" { 30 | use_feature("linux_kernel"); 31 | } 32 | } 33 | 34 | fn link_in_empty(name: &str) { 35 | let arch = var("CARGO_CFG_TARGET_ARCH").unwrap(); 36 | let outline_path = format!("empty/{}", arch); 37 | let to = format!("{}/lib{}.a", outline_path, name); 38 | println!("cargo:rerun-if-changed={}", to); 39 | 40 | // If "cc" is not enabled, use a pre-built library. 41 | #[cfg(not(feature = "cc"))] 42 | { 43 | println!( 44 | "cargo:rustc-link-search={}", 45 | std::fs::canonicalize(outline_path).unwrap().display() 46 | ); 47 | println!("cargo:rustc-link-lib=static={}", name); 48 | } 49 | 50 | // If "cc" is enabled, build the library from source, update the pre-built 51 | // version, and assert that the pre-built version is checked in. 52 | #[cfg(feature = "cc")] 53 | { 54 | let asm_name = "empty/empty.s"; 55 | let out_dir = var("OUT_DIR").unwrap(); 56 | Build::new().file(&asm_name).compile(&name); 57 | println!("cargo:rerun-if-changed={}", asm_name); 58 | let from = format!("{}/lib{}.a", out_dir, name); 59 | let prev_metadata = std::fs::metadata(&to); 60 | std::fs::copy(&from, &to).unwrap(); 61 | assert!( 62 | prev_metadata.is_ok(), 63 | "{} didn't previously exist; please inspect the new file and `git add` it", 64 | to 65 | ); 66 | assert!( 67 | std::process::Command::new("git") 68 | .arg("diff") 69 | .arg("--quiet") 70 | .arg(&to) 71 | .status() 72 | .unwrap() 73 | .success(), 74 | "{} changed; please inspect the change and `git commit` it", 75 | to 76 | ); 77 | } 78 | } 79 | 80 | fn use_feature(feature: &str) { 81 | println!("cargo:rustc-cfg={}", feature); 82 | } 83 | -------------------------------------------------------------------------------- /c-scape/empty/README.md: -------------------------------------------------------------------------------- 1 | This directory contains empty static libraries such as libc.a, libm.a, and 2 | others, to prevent the linker from opening the platform's version of those 3 | libraries, when we're in "take charge" mode. 4 | -------------------------------------------------------------------------------- /c-scape/empty/aarch64/libc.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/aarch64/libc.a -------------------------------------------------------------------------------- /c-scape/empty/aarch64/libcrypt.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/aarch64/libcrypt.a -------------------------------------------------------------------------------- /c-scape/empty/aarch64/libdl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/aarch64/libdl.a -------------------------------------------------------------------------------- /c-scape/empty/aarch64/libgcc.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/aarch64/libgcc.a -------------------------------------------------------------------------------- /c-scape/empty/aarch64/libgcc_s.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/aarch64/libgcc_s.a -------------------------------------------------------------------------------- /c-scape/empty/aarch64/libm.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/aarch64/libm.a -------------------------------------------------------------------------------- /c-scape/empty/aarch64/libpthread.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/aarch64/libpthread.a -------------------------------------------------------------------------------- /c-scape/empty/aarch64/libresolv.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/aarch64/libresolv.a -------------------------------------------------------------------------------- /c-scape/empty/aarch64/librt.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/aarch64/librt.a -------------------------------------------------------------------------------- /c-scape/empty/aarch64/libunwind.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/aarch64/libunwind.a -------------------------------------------------------------------------------- /c-scape/empty/aarch64/libutil.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/aarch64/libutil.a -------------------------------------------------------------------------------- /c-scape/empty/aarch64/libxnet.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/aarch64/libxnet.a -------------------------------------------------------------------------------- /c-scape/empty/arm/libc.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/arm/libc.a -------------------------------------------------------------------------------- /c-scape/empty/arm/libcrypt.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/arm/libcrypt.a -------------------------------------------------------------------------------- /c-scape/empty/arm/libdl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/arm/libdl.a -------------------------------------------------------------------------------- /c-scape/empty/arm/libgcc.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/arm/libgcc.a -------------------------------------------------------------------------------- /c-scape/empty/arm/libgcc_s.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/arm/libgcc_s.a -------------------------------------------------------------------------------- /c-scape/empty/arm/libm.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/arm/libm.a -------------------------------------------------------------------------------- /c-scape/empty/arm/libpthread.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/arm/libpthread.a -------------------------------------------------------------------------------- /c-scape/empty/arm/libresolv.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/arm/libresolv.a -------------------------------------------------------------------------------- /c-scape/empty/arm/librt.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/arm/librt.a -------------------------------------------------------------------------------- /c-scape/empty/arm/libunwind.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/arm/libunwind.a -------------------------------------------------------------------------------- /c-scape/empty/arm/libutil.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/arm/libutil.a -------------------------------------------------------------------------------- /c-scape/empty/arm/libxnet.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/arm/libxnet.a -------------------------------------------------------------------------------- /c-scape/empty/empty.s: -------------------------------------------------------------------------------- 1 | # This file is empty! 2 | -------------------------------------------------------------------------------- /c-scape/empty/riscv64/libc.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/riscv64/libc.a -------------------------------------------------------------------------------- /c-scape/empty/riscv64/libcrypt.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/riscv64/libcrypt.a -------------------------------------------------------------------------------- /c-scape/empty/riscv64/libdl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/riscv64/libdl.a -------------------------------------------------------------------------------- /c-scape/empty/riscv64/libgcc.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/riscv64/libgcc.a -------------------------------------------------------------------------------- /c-scape/empty/riscv64/libgcc_s.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/riscv64/libgcc_s.a -------------------------------------------------------------------------------- /c-scape/empty/riscv64/libm.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/riscv64/libm.a -------------------------------------------------------------------------------- /c-scape/empty/riscv64/libpthread.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/riscv64/libpthread.a -------------------------------------------------------------------------------- /c-scape/empty/riscv64/libresolv.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/riscv64/libresolv.a -------------------------------------------------------------------------------- /c-scape/empty/riscv64/librt.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/riscv64/librt.a -------------------------------------------------------------------------------- /c-scape/empty/riscv64/libunwind.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/riscv64/libunwind.a -------------------------------------------------------------------------------- /c-scape/empty/riscv64/libutil.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/riscv64/libutil.a -------------------------------------------------------------------------------- /c-scape/empty/riscv64/libxnet.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfishcode/c-ward/2e92f617b9c940dddf2afebe3648704af6b7d894/c-scape/empty/riscv64/libxnet.a -------------------------------------------------------------------------------- /c-scape/empty/x86/libc.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 280 ` 4 | ELFP4(.shstrtab.text.data.bss 4444 -------------------------------------------------------------------------------- /c-scape/empty/x86/libcrypt.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 280 ` 4 | ELFP4(.shstrtab.text.data.bss 4444 -------------------------------------------------------------------------------- /c-scape/empty/x86/libdl.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 280 ` 4 | ELFP4(.shstrtab.text.data.bss 4444 -------------------------------------------------------------------------------- /c-scape/empty/x86/libgcc.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 280 ` 4 | ELFP4(.shstrtab.text.data.bss 4444 -------------------------------------------------------------------------------- /c-scape/empty/x86/libgcc_s.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 280 ` 4 | ELFP4(.shstrtab.text.data.bss 4444 -------------------------------------------------------------------------------- /c-scape/empty/x86/libm.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 280 ` 4 | ELFP4(.shstrtab.text.data.bss 4444 -------------------------------------------------------------------------------- /c-scape/empty/x86/libpthread.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 280 ` 4 | ELFP4(.shstrtab.text.data.bss 4444 -------------------------------------------------------------------------------- /c-scape/empty/x86/libresolv.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 280 ` 4 | ELFP4(.shstrtab.text.data.bss 4444 -------------------------------------------------------------------------------- /c-scape/empty/x86/librt.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 280 ` 4 | ELFP4(.shstrtab.text.data.bss 4444 -------------------------------------------------------------------------------- /c-scape/empty/x86/libunwind.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 280 ` 4 | ELFP4(.shstrtab.text.data.bss 4444 -------------------------------------------------------------------------------- /c-scape/empty/x86/libutil.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 280 ` 4 | ELFP4(.shstrtab.text.data.bss 4444 -------------------------------------------------------------------------------- /c-scape/empty/x86/libxnet.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 280 ` 4 | ELFP4(.shstrtab.text.data.bss 4444 -------------------------------------------------------------------------------- /c-scape/empty/x86_64/libc.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 416 ` 4 | ELF>`@@.shstrtab.text.data.bss @@@@ -------------------------------------------------------------------------------- /c-scape/empty/x86_64/libcrypt.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 416 ` 4 | ELF>`@@.shstrtab.text.data.bss @@@@ -------------------------------------------------------------------------------- /c-scape/empty/x86_64/libdl.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 416 ` 4 | ELF>`@@.shstrtab.text.data.bss @@@@ -------------------------------------------------------------------------------- /c-scape/empty/x86_64/libgcc.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 416 ` 4 | ELF>`@@.shstrtab.text.data.bss @@@@ -------------------------------------------------------------------------------- /c-scape/empty/x86_64/libgcc_s.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 416 ` 4 | ELF>`@@.shstrtab.text.data.bss @@@@ -------------------------------------------------------------------------------- /c-scape/empty/x86_64/libm.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 416 ` 4 | ELF>`@@.shstrtab.text.data.bss @@@@ -------------------------------------------------------------------------------- /c-scape/empty/x86_64/libpthread.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 416 ` 4 | ELF>`@@.shstrtab.text.data.bss @@@@ -------------------------------------------------------------------------------- /c-scape/empty/x86_64/libresolv.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 416 ` 4 | ELF>`@@.shstrtab.text.data.bss @@@@ -------------------------------------------------------------------------------- /c-scape/empty/x86_64/librt.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 416 ` 4 | ELF>`@@.shstrtab.text.data.bss @@@@ -------------------------------------------------------------------------------- /c-scape/empty/x86_64/libunwind.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 416 ` 4 | ELF>`@@.shstrtab.text.data.bss @@@@ -------------------------------------------------------------------------------- /c-scape/empty/x86_64/libutil.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 416 ` 4 | ELF>`@@.shstrtab.text.data.bss @@@@ -------------------------------------------------------------------------------- /c-scape/empty/x86_64/libxnet.a: -------------------------------------------------------------------------------- 1 | ! 2 | / 0 0 0 0 4 ` 3 | empty.o/ 0 0 0 644 416 ` 4 | ELF>`@@.shstrtab.text.data.bss @@@@ -------------------------------------------------------------------------------- /c-scape/src/at_fork.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use rustix_futex_sync::Mutex; 3 | 4 | /// Functions registered with `at_fork`. 5 | static FORK_FUNCS: Mutex = Mutex::new(RegisteredForkFuncs::new()); 6 | 7 | /// A type for holding `fork` callbacks. 8 | #[derive(Default)] 9 | struct RegisteredForkFuncs { 10 | /// Functions called before calling `fork`. 11 | pub(crate) prepare: Vec, 12 | 13 | /// Functions called after calling `fork`, in the parent. 14 | pub(crate) parent: Vec, 15 | 16 | /// Functions called after calling `fork`, in the child. 17 | pub(crate) child: Vec, 18 | } 19 | 20 | impl RegisteredForkFuncs { 21 | pub(crate) const fn new() -> Self { 22 | Self { 23 | prepare: Vec::new(), 24 | parent: Vec::new(), 25 | child: Vec::new(), 26 | } 27 | } 28 | } 29 | 30 | /// Register functions to be called when `fork` is called. 31 | /// 32 | /// The handlers for each phase are called in the following order: 33 | /// - the prepare handlers are called in reverse order of registration; 34 | /// - the parent and child handlers are called in the order of registration. 35 | pub(crate) fn at_fork( 36 | prepare_func: Option, 37 | parent_func: Option, 38 | child_func: Option, 39 | ) { 40 | let mut funcs = FORK_FUNCS.lock(); 41 | 42 | // Add the callbacks to the lists. 43 | funcs.prepare.extend(prepare_func); 44 | funcs.parent.extend(parent_func); 45 | funcs.child.extend(child_func); 46 | } 47 | 48 | /// Fork implementation. 49 | /// 50 | /// # Safety 51 | /// 52 | /// Wildly unsafe. See the documentation comment for 53 | /// [`rustix::runtime::kernel_fork`]. On top of that, this calls the unsafe 54 | /// functions registered with [`at_fork`]. 55 | pub(crate) unsafe fn fork() -> rustix::io::Result> { 56 | let funcs = FORK_FUNCS.lock(); 57 | 58 | // Callbacks before calling `fork`. 59 | funcs.prepare.iter().rev().for_each(|func| func()); 60 | 61 | // Call `fork`. 62 | match rustix::runtime::kernel_fork()? { 63 | rustix::runtime::Fork::Child(pid) => { 64 | // The child's thread record is copied from the parent; 65 | // update it with the child's current-thread-id. 66 | #[cfg(feature = "thread")] 67 | origin::thread::set_current_id_after_a_fork(pid); 68 | #[cfg(not(feature = "thread"))] 69 | let _ = pid; 70 | 71 | // Callbacks after calling `fork`, in the child. 72 | funcs.child.iter().for_each(|func| func()); 73 | Ok(None) 74 | } 75 | rustix::runtime::Fork::ParentOf(child_pid) => { 76 | // Callbacks after calling `fork`, in the parent. 77 | funcs.parent.iter().for_each(|func| func()); 78 | Ok(Some(child_pid)) 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /c-scape/src/atoi.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{MulAssign, Neg, SubAssign}; 2 | use libc::{c_char, c_int, c_long, c_longlong}; 3 | 4 | #[no_mangle] 5 | unsafe extern "C" fn atoi(s: *const c_char) -> c_int { 6 | _atoi(s) 7 | } 8 | 9 | #[no_mangle] 10 | unsafe extern "C" fn atol(s: *const c_char) -> c_long { 11 | _atoi(s) 12 | } 13 | 14 | #[no_mangle] 15 | unsafe extern "C" fn atoll(s: *const c_char) -> c_longlong { 16 | _atoi(s) 17 | } 18 | 19 | unsafe fn _atoi + From + Default>( 20 | mut s: *const c_char, 21 | ) -> T { 22 | let mut negate = false; 23 | let mut n = T::default(); 24 | 25 | // Skip leading whitespace. 26 | while libc::isspace((*s).into()) != 0 { 27 | s = s.add(1); 28 | } 29 | 30 | // Handle a sign. 31 | match *s as u8 { 32 | b'-' => { 33 | negate = true; 34 | s = s.add(1); 35 | } 36 | b'+' => { 37 | s = s.add(1); 38 | } 39 | _ => {} 40 | } 41 | 42 | // Handle digits. 43 | while libc::isdigit((*s).into()) != 0 { 44 | n *= T::from(10u8); 45 | n -= (*s as u8 - b'0').into(); 46 | s = s.add(1); 47 | } 48 | 49 | if !negate { 50 | n = -n; 51 | } 52 | 53 | n 54 | } 55 | -------------------------------------------------------------------------------- /c-scape/src/base64.rs: -------------------------------------------------------------------------------- 1 | use core::cell::SyncUnsafeCell; 2 | use libc::{c_char, c_long}; 3 | 4 | #[no_mangle] 5 | unsafe extern "C" fn a64l(str64: *const c_char) -> c_long { 6 | let str64 = str64.cast::(); 7 | let mut x: u32 = 0; 8 | 9 | for i in 0..6 { 10 | let digit: u32 = match *str64.add(i) { 11 | b @ b'.'..=b'9' => b - b'.', 12 | b @ b'A'..=b'Z' => b - (b'A' - 12), 13 | b @ b'a'..=b'z' => b - (b'a' - 38), 14 | _ => break, 15 | } 16 | .into(); 17 | x |= digit << (6 * i); 18 | } 19 | 20 | x as c_long 21 | } 22 | 23 | #[no_mangle] 24 | unsafe extern "C" fn l64a(value: c_long) -> *mut c_char { 25 | static BUFFER: SyncUnsafeCell<[u8; 6 + 1]> = SyncUnsafeCell::new([0; 6 + 1]); 26 | let buffer = &mut *BUFFER.get(); 27 | 28 | let mut value = value as u32; 29 | let mut i = 0; 30 | 31 | while value != 0 { 32 | buffer[i] = match (value & 63) as u8 { 33 | v @ 0..=11 => b'.' + v, 34 | v @ 12..=37 => b'A' - 12 + v, 35 | v @ 38..=63 => b'a' - 38 + v, 36 | _ => unreachable!(), 37 | }; 38 | i += 1; 39 | value >>= 6; 40 | } 41 | buffer[i] = b'\0'; 42 | 43 | buffer.as_mut_ptr().cast() 44 | } 45 | -------------------------------------------------------------------------------- /c-scape/src/brk.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use core::ptr::{null_mut, without_provenance_mut}; 3 | use errno::{set_errno, Errno}; 4 | use libc::{c_int, c_void, intptr_t}; 5 | 6 | static mut CURRENT: *mut c_void = null_mut(); 7 | 8 | #[no_mangle] 9 | unsafe extern "C" fn brk(ptr: *mut c_void) -> c_int { 10 | libc!(libc::brk(ptr)); 11 | 12 | let new = match convert_res(rustix::runtime::kernel_brk(ptr)) { 13 | Some(new) => new, 14 | None => return -1, 15 | }; 16 | 17 | // The `brk` syscall returns the old value if it failed. 18 | if ptr != new { 19 | set_errno(Errno(libc::ENOMEM)); 20 | return -1; 21 | } 22 | 23 | CURRENT = new; 24 | 25 | 0 26 | } 27 | 28 | #[no_mangle] 29 | unsafe extern "C" fn sbrk(increment: intptr_t) -> *mut c_void { 30 | libc!(libc::sbrk(increment)); 31 | 32 | let mut old = CURRENT; 33 | 34 | if old.is_null() { 35 | // Read the current value from the OS. 36 | old = match convert_res(rustix::runtime::kernel_brk(null_mut())) { 37 | Some(old) => old, 38 | None => return without_provenance_mut(!0), 39 | }; 40 | } 41 | 42 | if increment == 0 { 43 | CURRENT = old; 44 | return old; 45 | } 46 | 47 | // Compute the desired address, and check for overflow. 48 | let want = old 49 | .cast::() 50 | .wrapping_add(increment as usize) 51 | .cast::(); 52 | let ok = if increment > 0 { 53 | want > old 54 | } else { 55 | want < old 56 | }; 57 | if !ok { 58 | CURRENT = old; 59 | set_errno(Errno(libc::ENOMEM)); 60 | return without_provenance_mut(!0); 61 | } 62 | 63 | // Install the new address. 64 | let new = match convert_res(rustix::runtime::kernel_brk(want)) { 65 | Some(new) => new, 66 | None => { 67 | CURRENT = old; 68 | return without_provenance_mut(!0); 69 | } 70 | }; 71 | 72 | CURRENT = new; 73 | 74 | // The `brk` syscall returns the old value if it failed. 75 | if new != want { 76 | set_errno(Errno(libc::ENOMEM)); 77 | return without_provenance_mut(!0); 78 | } 79 | 80 | old 81 | } 82 | -------------------------------------------------------------------------------- /c-scape/src/ctype/lsb_abi/ctype_tolower_loc.rs: -------------------------------------------------------------------------------- 1 | use core::cell::SyncUnsafeCell; 2 | use core::ptr::addr_of; 3 | 4 | use crate::sync_ptr::SyncConstPtr; 5 | 6 | static TABLE: [i32; 128 + 256] = [ 7 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 12 | 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 13 | 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 'a' as i32, 'b' as i32, 'c' as i32, 14 | 'd' as i32, 'e' as i32, 'f' as i32, 'g' as i32, 'h' as i32, 'i' as i32, 'j' as i32, 'k' as i32, 15 | 'l' as i32, 'm' as i32, 'n' as i32, 'o' as i32, 'p' as i32, 'q' as i32, 'r' as i32, 's' as i32, 16 | 't' as i32, 'u' as i32, 'v' as i32, 'w' as i32, 'x' as i32, 'y' as i32, 'z' as i32, 91, 92, 93, 17 | 94, 95, 96, 'a' as i32, 'b' as i32, 'c' as i32, 'd' as i32, 'e' as i32, 'f' as i32, 'g' as i32, 18 | 'h' as i32, 'i' as i32, 'j' as i32, 'k' as i32, 'l' as i32, 'm' as i32, 'n' as i32, 'o' as i32, 19 | 'p' as i32, 'q' as i32, 'r' as i32, 's' as i32, 't' as i32, 'u' as i32, 'v' as i32, 'w' as i32, 20 | 'x' as i32, 'y' as i32, 'z' as i32, 123, 124, 125, 126, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25 | ]; 26 | 27 | static PTR: SyncUnsafeCell> = 28 | SyncUnsafeCell::new(unsafe { SyncConstPtr::new(addr_of!(TABLE[128])) }); 29 | 30 | // https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/libutil---ctype-tolower-loc.html 31 | #[no_mangle] 32 | extern "C" fn __ctype_tolower_loc() -> *mut *const i32 { 33 | SyncUnsafeCell::get(&PTR) as *mut _ as *mut *const i32 34 | } 35 | -------------------------------------------------------------------------------- /c-scape/src/ctype/lsb_abi/ctype_toupper_loc.rs: -------------------------------------------------------------------------------- 1 | use core::cell::SyncUnsafeCell; 2 | use core::ptr::addr_of; 3 | 4 | use crate::sync_ptr::SyncConstPtr; 5 | 6 | static TABLE: [i32; 128 + 256] = [ 7 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 12 | 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 13 | 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 'A' as i32, 'B' as i32, 'C' as i32, 14 | 'D' as i32, 'E' as i32, 'F' as i32, 'G' as i32, 'H' as i32, 'I' as i32, 'J' as i32, 'K' as i32, 15 | 'L' as i32, 'M' as i32, 'N' as i32, 'O' as i32, 'P' as i32, 'Q' as i32, 'R' as i32, 'S' as i32, 16 | 'T' as i32, 'U' as i32, 'V' as i32, 'W' as i32, 'X' as i32, 'Y' as i32, 'Z' as i32, 91, 92, 93, 17 | 94, 95, 96, 'A' as i32, 'B' as i32, 'C' as i32, 'D' as i32, 'E' as i32, 'F' as i32, 'G' as i32, 18 | 'H' as i32, 'I' as i32, 'J' as i32, 'K' as i32, 'L' as i32, 'M' as i32, 'N' as i32, 'O' as i32, 19 | 'P' as i32, 'Q' as i32, 'R' as i32, 'S' as i32, 'T' as i32, 'U' as i32, 'V' as i32, 'W' as i32, 20 | 'X' as i32, 'Y' as i32, 'Z' as i32, 123, 124, 125, 126, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25 | ]; 26 | 27 | static PTR: SyncUnsafeCell> = 28 | SyncUnsafeCell::new(unsafe { SyncConstPtr::new(addr_of!(TABLE[128])) }); 29 | 30 | // https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/libutil---ctype-toupper-loc.html 31 | #[no_mangle] 32 | extern "C" fn __ctype_toupper_loc() -> *mut *const i32 { 33 | SyncUnsafeCell::get(&PTR) as *mut _ as *mut *const i32 34 | } 35 | -------------------------------------------------------------------------------- /c-scape/src/ctype/lsb_abi/mod.rs: -------------------------------------------------------------------------------- 1 | mod ctype_b_loc; 2 | mod ctype_tolower_loc; 3 | mod ctype_toupper_loc; 4 | 5 | #[no_mangle] 6 | extern "C" fn __ctype_get_mb_cur_max() -> libc::size_t { 7 | //libc!(libc::__ctype_get_mb_cur_max()); 8 | 9 | // Just C/POSIX. 10 | 1 11 | } 12 | -------------------------------------------------------------------------------- /c-scape/src/ctype/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(all(target_os = "linux", target_env = "gnu"))] 2 | mod lsb_abi; 3 | 4 | use libc::c_int; 5 | 6 | // This is correct because these functions 7 | // expect the value to be representable 8 | // in an unsigned char, or be EOF, or else 9 | // UB occurs. 10 | // 11 | // EOF is -1, which when truncated to u8 12 | // will be 255, correctly returning false for 13 | // all of these functions. 14 | fn truncate_to_u8(c: c_int) -> u8 { 15 | c as u8 16 | } 17 | 18 | #[no_mangle] 19 | unsafe extern "C" fn isalnum(c: c_int) -> c_int { 20 | libc!(libc::isalnum(c)); 21 | truncate_to_u8(c).is_ascii_alphanumeric() as c_int 22 | } 23 | 24 | #[no_mangle] 25 | unsafe extern "C" fn isalpha(c: c_int) -> c_int { 26 | libc!(libc::isalpha(c)); 27 | truncate_to_u8(c).is_ascii_alphabetic() as c_int 28 | } 29 | 30 | #[no_mangle] 31 | unsafe extern "C" fn isblank(c: c_int) -> c_int { 32 | libc!(libc::isblank(c)); 33 | (c == ' ' as c_int || c == '\t' as c_int) as c_int 34 | } 35 | 36 | #[no_mangle] 37 | unsafe extern "C" fn iscntrl(c: c_int) -> c_int { 38 | libc!(libc::iscntrl(c)); 39 | truncate_to_u8(c).is_ascii_control() as c_int 40 | } 41 | 42 | #[no_mangle] 43 | unsafe extern "C" fn isdigit(c: c_int) -> c_int { 44 | libc!(libc::isdigit(c)); 45 | truncate_to_u8(c).is_ascii_digit() as c_int 46 | } 47 | 48 | #[no_mangle] 49 | unsafe extern "C" fn isgraph(c: c_int) -> c_int { 50 | libc!(libc::isgraph(c)); 51 | truncate_to_u8(c).is_ascii_graphic() as c_int 52 | } 53 | 54 | #[no_mangle] 55 | unsafe extern "C" fn islower(c: c_int) -> c_int { 56 | libc!(libc::islower(c)); 57 | truncate_to_u8(c).is_ascii_lowercase() as c_int 58 | } 59 | 60 | #[no_mangle] 61 | unsafe extern "C" fn isprint(c: c_int) -> c_int { 62 | libc!(libc::isprint(c)); 63 | (c == 32 || isgraph(c) != 0) as c_int 64 | } 65 | 66 | #[no_mangle] 67 | unsafe extern "C" fn ispunct(c: c_int) -> c_int { 68 | libc!(libc::ispunct(c)); 69 | truncate_to_u8(c).is_ascii_punctuation() as c_int 70 | } 71 | 72 | #[no_mangle] 73 | unsafe extern "C" fn isspace(c: c_int) -> c_int { 74 | libc!(libc::isspace(c)); 75 | 76 | // We can't use the core function 77 | // for space testing here because we 78 | // have a slightly different definition. 79 | let is_space = c == ' ' as c_int 80 | || c == '\t' as c_int 81 | || c == '\n' as c_int 82 | || c == '\r' as c_int 83 | || c == 0x0b 84 | || c == 0x0c; 85 | 86 | is_space as c_int 87 | } 88 | 89 | #[no_mangle] 90 | unsafe extern "C" fn isupper(c: c_int) -> c_int { 91 | libc!(libc::isupper(c)); 92 | truncate_to_u8(c).is_ascii_uppercase() as c_int 93 | } 94 | 95 | #[no_mangle] 96 | unsafe extern "C" fn isxdigit(c: c_int) -> c_int { 97 | libc!(libc::isxdigit(c)); 98 | truncate_to_u8(c).is_ascii_hexdigit() as c_int 99 | } 100 | 101 | #[no_mangle] 102 | unsafe extern "C" fn tolower(c: c_int) -> c_int { 103 | libc!(libc::tolower(c)); 104 | c_int::from(truncate_to_u8(c).to_ascii_lowercase()) 105 | } 106 | 107 | #[no_mangle] 108 | unsafe extern "C" fn toupper(c: c_int) -> c_int { 109 | libc!(libc::toupper(c)); 110 | c_int::from(truncate_to_u8(c).to_ascii_uppercase()) 111 | } 112 | -------------------------------------------------------------------------------- /c-scape/src/deprecated.rs: -------------------------------------------------------------------------------- 1 | //! Functions which are not implemented and not expected to be. 2 | 3 | #[no_mangle] 4 | unsafe extern "C" fn gets() { 5 | unimplemented!("gets") 6 | } 7 | #[deprecated] 8 | #[no_mangle] 9 | unsafe extern "C" fn sighold() { 10 | unimplemented!("sighold") 11 | } 12 | #[deprecated] 13 | #[no_mangle] 14 | unsafe extern "C" fn sigignore() { 15 | unimplemented!("sigignore") 16 | } 17 | #[deprecated] 18 | #[no_mangle] 19 | unsafe extern "C" fn sigrelse() { 20 | unimplemented!("sigrelse") 21 | } 22 | #[deprecated] 23 | #[no_mangle] 24 | unsafe extern "C" fn sigset() { 25 | unimplemented!("sigset") 26 | } 27 | #[no_mangle] 28 | unsafe extern "C" fn __xpg_sigpause() { 29 | unimplemented!("__xpg_sigpause") 30 | } 31 | -------------------------------------------------------------------------------- /c-scape/src/env/get.rs: -------------------------------------------------------------------------------- 1 | //! Environment variable handling. 2 | 3 | use core::ptr::null_mut; 4 | use core::slice; 5 | use libc::{c_char, c_int}; 6 | use rustix::ffi::CStr; 7 | 8 | #[no_mangle] 9 | unsafe extern "C" fn getenv(key: *const c_char) -> *mut c_char { 10 | libc!(libc::getenv(key)); 11 | 12 | let key = CStr::from_ptr(key.cast()); 13 | let key_bytes = key.to_bytes(); 14 | 15 | _getenv(key_bytes) 16 | } 17 | 18 | #[no_mangle] 19 | unsafe extern "C" fn secure_getenv(key: *const c_char) -> *mut c_char { 20 | //libc!(libc::secure_getenv(key)); 21 | 22 | if rustix::runtime::linux_secure() { 23 | return null_mut(); 24 | } 25 | 26 | let key = CStr::from_ptr(key.cast()); 27 | let key_bytes = key.to_bytes(); 28 | 29 | _getenv(key_bytes) 30 | } 31 | 32 | pub(crate) unsafe fn _getenv(key_bytes: &[u8]) -> *mut c_char { 33 | let mut ptr = super::set::load_environ(); 34 | 35 | loop { 36 | let env = *ptr; 37 | if env.is_null() { 38 | break; 39 | } 40 | let mut c = env; 41 | while *c != (b'=' as c_char) { 42 | c = c.add(1); 43 | } 44 | if key_bytes 45 | == slice::from_raw_parts(env.cast::(), c.offset_from(env).try_into().unwrap()) 46 | { 47 | return c.add(1); 48 | } 49 | ptr = ptr.add(1); 50 | } 51 | 52 | null_mut() 53 | } 54 | 55 | #[no_mangle] 56 | unsafe extern "C" fn getlogin() -> *mut c_char { 57 | libc!(libc::getlogin()); 58 | 59 | _getenv(b"LOGNAME") 60 | } 61 | 62 | /// GLIBC and origin pass argc, argv, and envp to functions in .init_array, as 63 | /// a non-standard extension. Use priority 98 so that we run before any 64 | /// normal user-defined constructor functions and our own functions which 65 | /// depend on `getenv` working. 66 | #[cfg(any(target_env = "gnu", feature = "take-charge"))] 67 | #[link_section = ".init_array.00098"] 68 | #[used] 69 | static INIT_ARRAY: unsafe extern "C" fn(c_int, *mut *mut c_char, *mut *mut c_char) = { 70 | unsafe extern "C" fn function(_argc: c_int, _argv: *mut *mut c_char, envp: *mut *mut c_char) { 71 | super::set::init_from_envp(envp); 72 | } 73 | function 74 | }; 75 | 76 | #[cfg(not(any(target_env = "gnu", feature = "take-charge")))] 77 | static INIT_ARRAY: Unimplemented = Unimplemented::new(); 78 | -------------------------------------------------------------------------------- /c-scape/src/env/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod get; 2 | #[cfg_attr(feature = "threadsafe-setenv", path = "set_thread.rs")] 3 | pub(crate) mod set; 4 | -------------------------------------------------------------------------------- /c-scape/src/errno_.rs: -------------------------------------------------------------------------------- 1 | use alloc::borrow::ToOwned; 2 | use alloc::format; 3 | use core::cell::SyncUnsafeCell; 4 | use core::ptr::{copy_nonoverlapping, null_mut}; 5 | use libc::{c_char, c_int}; 6 | 7 | /// Return the address of the thread-local `errno` state. 8 | /// 9 | /// This function conforms to the [LSB `__errno_location`] ABI. 10 | /// 11 | /// [LSB `__errno_location`]: https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/baselib---errno-location.html 12 | #[no_mangle] 13 | #[cfg(feature = "take-charge")] 14 | unsafe extern "C" fn __errno_location() -> *mut c_int { 15 | libc!(libc::__errno_location()); 16 | 17 | #[cfg(feature = "thread")] 18 | return origin::thread::errno_location(); 19 | 20 | #[cfg(not(feature = "thread"))] 21 | { 22 | static mut ERRNO: i32 = 0; 23 | return core::ptr::addr_of_mut!(ERRNO); 24 | } 25 | } 26 | 27 | #[no_mangle] 28 | unsafe extern "C" fn strerror(errnum: c_int) -> *mut c_char { 29 | libc!(libc::strerror(errnum)); 30 | 31 | static STORAGE: SyncUnsafeCell<[c_char; 256]> = SyncUnsafeCell::new([0; 256]); 32 | 33 | let storage = SyncUnsafeCell::get(&STORAGE); 34 | __xpg_strerror_r(errnum, (*storage).as_mut_ptr(), (*storage).len()); 35 | (*storage).as_mut_ptr() 36 | } 37 | 38 | // 39 | #[no_mangle] 40 | unsafe extern "C" fn __xpg_strerror_r(errnum: c_int, buf: *mut c_char, buflen: usize) -> c_int { 41 | //libc!(libc::__xpg_strerror_r(errnum, buf, buflen)); 42 | libc!(libc::strerror_r(errnum, buf, buflen)); 43 | 44 | if buflen == 0 { 45 | return libc::ERANGE; 46 | } 47 | 48 | let message = if errnum == 0 { 49 | "Success".to_owned() 50 | } else { 51 | match crate::error_str::error_str(rustix::io::Errno::from_raw_os_error(errnum)) { 52 | Some(s) => s.to_owned(), 53 | None => format!("Unknown error {}", errnum), 54 | } 55 | }; 56 | 57 | let min = core::cmp::min(buflen - 1, message.len()); 58 | copy_nonoverlapping(message.as_ptr().cast(), buf, min); 59 | buf.add(min).write(b'\0' as libc::c_char); 60 | 0 61 | } 62 | 63 | /// glibc has a non-standard return type for its `strerror_r`. 64 | // 65 | #[cfg(target_env = "gnu")] 66 | #[no_mangle] 67 | unsafe extern "C" fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: usize) -> *mut c_char { 68 | //libc!(libc::strerror_r(errnum, buf, buflen)); 69 | if __xpg_strerror_r(errnum, buf, buflen) == 0 { 70 | buf 71 | } else { 72 | null_mut() 73 | } 74 | } 75 | 76 | #[cfg(not(target_env = "gnu"))] 77 | #[no_mangle] 78 | unsafe extern "C" fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: usize) -> c_int { 79 | libc!(libc::strerror_r(errnum, buf, buflen)); 80 | __xpg_strerror_r(errnum, buf, buflen) 81 | } 82 | -------------------------------------------------------------------------------- /c-scape/src/error.rs: -------------------------------------------------------------------------------- 1 | #[no_mangle] 2 | static mut error_message_count: libc::c_uint = 0; 3 | #[no_mangle] 4 | static mut error_one_per_line: libc::c_int = 0; 5 | #[no_mangle] 6 | static mut error_print_progname: Option = None; 7 | -------------------------------------------------------------------------------- /c-scape/src/exec.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use libc::{c_int, c_void}; 3 | 4 | #[cfg(not(target_os = "wasi"))] 5 | #[no_mangle] 6 | unsafe extern "C" fn fork() -> c_int { 7 | libc!(libc::fork()); 8 | match convert_res(crate::at_fork::fork()) { 9 | Some(Some(pid)) => pid.as_raw_nonzero().get() as c_int, 10 | Some(None) => 0, 11 | None => -1, 12 | } 13 | } 14 | 15 | #[allow(deprecated)] 16 | #[cfg(not(target_os = "wasi"))] 17 | #[no_mangle] 18 | unsafe extern "C" fn vfork() -> c_int { 19 | libc!(libc::vfork()); 20 | // It's not sound to do an actual `vfork` in Rust, so we just do a full 21 | // `fork`. 22 | fork() 23 | } 24 | 25 | // 26 | #[cfg(not(target_os = "wasi"))] 27 | #[no_mangle] 28 | unsafe extern "C" fn __register_atfork( 29 | prepare: Option, 30 | parent: Option, 31 | child: Option, 32 | _dso_handle: *mut c_void, 33 | ) -> c_int { 34 | //libc!(libc::__register_atfork(prepare, parent, child, _dso_handle)); 35 | crate::at_fork::at_fork(prepare, parent, child); 36 | 0 37 | } 38 | 39 | #[cfg(feature = "todo")] 40 | #[no_mangle] 41 | unsafe extern "C" fn clone3() { 42 | //libc!(libc::clone3()); 43 | 44 | // We also have disabled `clone3` support in `dlsym` for now. 45 | todo!("clone3") 46 | } 47 | -------------------------------------------------------------------------------- /c-scape/src/exit.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use libc::{c_int, c_void}; 3 | 4 | /// Register a function to be called when `exit` is called. 5 | /// 6 | /// This function conforms to the [LSB `__cxa_atexit`] ABI. 7 | /// 8 | /// [LSB `__cxa_atexit`]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/baselib---cxa-atexit.html 9 | #[no_mangle] 10 | unsafe extern "C" fn __cxa_atexit( 11 | func: unsafe extern "C" fn(*mut c_void), 12 | arg: *mut c_void, 13 | _dso: *mut c_void, 14 | ) -> c_int { 15 | // Tell Rust it's ok to send `arg` across threads even though it's a 16 | // `*mut c_void`. 17 | struct SendSync(*mut c_void); 18 | unsafe impl Send for SendSync {} 19 | unsafe impl Sync for SendSync {} 20 | let arg = SendSync(arg); 21 | 22 | origin::program::at_exit(Box::new(move || { 23 | let arg: SendSync = arg; 24 | func(arg.0); 25 | })); 26 | 27 | 0 28 | } 29 | 30 | #[no_mangle] 31 | unsafe extern "C" fn __cxa_finalize(_d: *mut c_void) {} 32 | 33 | /// C-compatible `atexit`. Consider using `__cxa_atexit` instead. 34 | #[no_mangle] 35 | unsafe extern "C" fn atexit(func: extern "C" fn()) -> c_int { 36 | libc!(libc::atexit(func)); 37 | origin::program::at_exit(Box::new(move || func())); 38 | 0 39 | } 40 | 41 | /// C-compatible `exit`. 42 | /// 43 | /// Call all the registered at-exit functions, and exit the program. 44 | #[no_mangle] 45 | unsafe extern "C" fn exit(status: c_int) -> ! { 46 | libc!(libc::exit(status)); 47 | origin::program::exit(status) 48 | } 49 | 50 | /// C-compatible `_Exit`. 51 | /// 52 | /// Just exit the process. 53 | #[no_mangle] 54 | unsafe extern "C" fn _Exit(status: c_int) -> ! { 55 | //libc!(libc::_Exit(status)); 56 | origin::program::immediate_exit(status) 57 | } 58 | 59 | /// POSIX-compatible `_exit`. 60 | /// 61 | /// Just exit the process. 62 | #[no_mangle] 63 | unsafe extern "C" fn _exit(status: c_int) -> ! { 64 | libc!(libc::_exit(status)); 65 | _Exit(status) 66 | } 67 | 68 | #[cold] 69 | #[no_mangle] 70 | unsafe extern "C" fn __stack_chk_fail() -> ! { 71 | let message = b"*** stack smashing detected ***"; 72 | let _ = libc::write(libc::STDERR_FILENO, message.as_ptr().cast(), message.len()); 73 | libc::abort() 74 | } 75 | 76 | // If we were building a dynamic library, this function could have a 77 | // restricted visibility. 78 | #[cold] 79 | #[no_mangle] 80 | unsafe extern "C" fn __stack_chk_fail_local() -> ! { 81 | __stack_chk_fail() 82 | } 83 | -------------------------------------------------------------------------------- /c-scape/src/fs/access.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::CStr; 2 | use rustix::fd::BorrowedFd; 3 | use rustix::fs::{Access, AtFlags}; 4 | 5 | use libc::{c_char, c_int}; 6 | 7 | use crate::convert_res; 8 | 9 | #[no_mangle] 10 | unsafe extern "C" fn access(pathname: *const c_char, amode: c_int) -> c_int { 11 | libc!(libc::access(pathname, amode)); 12 | 13 | faccessat(libc::AT_FDCWD, pathname, amode, 0) 14 | } 15 | 16 | #[no_mangle] 17 | unsafe extern "C" fn faccessat( 18 | fd: c_int, 19 | pathname: *const c_char, 20 | amode: c_int, 21 | flags: c_int, 22 | ) -> c_int { 23 | libc!(libc::faccessat(fd, pathname, amode, flags)); 24 | 25 | match convert_res(rustix::fs::accessat( 26 | BorrowedFd::borrow_raw(fd), 27 | CStr::from_ptr(pathname.cast()), 28 | Access::from_bits(amode as _).unwrap(), 29 | AtFlags::from_bits(flags as _).unwrap(), 30 | )) { 31 | Some(()) => 0, 32 | None => -1, 33 | } 34 | } 35 | 36 | #[no_mangle] 37 | unsafe extern "C" fn euidaccess(pathname: *const c_char, amode: c_int) -> c_int { 38 | libc!(libc::euidaccess(pathname, amode)); 39 | faccessat(libc::AT_FDCWD, pathname, amode, libc::AT_EACCESS) 40 | } 41 | 42 | #[no_mangle] 43 | unsafe extern "C" fn eaccess(pathname: *const c_char, amode: c_int) -> c_int { 44 | libc!(libc::eaccess(pathname, amode)); 45 | euidaccess(pathname, amode) 46 | } 47 | -------------------------------------------------------------------------------- /c-scape/src/fs/chmod.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::CStr; 2 | use rustix::fd::BorrowedFd; 3 | use rustix::fs::{AtFlags, Mode, CWD}; 4 | 5 | use libc::{c_char, c_int, mode_t}; 6 | 7 | use crate::convert_res; 8 | 9 | #[no_mangle] 10 | unsafe extern "C" fn chmod(pathname: *const c_char, mode: mode_t) -> c_int { 11 | libc!(libc::chmod(pathname, mode)); 12 | 13 | fchmodat(libc::AT_FDCWD, pathname, mode, 0) 14 | } 15 | 16 | #[no_mangle] 17 | unsafe extern "C" fn fchmod(fd: c_int, mode: mode_t) -> c_int { 18 | libc!(libc::fchmod(fd, mode)); 19 | 20 | let mode = Mode::from_bits((mode & !libc::S_IFMT) as _).unwrap(); 21 | match convert_res(rustix::fs::fchmod(BorrowedFd::borrow_raw(fd), mode)) { 22 | Some(()) => 0, 23 | None => -1, 24 | } 25 | } 26 | 27 | #[no_mangle] 28 | unsafe extern "C" fn fchmodat( 29 | fd: c_int, 30 | pathname: *const c_char, 31 | mode: mode_t, 32 | flags: c_int, 33 | ) -> c_int { 34 | libc!(libc::fchmodat(fd, pathname, mode, flags)); 35 | 36 | if flags != 0 { 37 | unimplemented!("flags support in fchmodat"); 38 | } 39 | 40 | let mode = Mode::from_bits((mode & !libc::S_IFMT) as _).unwrap(); 41 | match convert_res(rustix::fs::chmodat( 42 | CWD, 43 | CStr::from_ptr(pathname.cast()), 44 | mode, 45 | AtFlags::empty(), 46 | )) { 47 | Some(()) => 0, 48 | None => -1, 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /c-scape/src/fs/dir/dirfd.rs: -------------------------------------------------------------------------------- 1 | use libc::{c_int, c_void}; 2 | 3 | use super::CScapeDir; 4 | 5 | #[no_mangle] 6 | unsafe extern "C" fn dirfd(dir: *mut c_void) -> c_int { 7 | libc!(libc::dirfd(dir.cast())); 8 | 9 | let dir = dir.cast::(); 10 | (*dir).fd 11 | } 12 | -------------------------------------------------------------------------------- /c-scape/src/fs/dir/mod.rs: -------------------------------------------------------------------------------- 1 | mod dirfd; 2 | mod opendir; 3 | #[cfg(not(target_os = "wasi"))] 4 | mod readdir; 5 | 6 | use rustix::fd::RawFd; 7 | 8 | union LibcDirStorage { 9 | dirent: libc::dirent, 10 | dirent64: libc::dirent64, 11 | } 12 | 13 | struct CScapeDir { 14 | dir: rustix::fs::Dir, 15 | storage: LibcDirStorage, 16 | fd: RawFd, 17 | } 18 | -------------------------------------------------------------------------------- /c-scape/src/fs/dir/opendir.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use core::ffi::CStr; 3 | use rustix::fd::{FromRawFd, IntoRawFd, OwnedFd}; 4 | use rustix::fs::{Mode, OFlags, CWD}; 5 | 6 | use core::mem::zeroed; 7 | use core::ptr::null_mut; 8 | use libc::{c_char, c_int, c_void}; 9 | 10 | use super::CScapeDir; 11 | use crate::convert_res; 12 | 13 | #[no_mangle] 14 | unsafe extern "C" fn opendir(pathname: *const c_char) -> *mut c_void { 15 | libc!(libc::opendir(pathname).cast()); 16 | 17 | match convert_res(rustix::fs::openat( 18 | CWD, 19 | CStr::from_ptr(pathname.cast()), 20 | OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC, 21 | Mode::empty(), 22 | )) { 23 | Some(fd) => fdopendir(fd.into_raw_fd()), 24 | None => null_mut(), 25 | } 26 | } 27 | 28 | #[no_mangle] 29 | unsafe extern "C" fn fdopendir(fd: c_int) -> *mut c_void { 30 | libc!(libc::fdopendir(fd).cast()); 31 | 32 | // Use the unsafe `Dir::new` API, because that avoids opening ".", 33 | // which requires additional permissions that we may not have. It's 34 | // up to users of `fdopendir` to ensure that the fd isn't otherwise 35 | // used. 36 | match convert_res(rustix::fs::Dir::new(OwnedFd::from_raw_fd(fd))) { 37 | Some(dir) => Box::into_raw(Box::new(CScapeDir { 38 | dir, 39 | storage: zeroed(), 40 | fd, 41 | })) 42 | .cast(), 43 | None => null_mut(), 44 | } 45 | } 46 | 47 | #[no_mangle] 48 | unsafe extern "C" fn closedir(dir: *mut c_void) -> c_int { 49 | libc!(libc::closedir(dir.cast())); 50 | 51 | drop(Box::::from_raw(dir.cast())); 52 | 0 53 | } 54 | -------------------------------------------------------------------------------- /c-scape/src/fs/fadvise.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use core::num::NonZeroU64; 3 | use errno::{set_errno, Errno}; 4 | use libc::{c_int, off64_t, off_t}; 5 | use rustix::fd::BorrowedFd; 6 | use rustix::fs::Advice; 7 | 8 | #[no_mangle] 9 | unsafe extern "C" fn posix_fadvise64( 10 | fd: c_int, 11 | offset: off64_t, 12 | len: off64_t, 13 | advice: c_int, 14 | ) -> c_int { 15 | libc!(libc::posix_fadvise64(fd, offset, len, advice)); 16 | 17 | let advice = match advice { 18 | libc::POSIX_FADV_NORMAL => Advice::Normal, 19 | libc::POSIX_FADV_SEQUENTIAL => Advice::Sequential, 20 | libc::POSIX_FADV_RANDOM => Advice::Random, 21 | libc::POSIX_FADV_NOREUSE => Advice::NoReuse, 22 | libc::POSIX_FADV_WILLNEED => Advice::WillNeed, 23 | libc::POSIX_FADV_DONTNEED => Advice::DontNeed, 24 | _ => { 25 | set_errno(Errno(libc::EINVAL)); 26 | return -1; 27 | } 28 | }; 29 | match convert_res(rustix::fs::fadvise( 30 | BorrowedFd::borrow_raw(fd), 31 | offset as u64, 32 | NonZeroU64::new(len as u64), 33 | advice, 34 | )) { 35 | Some(()) => 0, 36 | None => -1, 37 | } 38 | } 39 | 40 | #[no_mangle] 41 | unsafe extern "C" fn posix_fadvise(fd: c_int, offset: off_t, len: off_t, advice: c_int) -> c_int { 42 | libc!(libc::posix_fadvise(fd, offset, len, advice)); 43 | 44 | posix_fadvise64(fd, offset.into(), len.into(), advice) 45 | } 46 | -------------------------------------------------------------------------------- /c-scape/src/fs/fallocate.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use libc::{c_int, off64_t, off_t}; 3 | use rustix::fd::BorrowedFd; 4 | use rustix::fs::FallocateFlags; 5 | 6 | #[no_mangle] 7 | unsafe extern "C" fn fallocate(fd: c_int, mode: c_int, offset: off_t, len: off_t) -> c_int { 8 | libc!(libc::fallocate(fd, mode, offset, len)); 9 | fallocate64(fd, mode, offset as _, len as _) 10 | } 11 | 12 | #[no_mangle] 13 | unsafe extern "C" fn fallocate64(fd: c_int, mode: c_int, offset: off64_t, len: off64_t) -> c_int { 14 | libc!(libc::fallocate64(fd, mode, offset, len)); 15 | 16 | let fd = BorrowedFd::borrow_raw(fd); 17 | let mode = FallocateFlags::from_bits_retain(mode as _); 18 | match convert_res(rustix::fs::fallocate(fd, mode, offset as _, len as _)) { 19 | Some(()) => 0, 20 | None => -1, 21 | } 22 | } 23 | 24 | #[no_mangle] 25 | unsafe extern "C" fn posix_fallocate(fd: c_int, offset: off_t, len: off_t) -> c_int { 26 | libc!(libc::posix_fallocate(fd, offset, len)); 27 | fallocate64(fd, 0, offset as _, len as _) 28 | } 29 | 30 | #[no_mangle] 31 | unsafe extern "C" fn posix_fallocate64(fd: c_int, offset: off64_t, len: off64_t) -> c_int { 32 | libc!(libc::posix_fallocate64(fd, offset, len)); 33 | fallocate64(fd, 0, offset, len) 34 | } 35 | -------------------------------------------------------------------------------- /c-scape/src/fs/fchown.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use core::ffi::CStr; 3 | use libc::{c_char, c_int, c_uint}; 4 | use rustix::fd::BorrowedFd; 5 | use rustix::fs::AtFlags; 6 | 7 | #[no_mangle] 8 | unsafe extern "C" fn fchownat( 9 | dirfd: c_int, 10 | pathname: *const c_char, 11 | owner: libc::uid_t, 12 | group: libc::gid_t, 13 | flags: c_int, 14 | ) -> c_int { 15 | libc!(libc::fchownat(dirfd, pathname, owner, group, flags)); 16 | 17 | let pathname = CStr::from_ptr(pathname); 18 | let owner = Some(rustix::process::Uid::from_raw(owner)); 19 | let group = Some(rustix::process::Gid::from_raw(group)); 20 | let flags = AtFlags::from_bits_retain(flags as c_uint); 21 | let dirfd = BorrowedFd::borrow_raw(dirfd); 22 | match convert_res(rustix::fs::chownat(dirfd, pathname, owner, group, flags)) { 23 | Some(()) => 0, 24 | None => -1, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /c-scape/src/fs/flock.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use errno::{set_errno, Errno}; 3 | use libc::c_int; 4 | use rustix::fd::BorrowedFd; 5 | use rustix::fs::FlockOperation; 6 | 7 | #[no_mangle] 8 | unsafe extern "C" fn flock(fd: c_int, operation: c_int) -> c_int { 9 | libc!(libc::flock(fd, operation)); 10 | 11 | let fd = BorrowedFd::borrow_raw(fd); 12 | let operation = if operation == libc::LOCK_SH { 13 | FlockOperation::LockShared 14 | } else if operation == libc::LOCK_EX { 15 | FlockOperation::LockExclusive 16 | } else if operation == libc::LOCK_UN { 17 | FlockOperation::Unlock 18 | } else if operation == libc::LOCK_SH | libc::LOCK_NB { 19 | FlockOperation::NonBlockingLockShared 20 | } else if operation == libc::LOCK_EX | libc::LOCK_NB { 21 | FlockOperation::NonBlockingLockExclusive 22 | } else if operation == libc::LOCK_UN | libc::LOCK_NB { 23 | FlockOperation::NonBlockingUnlock 24 | } else { 25 | set_errno(Errno(libc::EINVAL)); 26 | return -1; 27 | }; 28 | 29 | match convert_res(rustix::fs::flock(fd, operation)) { 30 | Some(()) => 0, 31 | None => -1, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /c-scape/src/fs/inotify.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use core::ffi::CStr; 3 | use libc::{c_char, c_int}; 4 | use rustix::fd::{BorrowedFd, IntoRawFd}; 5 | use rustix::fs::inotify::{self, CreateFlags, WatchFlags}; 6 | 7 | #[no_mangle] 8 | unsafe extern "C" fn inotify_init() -> c_int { 9 | libc!(libc::inotify_init()); 10 | inotify_init1(0) 11 | } 12 | 13 | #[no_mangle] 14 | unsafe extern "C" fn inotify_init1(flags: c_int) -> c_int { 15 | libc!(libc::inotify_init1(flags)); 16 | 17 | let flags = CreateFlags::from_bits(flags as _).unwrap(); 18 | match convert_res(inotify::init(flags)) { 19 | Some(fd) => fd.into_raw_fd(), 20 | None => -1, 21 | } 22 | } 23 | 24 | #[no_mangle] 25 | unsafe extern "C" fn inotify_add_watch(fd: c_int, pathname: *const c_char, mask: u32) -> c_int { 26 | libc!(libc::inotify_add_watch(fd, pathname, mask)); 27 | 28 | let fd = BorrowedFd::borrow_raw(fd); 29 | let pathname = CStr::from_ptr(pathname); 30 | let mask = WatchFlags::from_bits(mask).unwrap(); 31 | match convert_res(inotify::add_watch(fd, pathname, mask)) { 32 | Some(wd) => wd, 33 | None => -1, 34 | } 35 | } 36 | 37 | #[no_mangle] 38 | unsafe extern "C" fn inotify_rm_watch(fd: c_int, wd: c_int) -> c_int { 39 | libc!(libc::inotify_rm_watch(fd, wd)); 40 | 41 | let fd = BorrowedFd::borrow_raw(fd); 42 | match convert_res(inotify::remove_watch(fd, wd)) { 43 | Some(()) => 0, 44 | None => -1, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /c-scape/src/fs/link.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::CStr; 2 | use rustix::fd::BorrowedFd; 3 | use rustix::fs::AtFlags; 4 | 5 | use libc::{c_char, c_int}; 6 | 7 | use crate::convert_res; 8 | 9 | #[no_mangle] 10 | unsafe extern "C" fn link(oldpath: *const c_char, newpath: *const c_char) -> c_int { 11 | libc!(libc::link(oldpath, newpath)); 12 | 13 | linkat(libc::AT_FDCWD, oldpath, libc::AT_FDCWD, newpath, 0) 14 | } 15 | 16 | #[no_mangle] 17 | unsafe extern "C" fn linkat( 18 | olddirfd: c_int, 19 | oldpath: *const c_char, 20 | newdirfd: c_int, 21 | newpath: *const c_char, 22 | flags: c_int, 23 | ) -> c_int { 24 | libc!(libc::linkat(olddirfd, oldpath, newdirfd, newpath, flags)); 25 | 26 | let flags = AtFlags::from_bits(flags as _).unwrap(); 27 | match convert_res(rustix::fs::linkat( 28 | BorrowedFd::borrow_raw(olddirfd), 29 | CStr::from_ptr(oldpath.cast()), 30 | BorrowedFd::borrow_raw(newdirfd), 31 | CStr::from_ptr(newpath.cast()), 32 | flags, 33 | )) { 34 | Some(()) => 0, 35 | None => -1, 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /c-scape/src/fs/lseek.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use errno::{set_errno, Errno}; 3 | use libc::{c_int, off64_t, off_t}; 4 | use rustix::fd::BorrowedFd; 5 | 6 | #[no_mangle] 7 | unsafe extern "C" fn lseek(fd: c_int, offset: off_t, whence: c_int) -> off_t { 8 | libc!(libc::lseek(fd, offset, whence)); 9 | 10 | match lseek64(fd, off64_t::from(offset), whence).try_into() { 11 | Ok(v) => v, 12 | Err(_) => { 13 | set_errno(Errno(libc::EOVERFLOW)); 14 | -1 15 | } 16 | } 17 | } 18 | 19 | #[no_mangle] 20 | unsafe extern "C" fn lseek64(fd: c_int, offset: off64_t, whence: c_int) -> off64_t { 21 | libc!(libc::lseek64(fd, offset, whence)); 22 | 23 | if fd == -1 { 24 | set_errno(Errno(libc::EBADF)); 25 | return -1; 26 | } 27 | 28 | let seek_from = match whence { 29 | libc::SEEK_SET => rustix::fs::SeekFrom::Start(offset as u64), 30 | libc::SEEK_CUR => rustix::fs::SeekFrom::Current(offset), 31 | libc::SEEK_END => rustix::fs::SeekFrom::End(offset), 32 | #[cfg(any(apple, freebsdlike, linux_kernel, solarish))] 33 | libc::SEEK_DATA => rustix::fs::SeekFrom::Data(offset as u64), 34 | #[cfg(any(apple, freebsdlike, linux_kernel, solarish))] 35 | libc::SEEK_HOLE => rustix::fs::SeekFrom::Hole(offset as u64), 36 | _ => { 37 | set_errno(Errno(libc::EINVAL)); 38 | return -1; 39 | } 40 | }; 41 | match convert_res(rustix::fs::seek(BorrowedFd::borrow_raw(fd), seek_from)) { 42 | Some(offset) => offset as off64_t, 43 | None => -1, 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /c-scape/src/fs/makedev.rs: -------------------------------------------------------------------------------- 1 | //! `major`, `minor`, and `makedev`. 2 | //! 3 | //! These are macros in libc, so the Rust libc crate defines them itself as 4 | //! functions, so we can just call those functions here. 5 | 6 | #[no_mangle] 7 | unsafe extern "C" fn gnu_dev_major(dev: libc::dev_t) -> u32 { 8 | libc::major(dev) 9 | } 10 | 11 | #[no_mangle] 12 | unsafe extern "C" fn gnu_dev_minor(dev: libc::dev_t) -> u32 { 13 | libc::minor(dev) 14 | } 15 | 16 | #[no_mangle] 17 | unsafe extern "C" fn gnu_dev_makedev(major: u32, minor: u32) -> libc::dev_t { 18 | libc::makedev(major, minor) 19 | } 20 | -------------------------------------------------------------------------------- /c-scape/src/fs/memfd.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use core::ffi::CStr; 3 | use libc::{c_char, c_int, c_uint}; 4 | use rustix::fd::IntoRawFd; 5 | use rustix::fs::MemfdFlags; 6 | 7 | #[no_mangle] 8 | unsafe extern "C" fn memfd_create(name: *const c_char, flags: c_uint) -> c_int { 9 | libc!(libc::memfd_create(name, flags)); 10 | 11 | let name = CStr::from_ptr(name); 12 | let flags = MemfdFlags::from_bits_retain(flags); 13 | match convert_res(rustix::fs::memfd_create(name, flags)) { 14 | Some(fd) => fd.into_raw_fd(), 15 | None => -1, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /c-scape/src/fs/mkdir.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::CStr; 2 | use rustix::fd::BorrowedFd; 3 | use rustix::fs::Mode; 4 | 5 | use libc::{c_char, c_int}; 6 | 7 | use crate::convert_res; 8 | 9 | #[no_mangle] 10 | unsafe extern "C" fn mkdir(pathname: *const c_char, mode: libc::mode_t) -> c_int { 11 | libc!(libc::mkdir(pathname, mode)); 12 | 13 | mkdirat(libc::AT_FDCWD, pathname, mode) 14 | } 15 | 16 | #[no_mangle] 17 | unsafe extern "C" fn mkdirat(fd: c_int, pathname: *const c_char, mode: libc::mode_t) -> c_int { 18 | libc!(libc::mkdirat(fd, pathname, mode)); 19 | 20 | let mode = Mode::from_bits((mode & !libc::S_IFMT) as _).unwrap(); 21 | match convert_res(rustix::fs::mkdirat( 22 | BorrowedFd::borrow_raw(fd), 23 | CStr::from_ptr(pathname.cast()), 24 | mode, 25 | )) { 26 | Some(()) => 0, 27 | None => -1, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /c-scape/src/fs/mknod.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::CStr; 2 | use rustix::fd::BorrowedFd; 3 | use rustix::fs::{FileType, Mode}; 4 | 5 | use libc::{c_char, c_int}; 6 | 7 | use crate::convert_res; 8 | 9 | #[no_mangle] 10 | unsafe extern "C" fn mkfifo(pathname: *const c_char, mode: libc::mode_t) -> c_int { 11 | libc!(libc::mkfifo(pathname, mode)); 12 | 13 | mkfifoat(libc::AT_FDCWD, pathname, mode) 14 | } 15 | 16 | #[no_mangle] 17 | unsafe extern "C" fn mkfifoat(fd: c_int, pathname: *const c_char, mode: libc::mode_t) -> c_int { 18 | libc!(libc::mkfifoat(fd, pathname, mode)); 19 | 20 | mknodat(fd, pathname, mode | libc::S_IFIFO, 0) 21 | } 22 | 23 | #[no_mangle] 24 | unsafe extern "C" fn mknod(pathname: *const c_char, mode: libc::mode_t, dev: libc::dev_t) -> c_int { 25 | libc!(libc::mknod(pathname, mode, dev)); 26 | 27 | mknodat(libc::AT_FDCWD, pathname, mode, dev) 28 | } 29 | 30 | #[no_mangle] 31 | unsafe extern "C" fn mknodat( 32 | dirfd: c_int, 33 | pathname: *const c_char, 34 | mode: libc::mode_t, 35 | dev: libc::dev_t, 36 | ) -> c_int { 37 | libc!(libc::mknodat(dirfd, pathname, mode, dev)); 38 | 39 | let filetype = match mode & libc::S_IFMT { 40 | libc::S_IFREG => FileType::RegularFile, 41 | libc::S_IFDIR => FileType::Directory, 42 | libc::S_IFLNK => FileType::Symlink, 43 | libc::S_IFIFO => FileType::Fifo, 44 | libc::S_IFSOCK => FileType::Socket, 45 | libc::S_IFCHR => FileType::CharacterDevice, 46 | libc::S_IFBLK => FileType::BlockDevice, 47 | _ => FileType::Unknown, 48 | }; 49 | let mode = Mode::from_bits((mode & !libc::S_IFMT) as _).unwrap(); 50 | 51 | match convert_res(rustix::fs::mknodat( 52 | BorrowedFd::borrow_raw(dirfd), 53 | CStr::from_ptr(pathname.cast()), 54 | filetype, 55 | mode, 56 | dev, 57 | )) { 58 | Some(()) => 0, 59 | None => -1, 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /c-scape/src/fs/open.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::CStr; 2 | use errno::{set_errno, Errno}; 3 | use rustix::fd::{BorrowedFd, IntoRawFd}; 4 | use rustix::fs::{Mode, OFlags, CWD}; 5 | 6 | use libc::{c_char, c_int, mode_t}; 7 | 8 | use crate::convert_res; 9 | 10 | // This has to be a macro because we can't delegate C variadic functions. 11 | // This allows us to reduce code duplication as all of the `open` variants 12 | // should morally delegate to openat64. 13 | macro_rules! openat_impl { 14 | ($fd:expr, $pathname:ident, $flags:ident, $args:ident) => {{ 15 | let flags = OFlags::from_bits($flags as _).unwrap(); 16 | let mode = if flags.contains(OFlags::CREATE) || flags.contains(OFlags::TMPFILE) { 17 | let mode: libc::mode_t = $args.arg(); 18 | Mode::from_bits((mode & !libc::S_IFMT) as _).unwrap() 19 | } else { 20 | Mode::empty() 21 | }; 22 | match convert_res(rustix::fs::openat( 23 | &$fd, 24 | CStr::from_ptr($pathname.cast()), 25 | flags, 26 | mode, 27 | )) { 28 | Some(fd) => fd.into_raw_fd(), 29 | None => -1, 30 | } 31 | }}; 32 | } 33 | 34 | // we open all files with O_LARGEFILE as that is what Rustix does 35 | // hopefully this doesn't break any C programs 36 | #[no_mangle] 37 | unsafe extern "C" fn open(pathname: *const c_char, flags: c_int, mut args: ...) -> c_int { 38 | libc!(libc::open(pathname, flags, args)); 39 | 40 | openat_impl!(CWD, pathname, flags, args) 41 | } 42 | 43 | #[no_mangle] 44 | unsafe extern "C" fn open64(pathname: *const c_char, flags: c_int, mut args: ...) -> c_int { 45 | libc!(libc::open64(pathname, flags, args)); 46 | 47 | openat_impl!(CWD, pathname, flags, args) 48 | } 49 | 50 | // same behavior with `O_LARGEFILE` as open/open64 51 | #[no_mangle] 52 | unsafe extern "C" fn openat( 53 | fd: c_int, 54 | pathname: *const c_char, 55 | flags: c_int, 56 | mut args: ... 57 | ) -> c_int { 58 | libc!(libc::openat(fd, pathname, flags, args)); 59 | 60 | openat_impl!(BorrowedFd::borrow_raw(fd), pathname, flags, args) 61 | } 62 | 63 | #[no_mangle] 64 | unsafe extern "C" fn openat64( 65 | fd: c_int, 66 | pathname: *const c_char, 67 | flags: c_int, 68 | mut args: ... 69 | ) -> c_int { 70 | libc!(libc::openat64(fd, pathname, flags, args)); 71 | 72 | openat_impl!(BorrowedFd::borrow_raw(fd), pathname, flags, args) 73 | } 74 | 75 | #[no_mangle] 76 | unsafe extern "C" fn creat(name: *const c_char, mode: mode_t) -> c_int { 77 | libc!(libc::creat(name, mode)); 78 | 79 | creat64(name, mode) 80 | } 81 | 82 | #[no_mangle] 83 | unsafe extern "C" fn creat64(name: *const c_char, mode: mode_t) -> c_int { 84 | libc!(libc::creat64(name, mode)); 85 | 86 | open(name, libc::O_CREAT | libc::O_WRONLY | libc::O_TRUNC, mode) 87 | } 88 | 89 | #[no_mangle] 90 | unsafe extern "C" fn close(fd: c_int) -> c_int { 91 | libc!(libc::close(fd)); 92 | 93 | if fd == -1 { 94 | set_errno(Errno(libc::EBADF)); 95 | return -1; 96 | } 97 | 98 | rustix::io::close(fd); 99 | 0 100 | } 101 | -------------------------------------------------------------------------------- /c-scape/src/fs/readlink.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::CStr; 2 | use core::mem::MaybeUninit; 3 | use core::slice; 4 | use rustix::fd::BorrowedFd; 5 | 6 | use libc::{c_char, c_int}; 7 | 8 | use crate::convert_res; 9 | 10 | #[no_mangle] 11 | unsafe extern "C" fn readlink(pathname: *const c_char, buf: *mut c_char, bufsiz: usize) -> isize { 12 | libc!(libc::readlink(pathname, buf, bufsiz)); 13 | 14 | readlinkat(libc::AT_FDCWD, pathname, buf, bufsiz) 15 | } 16 | 17 | #[no_mangle] 18 | unsafe extern "C" fn readlinkat( 19 | fd: c_int, 20 | pathname: *const c_char, 21 | buf: *mut c_char, 22 | bufsiz: usize, 23 | ) -> isize { 24 | libc!(libc::readlinkat(fd, pathname, buf, bufsiz)); 25 | 26 | let buf = slice::from_raw_parts_mut(buf.cast::>(), bufsiz); 27 | let (yes, _no) = match convert_res(rustix::fs::readlinkat_raw( 28 | BorrowedFd::borrow_raw(fd), 29 | CStr::from_ptr(pathname.cast()), 30 | buf, 31 | )) { 32 | Some(slices) => slices, 33 | None => return -1, 34 | }; 35 | yes.len() as isize 36 | } 37 | -------------------------------------------------------------------------------- /c-scape/src/fs/realpath.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::CStr; 2 | 3 | use core::ptr::{copy_nonoverlapping, null_mut}; 4 | use errno::{set_errno, Errno}; 5 | use libc::{c_char, c_void, malloc, memcpy}; 6 | 7 | #[no_mangle] 8 | unsafe extern "C" fn realpath(path: *const c_char, resolved_path: *mut c_char) -> *mut c_char { 9 | libc!(libc::realpath(path, resolved_path)); 10 | 11 | let mut buf = [0; libc::PATH_MAX as usize]; 12 | match realpath_ext::realpath_raw( 13 | CStr::from_ptr(path.cast()).to_bytes(), 14 | &mut buf, 15 | realpath_ext::RealpathFlags::empty(), 16 | ) { 17 | Ok(len) => { 18 | if resolved_path.is_null() { 19 | let ptr = malloc(len + 1).cast::(); 20 | if ptr.is_null() { 21 | set_errno(Errno(libc::ENOMEM)); 22 | return null_mut(); 23 | } 24 | copy_nonoverlapping(buf.as_ptr().cast(), ptr, len); 25 | *ptr.add(len) = b'\0'; 26 | ptr.cast::() 27 | } else { 28 | memcpy( 29 | resolved_path.cast::(), 30 | buf[..len].as_ptr().cast::(), 31 | len, 32 | ); 33 | *resolved_path.add(len) = b'\0' as _; 34 | resolved_path 35 | } 36 | } 37 | Err(err) => { 38 | set_errno(Errno(err)); 39 | null_mut() 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /c-scape/src/fs/remove.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::CStr; 2 | use rustix::fd::BorrowedFd; 3 | use rustix::fs::AtFlags; 4 | 5 | use libc::{c_char, c_int}; 6 | 7 | use crate::convert_res; 8 | 9 | #[no_mangle] 10 | unsafe extern "C" fn rmdir(pathname: *const c_char) -> c_int { 11 | libc!(libc::rmdir(pathname)); 12 | 13 | unlinkat(libc::AT_FDCWD, pathname, libc::AT_REMOVEDIR) 14 | } 15 | 16 | #[no_mangle] 17 | unsafe extern "C" fn unlink(pathname: *const c_char) -> c_int { 18 | libc!(libc::unlink(pathname)); 19 | 20 | unlinkat(libc::AT_FDCWD, pathname, 0) 21 | } 22 | 23 | #[no_mangle] 24 | unsafe extern "C" fn unlinkat(fd: c_int, pathname: *const c_char, flags: c_int) -> c_int { 25 | libc!(libc::unlinkat(fd, pathname, flags)); 26 | 27 | let fd = BorrowedFd::borrow_raw(fd); 28 | let flags = AtFlags::from_bits(flags as _).unwrap(); 29 | match convert_res(rustix::fs::unlinkat( 30 | fd, 31 | CStr::from_ptr(pathname.cast()), 32 | flags, 33 | )) { 34 | Some(()) => 0, 35 | None => -1, 36 | } 37 | } 38 | 39 | #[no_mangle] 40 | unsafe extern "C" fn remove(pathname: *const c_char) -> c_int { 41 | libc!(libc::remove(pathname)); 42 | 43 | let pathname = CStr::from_ptr(pathname.cast()); 44 | 45 | let result = rustix::fs::unlink(pathname); 46 | 47 | if let Err(rustix::io::Errno::ISDIR) = result { 48 | rmdir(pathname.as_ptr()) 49 | } else { 50 | match convert_res(result) { 51 | Some(()) => 0, 52 | None => -1, 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /c-scape/src/fs/rename.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::CStr; 2 | use rustix::fd::BorrowedFd; 3 | 4 | #[cfg(not(target_env = "musl"))] 5 | use libc::c_uint; 6 | use libc::{c_char, c_int}; 7 | 8 | use crate::convert_res; 9 | 10 | #[no_mangle] 11 | unsafe extern "C" fn rename(old: *const c_char, new: *const c_char) -> c_int { 12 | libc!(libc::rename(old, new)); 13 | 14 | renameat(libc::AT_FDCWD, old, libc::AT_FDCWD, new) 15 | } 16 | 17 | #[no_mangle] 18 | unsafe extern "C" fn renameat( 19 | old_fd: c_int, 20 | old: *const c_char, 21 | new_fd: c_int, 22 | new: *const c_char, 23 | ) -> c_int { 24 | libc!(libc::renameat(old_fd, old, new_fd, new)); 25 | 26 | match convert_res(rustix::fs::renameat( 27 | BorrowedFd::borrow_raw(old_fd), 28 | CStr::from_ptr(old.cast()), 29 | BorrowedFd::borrow_raw(new_fd), 30 | CStr::from_ptr(new.cast()), 31 | )) { 32 | Some(()) => 0, 33 | None => -1, 34 | } 35 | } 36 | 37 | #[cfg(not(target_env = "musl"))] 38 | #[no_mangle] 39 | unsafe extern "C" fn renameat2( 40 | old_fd: c_int, 41 | old: *const c_char, 42 | new_fd: c_int, 43 | new: *const c_char, 44 | flags: c_uint, 45 | ) -> c_int { 46 | libc!(libc::renameat2(old_fd, old, new_fd, new, flags)); 47 | 48 | match convert_res(rustix::fs::renameat_with( 49 | BorrowedFd::borrow_raw(old_fd), 50 | CStr::from_ptr(old.cast()), 51 | BorrowedFd::borrow_raw(new_fd), 52 | CStr::from_ptr(new.cast()), 53 | rustix::fs::RenameFlags::from_bits_retain(flags), 54 | )) { 55 | Some(()) => 0, 56 | None => -1, 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /c-scape/src/fs/sendfile.rs: -------------------------------------------------------------------------------- 1 | use rustix::fd::BorrowedFd; 2 | 3 | use errno::{set_errno, Errno}; 4 | use libc::{c_int, off64_t, off_t, size_t}; 5 | 6 | use crate::convert_res; 7 | 8 | #[cfg(any(target_os = "android", target_os = "linux"))] 9 | #[no_mangle] 10 | unsafe extern "C" fn sendfile( 11 | out_fd: c_int, 12 | in_fd: c_int, 13 | offset: *mut off_t, 14 | count: size_t, 15 | ) -> isize { 16 | libc!(libc::sendfile(out_fd, in_fd, offset, count)); 17 | 18 | let mut offset_64: off64_t = 0; 19 | 20 | // Check for overflow into off64_t 21 | if !offset.is_null() { 22 | if *offset < 0 { 23 | set_errno(Errno(libc::EINVAL)); 24 | return -1; 25 | } 26 | 27 | // If count is too big then we just fail immediately 28 | if let Ok(add) = TryInto::::try_into(count) { 29 | // Check if count + offset could overflow and return EINVAL 30 | if add.overflowing_add(*offset).1 { 31 | set_errno(Errno(libc::EINVAL)); 32 | return -1; 33 | } 34 | } else { 35 | set_errno(Errno(libc::EINVAL)); 36 | return -1; 37 | } 38 | 39 | offset_64 = (*offset).into(); 40 | } 41 | 42 | let res = sendfile64(out_fd, in_fd, &mut offset_64, count); 43 | 44 | if !offset.is_null() { 45 | // Safe cast as we checked above 46 | *offset = offset_64 as off_t; 47 | } 48 | 49 | res 50 | } 51 | 52 | #[cfg(any(target_os = "android", target_os = "linux"))] 53 | #[no_mangle] 54 | unsafe extern "C" fn sendfile64( 55 | out_fd: c_int, 56 | in_fd: c_int, 57 | offset: *mut off64_t, 58 | count: size_t, 59 | ) -> isize { 60 | libc!(libc::sendfile64(out_fd, in_fd, offset, count)); 61 | 62 | let offset: *mut u64 = checked_cast!(offset); 63 | 64 | match convert_res(rustix::fs::sendfile( 65 | BorrowedFd::borrow_raw(out_fd), 66 | BorrowedFd::borrow_raw(in_fd), 67 | offset.as_mut(), 68 | count, 69 | )) { 70 | Some(num) => num.try_into().unwrap(), 71 | None => -1, 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /c-scape/src/fs/statvfs.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use core::ffi::CStr; 3 | use libc::{c_char, c_int}; 4 | use rustix::fd::BorrowedFd; 5 | 6 | #[no_mangle] 7 | unsafe extern "C" fn statvfs(path: *const c_char, buf: *mut libc::statvfs) -> c_int { 8 | libc!(libc::statvfs(path, buf)); 9 | 10 | let path = CStr::from_ptr(path); 11 | let val = match convert_res(rustix::fs::statvfs(path)) { 12 | Some(val) => val, 13 | None => return -1, 14 | }; 15 | rustix_to_libc(buf, val); 16 | 0 17 | } 18 | 19 | #[no_mangle] 20 | unsafe extern "C" fn fstatvfs(fd: c_int, buf: *mut libc::statvfs) -> c_int { 21 | libc!(libc::fstatvfs(fd, buf)); 22 | 23 | let fd = BorrowedFd::borrow_raw(fd); 24 | let val = match convert_res(rustix::fs::fstatvfs(fd)) { 25 | Some(val) => val, 26 | None => return -1, 27 | }; 28 | rustix_to_libc(buf, val); 29 | 0 30 | } 31 | 32 | unsafe fn rustix_to_libc(buf: *mut libc::statvfs, val: rustix::fs::StatVfs) { 33 | let mut converted = core::mem::zeroed::(); 34 | converted.f_bsize = val.f_bsize as _; 35 | converted.f_frsize = val.f_frsize as _; 36 | converted.f_blocks = val.f_blocks as _; 37 | converted.f_bfree = val.f_bfree as _; 38 | converted.f_bavail = val.f_bavail as _; 39 | converted.f_files = val.f_files as _; 40 | converted.f_ffree = val.f_ffree as _; 41 | converted.f_favail = val.f_favail as _; 42 | converted.f_fsid = val.f_fsid as _; 43 | converted.f_flag = val.f_flag.bits() as _; 44 | converted.f_namemax = val.f_namemax as _; 45 | *buf = converted; 46 | } 47 | -------------------------------------------------------------------------------- /c-scape/src/fs/symlink.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::CStr; 2 | use rustix::fd::BorrowedFd; 3 | 4 | use libc::{c_char, c_int}; 5 | 6 | use crate::convert_res; 7 | 8 | #[no_mangle] 9 | unsafe extern "C" fn symlink(target: *const c_char, linkpath: *const c_char) -> c_int { 10 | libc!(libc::symlink(target, linkpath)); 11 | 12 | symlinkat(target, libc::AT_FDCWD, linkpath) 13 | } 14 | 15 | #[no_mangle] 16 | unsafe extern "C" fn symlinkat( 17 | target: *const c_char, 18 | linkdirfd: c_int, 19 | linkpath: *const c_char, 20 | ) -> c_int { 21 | libc!(libc::symlinkat(target, linkdirfd, linkpath)); 22 | 23 | match convert_res(rustix::fs::symlinkat( 24 | CStr::from_ptr(target.cast()), 25 | BorrowedFd::borrow_raw(linkdirfd), 26 | CStr::from_ptr(linkpath.cast()), 27 | )) { 28 | Some(()) => 0, 29 | None => -1, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /c-scape/src/fs/sync.rs: -------------------------------------------------------------------------------- 1 | use rustix::fd::BorrowedFd; 2 | 3 | use libc::{c_int, c_uint, off64_t}; 4 | 5 | use crate::convert_res; 6 | 7 | #[no_mangle] 8 | unsafe extern "C" fn fsync(fd: c_int) -> c_int { 9 | libc!(libc::fsync(fd)); 10 | 11 | match convert_res(rustix::fs::fsync(BorrowedFd::borrow_raw(fd))) { 12 | Some(()) => 0, 13 | None => -1, 14 | } 15 | } 16 | 17 | #[no_mangle] 18 | unsafe extern "C" fn fdatasync(fd: c_int) -> c_int { 19 | libc!(libc::fdatasync(fd)); 20 | 21 | match convert_res(rustix::fs::fdatasync(BorrowedFd::borrow_raw(fd))) { 22 | Some(()) => 0, 23 | None => -1, 24 | } 25 | } 26 | 27 | #[no_mangle] 28 | unsafe extern "C" fn syncfs(fd: c_int) -> c_int { 29 | libc!(libc::syncfs(fd)); 30 | 31 | match convert_res(rustix::fs::syncfs(BorrowedFd::borrow_raw(fd))) { 32 | Some(()) => 0, 33 | None => -1, 34 | } 35 | } 36 | 37 | #[no_mangle] 38 | unsafe extern "C" fn sync() { 39 | libc!(libc::sync()); 40 | 41 | rustix::fs::sync() 42 | } 43 | 44 | #[no_mangle] 45 | unsafe extern "C" fn sync_file_range( 46 | fd: c_int, 47 | offset: off64_t, 48 | nbytes: off64_t, 49 | flags: c_uint, 50 | ) -> c_int { 51 | libc!(libc::sync_file_range(fd, offset, nbytes, flags)); 52 | 53 | let fd = BorrowedFd::borrow_raw(fd); 54 | 55 | // FIXME: Add `sync_file_range` to rustix and use it. 56 | match convert_res(rustix::fs::fdatasync(fd)) { 57 | Some(()) => 0, 58 | None => -1, 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /c-scape/src/fs/truncate.rs: -------------------------------------------------------------------------------- 1 | use rustix::fd::BorrowedFd; 2 | 3 | use libc::{c_int, off64_t, off_t}; 4 | 5 | use crate::convert_res; 6 | 7 | #[no_mangle] 8 | unsafe extern "C" fn ftruncate(fd: c_int, length: off_t) -> c_int { 9 | libc!(libc::ftruncate(fd, length)); 10 | 11 | ftruncate64(fd, length as off64_t) 12 | } 13 | 14 | #[no_mangle] 15 | unsafe extern "C" fn ftruncate64(fd: c_int, length: off64_t) -> c_int { 16 | libc!(libc::ftruncate64(fd, length)); 17 | 18 | match convert_res(rustix::fs::ftruncate( 19 | BorrowedFd::borrow_raw(fd), 20 | length as u64, 21 | )) { 22 | Some(()) => 0, 23 | None => -1, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /c-scape/src/glibc_versioning.rs: -------------------------------------------------------------------------------- 1 | use libc::c_char; 2 | 3 | #[no_mangle] 4 | unsafe extern "C" fn gnu_get_libc_version() -> *const c_char { 5 | // We're implementing the glibc ABI, but we aren't actually glibc. This 6 | // is used by `std` to test for certain behaviors. For now, return 2.23 7 | // which is a glibc version where `posix_spawn` doesn't handle `ENOENT` 8 | // as std wants, so it uses `fork`+`exec` instead, since we don't yet 9 | // implement `posix_spawn`. 10 | c"2.23".as_ptr() 11 | } 12 | -------------------------------------------------------------------------------- /c-scape/src/int.rs: -------------------------------------------------------------------------------- 1 | //! Integer math. 2 | 3 | use libc::{c_int, c_long, c_longlong, intmax_t}; 4 | 5 | #[no_mangle] 6 | unsafe extern "C" fn ffs(i: c_int) -> c_int { 7 | //libc!(libc::ffs(i)); 8 | 9 | if i == 0 { 10 | 0 11 | } else { 12 | i.trailing_zeros() as c_int + 1 13 | } 14 | } 15 | 16 | #[no_mangle] 17 | unsafe extern "C" fn ffsl(i: c_long) -> c_int { 18 | //libc!(libc::ffsl(i)); 19 | 20 | if i == 0 { 21 | 0 22 | } else { 23 | i.trailing_zeros() as c_int + 1 24 | } 25 | } 26 | 27 | #[no_mangle] 28 | unsafe extern "C" fn ffsll(i: c_longlong) -> c_int { 29 | //libc!(libc::ffsll(i)); 30 | 31 | if i == 0 { 32 | 0 33 | } else { 34 | i.trailing_zeros() as c_int + 1 35 | } 36 | } 37 | 38 | #[no_mangle] 39 | unsafe extern "C" fn abs(i: c_int) -> c_int { 40 | libc!(libc::abs(i)); 41 | 42 | i.abs() 43 | } 44 | 45 | #[no_mangle] 46 | unsafe extern "C" fn labs(i: c_long) -> c_long { 47 | libc!(libc::labs(i)); 48 | 49 | i.abs() 50 | } 51 | 52 | #[no_mangle] 53 | unsafe extern "C" fn llabs(i: c_longlong) -> c_longlong { 54 | //libc!(libc::llabs(i)); 55 | 56 | i.abs() 57 | } 58 | 59 | #[no_mangle] 60 | unsafe extern "C" fn imaxabs(i: intmax_t) -> intmax_t { 61 | //libc!(libc::imaxabs(i)); 62 | 63 | i.abs() 64 | } 65 | 66 | // The libc crate doesn't currently have `div_t` etc., so define them here. 67 | #[repr(C)] 68 | struct div_t { 69 | quot: c_int, 70 | rem: c_int, 71 | } 72 | 73 | #[repr(C)] 74 | struct ldiv_t { 75 | quot: c_long, 76 | rem: c_long, 77 | } 78 | 79 | #[repr(C)] 80 | struct lldiv_t { 81 | quot: c_longlong, 82 | rem: c_longlong, 83 | } 84 | 85 | #[no_mangle] 86 | unsafe extern "C" fn div(numerator: c_int, denominator: c_int) -> div_t { 87 | //libc!(libc::div(numerator, denominator)); 88 | 89 | div_t { 90 | quot: numerator / denominator, 91 | rem: numerator % denominator, 92 | } 93 | } 94 | 95 | #[no_mangle] 96 | unsafe extern "C" fn ldiv(numerator: c_long, denominator: c_long) -> ldiv_t { 97 | //libc!(libc::ldiv(numerator, denominator)); 98 | 99 | ldiv_t { 100 | quot: numerator / denominator, 101 | rem: numerator % denominator, 102 | } 103 | } 104 | 105 | #[no_mangle] 106 | unsafe extern "C" fn lldiv(numerator: c_longlong, denominator: c_longlong) -> lldiv_t { 107 | //libc!(libc::lldiv(numerator, denominator)); 108 | 109 | lldiv_t { 110 | quot: numerator / denominator, 111 | rem: numerator % denominator, 112 | } 113 | } 114 | 115 | #[no_mangle] 116 | unsafe extern "C" fn __ffsdi2(i: c_long) -> c_int { 117 | //libc!(libc::__ffsdi2(i)); 118 | 119 | ffsl(i) 120 | } 121 | 122 | #[no_mangle] 123 | unsafe extern "C" fn __clzdi2(i: c_long) -> c_int { 124 | //libc!(libc::__clzdi2(i)); 125 | 126 | i.leading_zeros() as c_int 127 | } 128 | 129 | #[no_mangle] 130 | unsafe extern "C" fn __ctzdi2(i: c_long) -> c_int { 131 | //libc!(libc::__ctzdi2(i)); 132 | 133 | i.trailing_zeros() as c_int 134 | } 135 | 136 | #[no_mangle] 137 | unsafe extern "C" fn __popcountdi2(i: c_long) -> c_int { 138 | //libc!(libc::__popcountdi2(i)); 139 | 140 | i.count_ones() as c_int 141 | } 142 | -------------------------------------------------------------------------------- /c-scape/src/io/dup.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use errno::{set_errno, Errno}; 3 | use libc::c_int; 4 | use rustix::fd::{BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; 5 | use rustix::io::DupFlags; 6 | 7 | #[no_mangle] 8 | unsafe extern "C" fn dup(fd: c_int) -> c_int { 9 | libc!(libc::dup(fd)); 10 | 11 | if fd == -1 { 12 | set_errno(Errno(libc::EBADF)); 13 | return -1; 14 | } 15 | 16 | match convert_res(rustix::io::dup(BorrowedFd::borrow_raw(fd))) { 17 | Some(fd) => fd.into_raw_fd(), 18 | None => -1, 19 | } 20 | } 21 | 22 | #[no_mangle] 23 | unsafe extern "C" fn dup2(fd: c_int, to: c_int) -> c_int { 24 | libc!(libc::dup2(fd, to)); 25 | 26 | if fd == -1 { 27 | set_errno(Errno(libc::EBADF)); 28 | return -1; 29 | } 30 | 31 | // `dup2` requires an `OwnedFd` since it closes the old fd before reusing 32 | // the index for the new fd. 33 | let mut to = OwnedFd::from_raw_fd(to); 34 | 35 | let result = convert_res(rustix::io::dup2(BorrowedFd::borrow_raw(fd), &mut to)); 36 | 37 | // Convert it back into a raw fd, so that we don't drop it. 38 | let to = to.into_raw_fd(); 39 | 40 | match result { 41 | Some(()) => to, 42 | None => -1, 43 | } 44 | } 45 | 46 | #[no_mangle] 47 | unsafe extern "C" fn dup3(fd: c_int, to: c_int, flags: c_int) -> c_int { 48 | libc!(libc::dup3(fd, to, flags)); 49 | 50 | if fd == -1 { 51 | set_errno(Errno(libc::EBADF)); 52 | return -1; 53 | } 54 | 55 | let flags = DupFlags::from_bits(flags as _).unwrap(); 56 | 57 | // `dup2` requires an `OwnedFd` since it closes the old fd before reusing 58 | // the index for the new fd. 59 | let mut to = OwnedFd::from_raw_fd(to); 60 | 61 | let result = convert_res(rustix::io::dup3(BorrowedFd::borrow_raw(fd), &mut to, flags)); 62 | 63 | // Convert it back into a raw fd, so that we don't drop it. 64 | let to = to.into_raw_fd(); 65 | 66 | match result { 67 | Some(()) => to, 68 | None => -1, 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /c-scape/src/io/isatty.rs: -------------------------------------------------------------------------------- 1 | use rustix::fd::BorrowedFd; 2 | 3 | use libc::c_int; 4 | 5 | use crate::convert_res; 6 | 7 | #[no_mangle] 8 | unsafe extern "C" fn isatty(fd: c_int) -> c_int { 9 | libc!(libc::isatty(fd)); 10 | 11 | match convert_res(rustix::termios::tcgetwinsize(BorrowedFd::borrow_raw(fd))) { 12 | Some(_) => 1, 13 | None => 0, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /c-scape/src/io/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(target_os = "wasi"))] 2 | mod dup; 3 | #[cfg(any(target_os = "android", target_os = "linux"))] 4 | mod epoll; 5 | mod isatty; 6 | mod pipe; 7 | mod poll; 8 | mod read; 9 | mod select; 10 | mod splice; 11 | #[cfg(any(target_os = "android", target_os = "linux"))] 12 | mod timerfd; 13 | mod write; 14 | 15 | use rustix::event::EventfdFlags; 16 | use rustix::fd::{BorrowedFd, IntoRawFd}; 17 | 18 | use libc::{c_int, c_long, c_uint}; 19 | 20 | use crate::convert_res; 21 | 22 | #[cfg(not(target_os = "wasi"))] 23 | #[no_mangle] 24 | unsafe extern "C" fn ioctl(fd: c_int, request: c_long, mut args: ...) -> c_int { 25 | const TCGETS: c_long = libc::TCGETS as c_long; 26 | const FIONBIO: c_long = libc::FIONBIO as c_long; 27 | const TIOCINQ: c_long = libc::TIOCINQ as c_long; 28 | const TIOCGWINSZ: c_long = libc::TIOCGWINSZ as c_long; 29 | const FICLONE: c_long = libc::FICLONE as c_long; 30 | match request { 31 | TCGETS => { 32 | libc!(libc::ioctl(fd, libc::TCGETS)); 33 | let fd = BorrowedFd::borrow_raw(fd); 34 | match convert_res(rustix::termios::tcgetattr(fd)) { 35 | Some(x) => { 36 | args.arg::<*mut rustix::termios::Termios>().write(x); 37 | 0 38 | } 39 | None => -1, 40 | } 41 | } 42 | FIONBIO | TIOCINQ => { 43 | let ptr = args.arg::<*mut c_int>(); 44 | let value = *ptr != 0; 45 | libc!(libc::ioctl(fd, libc::FIONBIO, value as c_int)); 46 | let fd = BorrowedFd::borrow_raw(fd); 47 | match convert_res(rustix::io::ioctl_fionbio(fd, value)) { 48 | Some(()) => 0, 49 | None => -1, 50 | } 51 | } 52 | TIOCGWINSZ => { 53 | libc!(libc::ioctl(fd, libc::TIOCGWINSZ)); 54 | let fd = BorrowedFd::borrow_raw(fd); 55 | match convert_res(rustix::termios::tcgetwinsize(fd)) { 56 | Some(size) => { 57 | let size = libc::winsize { 58 | ws_row: size.ws_row, 59 | ws_col: size.ws_col, 60 | ws_xpixel: size.ws_xpixel, 61 | ws_ypixel: size.ws_ypixel, 62 | }; 63 | args.arg::<*mut libc::winsize>().write(size); 64 | 0 65 | } 66 | None => -1, 67 | } 68 | } 69 | FICLONE => { 70 | let src_fd = args.arg::(); 71 | libc!(libc::ioctl(fd, libc::FICLONE as _, src_fd)); 72 | let fd = BorrowedFd::borrow_raw(fd); 73 | let src_fd = BorrowedFd::borrow_raw(src_fd); 74 | match convert_res(rustix::fs::ioctl_ficlone(fd, src_fd)) { 75 | Some(()) => 0, 76 | None => -1, 77 | } 78 | } 79 | _ => panic!("unrecognized ioctl({})", request), 80 | } 81 | } 82 | 83 | #[cfg(any(target_os = "android", target_os = "linux"))] 84 | #[no_mangle] 85 | unsafe extern "C" fn eventfd(initval: c_uint, flags: c_int) -> c_int { 86 | libc!(libc::eventfd(initval, flags)); 87 | let flags = EventfdFlags::from_bits(flags.try_into().unwrap()).unwrap(); 88 | match convert_res(rustix::event::eventfd(initval, flags)) { 89 | Some(fd) => fd.into_raw_fd(), 90 | None => -1, 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /c-scape/src/io/pipe.rs: -------------------------------------------------------------------------------- 1 | use rustix::fd::{BorrowedFd, IntoRawFd}; 2 | use rustix::pipe::PipeFlags; 3 | 4 | use libc::{c_int, c_uint, size_t, ssize_t}; 5 | 6 | use crate::convert_res; 7 | 8 | #[no_mangle] 9 | unsafe extern "C" fn pipe(pipefd: *mut c_int) -> c_int { 10 | libc!(libc::pipe(pipefd)); 11 | 12 | match convert_res(rustix::pipe::pipe()) { 13 | Some((a, b)) => { 14 | *pipefd = a.into_raw_fd(); 15 | *pipefd.add(1) = b.into_raw_fd(); 16 | 0 17 | } 18 | None => -1, 19 | } 20 | } 21 | 22 | #[cfg(any(target_os = "android", target_os = "linux"))] 23 | #[no_mangle] 24 | unsafe extern "C" fn pipe2(pipefd: *mut c_int, flags: c_int) -> c_int { 25 | libc!(libc::pipe2(pipefd, flags)); 26 | 27 | let flags = PipeFlags::from_bits(flags as _).unwrap(); 28 | match convert_res(rustix::pipe::pipe_with(flags)) { 29 | Some((a, b)) => { 30 | *pipefd = a.into_raw_fd(); 31 | *pipefd.add(1) = b.into_raw_fd(); 32 | 0 33 | } 34 | None => -1, 35 | } 36 | } 37 | 38 | #[no_mangle] 39 | unsafe extern "C" fn tee(fd_in: c_int, fd_out: c_int, len: size_t, flags: c_uint) -> ssize_t { 40 | libc!(libc::tee(fd_in, fd_out, len, flags)); 41 | 42 | let fd_in = BorrowedFd::borrow_raw(fd_in); 43 | let fd_out = BorrowedFd::borrow_raw(fd_out); 44 | let flags = rustix::pipe::SpliceFlags::from_bits_retain(flags); 45 | match convert_res(rustix::pipe::tee(fd_in, fd_out, len, flags)) { 46 | Some(num) => num as _, 47 | None => -1, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /c-scape/src/io/poll.rs: -------------------------------------------------------------------------------- 1 | use core::slice; 2 | use libc::c_int; 3 | 4 | use crate::convert_res; 5 | 6 | #[no_mangle] 7 | unsafe extern "C" fn poll(fds: *mut libc::pollfd, nfds: libc::nfds_t, timeout: c_int) -> c_int { 8 | libc!(libc::poll(fds, nfds, timeout)); 9 | 10 | let pollfds: *mut rustix::event::PollFd<'_> = checked_cast!(fds); 11 | 12 | let fds = slice::from_raw_parts_mut(pollfds, nfds.try_into().unwrap()); 13 | let timeout = if timeout < 0 { 14 | None 15 | } else { 16 | Some(rustix::event::Timespec { 17 | tv_sec: i64::from(timeout) / 1000, 18 | tv_nsec: (i64::from(timeout) % 1000) * 1_000_000, 19 | }) 20 | }; 21 | match convert_res(rustix::event::poll(fds, timeout.as_ref())) { 22 | Some(num) => num.try_into().unwrap(), 23 | None => -1, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /c-scape/src/io/select.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use alloc::vec::Vec; 3 | use errno::{set_errno, Errno}; 4 | use libc::c_int; 5 | use rustix::event::{PollFd, PollFlags, Timespec}; 6 | use rustix::fd::{AsFd, AsRawFd, BorrowedFd}; 7 | 8 | #[deprecated] 9 | #[no_mangle] 10 | unsafe extern "C" fn select( 11 | nfds: c_int, 12 | readfds: *mut libc::fd_set, 13 | writefds: *mut libc::fd_set, 14 | exceptfds: *mut libc::fd_set, 15 | timeout: *mut libc::timeval, 16 | ) -> c_int { 17 | libc!(libc::select(nfds, readfds, writefds, exceptfds, timeout)); 18 | 19 | if nfds < 0 || nfds > libc::FD_SETSIZE as c_int { 20 | set_errno(Errno(libc::EINVAL)); 21 | return -1; 22 | }; 23 | 24 | let mut poll_fds = Vec::new(); 25 | 26 | for fd in 0..nfds { 27 | let mut events = PollFlags::empty(); 28 | 29 | if set_contains(readfds, fd) { 30 | events |= PollFlags::IN; 31 | libc::FD_CLR(fd, readfds); 32 | } 33 | if set_contains(writefds, fd) { 34 | events |= PollFlags::OUT; 35 | libc::FD_CLR(fd, writefds); 36 | } 37 | if set_contains(exceptfds, fd) { 38 | events |= PollFlags::ERR; 39 | libc::FD_CLR(fd, exceptfds); 40 | } 41 | 42 | if !events.is_empty() { 43 | let event = PollFd::from_borrowed_fd(BorrowedFd::borrow_raw(fd), events); 44 | poll_fds.push(event); 45 | } 46 | } 47 | 48 | let timeout = if timeout.is_null() { 49 | None 50 | } else { 51 | Some(Timespec { 52 | tv_sec: (*timeout).tv_sec.into(), 53 | tv_nsec: (*timeout).tv_usec as rustix::time::Nsecs * 1000, 54 | }) 55 | }; 56 | // TODO: use rustix::event::select 57 | let res = match convert_res(rustix::event::poll(&mut poll_fds, timeout.as_ref())) { 58 | Some(res) => res, 59 | None => return -1, 60 | }; 61 | 62 | for event in &poll_fds[..res] { 63 | let fd = event.as_fd().as_raw_fd(); 64 | if event.revents().contains(PollFlags::IN) { 65 | libc::FD_SET(fd, readfds); 66 | } 67 | if event.revents().contains(PollFlags::OUT) { 68 | libc::FD_SET(fd, writefds); 69 | } 70 | if event.revents().contains(PollFlags::ERR) { 71 | libc::FD_SET(fd, exceptfds); 72 | } 73 | } 74 | res as c_int 75 | } 76 | 77 | unsafe fn set_contains(fds: *mut libc::fd_set, fd: i32) -> bool { 78 | if fds.is_null() { 79 | return false; 80 | } 81 | 82 | libc::FD_ISSET(fd, fds) 83 | } 84 | -------------------------------------------------------------------------------- /c-scape/src/io/splice.rs: -------------------------------------------------------------------------------- 1 | use rustix::fd::BorrowedFd; 2 | use rustix::pipe::SpliceFlags; 3 | 4 | use libc::{c_int, c_uint, loff_t}; 5 | 6 | use crate::convert_res; 7 | 8 | #[cfg(any(target_os = "android", target_os = "linux"))] 9 | #[no_mangle] 10 | unsafe extern "C" fn splice( 11 | fd_in: c_int, 12 | off_in: *mut loff_t, 13 | fd_out: c_int, 14 | off_out: *mut loff_t, 15 | len: usize, 16 | flags: c_uint, 17 | ) -> isize { 18 | libc!(libc::splice(fd_in, off_in, fd_out, off_out, len, flags)); 19 | 20 | let off_in: *mut u64 = checked_cast!(off_in); 21 | let off_out: *mut u64 = checked_cast!(off_out); 22 | 23 | match convert_res(rustix::pipe::splice( 24 | BorrowedFd::borrow_raw(fd_in), 25 | off_in.as_mut(), 26 | BorrowedFd::borrow_raw(fd_out), 27 | off_out.as_mut(), 28 | len, 29 | SpliceFlags::from_bits(flags as _).unwrap(), 30 | )) { 31 | Some(num) => num.try_into().unwrap(), 32 | None => -1, 33 | } 34 | } 35 | 36 | #[cfg(any(target_os = "android", target_os = "linux"))] 37 | #[no_mangle] 38 | unsafe extern "C" fn vmsplice( 39 | fd: c_int, 40 | iov: *const libc::iovec, 41 | nr_segs: usize, 42 | flags: libc::c_uint, 43 | ) -> libc::ssize_t { 44 | libc!(libc::vmsplice(fd, iov, nr_segs, flags)); 45 | 46 | let bufs = core::slice::from_raw_parts(iov.cast::>(), nr_segs); 47 | let flags = SpliceFlags::from_bits(flags).unwrap(); 48 | match convert_res(rustix::pipe::vmsplice( 49 | BorrowedFd::borrow_raw(fd), 50 | bufs, 51 | flags, 52 | )) { 53 | Some(num) => num.try_into().unwrap(), 54 | None => -1, 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /c-scape/src/io/timerfd.rs: -------------------------------------------------------------------------------- 1 | use crate::{convert_res, set_errno, Errno}; 2 | use libc::c_int; 3 | use rustix::fd::{BorrowedFd, IntoRawFd}; 4 | use rustix::time::{TimerfdClockId, TimerfdFlags, TimerfdTimerFlags}; 5 | 6 | #[no_mangle] 7 | unsafe extern "C" fn timerfd_create(clockid: c_int, flags: c_int) -> c_int { 8 | libc!(libc::timerfd_create(clockid, flags)); 9 | 10 | let clockid = match clockid { 11 | libc::CLOCK_REALTIME => TimerfdClockId::Realtime, 12 | libc::CLOCK_MONOTONIC => TimerfdClockId::Monotonic, 13 | libc::CLOCK_BOOTTIME => TimerfdClockId::Boottime, 14 | libc::CLOCK_REALTIME_ALARM => TimerfdClockId::RealtimeAlarm, 15 | libc::CLOCK_BOOTTIME_ALARM => TimerfdClockId::BoottimeAlarm, 16 | _ => { 17 | set_errno(Errno(libc::EINVAL)); 18 | return -1; 19 | } 20 | }; 21 | let flags = TimerfdFlags::from_bits(flags.try_into().unwrap()).unwrap(); 22 | match convert_res(rustix::time::timerfd_create(clockid, flags)) { 23 | Some(fd) => fd.into_raw_fd(), 24 | None => -1, 25 | } 26 | } 27 | 28 | #[no_mangle] 29 | unsafe extern "C" fn timerfd_settime( 30 | fd: c_int, 31 | flags: c_int, 32 | new_value: *const libc::itimerspec, 33 | old_value: *mut libc::itimerspec, 34 | ) -> c_int { 35 | libc!(libc::timerfd_settime(fd, flags, new_value, old_value)); 36 | 37 | let new_value = new_value.read(); 38 | let new_value = rustix::time::Itimerspec { 39 | it_interval: rustix::time::Timespec { 40 | tv_sec: new_value.it_interval.tv_sec.into(), 41 | tv_nsec: new_value.it_interval.tv_nsec as _, 42 | }, 43 | it_value: rustix::time::Timespec { 44 | tv_sec: new_value.it_value.tv_sec.into(), 45 | tv_nsec: new_value.it_value.tv_nsec as _, 46 | }, 47 | }; 48 | let flags = TimerfdTimerFlags::from_bits(flags.try_into().unwrap()).unwrap(); 49 | match convert_res(rustix::time::timerfd_settime( 50 | BorrowedFd::borrow_raw(fd), 51 | flags, 52 | &new_value, 53 | )) { 54 | Some(value) => { 55 | if !old_value.is_null() { 56 | let value = libc::itimerspec { 57 | it_interval: libc::timespec { 58 | tv_sec: value.it_interval.tv_sec as _, 59 | tv_nsec: value.it_interval.tv_nsec as _, 60 | }, 61 | it_value: libc::timespec { 62 | tv_sec: value.it_value.tv_sec as _, 63 | tv_nsec: value.it_value.tv_nsec as _, 64 | }, 65 | }; 66 | old_value.write(value); 67 | } 68 | 0 69 | } 70 | None => -1, 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /c-scape/src/locale.rs: -------------------------------------------------------------------------------- 1 | //! Minimal implementation of locales. 2 | //! 3 | //! This currently only supports the C/POSIX locale. 4 | 5 | use core::ptr::{addr_of, null_mut}; 6 | use libc::{c_char, c_int, lconv, size_t}; 7 | 8 | static EMPTY_STR: [c_char; 1] = [b'\0' as _]; 9 | static C_STR: [c_char; 2] = [b'C' as _, b'\0' as _]; 10 | static POSIX_STR: [c_char; 6] = [ 11 | b'P' as _, b'O' as _, b'S' as _, b'I' as _, b'X' as _, b'\0' as _, 12 | ]; 13 | static DOT_STR: [c_char; 2] = [b'.' as _, b'\0' as _]; 14 | 15 | struct SyncLconv(lconv); 16 | 17 | // SAFETY: The `lconv` instance below is not mutable. 18 | unsafe impl Sync for SyncLconv {} 19 | 20 | static THE_C_GLOBAL_LOCALE: SyncLconv = SyncLconv(lconv { 21 | currency_symbol: EMPTY_STR.as_ptr().cast_mut(), 22 | decimal_point: DOT_STR.as_ptr().cast_mut(), 23 | frac_digits: c_char::MAX, 24 | grouping: EMPTY_STR.as_ptr().cast_mut(), 25 | int_curr_symbol: EMPTY_STR.as_ptr().cast_mut(), 26 | int_frac_digits: c_char::MAX, 27 | int_n_cs_precedes: c_char::MAX, 28 | int_n_sep_by_space: c_char::MAX, 29 | int_n_sign_posn: c_char::MAX, 30 | int_p_cs_precedes: c_char::MAX, 31 | int_p_sep_by_space: c_char::MAX, 32 | int_p_sign_posn: c_char::MAX, 33 | mon_decimal_point: EMPTY_STR.as_ptr().cast_mut(), 34 | mon_grouping: EMPTY_STR.as_ptr().cast_mut(), 35 | mon_thousands_sep: EMPTY_STR.as_ptr().cast_mut(), 36 | negative_sign: EMPTY_STR.as_ptr().cast_mut(), 37 | n_cs_precedes: c_char::MAX, 38 | n_sep_by_space: c_char::MAX, 39 | n_sign_posn: c_char::MAX, 40 | positive_sign: EMPTY_STR.as_ptr().cast_mut(), 41 | p_cs_precedes: c_char::MAX, 42 | p_sep_by_space: c_char::MAX, 43 | p_sign_posn: c_char::MAX, 44 | thousands_sep: EMPTY_STR.as_ptr().cast_mut(), 45 | }); 46 | 47 | #[no_mangle] 48 | unsafe extern "C" fn setlocale(_category: c_int, locale: *const c_char) -> *mut c_char { 49 | libc!(libc::setlocale(_category, locale)); 50 | 51 | if locale.is_null() 52 | || libc::strcmp(locale, C_STR.as_ptr()) == 0 53 | || libc::strcmp(locale, POSIX_STR.as_ptr()) == 0 54 | { 55 | return C_STR.as_ptr().cast_mut(); 56 | } 57 | 58 | null_mut() 59 | } 60 | 61 | #[no_mangle] 62 | unsafe extern "C" fn localeconv() -> *mut lconv { 63 | libc!(libc::localeconv()); 64 | 65 | addr_of!(THE_C_GLOBAL_LOCALE.0).cast_mut() 66 | } 67 | 68 | #[no_mangle] 69 | unsafe extern "C" fn strcoll(l: *const c_char, r: *const c_char) -> c_int { 70 | libc!(libc::strcoll(l, r)); 71 | 72 | libc::strcmp(l, r) 73 | } 74 | 75 | #[no_mangle] 76 | unsafe extern "C" fn strxfrm(dest: *mut c_char, src: *const c_char, n: size_t) -> size_t { 77 | libc!(libc::strxfrm(dest, src, n)); 78 | 79 | let src_len = libc::strlen(src); 80 | 81 | if src_len < n { 82 | libc::strcpy(dest, src); 83 | } 84 | 85 | src_len 86 | } 87 | -------------------------------------------------------------------------------- /c-scape/src/mem/mod.rs: -------------------------------------------------------------------------------- 1 | mod chk; 2 | mod mem; 3 | mod ntbs; 4 | -------------------------------------------------------------------------------- /c-scape/src/mkostemps.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use core::ptr::null_mut; 3 | use errno::{set_errno, Errno}; 4 | use libc::{c_char, c_int}; 5 | use rand::{Rng, TryRngCore}; 6 | use rand_core::OsRng; 7 | use rustix::fd::IntoRawFd; 8 | use rustix::fs::MemfdFlags; 9 | 10 | #[no_mangle] 11 | unsafe extern "C" fn mkstemp(template: *mut c_char) -> c_int { 12 | libc!(libc::mkstemp(template)); 13 | 14 | mkstemp64(template) 15 | } 16 | 17 | #[no_mangle] 18 | unsafe extern "C" fn mkstemp64(template: *mut c_char) -> c_int { 19 | //libc!(libc::mkstemp64(template)); 20 | 21 | mkostemps(template, 0, 0) 22 | } 23 | 24 | #[no_mangle] 25 | unsafe extern "C" fn mkostemp(template: *mut c_char, flags: c_int) -> c_int { 26 | libc!(libc::mkostemp(template, flags)); 27 | 28 | mkostemps(template, 0, flags) 29 | } 30 | 31 | #[no_mangle] 32 | unsafe extern "C" fn mkstemps(template: *mut c_char, suffixlen: c_int) -> c_int { 33 | libc!(libc::mkstemps(template, suffixlen)); 34 | 35 | mkostemps(template, suffixlen, 0) 36 | } 37 | 38 | #[deprecated] 39 | #[no_mangle] 40 | unsafe extern "C" fn mktemp(template: *mut c_char) -> *mut c_char { 41 | //libc!(libc::mktemp(template)); 42 | 43 | let fd = mkstemp(template); 44 | if fd < 0 { 45 | return null_mut(); 46 | } 47 | libc::close(fd); 48 | libc::unlink(template); 49 | template 50 | } 51 | 52 | #[no_mangle] 53 | unsafe extern "C" fn tmpfile64() -> *mut libc::FILE { 54 | libc!(libc::tmpfile64()); 55 | 56 | let fd = match convert_res(rustix::fs::memfd_create( 57 | c"libc::tmpfile", 58 | MemfdFlags::empty(), 59 | )) { 60 | Some(fd) => fd, 61 | None => return null_mut(), 62 | }; 63 | let fd = fd.into_raw_fd(); 64 | libc::fdopen(fd, c"w+".as_ptr()) 65 | } 66 | 67 | #[no_mangle] 68 | unsafe extern "C" fn tmpfile() -> *mut libc::FILE { 69 | libc!(libc::tmpfile()); 70 | 71 | tmpfile64() 72 | } 73 | 74 | #[no_mangle] 75 | unsafe extern "C" fn mkostemps(template: *mut c_char, suffixlen: c_int, flags: c_int) -> c_int { 76 | libc!(libc::mkostemps(template, suffixlen, flags)); 77 | 78 | const XXXXXX: &[u8; 6] = b"XXXXXX"; 79 | const ALNUM: &[u8; 62] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 80 | 81 | let flags = (flags & !libc::O_ACCMODE) | libc::O_RDWR | libc::O_CREAT | libc::O_EXCL; 82 | 83 | let len = libc::strlen(template); 84 | let suffixlen = suffixlen as usize; 85 | let template = template.cast::(); 86 | 87 | if len < 6 || suffixlen > len - 6 { 88 | set_errno(Errno(libc::EINVAL)); 89 | return -1; 90 | } 91 | 92 | if libc::memcmp( 93 | template.add(len - suffixlen - 6).cast(), 94 | XXXXXX.as_ptr().cast(), 95 | XXXXXX.len(), 96 | ) != 0 97 | { 98 | set_errno(Errno(libc::EINVAL)); 99 | return -1; 100 | } 101 | 102 | for _ in 0..(ALNUM.len() * ALNUM.len() * ALNUM.len()) { 103 | for i in 0..XXXXXX.len() { 104 | let r = OsRng.unwrap_err().random_range(0..ALNUM.len()); 105 | *template.add(len - suffixlen - 6 + i) = ALNUM[r] as c_char; 106 | } 107 | 108 | let fd = libc::open(template, flags, 0o600); 109 | if fd >= 0 { 110 | return fd; 111 | } 112 | } 113 | 114 | set_errno(Errno(libc::EEXIST)); 115 | -1 116 | } 117 | -------------------------------------------------------------------------------- /c-scape/src/net/inet.rs: -------------------------------------------------------------------------------- 1 | #[no_mangle] 2 | extern "C" fn htonl(hostlong: u32) -> u32 { 3 | hostlong.to_be() 4 | } 5 | 6 | #[no_mangle] 7 | extern "C" fn htons(hostshort: u16) -> u16 { 8 | hostshort.to_be() 9 | } 10 | 11 | #[no_mangle] 12 | extern "C" fn ntohl(netlong: u32) -> u32 { 13 | u32::from_be(netlong) 14 | } 15 | 16 | #[no_mangle] 17 | extern "C" fn ntohs(netshort: u16) -> u16 { 18 | u16::from_be(netshort) 19 | } 20 | -------------------------------------------------------------------------------- /c-scape/src/nss.rs: -------------------------------------------------------------------------------- 1 | //! Most of the nss functions are implement in c-gull rather than c-scape. 2 | //! But we provide a `getpwuid_r` stub definition here in no-std mode because 3 | //! it's referenced by libstd. libstd doesn't use it for anything other than 4 | //! a fallback for when the HOME environment variable is unset, and HOME is 5 | //! set in any reasonable use case where this would be called, so a stub 6 | //! suffices. 7 | 8 | #[cfg(not(feature = "std"))] 9 | #[cfg(not(target_os = "wasi"))] 10 | use libc::{c_char, c_int, passwd, uid_t}; 11 | 12 | #[cfg(not(feature = "std"))] // Avoid conflicting with c-gull's more complete `getpwuid_r`. 13 | #[cfg(not(target_os = "wasi"))] 14 | #[no_mangle] 15 | unsafe extern "C" fn getpwuid_r( 16 | _uid: uid_t, 17 | _pwd: *mut passwd, 18 | _buf: *mut c_char, 19 | _buflen: usize, 20 | _result: *mut *mut passwd, 21 | ) -> c_int { 22 | libc!(libc::getpwuid_r(_uid, _pwd, _buf, _buflen, _result)); 23 | 24 | // `getpwuid_r` is currently implemented in c-gull. 25 | unimplemented!("getpwuid_r without the \"std\" feature") 26 | } 27 | -------------------------------------------------------------------------------- /c-scape/src/pause.rs: -------------------------------------------------------------------------------- 1 | use errno::{set_errno, Errno}; 2 | use libc::c_int; 3 | 4 | #[no_mangle] 5 | unsafe extern "C" fn pause() -> c_int { 6 | libc!(libc::pause()); 7 | 8 | rustix::event::pause(); 9 | 10 | // `pause` sleeps until it is interrupted by a signal, so it always fails 11 | // with `EINTR`. 12 | set_errno(Errno(libc::EINTR)); 13 | -1 14 | } 15 | -------------------------------------------------------------------------------- /c-scape/src/posix_spawn.rs: -------------------------------------------------------------------------------- 1 | use libc::{c_int, c_void}; 2 | 3 | #[cfg(not(target_os = "wasi"))] 4 | #[no_mangle] 5 | unsafe extern "C" fn posix_spawnp() { 6 | //libc!(libc::posix_spawnp()); 7 | todo!("posix_spawnp"); 8 | } 9 | 10 | #[cfg(not(target_os = "wasi"))] 11 | #[no_mangle] 12 | unsafe extern "C" fn posix_spawnattr_destroy(_ptr: *const c_void) -> c_int { 13 | //libc!(libc::posix_spawn_spawnattr_destroy(ptr)); 14 | rustix::io::write( 15 | rustix::stdio::stderr(), 16 | b"unimplemented: posix_spawn_spawnattr_destroy\n", 17 | ) 18 | .ok(); 19 | 0 20 | } 21 | 22 | #[cfg(not(target_os = "wasi"))] 23 | #[no_mangle] 24 | unsafe extern "C" fn posix_spawnattr_init(_ptr: *const c_void) -> c_int { 25 | //libc!(libc::posix_spawnattr_init(ptr)); 26 | rustix::io::write( 27 | rustix::stdio::stderr(), 28 | b"unimplemented: posix_spawnattr_init\n", 29 | ) 30 | .ok(); 31 | 0 32 | } 33 | 34 | #[cfg(not(target_os = "wasi"))] 35 | #[no_mangle] 36 | unsafe extern "C" fn posix_spawnattr_setflags() { 37 | //libc!(libc::posix_spawnattr_setflags()); 38 | unimplemented!("posix_spawnattr_setflags") 39 | } 40 | 41 | #[cfg(not(target_os = "wasi"))] 42 | #[no_mangle] 43 | unsafe extern "C" fn posix_spawnattr_setsigdefault() { 44 | //libc!(libc::posix_spawnattr_setsigdefault()); 45 | unimplemented!("posix_spawnattr_setsigdefault") 46 | } 47 | 48 | #[cfg(not(target_os = "wasi"))] 49 | #[no_mangle] 50 | unsafe extern "C" fn posix_spawnattr_setsigmask() { 51 | //libc!(libc::posix_spawnattr_setsigmask()); 52 | unimplemented!("posix_spawnsetsigmask") 53 | } 54 | 55 | #[cfg(not(target_os = "wasi"))] 56 | #[no_mangle] 57 | unsafe extern "C" fn posix_spawnattr_setpgroup(_ptr: *mut c_void, _pgroup: c_int) -> c_int { 58 | //libc!(libc::posix_spawnattr_setpgroup(ptr, pgroup)); 59 | unimplemented!("posix_spawnattr_setpgroup") 60 | } 61 | 62 | #[cfg(not(target_os = "wasi"))] 63 | #[no_mangle] 64 | unsafe extern "C" fn posix_spawn_file_actions_adddup2() { 65 | //libc!(libc::posix_spawn_file_actions_adddup2()); 66 | unimplemented!("posix_spawn_file_actions_adddup2") 67 | } 68 | 69 | #[cfg(not(target_os = "wasi"))] 70 | #[no_mangle] 71 | unsafe extern "C" fn posix_spawn_file_actions_addchdir_np() { 72 | //libc!(libc::posix_spawn_file_actions_addchdir_np()); 73 | unimplemented!("posix_spawn_file_actions_addchdir_np") 74 | } 75 | 76 | #[cfg(not(target_os = "wasi"))] 77 | #[no_mangle] 78 | unsafe extern "C" fn posix_spawn_file_actions_destroy(_ptr: *const c_void) -> c_int { 79 | //libc!(libc::posix_spawn_file_actions_destroy(ptr)); 80 | rustix::io::write( 81 | rustix::stdio::stderr(), 82 | b"unimplemented: posix_spawn_file_actions_destroy\n", 83 | ) 84 | .ok(); 85 | 0 86 | } 87 | 88 | #[cfg(not(target_os = "wasi"))] 89 | #[no_mangle] 90 | unsafe extern "C" fn posix_spawn_file_actions_init(_ptr: *const c_void) -> c_int { 91 | //libc!(libc::posix_spawn_file_actions_init(ptr)); 92 | rustix::io::write( 93 | rustix::stdio::stderr(), 94 | b"unimplemented: posix_spawn_file_actions_init\n", 95 | ) 96 | .ok(); 97 | 0 98 | } 99 | -------------------------------------------------------------------------------- /c-scape/src/process/chdir.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::CStr; 2 | use rustix::fd::BorrowedFd; 3 | 4 | use libc::{c_char, c_int}; 5 | 6 | use crate::convert_res; 7 | 8 | #[no_mangle] 9 | unsafe extern "C" fn chdir(path: *const c_char) -> c_int { 10 | libc!(libc::chdir(path)); 11 | 12 | let path = CStr::from_ptr(path.cast()); 13 | match convert_res(rustix::process::chdir(path)) { 14 | Some(()) => 0, 15 | None => -1, 16 | } 17 | } 18 | 19 | #[no_mangle] 20 | unsafe extern "C" fn fchdir(fd: c_int) -> c_int { 21 | libc!(libc::fchdir(fd)); 22 | 23 | let fd = BorrowedFd::borrow_raw(fd); 24 | match convert_res(rustix::process::fchdir(fd)) { 25 | Some(()) => 0, 26 | None => -1, 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /c-scape/src/process/chroot.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::CStr; 2 | 3 | use libc::{c_char, c_int}; 4 | 5 | use crate::convert_res; 6 | 7 | #[no_mangle] 8 | unsafe extern "C" fn chroot(path: *const c_char) -> c_int { 9 | libc!(libc::chroot(path)); 10 | 11 | let path = CStr::from_ptr(path.cast()); 12 | match convert_res(rustix::process::chroot(path)) { 13 | Some(()) => 0, 14 | None => -1, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /c-scape/src/process/daemon.rs: -------------------------------------------------------------------------------- 1 | use libc::c_int; 2 | use rustix::fd::{AsRawFd, FromRawFd, OwnedFd}; 3 | 4 | #[no_mangle] 5 | unsafe extern "C" fn daemon(nochdir: c_int, noclose: c_int) -> c_int { 6 | libc!(libc::daemon(nochdir, noclose)); 7 | 8 | let nochdir = nochdir != 0; 9 | let noclose = noclose != 0; 10 | 11 | if !nochdir { 12 | if libc::chdir(c"/".as_ptr()) != 0 { 13 | return -1; 14 | } 15 | } 16 | 17 | if !noclose { 18 | let dev_null = libc::open(c"/dev/null".as_ptr(), libc::O_RDWR); 19 | if dev_null < 0 { 20 | return -1; 21 | } 22 | let dev_null = OwnedFd::from_raw_fd(dev_null); 23 | 24 | if libc::dup2(dev_null.as_raw_fd(), libc::STDIN_FILENO) < 0 25 | || libc::dup2(dev_null.as_raw_fd(), libc::STDOUT_FILENO) < 0 26 | || libc::dup2(dev_null.as_raw_fd(), libc::STDERR_FILENO) < 0 27 | { 28 | return -1; 29 | } 30 | } 31 | 32 | match libc::fork() { 33 | -1 => -1, 34 | 0 => libc::setsid(), 35 | _pid => libc::_exit(0), 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /c-scape/src/process/egid.rs: -------------------------------------------------------------------------------- 1 | use libc::gid_t; 2 | 3 | #[no_mangle] 4 | unsafe extern "C" fn getegid() -> gid_t { 5 | libc!(libc::getegid()); 6 | rustix::process::getegid().as_raw() 7 | } 8 | -------------------------------------------------------------------------------- /c-scape/src/process/euid.rs: -------------------------------------------------------------------------------- 1 | use libc::uid_t; 2 | 3 | #[no_mangle] 4 | unsafe extern "C" fn geteuid() -> uid_t { 5 | libc!(libc::geteuid()); 6 | rustix::process::geteuid().as_raw() 7 | } 8 | -------------------------------------------------------------------------------- /c-scape/src/process/getcwd.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use core::ptr::{copy_nonoverlapping, null_mut}; 3 | use errno::{set_errno, Errno}; 4 | use libc::c_char; 5 | 6 | use crate::convert_res; 7 | 8 | #[no_mangle] 9 | unsafe extern "C" fn getcwd(buf: *mut c_char, len: usize) -> *mut c_char { 10 | libc!(libc::getcwd(buf, len)); 11 | 12 | if len == 0 && !buf.is_null() { 13 | set_errno(Errno(libc::EINVAL)); 14 | return null_mut(); 15 | } 16 | 17 | match convert_res(rustix::process::getcwd(Vec::new())) { 18 | Some(path) => { 19 | let path = path.as_bytes(); 20 | 21 | let len = if len != 0 { len } else { path.len() + 1 }; 22 | 23 | // Test whether we have enough room to store `path` plus a trailing 24 | // NUL in `buf` which has length `len`. 25 | if path.len() < len { 26 | let mut buf = buf; 27 | if buf.is_null() { 28 | buf = libc::malloc(len).cast::(); 29 | if buf.is_null() { 30 | return buf; 31 | } 32 | } 33 | copy_nonoverlapping(path.as_ptr().cast::(), buf.cast::(), path.len()); 34 | *buf.add(path.len()) = 0; 35 | buf 36 | } else { 37 | set_errno(Errno(libc::ERANGE)); 38 | null_mut() 39 | } 40 | } 41 | None => null_mut(), 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /c-scape/src/process/gid.rs: -------------------------------------------------------------------------------- 1 | use libc::{c_int, gid_t}; 2 | 3 | #[no_mangle] 4 | unsafe extern "C" fn getgid() -> gid_t { 5 | libc!(libc::getgid()); 6 | rustix::process::getgid().as_raw() 7 | } 8 | 9 | #[no_mangle] 10 | unsafe extern "C" fn setgid(_gid: gid_t) -> c_int { 11 | libc!(libc::setgid(_gid)); 12 | 13 | // rustix has a `set_thread_gid` function, but it just wraps the Linux 14 | // syscall which sets a per-thread GID rather than the whole process GID. 15 | // Linux expects libc's to have logic to set the GID for all the threads. 16 | todo!("setgid") 17 | } 18 | -------------------------------------------------------------------------------- /c-scape/src/process/groups.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use errno::{set_errno, Errno}; 3 | use libc::c_int; 4 | 5 | #[no_mangle] 6 | unsafe extern "C" fn getgroups(num: c_int, groups: *mut libc::gid_t) -> c_int { 7 | libc!(libc::getgroups(num, groups)); 8 | 9 | let returned_groups = match convert_res(rustix::process::getgroups()) { 10 | Some(returned_groups) => returned_groups, 11 | None => return -1, 12 | }; 13 | 14 | if num == 0 { 15 | return match returned_groups.len().try_into() { 16 | Ok(converted) => converted, 17 | Err(_) => { 18 | set_errno(Errno(libc::ENOMEM)); 19 | -1 20 | } 21 | }; 22 | } 23 | 24 | let mut num_out = 0; 25 | let mut groups = groups; 26 | 27 | for group in returned_groups { 28 | if num_out == num { 29 | set_errno(Errno(libc::EINVAL)); 30 | return -1; 31 | } 32 | num_out += 1; 33 | groups.write(group.as_raw()); 34 | groups = groups.add(1); 35 | } 36 | 37 | num_out 38 | } 39 | 40 | #[cfg(any(target_os = "android", target_os = "linux"))] 41 | #[no_mangle] 42 | unsafe extern "C" fn setgroups() { 43 | //libc!(libc::setgroups()); 44 | todo!("setgroups") 45 | } 46 | -------------------------------------------------------------------------------- /c-scape/src/process/kill.rs: -------------------------------------------------------------------------------- 1 | use rustix::process::{Pid, Signal}; 2 | 3 | use errno::{set_errno, Errno}; 4 | use libc::{c_int, pid_t}; 5 | 6 | use crate::convert_res; 7 | 8 | #[no_mangle] 9 | unsafe extern "C" fn kill(pid: pid_t, sig: c_int) -> c_int { 10 | libc!(libc::kill(pid, sig)); 11 | 12 | if sig == 0 { 13 | let res = if pid < 0 { 14 | rustix::process::test_kill_process_group(Pid::from_raw(-pid as _).unwrap()) 15 | } else if let Some(pid) = Pid::from_raw(pid as _) { 16 | rustix::process::test_kill_process(pid) 17 | } else { 18 | rustix::process::test_kill_current_process_group() 19 | }; 20 | 21 | return match convert_res(res) { 22 | Some(()) => 0, 23 | None => -1, 24 | }; 25 | } 26 | 27 | let sig = Signal::from_raw_unchecked(sig); 28 | 29 | let res = if pid < 0 { 30 | rustix::process::kill_process_group(Pid::from_raw(-pid as _).unwrap(), sig) 31 | } else if let Some(pid) = Pid::from_raw(pid as _) { 32 | rustix::process::kill_process(pid, sig) 33 | } else { 34 | rustix::process::kill_current_process_group(sig) 35 | }; 36 | 37 | match convert_res(res) { 38 | Some(()) => 0, 39 | None => -1, 40 | } 41 | } 42 | 43 | #[no_mangle] 44 | unsafe extern "C" fn killpg(pgid: pid_t, sig: c_int) -> c_int { 45 | libc!(libc::killpg(pgid, sig)); 46 | 47 | if pgid < 0 { 48 | set_errno(Errno(libc::EINVAL)); 49 | return -1; 50 | } 51 | 52 | kill(-pgid, sig) 53 | } 54 | -------------------------------------------------------------------------------- /c-scape/src/process/mod.rs: -------------------------------------------------------------------------------- 1 | mod chdir; 2 | mod chroot; 3 | mod daemon; 4 | mod egid; 5 | mod euid; 6 | mod exec; 7 | mod getcwd; 8 | mod gid; 9 | mod groups; 10 | mod kill; 11 | mod pid; 12 | mod pidfd; 13 | mod priority; 14 | mod rlimit; 15 | mod sid; 16 | mod system; 17 | mod uid; 18 | mod umask; 19 | mod wait; 20 | 21 | // this entire module is 22 | // #[cfg(not(target_os = "wasi"))] 23 | -------------------------------------------------------------------------------- /c-scape/src/process/pid.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use errno::{set_errno, Errno}; 3 | use libc::{c_int, pid_t}; 4 | use rustix::process::Pid; 5 | 6 | #[no_mangle] 7 | unsafe extern "C" fn getpid() -> pid_t { 8 | libc!(libc::getpid()); 9 | rustix::process::getpid().as_raw_nonzero().get() as _ 10 | } 11 | 12 | #[no_mangle] 13 | unsafe extern "C" fn getppid() -> pid_t { 14 | libc!(libc::getppid()); 15 | Pid::as_raw(rustix::process::getppid()) as _ 16 | } 17 | 18 | #[no_mangle] 19 | unsafe extern "C" fn setpgid(pid: pid_t, pgid: pid_t) -> c_int { 20 | libc!(libc::setpgid(pid, pgid)); 21 | 22 | match convert_res(rustix::process::setpgid( 23 | Pid::from_raw(pid as _), 24 | Pid::from_raw(pgid as _), 25 | )) { 26 | Some(()) => 0, 27 | None => -1, 28 | } 29 | } 30 | 31 | #[no_mangle] 32 | unsafe extern "C" fn setpgrp() -> c_int { 33 | //libc!(libc::setpgrp()); 34 | 35 | setpgid(0, 0) 36 | } 37 | 38 | #[no_mangle] 39 | unsafe extern "C" fn getpgid(pid: pid_t) -> pid_t { 40 | libc!(libc::getpgid(pid)); 41 | 42 | if pid < 0 { 43 | set_errno(Errno(libc::ESRCH)); 44 | return -1; 45 | } 46 | 47 | match convert_res(rustix::process::getpgid(Pid::from_raw(pid as _))) { 48 | Some(pid) => pid.as_raw_nonzero().get(), 49 | None => -1, 50 | } 51 | } 52 | 53 | #[no_mangle] 54 | unsafe extern "C" fn getpgrp() -> pid_t { 55 | libc!(libc::getpgrp()); 56 | 57 | rustix::process::getpgrp().as_raw_nonzero().get() 58 | } 59 | -------------------------------------------------------------------------------- /c-scape/src/process/pidfd.rs: -------------------------------------------------------------------------------- 1 | use errno::{set_errno, Errno}; 2 | use libc::c_int; 3 | 4 | #[no_mangle] 5 | unsafe extern "C" fn pidfd_getpid(fd: c_int) -> c_int { 6 | //libc!(libc::pidfd_getpid(fd)); 7 | 8 | // ensure std::process uses fork as fallback code on linux 9 | let _ = fd; 10 | set_errno(Errno(libc::ENOSYS)); 11 | -1 12 | } 13 | 14 | #[no_mangle] 15 | unsafe extern "C" fn pidfd_spawnp( 16 | pid: *mut libc::c_int, 17 | path: *const libc::c_char, 18 | file_actions: *const libc::posix_spawn_file_actions_t, 19 | attrp: *const libc::posix_spawnattr_t, 20 | argv: *const *mut libc::c_char, 21 | envp: *const *mut libc::c_char, 22 | ) -> libc::c_int { 23 | //libc!(libc::pidfd_spawnp( 24 | // pid, 25 | // path, 26 | // file_actions, 27 | // attrp, 28 | // argv, 29 | // envp 30 | //)); 31 | 32 | // ensure std::process uses fork as fallback code on linux 33 | let _ = pid; 34 | let _ = path; 35 | let _ = file_actions; 36 | let _ = attrp; 37 | let _ = argv; 38 | let _ = envp; 39 | set_errno(Errno(libc::ENOSYS)); 40 | -1 41 | } 42 | -------------------------------------------------------------------------------- /c-scape/src/process/priority.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use errno::{set_errno, Errno}; 3 | use libc::{c_int, id_t, pid_t}; 4 | use rustix::process::{Pid, Uid}; 5 | 6 | #[cfg(not(target_env = "musl"))] 7 | use libc::__priority_which_t; 8 | #[cfg(target_env = "musl")] 9 | #[allow(non_camel_case_types)] 10 | type __priority_which_t = c_int; 11 | 12 | #[no_mangle] 13 | unsafe extern "C" fn getpriority(which: __priority_which_t, who: id_t) -> c_int { 14 | libc!(libc::getpriority(which, who)); 15 | 16 | let result = match which { 17 | libc::PRIO_PROCESS => rustix::process::getpriority_process(Pid::from_raw(who as pid_t)), 18 | libc::PRIO_PGRP => rustix::process::getpriority_pgrp(Pid::from_raw(who as pid_t)), 19 | libc::PRIO_USER => rustix::process::getpriority_user(Uid::from_raw(who)), 20 | _ => { 21 | set_errno(Errno(libc::EINVAL)); 22 | return -1; 23 | } 24 | }; 25 | 26 | match convert_res(result) { 27 | Some(prio) => prio, 28 | None => -1, 29 | } 30 | } 31 | 32 | #[no_mangle] 33 | unsafe extern "C" fn setpriority(which: __priority_which_t, who: id_t, prio: c_int) -> c_int { 34 | libc!(libc::setpriority(which, who, prio)); 35 | 36 | let result = match which { 37 | libc::PRIO_PROCESS => { 38 | rustix::process::setpriority_process(Pid::from_raw(who as pid_t), prio) 39 | } 40 | libc::PRIO_PGRP => rustix::process::setpriority_pgrp(Pid::from_raw(who as pid_t), prio), 41 | libc::PRIO_USER => rustix::process::setpriority_user(Uid::from_raw(who), prio), 42 | _ => { 43 | set_errno(Errno(libc::EINVAL)); 44 | return -1; 45 | } 46 | }; 47 | 48 | match convert_res(result) { 49 | Some(()) => 0, 50 | None => -1, 51 | } 52 | } 53 | 54 | #[no_mangle] 55 | unsafe extern "C" fn nice(inc: c_int) -> c_int { 56 | libc!(libc::nice(inc)); 57 | 58 | match convert_res(rustix::process::nice(inc)) { 59 | Some(new) => new, 60 | None => -1, 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /c-scape/src/process/sid.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use libc::pid_t; 3 | use rustix::process::Pid; 4 | 5 | #[no_mangle] 6 | unsafe extern "C" fn getsid(pid: pid_t) -> pid_t { 7 | libc!(libc::getsid(pid)); 8 | 9 | match convert_res(rustix::process::getsid(Pid::from_raw(pid as _))) { 10 | Some(v) => v.as_raw_nonzero().get() as _, 11 | None => -1, 12 | } 13 | } 14 | 15 | #[no_mangle] 16 | unsafe extern "C" fn setsid() -> pid_t { 17 | libc!(libc::setsid()); 18 | 19 | match convert_res(rustix::process::setsid()) { 20 | Some(v) => v.as_raw_nonzero().get() as _, 21 | None => -1, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /c-scape/src/process/uid.rs: -------------------------------------------------------------------------------- 1 | use libc::{c_int, uid_t}; 2 | 3 | #[no_mangle] 4 | unsafe extern "C" fn getuid() -> uid_t { 5 | libc!(libc::getuid()); 6 | rustix::process::getuid().as_raw() 7 | } 8 | 9 | #[no_mangle] 10 | unsafe extern "C" fn setuid(uid: uid_t) -> c_int { 11 | libc!(libc::setuid(uid)); 12 | 13 | // rustix has a `set_thread_uid` function, but it just wraps the Linux 14 | // syscall which sets a per-thread UID rather than the whole process UID. 15 | // Linux expects libc's to have logic to set the UID for all the threads. 16 | todo!("setuid") 17 | } 18 | -------------------------------------------------------------------------------- /c-scape/src/process/umask.rs: -------------------------------------------------------------------------------- 1 | use libc::mode_t; 2 | use rustix::fs::Mode; 3 | 4 | #[no_mangle] 5 | unsafe extern "C" fn umask(mask: mode_t) -> mode_t { 6 | libc!(libc::umask(mask)); 7 | rustix::process::umask(Mode::from_raw_mode(mask)).as_raw_mode() 8 | } 9 | -------------------------------------------------------------------------------- /c-scape/src/pty.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use libc::c_int; 3 | use rustix::fd::{BorrowedFd, IntoRawFd}; 4 | 5 | #[no_mangle] 6 | unsafe extern "C" fn posix_openpt(flags: c_int) -> c_int { 7 | libc!(libc::posix_openpt(flags)); 8 | 9 | let flags = rustix::pty::OpenptFlags::from_bits_retain(flags as _); 10 | match convert_res(rustix::pty::openpt(flags)) { 11 | Some(fd) => fd.into_raw_fd(), 12 | None => -1, 13 | } 14 | } 15 | 16 | #[no_mangle] 17 | unsafe extern "C" fn grantpt(fd: c_int) -> c_int { 18 | libc!(libc::grantpt(fd)); 19 | 20 | let fd = BorrowedFd::borrow_raw(fd); 21 | match convert_res(rustix::pty::grantpt(fd)) { 22 | Some(()) => 0, 23 | None => -1, 24 | } 25 | } 26 | 27 | #[no_mangle] 28 | unsafe extern "C" fn unlockpt(fd: c_int) -> c_int { 29 | libc!(libc::unlockpt(fd)); 30 | 31 | let fd = BorrowedFd::borrow_raw(fd); 32 | match convert_res(rustix::pty::unlockpt(fd)) { 33 | Some(()) => 0, 34 | None => -1, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /c-scape/src/rand/mod.rs: -------------------------------------------------------------------------------- 1 | use libc::{c_int, c_uint}; 2 | 3 | use rand::Rng; 4 | use rand_core::SeedableRng; 5 | use rand_pcg::Pcg32; 6 | 7 | use rustix_futex_sync::Mutex; 8 | 9 | #[cfg(test)] 10 | static_assertions::assert_eq_size!(c_uint, u32); 11 | 12 | // We use PCG32 here, which takes just 16 bytes and offers us a relatively nice 13 | // non-cryptographic RNG for most applications. 14 | // TODO: Remove the option once Pcg32 has seedable const fn things 15 | static STATE: Mutex> = Mutex::new(None); 16 | 17 | #[no_mangle] 18 | unsafe extern "C" fn rand() -> c_int { 19 | libc!(libc::rand()); 20 | 21 | // POSIX requires that if the user hasn't initialized the RNG, it behaves 22 | // as-if `srand(1)` was called. 23 | let mut guard = STATE.lock(); 24 | if guard.is_none() { 25 | internal_seed(&mut guard, 1); 26 | } 27 | 28 | // Sample using a uniform distribution 29 | (*guard).as_mut().unwrap().random_range(0..libc::RAND_MAX) as c_int 30 | } 31 | 32 | #[no_mangle] 33 | unsafe extern "C" fn srand(seed: c_uint) { 34 | libc!(libc::srand(seed)); 35 | 36 | internal_seed(&mut STATE.lock(), seed); 37 | } 38 | 39 | fn internal_seed(state: &mut Option, seed: c_uint) { 40 | *state = Some(Pcg32::seed_from_u64(u64::from(seed))); 41 | } 42 | 43 | // `rand_r` gives us at most 32 bits of internal state, which is quite frankly 44 | // terrible. Still, do the best we can. 45 | // 46 | // The algorithm used for this is Mulberry32, which 47 | // is under the CC0 license. 48 | // https://gist.github.com/tommyettinger/46a874533244883189143505d203312c 49 | #[no_mangle] 50 | unsafe extern "C" fn rand_r(seed: *mut c_uint) -> c_int { 51 | // libc!(libc::rand_r(seed)); 52 | 53 | let mut z = (*seed).wrapping_add(0x6D2B79F5); 54 | *seed = z; 55 | 56 | z = (z ^ (z >> 15)).wrapping_mul(z | 1); 57 | z ^= z.wrapping_add((z ^ (z >> 7)).wrapping_mul(z | 61)); 58 | (z ^ (z >> 14)) as c_int 59 | } 60 | -------------------------------------------------------------------------------- /c-scape/src/rand_.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use core::mem::MaybeUninit; 3 | use core::slice; 4 | #[cfg(not(target_env = "musl"))] 5 | use errno::{set_errno, Errno}; 6 | use libc::c_void; 7 | 8 | #[cfg(any(target_os = "android", target_os = "linux"))] 9 | #[no_mangle] 10 | unsafe extern "C" fn getrandom(ptr: *mut c_void, len: usize, flags: u32) -> isize { 11 | libc!(libc::getrandom(ptr, len, flags)); 12 | 13 | if len == 0 { 14 | return 0; 15 | } 16 | 17 | let flags = rustix::rand::GetRandomFlags::from_bits_retain(flags); 18 | let buf = slice::from_raw_parts_mut(ptr.cast::>(), len); 19 | 20 | match convert_res(rustix::rand::getrandom(buf, flags)) { 21 | Some((init, _uninit)) => init.len() as isize, 22 | None => -1, 23 | } 24 | } 25 | 26 | #[cfg(any(target_os = "android", target_os = "linux"))] 27 | #[cfg(not(target_env = "musl"))] 28 | #[no_mangle] 29 | unsafe extern "C" fn getentropy(ptr: *mut c_void, len: usize) -> i32 { 30 | libc!(libc::getentropy(ptr, len)); 31 | 32 | if len == 0 { 33 | return 0; 34 | } 35 | 36 | if len > 256 { 37 | set_errno(Errno(libc::EIO)); 38 | return -1; 39 | } 40 | 41 | let flags = rustix::rand::GetRandomFlags::empty(); 42 | let buf = slice::from_raw_parts_mut(ptr.cast::>(), len); 43 | 44 | let mut filled = 0usize; 45 | 46 | while filled < buf.len() { 47 | match rustix::rand::getrandom(&mut buf[filled..], flags) { 48 | Ok((init, _uninit)) => filled += init.len(), 49 | Err(rustix::io::Errno::INTR) => {} 50 | Err(err) => { 51 | set_errno(Errno(err.raw_os_error())); 52 | return -1; 53 | } 54 | } 55 | } 56 | 57 | 0 58 | } 59 | 60 | #[cfg(test)] 61 | mod tests { 62 | use super::*; 63 | 64 | #[cfg(any(target_os = "android", target_os = "linux"))] 65 | #[test] 66 | fn test_getentropy() { 67 | unsafe { 68 | let mut buf = [0; 257]; 69 | assert_eq!(getentropy(buf.as_mut_ptr().cast(), 257), -1); 70 | assert_eq!(errno::errno().0, libc::EIO); 71 | 72 | let mut buf = [0; 257]; 73 | assert_eq!(getentropy(buf.as_mut_ptr().cast(), 256), 0); 74 | assert!(buf.iter().any(|b| *b != 0)); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /c-scape/src/shm/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::convert_res; 2 | use core::ffi::CStr; 3 | use libc::{c_char, c_int, mode_t}; 4 | use rustix::fd::IntoRawFd; 5 | use rustix::fs::Mode; 6 | use rustix::shm; 7 | 8 | #[no_mangle] 9 | unsafe extern "C" fn shm_open(name: *const c_char, oflags: c_int, mode: mode_t) -> c_int { 10 | libc!(libc::shm_open(name, oflags, mode)); 11 | 12 | let name = CStr::from_ptr(name); 13 | let mode = Mode::from_bits((mode & !libc::S_IFMT) as _).unwrap(); 14 | let oflags = shm::OFlags::from_bits(oflags as _).unwrap(); 15 | match convert_res(shm::open(name, oflags, mode)) { 16 | Some(fd) => fd.into_raw_fd(), 17 | None => -1, 18 | } 19 | } 20 | 21 | #[no_mangle] 22 | unsafe extern "C" fn shm_unlink(name: *const c_char) -> c_int { 23 | libc!(libc::shm_unlink(name)); 24 | 25 | let name = CStr::from_ptr(name); 26 | match convert_res(shm::unlink(name)) { 27 | Some(()) => 0, 28 | None => -1, 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /c-scape/src/sort/bsearch.rs: -------------------------------------------------------------------------------- 1 | use core::ptr::null_mut; 2 | use libc::{c_int, c_void, size_t}; 3 | 4 | #[no_mangle] 5 | unsafe extern "C" fn bsearch( 6 | key: *const c_void, 7 | base: *const c_void, 8 | nmemb: size_t, 9 | width: size_t, 10 | compar: Option c_int>, 11 | ) -> *mut c_void { 12 | libc!(libc::bsearch(key, base, nmemb, width, compar)); 13 | 14 | let compar = compar.unwrap(); 15 | let mut base = base.cast::(); 16 | let mut nmemb = nmemb; 17 | 18 | while nmemb > 0 { 19 | let half = nmemb / 2; 20 | let mid = base.add(width * half); 21 | let sign = compar(key, mid.cast::()); 22 | if sign < 0 { 23 | nmemb = half; 24 | } else if sign > 0 { 25 | base = mid.add(width); 26 | nmemb -= half + 1; 27 | } else { 28 | return mid.cast::().cast_mut(); 29 | } 30 | } 31 | 32 | null_mut() 33 | } 34 | -------------------------------------------------------------------------------- /c-scape/src/sort/mod.rs: -------------------------------------------------------------------------------- 1 | mod bsearch; 2 | mod qsort; 3 | -------------------------------------------------------------------------------- /c-scape/src/sort/qsort.rs: -------------------------------------------------------------------------------- 1 | //! A simple `qsort` implementation based on the Combsort algorithm. 2 | 3 | use libc::{c_int, c_void, size_t}; 4 | 5 | #[no_mangle] 6 | unsafe extern "C" fn qsort( 7 | base: *mut c_void, 8 | nmemb: size_t, 9 | width: size_t, 10 | compar: Option c_int>, 11 | ) { 12 | libc!(libc::qsort(base, nmemb, width, compar)); 13 | 14 | let compar = compar.unwrap(); 15 | 16 | if nmemb <= 1 { 17 | return; 18 | } 19 | 20 | let base = base.cast::(); 21 | let mut gap = nmemb; 22 | 23 | loop { 24 | gap = next_gap(gap); 25 | 26 | let mut any_swapped = false; 27 | let mut a = base; 28 | let mut b = base.add(gap * width); 29 | for _ in 0..nmemb - gap { 30 | if compar(a.cast(), b.cast()) > 0 { 31 | swap(a, b, width); 32 | any_swapped = true; 33 | } 34 | a = a.add(width); 35 | b = b.add(width); 36 | } 37 | 38 | if gap <= 1 && !any_swapped { 39 | break; 40 | } 41 | } 42 | } 43 | 44 | #[cfg(not(target_env = "musl"))] 45 | #[no_mangle] 46 | unsafe extern "C" fn qsort_r( 47 | base: *mut c_void, 48 | nmemb: size_t, 49 | width: size_t, 50 | compar: Option c_int>, 51 | arg: *mut c_void, 52 | ) { 53 | libc!(libc::qsort_r(base, nmemb, width, compar, arg)); 54 | 55 | let compar = compar.unwrap(); 56 | 57 | if nmemb <= 1 { 58 | return; 59 | } 60 | 61 | let base = base.cast::(); 62 | let mut gap = nmemb; 63 | 64 | loop { 65 | gap = next_gap(gap); 66 | 67 | let mut any_swapped = false; 68 | let mut a = base; 69 | let mut b = base.add(gap * width); 70 | for _ in 0..nmemb - gap { 71 | if compar(a.cast(), b.cast(), arg) > 0 { 72 | swap(a, b, width); 73 | any_swapped = true; 74 | } 75 | a = a.add(width); 76 | b = b.add(width); 77 | } 78 | 79 | if gap <= 1 && !any_swapped { 80 | break; 81 | } 82 | } 83 | } 84 | 85 | fn next_gap(gap: size_t) -> size_t { 86 | let gap = (gap * 10) / 13; 87 | 88 | if gap == 9 || gap == 10 { 89 | 11 // apply the "rule of 11" 90 | } else if gap <= 1 { 91 | 1 92 | } else { 93 | gap 94 | } 95 | } 96 | 97 | unsafe fn swap(a: *mut u8, b: *mut u8, width: size_t) { 98 | for i in 0..width { 99 | core::ptr::swap(a.add(i), b.add(i)); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /c-scape/src/stdio/buf.rs: -------------------------------------------------------------------------------- 1 | use core::ptr::null_mut; 2 | use libc::{c_char, c_int, size_t, FILE}; 3 | 4 | #[no_mangle] 5 | unsafe extern "C" fn setbuf(stream: *mut FILE, buf: *mut c_char) { 6 | libc!(libc::setbuf(stream, buf)); 7 | 8 | setbuffer(stream, buf, libc::BUFSIZ as size_t) 9 | } 10 | 11 | #[no_mangle] 12 | unsafe extern "C" fn setbuffer(stream: *mut FILE, buf: *mut c_char, size: size_t) { 13 | //libc!(libc::setbuffer(stream, buf, size)); 14 | 15 | let mode = if buf.is_null() { 16 | libc::_IONBF 17 | } else { 18 | libc::_IOFBF 19 | }; 20 | 21 | setvbuf(stream, buf, mode, size); 22 | } 23 | 24 | #[no_mangle] 25 | unsafe extern "C" fn setlinebuf(stream: *mut FILE) { 26 | //libc!(libc::setlinebuf(stream)); 27 | 28 | setvbuf(stream, null_mut(), libc::_IOLBF, 0); 29 | } 30 | 31 | #[no_mangle] 32 | unsafe extern "C" fn setvbuf( 33 | stream: *mut FILE, 34 | buf: *mut c_char, 35 | mode: c_int, 36 | size: size_t, 37 | ) -> c_int { 38 | libc!(libc::setvbuf(stream, buf, mode, size)); 39 | 40 | // This implementation does not current perform buffering. This is mostly 41 | // just a missing optimization, but it'd be observable with user-provided 42 | // buffers. 43 | match mode { 44 | libc::_IONBF => 0, 45 | libc::_IOLBF | libc::_IOFBF => { 46 | if !buf.is_null() && size != 0 { 47 | todo!("buffered I/O with a user-provided buffer"); 48 | } 49 | 0 50 | } 51 | _ => -1, 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /c-scape/src/sync_ptr.rs: -------------------------------------------------------------------------------- 1 | /// A const pointer to `T` that implements [`Sync`]. 2 | #[repr(transparent)] 3 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 4 | pub(crate) struct SyncConstPtr(*const T); 5 | unsafe impl Sync for SyncConstPtr {} 6 | 7 | impl SyncConstPtr { 8 | /// Creates a `SyncConstPointer` from a raw pointer. 9 | /// 10 | /// Behavior is undefined if `ptr` is actually not 11 | /// safe to share across threads. 12 | pub const unsafe fn new(ptr: *const T) -> Self { 13 | Self(ptr) 14 | } 15 | } 16 | 17 | /// A mut pointer to `T` that implements [`Sync`]. 18 | #[repr(transparent)] 19 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 20 | pub(crate) struct SyncMutPtr(*mut T); 21 | unsafe impl Sync for SyncMutPtr {} 22 | 23 | impl SyncMutPtr { 24 | /// Creates a `SyncMutPtr` from a raw pointer. 25 | /// 26 | /// Behavior is undefined if `ptr` is actually not 27 | /// safe to share across threads. 28 | pub const unsafe fn new(ptr: *mut T) -> Self { 29 | Self(ptr) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /c-scape/src/system/mod.rs: -------------------------------------------------------------------------------- 1 | use errno::{set_errno, Errno}; 2 | use libc::c_int; 3 | 4 | use crate::convert_res; 5 | 6 | #[no_mangle] 7 | unsafe extern "C" fn reboot(cmd: c_int) -> c_int { 8 | libc!(libc::reboot(cmd)); 9 | 10 | let arg = match cmd { 11 | libc::LINUX_REBOOT_CMD_CAD_OFF => rustix::system::RebootCommand::CadOff, 12 | libc::LINUX_REBOOT_CMD_CAD_ON => rustix::system::RebootCommand::CadOn, 13 | libc::LINUX_REBOOT_CMD_HALT => rustix::system::RebootCommand::Halt, 14 | libc::LINUX_REBOOT_CMD_KEXEC => rustix::system::RebootCommand::Kexec, 15 | libc::LINUX_REBOOT_CMD_POWER_OFF => rustix::system::RebootCommand::PowerOff, 16 | libc::LINUX_REBOOT_CMD_RESTART => rustix::system::RebootCommand::Restart, 17 | libc::LINUX_REBOOT_CMD_SW_SUSPEND => rustix::system::RebootCommand::SwSuspend, 18 | _ => { 19 | set_errno(Errno(libc::EINVAL)); 20 | return -1; 21 | } 22 | }; 23 | 24 | match convert_res(rustix::system::reboot(arg)) { 25 | Some(()) => 0, 26 | None => -1, 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /c-scape/src/thread/once.rs: -------------------------------------------------------------------------------- 1 | use rustix_futex_sync::Once; 2 | 3 | use libc::c_int; 4 | 5 | libc_type!(Once, pthread_once_t); 6 | // Assert that `PTHREAD_ONCE_INIT` is zero, just like 7 | // `rustix_futex_sync::Once::new()` is documented to be. 8 | #[cfg(test)] 9 | static_assertions::const_assert_eq!(libc::PTHREAD_ONCE_INIT, unsafe { 10 | core::mem::transmute(Once::new()) 11 | }); 12 | 13 | #[no_mangle] 14 | unsafe extern "C" fn pthread_once( 15 | once_control: *mut libc::pthread_once_t, 16 | init_routine: extern "C" fn(), 17 | ) -> c_int { 18 | libc!(libc::pthread_once(once_control, init_routine)); 19 | 20 | // Cast the `*mut pthread_once_t` to `*mut Once`, which we can do since 21 | // `rustix_futex_sync` is documented to be a `repr(transparent)` wrapper 22 | // around `AtomicU32`. 23 | (*once_control.cast::()).call_once(move || { 24 | init_routine(); 25 | }); 26 | 27 | 0 28 | } 29 | -------------------------------------------------------------------------------- /c-scape/src/thread/spinlock.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::{AtomicU32, Ordering}; 2 | 3 | use libc::c_int; 4 | 5 | type PthreadSpinlockT = AtomicU32; 6 | libc_type!(PthreadSpinlockT, pthread_spinlock_t); 7 | 8 | #[no_mangle] 9 | unsafe extern "C" fn pthread_spin_destroy(lock: *mut PthreadSpinlockT) -> c_int { 10 | libc!(libc::pthread_spin_destroy(checked_cast!(lock))); 11 | 0 12 | } 13 | 14 | #[no_mangle] 15 | unsafe extern "C" fn pthread_spin_init(lock: *mut PthreadSpinlockT, pshared: c_int) -> c_int { 16 | libc!(libc::pthread_spin_init(checked_cast!(lock), pshared)); 17 | 18 | let lock = &*lock; 19 | 20 | lock.store(0, Ordering::Release); 21 | 0 22 | } 23 | 24 | #[no_mangle] 25 | unsafe extern "C" fn pthread_spin_lock(lock: *mut PthreadSpinlockT) -> c_int { 26 | libc!(libc::pthread_spin_lock(checked_cast!(lock))); 27 | 28 | let lock = &*lock; 29 | 30 | while lock.swap(1, Ordering::Acquire) == 1 { 31 | core::hint::spin_loop(); 32 | } 33 | 34 | 0 35 | } 36 | #[no_mangle] 37 | unsafe extern "C" fn pthread_spin_trylock(lock: *mut PthreadSpinlockT) -> c_int { 38 | libc!(libc::pthread_spin_trylock(checked_cast!(lock))); 39 | 40 | let lock = &*lock; 41 | 42 | if lock.swap(1, Ordering::Acquire) == 1 { 43 | libc::EBUSY 44 | } else { 45 | 0 46 | } 47 | } 48 | 49 | #[no_mangle] 50 | unsafe extern "C" fn pthread_spin_unlock(lock: *mut PthreadSpinlockT) -> c_int { 51 | libc!(libc::pthread_spin_unlock(checked_cast!(lock))); 52 | 53 | let lock = &*lock; 54 | 55 | lock.store(0, Ordering::Release); 56 | 0 57 | } 58 | -------------------------------------------------------------------------------- /c-scape/src/todo/aio.rs: -------------------------------------------------------------------------------- 1 | //! POSIX async I/O API. Not widely used. 2 | 3 | #[no_mangle] 4 | unsafe extern "C" fn aio_cancel() { 5 | todo!("aio_cancel") 6 | } 7 | #[no_mangle] 8 | unsafe extern "C" fn aio_error() { 9 | todo!("aio_error") 10 | } 11 | #[no_mangle] 12 | unsafe extern "C" fn aio_fsync() { 13 | todo!("aio_fsync") 14 | } 15 | #[no_mangle] 16 | unsafe extern "C" fn aio_read() { 17 | todo!("aio_read") 18 | } 19 | #[no_mangle] 20 | unsafe extern "C" fn aio_write() { 21 | todo!("aio_write") 22 | } 23 | #[no_mangle] 24 | unsafe extern "C" fn aio_return() { 25 | todo!("aio_return") 26 | } 27 | #[no_mangle] 28 | unsafe extern "C" fn aio_suspend() { 29 | todo!("aio_suspend") 30 | } 31 | #[no_mangle] 32 | unsafe extern "C" fn lio_listio() { 33 | todo!("lio_listio") 34 | } 35 | -------------------------------------------------------------------------------- /c-scape/src/todo/cat.rs: -------------------------------------------------------------------------------- 1 | //! Posix message catalog API. Not widely used. 2 | 3 | #[no_mangle] 4 | unsafe extern "C" fn catclose() { 5 | todo!("catclose") 6 | } 7 | #[no_mangle] 8 | unsafe extern "C" fn catgets() { 9 | todo!("catgets") 10 | } 11 | #[no_mangle] 12 | unsafe extern "C" fn catopen() { 13 | todo!("catopen") 14 | } 15 | -------------------------------------------------------------------------------- /c-scape/src/todo/dl.rs: -------------------------------------------------------------------------------- 1 | #[no_mangle] 2 | unsafe extern "C" fn dladdr() { 3 | todo!("dladdr") 4 | } 5 | #[no_mangle] 6 | unsafe extern "C" fn dlclose() { 7 | todo!("dlclose") 8 | } 9 | #[no_mangle] 10 | unsafe extern "C" fn dlerror() { 11 | todo!("dlerror") 12 | } 13 | #[no_mangle] 14 | unsafe extern "C" fn dlopen() { 15 | todo!("dlopen") 16 | } 17 | -------------------------------------------------------------------------------- /c-scape/src/todo/fenv.rs: -------------------------------------------------------------------------------- 1 | //! Support for dynamic floating-point rounding modes and floating-point 2 | //! exception flags. 3 | //! 4 | //! Rust in general doesn't support dynamic rounding modes or floating-point 5 | //! expecting handling. 6 | 7 | #[no_mangle] 8 | unsafe extern "C" fn fegetenv() { 9 | todo!("fegetenv") 10 | } 11 | #[no_mangle] 12 | unsafe extern "C" fn fesetenv() { 13 | todo!("fesetenv") 14 | } 15 | #[no_mangle] 16 | unsafe extern "C" fn fegetround() { 17 | todo!("fegetround") 18 | } 19 | #[no_mangle] 20 | unsafe extern "C" fn fesetround() { 21 | todo!("fesetround") 22 | } 23 | #[no_mangle] 24 | unsafe extern "C" fn feclearexcept() { 25 | todo!("feclearexcept") 26 | } 27 | #[no_mangle] 28 | unsafe extern "C" fn feraiseexcept() { 29 | todo!("feraiseexcept") 30 | } 31 | #[no_mangle] 32 | unsafe extern "C" fn fetestexcept() { 33 | todo!("fetestexcept") 34 | } 35 | #[no_mangle] 36 | unsafe extern "C" fn fegetexceptflag() { 37 | todo!("fegetexceptflag") 38 | } 39 | #[no_mangle] 40 | unsafe extern "C" fn fesetexceptflag() { 41 | todo!("fesetexceptflag") 42 | } 43 | #[no_mangle] 44 | unsafe extern "C" fn feholdexcept() { 45 | todo!("feholdexcept") 46 | } 47 | #[no_mangle] 48 | unsafe extern "C" fn feupdateenv() { 49 | todo!("feupdateenv") 50 | } 51 | -------------------------------------------------------------------------------- /c-scape/src/todo/jmp.rs: -------------------------------------------------------------------------------- 1 | //! Support for `setjmp`/`longjmp` and `setcontext`/`getcontext`/etc. 2 | //! 3 | //! Rust in general doesn't support `setjmp`/`longjmp` or 4 | //! `setcontext`/`getcontext`/etc. 5 | 6 | #[no_mangle] 7 | unsafe extern "C" fn __longjmp_chk() { 8 | todo!("__longjmp_chk") 9 | } 10 | #[no_mangle] 11 | unsafe extern "C" fn getcontext() { 12 | todo!("getcontext") 13 | } 14 | #[no_mangle] 15 | unsafe extern "C" fn setcontext() { 16 | todo!("setcontext") 17 | } 18 | #[no_mangle] 19 | unsafe extern "C" fn makecontext() { 20 | todo!("makecontext") 21 | } 22 | #[no_mangle] 23 | unsafe extern "C" fn swapcontext() { 24 | todo!("swapcontext") 25 | } 26 | -------------------------------------------------------------------------------- /c-scape/src/todo/locale.rs: -------------------------------------------------------------------------------- 1 | //! C locale support. 2 | 3 | #[no_mangle] 4 | unsafe extern "C" fn newlocale() { 5 | todo!("newlocale") 6 | } 7 | #[no_mangle] 8 | unsafe extern "C" fn freelocale() { 9 | todo!("freelocale") 10 | } 11 | #[no_mangle] 12 | unsafe extern "C" fn uselocale() { 13 | todo!("uselocale") 14 | } 15 | #[no_mangle] 16 | unsafe extern "C" fn nl_langinfo() { 17 | todo!("nl_langinfo") 18 | } 19 | #[no_mangle] 20 | unsafe extern "C" fn strftime_l() { 21 | todo!("strftime_l") 22 | } 23 | #[no_mangle] 24 | unsafe extern "C" fn strtod_l() { 25 | todo!("strtod_l") 26 | } 27 | #[no_mangle] 28 | unsafe extern "C" fn strtof_l() { 29 | todo!("strtof_l") 30 | } 31 | #[no_mangle] 32 | unsafe extern "C" fn strtold_l() { 33 | todo!("strtold_l") 34 | } 35 | #[no_mangle] 36 | unsafe extern "C" fn strtoll_l() { 37 | todo!("strtoll_l") 38 | } 39 | #[no_mangle] 40 | unsafe extern "C" fn strtoull_l() { 41 | todo!("strtoull_l") 42 | } 43 | -------------------------------------------------------------------------------- /c-scape/src/todo/long_double_complex.rs: -------------------------------------------------------------------------------- 1 | //! Rust doesn't support ABI-compatible long double complex yet. 2 | 3 | #[no_mangle] 4 | unsafe extern "C" fn creall() { 5 | todo!("creall") 6 | } 7 | #[no_mangle] 8 | unsafe extern "C" fn cimagl() { 9 | todo!("cimagl") 10 | } 11 | #[no_mangle] 12 | unsafe extern "C" fn cabsl() { 13 | todo!("cabsl") 14 | } 15 | #[no_mangle] 16 | unsafe extern "C" fn cargl() { 17 | todo!("cargl") 18 | } 19 | #[no_mangle] 20 | unsafe extern "C" fn cacosl() { 21 | todo!("cacosl") 22 | } 23 | #[no_mangle] 24 | unsafe extern "C" fn casinl() { 25 | todo!("casinl") 26 | } 27 | #[no_mangle] 28 | unsafe extern "C" fn catanl() { 29 | todo!("catanl") 30 | } 31 | #[no_mangle] 32 | unsafe extern "C" fn ccosl() { 33 | todo!("ccosl") 34 | } 35 | #[no_mangle] 36 | unsafe extern "C" fn csinl() { 37 | todo!("csinl") 38 | } 39 | #[no_mangle] 40 | unsafe extern "C" fn ctanl() { 41 | todo!("ctanl") 42 | } 43 | #[no_mangle] 44 | unsafe extern "C" fn cacoshl() { 45 | todo!("cacoshl") 46 | } 47 | #[no_mangle] 48 | unsafe extern "C" fn casinhl() { 49 | todo!("casinhl") 50 | } 51 | #[no_mangle] 52 | unsafe extern "C" fn catanhl() { 53 | todo!("catanhl") 54 | } 55 | #[no_mangle] 56 | unsafe extern "C" fn ccoshl() { 57 | todo!("ccoshl") 58 | } 59 | #[no_mangle] 60 | unsafe extern "C" fn csinhl() { 61 | todo!("csinhl") 62 | } 63 | #[no_mangle] 64 | unsafe extern "C" fn ctanhl() { 65 | todo!("ctanhl") 66 | } 67 | #[no_mangle] 68 | unsafe extern "C" fn cexpl() { 69 | todo!("cexpl") 70 | } 71 | #[no_mangle] 72 | unsafe extern "C" fn clogl() { 73 | todo!("clogl") 74 | } 75 | #[no_mangle] 76 | unsafe extern "C" fn clog10l() { 77 | todo!("clog10l") 78 | } 79 | #[no_mangle] 80 | unsafe extern "C" fn csqrtl() { 81 | todo!("csqrtl") 82 | } 83 | #[no_mangle] 84 | unsafe extern "C" fn conjl() { 85 | todo!("conjl") 86 | } 87 | #[no_mangle] 88 | unsafe extern "C" fn cprojl() { 89 | todo!("cprojl") 90 | } 91 | #[no_mangle] 92 | unsafe extern "C" fn cpowl() { 93 | todo!("cpowl") 94 | } 95 | -------------------------------------------------------------------------------- /c-scape/src/todo/pthread_cancel.rs: -------------------------------------------------------------------------------- 1 | //! Unimplemented pthread cancellation functions. 2 | //! 3 | //! Thread cancellation is not used by Rust code, and more broadly, it adds a 4 | //! lot of complexity to a lot of things without adding being proportionately 5 | //! valuable. 6 | 7 | use libc::c_int; 8 | 9 | #[no_mangle] 10 | unsafe extern "C" fn pthread_exit() -> c_int { 11 | todo!("pthread_exit") 12 | } 13 | #[no_mangle] 14 | unsafe extern "C" fn pthread_cancel() -> c_int { 15 | todo!("pthread_cancel") 16 | } 17 | #[no_mangle] 18 | unsafe extern "C" fn __pthread_register_cancel() { 19 | todo!("__pthread_register_cancel") 20 | } 21 | #[no_mangle] 22 | unsafe extern "C" fn __pthread_unregister_cancel() { 23 | todo!("__pthread_unregister_cancel") 24 | } 25 | #[no_mangle] 26 | unsafe extern "C" fn pthread_cleanup_push() -> c_int { 27 | todo!("pthread_cleanup_push") 28 | } 29 | #[no_mangle] 30 | unsafe extern "C" fn pthread_cleanup_pop() -> c_int { 31 | todo!("pthread_cleanup_pop") 32 | } 33 | #[no_mangle] 34 | unsafe extern "C" fn pthread_setcancelstate() -> c_int { 35 | todo!("pthread_setcancelstate") 36 | } 37 | #[no_mangle] 38 | unsafe extern "C" fn pthread_setcanceltype() -> c_int { 39 | todo!("pthread_setcanceltype") 40 | } 41 | #[no_mangle] 42 | unsafe extern "C" fn pthread_testcancel() -> c_int { 43 | todo!("pthread_testcancel") 44 | } 45 | -------------------------------------------------------------------------------- /c-scape/src/todo/set_id.rs: -------------------------------------------------------------------------------- 1 | //! Linux's system calls for these functions only set the IDs for one thread, 2 | //! and we need to set the IDs for all threads in a process. 3 | //! 4 | //! This would typically entail taking a lock that prevents thread creation, 5 | //! setting the IDs for each thread manually by sending signals to them and 6 | //! having signal handlers that perform the set operation, waiting for all 7 | //! the handlers to run, and then releasing the lock. 8 | 9 | #[no_mangle] 10 | unsafe extern "C" fn seteuid() { 11 | todo!("seteuid") 12 | } 13 | #[no_mangle] 14 | unsafe extern "C" fn setegid() { 15 | todo!("setegid") 16 | } 17 | #[no_mangle] 18 | unsafe extern "C" fn setreuid() { 19 | todo!("setreuid") 20 | } 21 | #[no_mangle] 22 | unsafe extern "C" fn setregid() { 23 | todo!("setregid") 24 | } 25 | #[no_mangle] 26 | unsafe extern "C" fn setresuid() { 27 | todo!("setresuid") 28 | } 29 | #[no_mangle] 30 | unsafe extern "C" fn setresgid() { 31 | todo!("setresgid") 32 | } 33 | -------------------------------------------------------------------------------- /c-scape/src/todo/sysv.rs: -------------------------------------------------------------------------------- 1 | //! System V APIs, which are usually obviated by POSIX equivalents. 2 | 3 | #[no_mangle] 4 | unsafe extern "C" fn semctl() { 5 | todo!("semctl") 6 | } 7 | #[no_mangle] 8 | unsafe extern "C" fn semget() { 9 | todo!("semget") 10 | } 11 | #[no_mangle] 12 | unsafe extern "C" fn semop() { 13 | todo!("semop") 14 | } 15 | #[no_mangle] 16 | unsafe extern "C" fn shmat() { 17 | todo!("shmat") 18 | } 19 | #[no_mangle] 20 | unsafe extern "C" fn shmget() { 21 | todo!("shmget") 22 | } 23 | #[no_mangle] 24 | unsafe extern "C" fn shmdt() { 25 | todo!("shmdt") 26 | } 27 | #[no_mangle] 28 | unsafe extern "C" fn shmctl() { 29 | todo!("shmctl") 30 | } 31 | #[no_mangle] 32 | unsafe extern "C" fn msgctl() { 33 | todo!("msgctl") 34 | } 35 | #[no_mangle] 36 | unsafe extern "C" fn msgget() { 37 | todo!("msgget") 38 | } 39 | #[no_mangle] 40 | unsafe extern "C" fn msgrcv() { 41 | todo!("msgrcv") 42 | } 43 | #[no_mangle] 44 | unsafe extern "C" fn msgsnd() { 45 | todo!("msgsnd") 46 | } 47 | #[no_mangle] 48 | unsafe extern "C" fn ftok() { 49 | todo!("ftok") 50 | } 51 | -------------------------------------------------------------------------------- /ci/aarch64-o-largefile.patch: -------------------------------------------------------------------------------- 1 | From Dan Gohman 2 | Subject: [PATCH] Correct the definition of `O_LARGEFILE` on aarch64 3 | 4 | This fixes `fcntl` with `F_GETFL` from spuriously returning `O_NOFOLLOW` 5 | on hosts such as x86_64. 6 | 7 | diff -ur a/linux-user/aarch64/target_fcntl.h b/linux-user/aarch64/target_fcntl.h 8 | --- a/linux-user/aarch64/target_fcntl.h 9 | +++ b/linux-user/aarch64/target_fcntl.h 10 | @@ -11,6 +11,7 @@ 11 | #define TARGET_O_DIRECTORY 040000 /* must be a directory */ 12 | #define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ 13 | #define TARGET_O_DIRECT 0200000 /* direct disk access hint */ 14 | +#define TARGET_O_LARGEFILE 0400000 15 | 16 | #include "../generic/fcntl.h" 17 | #endif 18 | -------------------------------------------------------------------------------- /ci/getsockopt-timeouts.patch: -------------------------------------------------------------------------------- 1 | From: Dan Gohman 2 | Subject: [PATCH] Avoid storing unexpected values for `SO_RCVTIMEO_NEW` etc. 3 | 4 | This issue is reported upstream [here]. 5 | 6 | [here]: https://gitlab.com/qemu-project/qemu/-/issues/885 7 | 8 | --- 9 | linux-user/generic/sockbits.h | 2 ++ 10 | linux-user/mips/sockbits.h | 2 ++ 11 | linux-user/sparc/sockbits.h | 2 ++ 12 | linux-user/syscall.c | 6 ++++++ 13 | 4 files changed, 12 insertions(+) 14 | 15 | diff --git a/linux-user/generic/sockbits.h b/linux-user/generic/sockbits.h 16 | index b3b4a8e44c..f95747e3cc 100644 17 | --- a/linux-user/generic/sockbits.h 18 | +++ b/linux-user/generic/sockbits.h 19 | @@ -36,6 +36,8 @@ 20 | #define TARGET_SO_SNDLOWAT 19 21 | #define TARGET_SO_RCVTIMEO 20 22 | #define TARGET_SO_SNDTIMEO 21 23 | +#define TARGET_SO_RCVTIMEO_NEW 66 24 | +#define TARGET_SO_SNDTIMEO_NEW 67 25 | 26 | /* Security levels - as per NRL IPv6 - don't actually do anything */ 27 | #define TARGET_SO_SECURITY_AUTHENTICATION 22 28 | diff --git a/linux-user/mips/sockbits.h b/linux-user/mips/sockbits.h 29 | index 562cad88e2..4d411f7b61 100644 30 | --- a/linux-user/mips/sockbits.h 31 | +++ b/linux-user/mips/sockbits.h 32 | @@ -39,6 +39,8 @@ 33 | #define TARGET_SO_RCVLOWAT 0x1004 /* receive low-water mark */ 34 | #define TARGET_SO_SNDTIMEO 0x1005 /* send timeout */ 35 | #define TARGET_SO_RCVTIMEO 0x1006 /* receive timeout */ 36 | +#define TARGET_SO_RCVTIMEO_NEW 66 37 | +#define TARGET_SO_SNDTIMEO_NEW 67 38 | #define TARGET_SO_ACCEPTCONN 0x1009 39 | #define TARGET_SO_PROTOCOL 0x1028 /* protocol type */ 40 | #define TARGET_SO_DOMAIN 0x1029 /* domain/socket family */ 41 | diff --git a/linux-user/sparc/sockbits.h b/linux-user/sparc/sockbits.h 42 | index 0a822e3e1f..8420ef9953 100644 43 | --- a/linux-user/sparc/sockbits.h 44 | +++ b/linux-user/sparc/sockbits.h 45 | @@ -26,6 +26,8 @@ 46 | #define TARGET_SO_SNDLOWAT 0x1000 47 | #define TARGET_SO_RCVTIMEO 0x2000 48 | #define TARGET_SO_SNDTIMEO 0x4000 49 | +#define TARGET_SO_RCVTIMEO_NEW 68 50 | +#define TARGET_SO_SNDTIMEO_NEW 69 51 | #define TARGET_SO_ACCEPTCONN 0x8000 52 | 53 | #define TARGET_SO_SNDBUF 0x1001 54 | diff --git a/linux-user/syscall.c b/linux-user/syscall.c 55 | index a8eae3c4ac..8326e03a19 100644 56 | --- a/linux-user/syscall.c 57 | +++ b/linux-user/syscall.c 58 | @@ -2348,6 +2348,9 @@ set_timeout: 59 | case TARGET_SO_SNDTIMEO: 60 | optname = SO_SNDTIMEO; 61 | goto set_timeout; 62 | + case TARGET_SO_RCVTIMEO_NEW: 63 | + case TARGET_SO_SNDTIMEO_NEW: 64 | + return -TARGET_ENOPROTOOPT; 65 | case TARGET_SO_ATTACH_FILTER: 66 | { 67 | struct target_sock_fprog *tfprog; 68 | @@ -2595,6 +2598,9 @@ get_timeout: 69 | case TARGET_SO_SNDTIMEO: 70 | optname = SO_SNDTIMEO; 71 | goto get_timeout; 72 | + case TARGET_SO_RCVTIMEO_NEW: 73 | + case TARGET_SO_SNDTIMEO_NEW: 74 | + return -TARGET_ENOPROTOOPT; 75 | case TARGET_SO_PEERCRED: { 76 | struct ucred cr; 77 | socklen_t crlen; 78 | -- 79 | 2.32.0 80 | 81 | -------------------------------------------------------------------------------- /ci/s390x-stat-have-nsec.patch: -------------------------------------------------------------------------------- 1 | From: Dan Gohman 2 | Subject: [PATCH] Define `TARGET_STAT_HAVE_NSEC` for s390x 3 | 4 | Without this, The `fstat` syscall sets `st_mtime_nsec` and the other `_nsec` 5 | fields to 0. Libc `fstat` will sometimes use the `fstatat` or `fstat64` 6 | syscalls instead, which aren't affected, but the libc `fstat` on ubuntu-20.04 7 | on Github Actions appears to be affected. 8 | 9 | This can be seen in the `st_mtime_nsec` assert in tests/fs/futimens.rs. 10 | 11 | It's not yet known why upstream qemu doesn't define this. 12 | 13 | --- 14 | linux-user/generic/sockbits.h | 1 + 15 | 1 files changed, 1 insertions(+) 16 | 17 | diff -ur a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h 18 | --- a/linux-user/syscall_defs.h 2021-08-24 10:35:41.000000000 -0700 19 | +++ b/linux-user/syscall_defs.h 2022-04-12 13:23:25.291064887 -0700 20 | @@ -2040,6 +2040,7 @@ 21 | abi_long __unused[3]; 22 | }; 23 | #elif defined(TARGET_S390X) 24 | +#define TARGET_STAT_HAVE_NSEC 25 | struct target_stat { 26 | abi_ulong st_dev; 27 | abi_ulong st_ino; 28 | -------------------------------------------------------------------------------- /ci/translate-errno.patch: -------------------------------------------------------------------------------- 1 | From: Dan Gohman 2 | Subject: [PATCH] Translate errno codes from host to target for `SO_ERROR`. 3 | 4 | This issue is reported upstream [here]. 5 | 6 | [here]: https://gitlab.com/qemu-project/qemu/-/issues/872 7 | 8 | --- 9 | linux-user/syscall.c | 3 +++ 10 | 1 file changed, 3 insertions(+) 11 | 12 | diff --git a/linux-user/syscall.c b/linux-user/syscall.c 13 | index b9b18a7eaf..a8eae3c4ac 100644 14 | --- a/linux-user/syscall.c 15 | +++ b/linux-user/syscall.c 16 | @@ -2767,6 +2767,9 @@ get_timeout: 17 | if (optname == SO_TYPE) { 18 | val = host_to_target_sock_type(val); 19 | } 20 | + if (level == SOL_SOCKET && optname == SO_ERROR) { 21 | + val = host_to_target_errno(val); 22 | + } 23 | if (len > lv) 24 | len = lv; 25 | if (len == 4) { 26 | -- 27 | 2.32.0 28 | 29 | -------------------------------------------------------------------------------- /example-crates/c-gull-example-panic-abort/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /example-crates/c-gull-example-panic-abort/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "c-gull-example-panic-abort" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies.libc] 8 | path = "../../c-gull" 9 | default-features = false 10 | features = [ 11 | "take-charge", 12 | "std", 13 | "thread", 14 | "call-main", 15 | "malloc-via-crates", 16 | "threadsafe-setenv" 17 | ] 18 | package = "c-gull" 19 | 20 | [profile.dev] 21 | panic = "abort" 22 | [profile.release] 23 | panic = "abort" 24 | 25 | # This is just an example crate, and not part of the c-ward workspace. 26 | [workspace] 27 | -------------------------------------------------------------------------------- /example-crates/c-gull-example-panic-abort/README.md: -------------------------------------------------------------------------------- 1 | Like the [c-gull-example example], but uses panic=abort in Cargo.toml. 2 | 3 | [c-gull-example example]: https://github.com/sunfishcode/c-ward/blob/main/example-crates/c-gull-example/README.md 4 | -------------------------------------------------------------------------------- /example-crates/c-gull-example-panic-abort/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Pass -nostartfiles to the linker. 3 | println!("cargo:rustc-link-arg=-nostartfiles"); 4 | } 5 | -------------------------------------------------------------------------------- /example-crates/c-gull-example-panic-abort/src/main.rs: -------------------------------------------------------------------------------- 1 | //! A simple example using "take-charge" mode. 2 | 3 | fn main() { 4 | println!("Hello world using Rust `println!`!"); 5 | unsafe { 6 | libc::printf("Hello world using libc `printf`!\n\0".as_ptr().cast()); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /example-crates/c-gull-example/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /example-crates/c-gull-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "c-gull-example" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies.libc] 8 | path = "../../c-gull" 9 | default-features = false 10 | features = [ 11 | "take-charge", 12 | "std", 13 | "thread", 14 | "call-main", 15 | "malloc-via-crates", 16 | "threadsafe-setenv" 17 | ] 18 | package = "c-gull" 19 | 20 | # This is just an example crate, and not part of the c-ward workspace. 21 | [workspace] 22 | -------------------------------------------------------------------------------- /example-crates/c-gull-example/README.md: -------------------------------------------------------------------------------- 1 | This crate demonstrates the use of c-gull in "take-charge" mode. 2 | 3 | This version uses `-nostartfiles` and origin is in control from the very 4 | beginning. 5 | -------------------------------------------------------------------------------- /example-crates/c-gull-example/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Pass -nostartfiles to the linker. 3 | println!("cargo:rustc-link-arg=-nostartfiles"); 4 | } 5 | -------------------------------------------------------------------------------- /example-crates/c-gull-example/src/main.rs: -------------------------------------------------------------------------------- 1 | //! A simple example using "take-charge" mode. Origin starts the process, 2 | //! calls `origin_main`, which transfers control to c-scape which calls the 3 | //! C-ABI-compatible extern `main` definition, which transfers control to 4 | //! the Rust std initialization code, which calls the user `main` function 5 | //! here. 6 | //! 7 | //! The end result is that we get all of `std`, using c-gull to implement 8 | //! all the libc calls underneath, and we can write totally normal Rust code. 9 | 10 | fn main() { 11 | println!("Hello world using Rust `println!`!"); 12 | unsafe { 13 | libc::printf("Hello world using libc `printf`!\n\0".as_ptr().cast()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example-crates/c-gull-lto/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /example-crates/c-gull-lto/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "c-gull-lto" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies.libc] 8 | path = "../../c-gull" 9 | default-features = false 10 | features = [ 11 | "take-charge", 12 | "std", 13 | "thread", 14 | "call-main", 15 | "malloc-via-crates", 16 | ] 17 | package = "c-gull" 18 | 19 | # Enable LTO. 20 | [profile.release] 21 | lto = true 22 | 23 | # This is just an example crate, and not part of the c-ward workspace. 24 | [workspace] 25 | -------------------------------------------------------------------------------- /example-crates/c-gull-lto/README.md: -------------------------------------------------------------------------------- 1 | This crate demonstrates the use of c-gull with LTO. 2 | 3 | It's the same as [c-gull-example], but enables LTO in Cargo.toml. 4 | 5 | [c-gull-example]: https://github.com/sunfishcode/c-ward/tree/main/example-crates/c-gull-example#readme 6 | -------------------------------------------------------------------------------- /example-crates/c-gull-lto/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Pass -nostartfiles to the linker. 3 | println!("cargo:rustc-link-arg=-nostartfiles"); 4 | } 5 | -------------------------------------------------------------------------------- /example-crates/c-gull-lto/src/main.rs: -------------------------------------------------------------------------------- 1 | //! A simple example using "take-charge" mode. Origin starts the process, 2 | //! calls `origin_main`, which transfers control to c-scape which calls the 3 | //! C-ABI-compatible extern `main` definition, which transfers control to 4 | //! the Rust std initialization code, which calls the user `main` function 5 | //! here. 6 | //! 7 | //! The end result is that we get all of `std`, using c-gull to implement 8 | //! all the libc calls underneath, and we can write totally normal Rust code. 9 | 10 | fn main() { 11 | println!("Hello world using Rust `println!`!"); 12 | unsafe { 13 | libc::printf("Hello world using libc `printf`!\n\0".as_ptr().cast()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example-crates/c-gull-unwinding/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /example-crates/c-gull-unwinding/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "c-gull-unwinding" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies.libc] 8 | path = "../../c-gull" 9 | default-features = false 10 | features = [ 11 | "take-charge", 12 | "std", 13 | "thread", 14 | "call-main", 15 | "malloc-via-crates", 16 | "threadsafe-setenv" 17 | ] 18 | package = "c-gull" 19 | 20 | # This is just an example crate, and not part of the c-ward workspace. 21 | [workspace] 22 | -------------------------------------------------------------------------------- /example-crates/c-gull-unwinding/README.md: -------------------------------------------------------------------------------- 1 | This crate demonstrates the use of c-gull in "take-charge" mode, and includes 2 | unwinding panic support. 3 | 4 | It's the same as [c-gull-example], but it performs an unwind. 5 | 6 | [c-gull-example]: https://github.com/sunfishcode/c-ward/tree/main/example-crates/c-gull-example#readme 7 | -------------------------------------------------------------------------------- /example-crates/c-gull-unwinding/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Pass -nostartfiles to the linker. 3 | println!("cargo:rustc-link-arg=-nostartfiles"); 4 | } 5 | -------------------------------------------------------------------------------- /example-crates/c-gull-unwinding/src/main.rs: -------------------------------------------------------------------------------- 1 | //! A simple example using `std::panic::catch_unwind`. 2 | 3 | fn main() { 4 | // Panic and catch it. 5 | std::panic::catch_unwind(|| call_do_panic()).unwrap_err(); 6 | 7 | println!("Hello, world!"); 8 | unsafe { 9 | libc::printf("Hello world using libc `printf`!\n\0".as_ptr().cast()); 10 | } 11 | } 12 | 13 | fn call_do_panic() { 14 | do_panic() 15 | } 16 | 17 | fn do_panic() { 18 | panic!("catch me!"); 19 | } 20 | -------------------------------------------------------------------------------- /example-crates/c-scape-example-panic-abort/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /example-crates/c-scape-example-panic-abort/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "c-scape-example" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies.libc] 8 | path = "../../c-scape" 9 | # Disable the default features, and enable "take-charge" mode. 10 | default-features = false 11 | features = [ 12 | "take-charge", 13 | "thread", 14 | "call-main", 15 | "malloc-via-rust-global-alloc", 16 | "threadsafe-setenv", 17 | # This simple example will never unwind. 18 | "eh-personality-continue", 19 | # This simple example will never panic. 20 | "panic-handler-trap", 21 | "global-allocator", 22 | ] 23 | package = "c-scape" 24 | 25 | [dependencies] 26 | errno = { version = "0.3.3", default-features = false } 27 | 28 | [profile.dev] 29 | panic = "abort" 30 | [profile.release] 31 | panic = "abort" 32 | 33 | # This is just an example crate, and not part of the c-ward workspace. 34 | [workspace] 35 | -------------------------------------------------------------------------------- /example-crates/c-scape-example-panic-abort/README.md: -------------------------------------------------------------------------------- 1 | Like the [c-scape-example example], but uses panic=abort in Cargo.toml. 2 | 3 | [c-scape-example example]: https://github.com/sunfishcode/c-ward/blob/main/example-crates/c-scape-example/README.md 4 | -------------------------------------------------------------------------------- /example-crates/c-scape-example-panic-abort/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Pass -nostartfiles to the linker. 3 | println!("cargo:rustc-link-arg=-nostartfiles"); 4 | } 5 | -------------------------------------------------------------------------------- /example-crates/c-scape-example-panic-abort/src/main.rs: -------------------------------------------------------------------------------- 1 | //! A simple example using `no_main`, `no_std`, and "take-charge" mode. 2 | 3 | #![no_std] 4 | #![no_main] 5 | 6 | #[no_mangle] 7 | unsafe extern "C" fn main(_argc: i32, _argv: *const *const u8, _envp: *const *const u8) -> i32 { 8 | // Call functions declared in the `libc` crate, which will be resolved by 9 | // c-scape. c-scape doesn't have `printf`, so we do it by hand. 10 | let message = b"Hello, world!\n"; 11 | let mut remaining = &message[..]; 12 | while !remaining.is_empty() { 13 | match libc::write(libc::STDOUT_FILENO, message.as_ptr().cast(), message.len()) { 14 | -1 => match errno::errno().0 { 15 | libc::EINTR => continue, 16 | _ => panic!(), 17 | }, 18 | n => remaining = &remaining[n as usize..], 19 | } 20 | } 21 | libc::exit(0); 22 | } 23 | -------------------------------------------------------------------------------- /example-crates/c-scape-example/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /example-crates/c-scape-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "c-scape-example" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies.libc] 8 | path = "../../c-scape" 9 | # Disable the default features, and enable "take-charge" mode. 10 | default-features = false 11 | features = [ 12 | "take-charge", 13 | "thread", 14 | "call-main", 15 | "malloc-via-rust-global-alloc", 16 | "threadsafe-setenv", 17 | # This simple example will never unwind. 18 | "eh-personality-continue", 19 | # This simple example will never panic. 20 | "panic-handler-trap", 21 | "global-allocator", 22 | ] 23 | package = "c-scape" 24 | 25 | [dependencies] 26 | errno = { version = "0.3.3", default-features = false } 27 | 28 | # This is just an example crate, and not part of the c-ward workspace. 29 | [workspace] 30 | -------------------------------------------------------------------------------- /example-crates/c-scape-example/README.md: -------------------------------------------------------------------------------- 1 | This crate demonstrates the use of c-scape in "take-charge" mode with `no_main` 2 | and `no_std`. 3 | 4 | This version uses `-nostartfiles` and origin is in control from the very 5 | beginning. 6 | 7 | This crate uses stub `#[panic_handler]` and `#[lang = "eh_personality"]` 8 | functions which are simple and have small code size, but which don't support 9 | unwinding. See the [c-scape-unwinding] crate for an example that includes 10 | unwinding support. 11 | 12 | [c-scape-unwinding]: https://github.com/sunfishcode/c-ward/tree/main/example-crates/c-scape-unwinding#readme 13 | -------------------------------------------------------------------------------- /example-crates/c-scape-example/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Pass -nostartfiles to the linker. 3 | println!("cargo:rustc-link-arg=-nostartfiles"); 4 | } 5 | -------------------------------------------------------------------------------- /example-crates/c-scape-example/src/main.rs: -------------------------------------------------------------------------------- 1 | //! A simple example using `no_main`, `no_std`, and "take-charge" mode. 2 | 3 | #![no_std] 4 | #![no_main] 5 | 6 | #[no_mangle] 7 | unsafe extern "C" fn main(_argc: i32, _argv: *const *const u8, _envp: *const *const u8) -> i32 { 8 | // Call functions declared in the `libc` crate, which will be resolved by 9 | // c-scape. c-scape doesn't have `printf`, so we do it by hand. 10 | let message = b"Hello, world!\n"; 11 | let mut remaining = &message[..]; 12 | while !remaining.is_empty() { 13 | match libc::write(libc::STDOUT_FILENO, message.as_ptr().cast(), message.len()) { 14 | -1 => match errno::errno().0 { 15 | libc::EINTR => continue, 16 | _ => panic!(), 17 | }, 18 | n => remaining = &remaining[n as usize..], 19 | } 20 | } 21 | libc::exit(0); 22 | } 23 | -------------------------------------------------------------------------------- /example-crates/c-scape-unwinding/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /example-crates/c-scape-unwinding/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "c-scape-unwinding" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies.libc] 8 | path = "../../c-scape" 9 | # Disable the default features, enable "take-charge" mode, and enable the 10 | # "eh-personality" and "panic-handler" features. 11 | default-features = false 12 | features = [ 13 | "take-charge", 14 | "thread", 15 | "call-main", 16 | "malloc-via-rust-global-alloc", 17 | "threadsafe-setenv", 18 | "eh-personality", 19 | "panic-handler", 20 | "global-allocator", 21 | ] 22 | package = "c-scape" 23 | 24 | [dependencies] 25 | errno = { version = "0.3.3", default-features = false } 26 | rustix-dlmalloc = { version = "0.2.1", features = ["global"] } 27 | # Depend on `unwinding` so that we can do `catch_unwind`. 28 | unwinding = { version = "0.2.6", default-features = false, features = ["panic"] } 29 | 30 | # This is just an example crate, and not part of the c-ward workspace. 31 | [workspace] 32 | -------------------------------------------------------------------------------- /example-crates/c-scape-unwinding/README.md: -------------------------------------------------------------------------------- 1 | This crate demonstrates the use of c-scape in "take-charge" mode with `no_main` 2 | and `no_std`, and includes unwinding panic support. 3 | 4 | It's the same as [c-scape-example], but enables the "eh-personality" and 5 | "panic-handler" features instead of defining stub `#[panic_handler]` and 6 | `#[lang = "eh_personality"]` functions, and it performs an unwind. 7 | 8 | [c-scape-example]: https://github.com/sunfishcode/c-ward/tree/main/example-crates/c-scape-example#readme 9 | -------------------------------------------------------------------------------- /example-crates/c-scape-unwinding/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Pass -nostartfiles to the linker. 3 | println!("cargo:rustc-link-arg=-nostartfiles"); 4 | } 5 | -------------------------------------------------------------------------------- /example-crates/c-scape-unwinding/src/main.rs: -------------------------------------------------------------------------------- 1 | //! A simple example using `no_main`, `no_std`, and "take-charge" mode, using 2 | //! the "eh-personaliity" and "panic_handler" features to support unwinding. 3 | 4 | #![no_std] 5 | #![no_main] 6 | 7 | #[no_mangle] 8 | unsafe extern "C" fn main(_argc: i32, _argv: *const *const u8, _envp: *const *const u8) -> i32 { 9 | // Panic and catch it. 10 | unwinding::panic::catch_unwind(|| call_do_panic()).unwrap_err(); 11 | 12 | // Call functions declared in the `libc` crate, which will be resolved by 13 | // c-scape. c-scape doesn't have `printf`, so we do it by hand. 14 | let message = b"Hello, world!\n"; 15 | let mut remaining = &message[..]; 16 | while !remaining.is_empty() { 17 | match libc::write(libc::STDOUT_FILENO, message.as_ptr().cast(), message.len()) { 18 | -1 => match errno::errno().0 { 19 | libc::EINTR => continue, 20 | _ => panic!(), 21 | }, 22 | n => remaining = &remaining[n as usize..], 23 | } 24 | } 25 | libc::exit(0); 26 | } 27 | 28 | fn call_do_panic() { 29 | do_panic() 30 | } 31 | 32 | fn do_panic() { 33 | panic!("catch me!"); 34 | } 35 | -------------------------------------------------------------------------------- /example-crates/custom-allocator/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /example-crates/custom-allocator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "custom-allocator" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies.libc] 8 | path = "../../c-gull" 9 | # Disable the default features, and enable "take-charge" mode. And enable 10 | # malloc via the rust global allocator, which works since we've redirected 11 | # the global allocator to not use malloc. 12 | default-features = false 13 | features = [ 14 | "take-charge", 15 | "std", 16 | "thread", 17 | "call-main", 18 | "malloc-via-rust-global-alloc", 19 | ] 20 | package = "c-gull" 21 | 22 | [dependencies] 23 | # We'll use this to provide our custom allocator. 24 | rustix-dlmalloc = { version = "0.2.1", features = ["global"] } 25 | 26 | # This is just an example crate, and not part of the c-ward workspace. 27 | [workspace] 28 | -------------------------------------------------------------------------------- /example-crates/custom-allocator/README.md: -------------------------------------------------------------------------------- 1 | This crate demonstrates the use of c-gull in "take-charge" mode using a 2 | custom global allocator. 3 | 4 | This version uses `-nostartfiles` and origin is in control from the very 5 | beginning. 6 | -------------------------------------------------------------------------------- /example-crates/custom-allocator/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Pass -nostartfiles to the linker. 3 | println!("cargo:rustc-link-arg=-nostartfiles"); 4 | } 5 | -------------------------------------------------------------------------------- /example-crates/custom-allocator/src/main.rs: -------------------------------------------------------------------------------- 1 | //! A simple example using "take-charge" mode and a custom global allocator. 2 | 3 | #[global_allocator] 4 | static GLOBAL_ALLOCATOR: rustix_dlmalloc::GlobalDlmalloc = rustix_dlmalloc::GlobalDlmalloc; 5 | 6 | fn main() { 7 | println!("Hello world using Rust `println!`!"); 8 | unsafe { 9 | libc::printf("Hello world using libc `printf`!\n\0".as_ptr().cast()); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /example-crates/dns/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /example-crates/dns/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dns" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies.libc] 8 | path = "../../c-gull" 9 | default-features = false 10 | features = [ 11 | "take-charge", 12 | "std", 13 | "thread", 14 | "call-main", 15 | "malloc-via-crates", 16 | ] 17 | package = "c-gull" 18 | 19 | # This is just an example crate, and not part of the c-ward workspace. 20 | [workspace] 21 | -------------------------------------------------------------------------------- /example-crates/dns/README.md: -------------------------------------------------------------------------------- 1 | This crate demonstrates the use of c-gull's DNS resolver. 2 | 3 | c-gull parses the output of the `getent` command, so its behavior should 4 | respect the system DNS and NSS configuration, without using `dlopen`. 5 | -------------------------------------------------------------------------------- /example-crates/dns/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Pass -nostartfiles to the linker. 3 | println!("cargo:rustc-link-arg=-nostartfiles"); 4 | } 5 | -------------------------------------------------------------------------------- /example-crates/dns/src/main.rs: -------------------------------------------------------------------------------- 1 | //! A simple DNS client. 2 | 3 | extern crate libc; 4 | 5 | use std::net::ToSocketAddrs; 6 | 7 | fn main() { 8 | let mut args = std::env::args(); 9 | let _ = args.next(); 10 | for arg in args { 11 | println!("resolving '{}:", arg); 12 | let addrs_iter = arg.to_socket_addrs().unwrap(); 13 | for addr in addrs_iter { 14 | println!(" - {}", addr); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /example-crates/libc-replacement/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /example-crates/libc-replacement/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libc-replacement" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | # Ordinarily, one would use libc like this: 9 | #libc = "0.2" 10 | 11 | # But in this example, we use c-gull instead of libc: 12 | libc = { path = "../../c-gull", features = ["coexist-with-libc"], package = "c-gull" } 13 | 14 | # This is just an example crate, and not part of the c-ward workspace. 15 | [workspace] 16 | -------------------------------------------------------------------------------- /example-crates/libc-replacement/README.md: -------------------------------------------------------------------------------- 1 | c-gull re-exports libc's API, so it can be used as a drop-in replacement. 2 | 3 | This line: 4 | 5 | ```toml 6 | libc = { path = "../../c-gull", features = ["coexist-with-libc"], package = "c-gull" } 7 | ``` 8 | 9 | tells cargo to use c-gull in place of libc. In this configuration, it coexists 10 | with the system libc and doesn't replace the malloc, pthread, or getauxval 11 | functions, but it replaces everything else that it can, such as the `getuid` 12 | and `getgid` functions in the example. 13 | -------------------------------------------------------------------------------- /example-crates/libc-replacement/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world! uid={}", unsafe { libc::getuid() }); 3 | unsafe { 4 | libc::printf( 5 | "Hello world with printf! gid=%u\n\0".as_ptr().cast(), 6 | libc::getgid(), 7 | ); 8 | } 9 | unsafe { 10 | libc::atexit(atexit_func); 11 | } 12 | } 13 | 14 | extern "C" fn atexit_func() { 15 | unsafe { 16 | libc::printf("Hello world from `atexit_func`\n\0".as_ptr().cast()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example-crates/threadsafe-setenv/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /example-crates/threadsafe-setenv/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "threadsafe-setenv" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies.libc] 8 | path = "../../c-gull" 9 | default-features = false 10 | # Enable the "threadsafe-setenv" feature to make `setenv` threadsafe! 11 | features = [ 12 | "take-charge", 13 | "std", 14 | "thread", 15 | "call-main", 16 | "malloc-via-crates", 17 | "threadsafe-setenv" 18 | ] 19 | package = "c-gull" 20 | 21 | # This is just an example crate, and not part of the c-ward workspace. 22 | [workspace] 23 | -------------------------------------------------------------------------------- /example-crates/threadsafe-setenv/README.md: -------------------------------------------------------------------------------- 1 | c-scape has a "threadsafe-setenv" feature which makes `setenv` threadsafe, 2 | fixing a [Rust soundness bug]. This example demonstrates it in use on an 3 | exampled linked to from that issue. 4 | 5 | [Rust soundness bug]: https://github.com/rust-lang/rust/issues/27970 6 | -------------------------------------------------------------------------------- /example-crates/threadsafe-setenv/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Pass -nostartfiles to the linker. 3 | println!("cargo:rustc-link-arg=-nostartfiles"); 4 | } 5 | -------------------------------------------------------------------------------- /example-crates/threadsafe-setenv/src/main.rs: -------------------------------------------------------------------------------- 1 | //! An example derived from 2 | //! 3 | //! showing `setenv` unsoundness in practice. c-ward fixes this with its 4 | //! "threadsafe-setenv" feature. 5 | 6 | extern crate libc; 7 | 8 | use std::net::ToSocketAddrs; 9 | 10 | fn main() { 11 | const NUM_ITERATIONS: usize = 100; 12 | 13 | // terrible argument parsing without dependencies 14 | const TRY_GETENV: &str = "try_getenv"; 15 | let program_args: Vec = std::env::args().collect(); 16 | if program_args.len() > 2 { 17 | eprintln!("ERROR: rustsetenvcrash only accepts one optional argument: {TRY_GETENV}; found {} args", 18 | program_args.len()); 19 | std::process::exit(1); 20 | } 21 | if program_args.len() == 2 && program_args[1] != TRY_GETENV { 22 | eprintln!( 23 | "ERROR: rustsetenvcrash only accepts one optional argument: {TRY_GETENV}; found {}", 24 | program_args[1] 25 | ); 26 | std::process::exit(1); 27 | } 28 | let try_getenv = program_args.len() == 2; 29 | 30 | println!("will call std::env::set_var() {NUM_ITERATIONS} times ..."); 31 | 32 | let t = if try_getenv { 33 | println!( 34 | "spawning thread to call std::env::var (will not crash: Rust holds lock for getenv)..." 35 | ); 36 | std::thread::spawn(do_getenv) 37 | } else { 38 | println!("spawning thread to lookup localhost (may crash with glibc; run with {TRY_GETENV} to call getenv instead)..."); 39 | std::thread::spawn(lookup_localhost) 40 | }; 41 | 42 | for i in 0..NUM_ITERATIONS { 43 | std::env::set_var(format!("ENV_VAR_{i}"), "value"); 44 | } 45 | 46 | t.join().expect("BUG thread must succeed"); 47 | 48 | println!("exiting without error"); 49 | } 50 | 51 | fn lookup_localhost() { 52 | let addrs_iter = "localhost:1".to_socket_addrs().unwrap(); 53 | for addr in addrs_iter { 54 | println!("localhost: ip={} port={}", addr.ip(), addr.port()); 55 | } 56 | } 57 | 58 | fn do_getenv() { 59 | for _ in 0..1000 { 60 | let r = std::env::var("doesnotexist"); 61 | assert!(!r.is_ok(), "env var should not exist"); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-04-28" 3 | components = ["rustc", "cargo", "rust-std", "rust-src", "rustfmt"] 4 | --------------------------------------------------------------------------------