Skip to content

Add Cosmopolitan Libc target #142609

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed

Add Cosmopolitan Libc target #142609

wants to merge 1 commit into from

Conversation

io12
Copy link

@io12 io12 commented Jun 17, 2025

This PR adds support for Cosmopolitan Libc, building on prior work from @ahgamut. It depends on support in the libc crate.

Cosmopolitan is a unique target, because some of the system constants (e.g. EINVAL, MAP_ANONYMOUS, O_CLOEXEC) are actually global variables that are set to the correct values at runtime based on the detected OS. This means unfortunately any pattern matching on them like libc::EINVAL => {} must be changed to use if guards instead like e if e == libc::EINVAL => {}. For switch statements in C, the solution was a gcc patch that wasn't upstreamed, so I'm not sure if it makes sense for Rust to upstream this, but I figured I would submit a PR anyway just to make it easier to find.

The code is also a bit messy right now, but it works surprisingly well in its current state.

Testing

Building tests

I built the library tests with

./x test library --target x86_64-unknown-cosmo
./x test library --target aarch64-unknown-cosmo

Then converted the test runners from ELF executables into APE executables with apelink

apelink -o test.com -l $COSMO/.cosmocc/3.9.2/bin/ape-x86_64.elf std-04db952a3d57c5d1.com.dbg

Then copied the APE test runners to different supported platforms and ran them.

Current test results

  • PASS
    • x86_64
      • Linux
      • FreeBSD
      • OpenBSD
      • NetBSD
      • MacOS
    • aarch64
      • Linux
      • FreeBSD
  • FAIL
    • x86_64
      • Windows
  • Untested
    • aarch64
      • MacOS
      • Windows

Caution

Concerns (1 active)

Managed by @rustbot—see help for details.

@rustbot rustbot added O-unix Operating system: Unix-like T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jun 17, 2025
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer
Copy link
Collaborator

The job mingw-check-tidy failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)
fmt check
fmt: checked 6077 files
tidy check

thread 'deps (.)' panicked at src/tools/tidy/src/deps.rs:594:24:
cmd.exec() failed with `cargo metadata` exited with an error: error: failed to load source for dependency `libc`

Caused by:
  Unable to update /checkout/library/TODO change to fork

Caused by:
---
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

thread 'main' panicked at src/tools/tidy/src/main.rs:62:49:
called `Result::unwrap()` on an `Err` value: Any { .. }
Command has failed. Rerun with -v to see more details.
Build completed unsuccessfully in 0:00:48
  local time: Tue Jun 17 05:18:01 UTC 2025
  network time: Tue, 17 Jun 2025 05:18:01 GMT
##[error]Process completed with exit code 1.
Post job cleanup.

Copy link
Member

@workingjubilee workingjubilee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course, my patch isn’t perfect. It can’t handle some anonymous structs, enums, const ints, or amazing things like using SIGTERM as an array index within an initializer. Rare compiler crashes may still occur in some weird static initializations, or if you try stuffing SIGTERM into a static const int8_t. But I’ve spent a good chunk of time removing obvious counterexamples, and a lot of popular software builds seamlessly. A stringent testing setup will reveal more things to improve.

If that's "seamlessly", then I would hate to know what is "with some difficulty".

I do not think we really want to add a target where none of libc is allowed in const eval, certainly not like this. This is Rust, not C++, and CTFE is a rule when in const {} or any other const context, not a suggestion like it is in constexpr. Many crates do something like this:

const NOT_FOUND: u32 = libc::ENOENT as _;

and then we can't evaluate that, so you're not getting much to begin with. It isn't very common for any one libc symbol, but when considering all the constants that are redefined, it adds up to quite a lot.

And that's assuming that those symbols are even sound, which may be an overly generous assumption.

Comment on lines +240 to +278
x if x == libc::E2BIG => ArgumentListTooLong,
x if x == libc::EADDRINUSE => AddrInUse,
x if x == libc::EADDRNOTAVAIL => AddrNotAvailable,
x if x == libc::EBUSY => ResourceBusy,
x if x == libc::ECONNABORTED => ConnectionAborted,
x if x == libc::ECONNREFUSED => ConnectionRefused,
x if x == libc::ECONNRESET => ConnectionReset,
x if x == libc::EDEADLK => Deadlock,
x if x == libc::EDQUOT => QuotaExceeded,
x if x == libc::EEXIST => AlreadyExists,
x if x == libc::EFBIG => FileTooLarge,
x if x == libc::EHOSTUNREACH => HostUnreachable,
x if x == libc::EINTR => Interrupted,
x if x == libc::EINVAL => InvalidInput,
x if x == libc::EISDIR => IsADirectory,
x if x == libc::ELOOP => FilesystemLoop,
x if x == libc::ENOENT => NotFound,
x if x == libc::ENOMEM => OutOfMemory,
x if x == libc::ENOSPC => StorageFull,
x if x == libc::ENOSYS => Unsupported,
x if x == libc::EMLINK => TooManyLinks,
x if x == libc::ENAMETOOLONG => InvalidFilename,
x if x == libc::ENETDOWN => NetworkDown,
x if x == libc::ENETUNREACH => NetworkUnreachable,
x if x == libc::ENOTCONN => NotConnected,
x if x == libc::ENOTDIR => NotADirectory,
#[cfg(not(target_os = "aix"))]
libc::ENOTEMPTY => DirectoryNotEmpty,
libc::EPIPE => BrokenPipe,
libc::EROFS => ReadOnlyFilesystem,
libc::ESPIPE => NotSeekable,
libc::ESTALE => StaleNetworkFileHandle,
libc::ETIMEDOUT => TimedOut,
libc::ETXTBSY => ExecutableFileBusy,
libc::EXDEV => CrossesDevices,
libc::EINPROGRESS => InProgress,
libc::EOPNOTSUPP => Unsupported,

libc::EACCES | libc::EPERM => PermissionDenied,
x if x == libc::ENOTEMPTY => DirectoryNotEmpty,
x if x == libc::EPIPE => BrokenPipe,
x if x == libc::EROFS => ReadOnlyFilesystem,
x if x == libc::ESPIPE => NotSeekable,
x if x == libc::ESTALE => StaleNetworkFileHandle,
x if x == libc::ETIMEDOUT => TimedOut,
x if x == libc::ETXTBSY => ExecutableFileBusy,
x if x == libc::EXDEV => CrossesDevices,
x if x == libc::EINPROGRESS => InProgress,
x if x == libc::EOPNOTSUPP => Unsupported,

x if x == libc::EACCES || x == libc::EPERM => PermissionDenied,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I think not.

The correctness losses here are unacceptable, because changing these to be symbolic, even if it could be done automatically, means that we no longer can benefit from exhaustiveness checking. Thus we cannot do things such as determining if the current match arm is covered by a previous one, or etc., requiring manual rechecking on every single review.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This need not be added for new targets.

let program = self.get_program_cstr();
let argv = self.get_argv().as_ptr();
#[cfg(target_os = "cosmo")]
if libc::IsWindows() && !program.to_bytes().ends_with(b".exe") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think we want more single-target-specific functions inside the libc crate, either.

no_default_libraries: true,
max_atomic_width: Some(64),
linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No),
stack_probes: StackProbeType::None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't this use inline stack probes?

metadata: TargetMetadata {
description: Some("64-bit Cosmopolitan Libc".into()),
tier: Some(3),
host_tools: Some(true),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
host_tools: Some(true),
host_tools: Some(false),

I'm pretty sure rustc can't be compiled for this based on what @workingjubilee already noted.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stray file

debug_assert_eq!(res, 0);
cfg_if::cfg_if! {
if #[cfg(target_os = "cosmo")] {
let _ = res;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change?

@Urgau
Copy link
Member

Urgau commented Jun 17, 2025

@rustbot concern libc-system-constants-no-longer-const

I concur with @workingjubilee, loosing the constness of libc system constants is a very big downsides, both in terms of correctness (no more exhaustiveness) but also in terms of usability.

For switch statements in C, the solution was a gcc patch that wasn't upstreamed

If gcc didn't wanted it, then it's not really a solution. I would very much want to see a proper and accepted solution in C before attempting any port in Rust.

@rustbot rustbot added the S-waiting-on-concerns Status: Awaiting concerns to be addressed by the author label Jun 17, 2025
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(picking a random file to get an inline comment) new targets should land as no_std first. Once we have that to indicate acceptance in rustc, other ecosystem updates can be considered.

Though maybe, if accepted, this is just easier to support as a no_std target indefinitely? That sidesteps any concerns with libc or hacks in std. It would be possible to publish a cosmo-std crate that drops into the std APIs that are well supported.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't think of this, but it seems like a good idea. In that case, maybe it makes more sense for it to not be part of rustc at all since custom targets can be specified with json files?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parts you have in compiler/ here can indeed be specified with the target json file, and that is worth trying. I suspect you may eventually need further compiler support to get things working "smoothly", though.

Based on the reactions it seems there are some concerns here. It would probably be a good idea for you to drop in on Zulip and discuss a bit what the best way forward might be https://rust-lang.zulipchat.com/.

@io12
Copy link
Author

io12 commented Jun 19, 2025

I agree the changes to make system constants symbolic are probably not worth it. Thanks for the feedback!

@io12 io12 closed this Jun 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
O-unix Operating system: Unix-like S-waiting-on-concerns Status: Awaiting concerns to be addressed by the author T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants