# Cosmopolitan Libc: build-once run-anywhere C with cosmocc

> Cosmopolitan Libc turns stock GCC and Clang into a toolchain that emits one polyglot binary running on Linux, macOS, Windows, FreeBSD, OpenBSD, NetBSD and BIOS. Here is how cosmocc works, how to build with it, and where it stops being the right tool.

**jart/cosmopolitan** — build-once run-anywhere c library

- Repository: https://github.com/jart/cosmopolitan
- Stars: 21,316 · Forks: 778
- Language: C
- License: ISC
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/jart-cosmopolitan

## What Cosmopolitan Libc actually solves

Cross-platform C distribution normally means a build farm. You compile once per operating system, per architecture, per libc, then ship a directory of binaries or a container image and hope the target machine matches. Cosmopolitan attacks the problem at the output format instead of the build system. The README describes the goal as making C/C++ a "build-once run-anywhere language, like Java, except it doesn't need an interpreter or virtual machine." The mechanism is a polyglot executable format that is simultaneously a valid PE, ELF and Mach-O file, so each operating system's loader picks up the part it understands. The audience is narrow and specific: people who write C or C++ command line tools, system utilities, or small servers and want to hand a single file to a user on an unknown machine. It is not a general application framework and it is not trying to be one.

## How the polyglot format and the libc fit together

Two pieces do the work. The first is the executable format, which the project calls APE, short for Actually Portable Executable. The same file is a valid MZ/PE image for Windows, an ELF image for Linux, FreeBSD, OpenBSD and NetBSD, and a Mach-O image for Darwin. Because the formats disagree about headers and entry points, the binary is laid out so that each loader finds something it accepts. The README's support vector lists AMD K8 from 2003 and Intel Core from 2006 as the minimum CPUs, Linux 2.6.18, Windows 8, Darwin 23.1.0, OpenBSD 7.3, and BIOS, which is why the same artifact can boot on bare metal as well as run under an OS.

The second piece is the libc itself. Cosmopolitan reimplements the POSIX surface rather than linking against the host libc, which is what lets one binary keep working across systems whose libc versions differ. It also links troubleshooting machinery by default: the README notes that the runtime includes "heavyweight troubleshooting features" that are useful to developers and admins, exposed as --strace for system call logging and --ftrace for function call logging. Both write through kprintf(), a facility the README calls unbreakable, and both can be redirected to a file with the KPRINTF_LOG environment variable. That is a deliberate trade: a larger default binary in exchange for being able to diagnose a failure on a machine you do not control.

## Installing cosmocc and compiling a first program

The toolchain ships as a zip. The README gives the download and unpack steps directly, and there is no package manager step in between.

```bash
mkdir -p cosmocc
cd cosmocc
wget https://cosmo.zip/pub/cosmocc/cosmocc.zip
unzip cosmocc.zip
```

With that directory on your path, a normal C program compiles the same way it would with gcc. The README's example is a two line hello world.

```c
// hello.c
#include <stdio.h>

int main() {
  printf("hello world\n");
}
```

Compile and run it with cosmocc in place of cc. The output binary is the artifact you distribute; the same file is what you copy to a Windows, macOS or BSD machine.

```bash
cosmocc -o hello hello.c
./hello
```

If something goes wrong at runtime, the troubleshooting flags are built in rather than requiring a separate debug build. Running ./hello --strace prints a system call log to stderr, and ./hello --ftrace prints a function call log instead. To capture either to a file rather than the terminal, set KPRINTF_LOG before running.

```bash
export KPRINTF_LOG=log
./hello --strace
```

For a project that already uses autotools, the README's recommended approach is to point CC and CXX at the Cosmopolitan compiler wrappers and configure as usual. Note the prefix in the example, which places the install tree under /opt/cosmos/x86_64.

```bash
export CC=x86_64-unknown-cosmo-cc
export CXX=x86_64-unknown-cosmo-c++
./configure --prefix=/opt/cosmos/x86_64
make -j
make install
```

## Building Cosmopolitan from source, and the tiny build modes

Building the repository itself is a different exercise from using cosmocc. The Makefile is explicit that you can run your programs anywhere, but you have to build them on Linux 2.6+ or WSL using GNU Make. That constraint is easy to miss when the whole selling point is portability of the output.

The README recommends installing a systemwide APE loader first, which requires sudo and registers the format with binfmt_misc on Linux. The Makefile then bootstraps by downloading cosmocc automatically. A common pattern is to build one target rather than the whole mono repo, since the tree is large.

```sh
rm -rf o//libc o//test
.cosmocc/current/bin/make o//test/posix/signal_test
o//test/posix/signal_test
```

The build has named modes, and the README calls out two of them for size. m=tiny produces binaries as small as 12kb according to the README, and m=tinylinux drops the other operating systems entirely, which the README says makes Cosmopolitan behave much more like Musl Libc. That is the honest framing of the trade: the portability is what costs bytes, and if you only need Linux you can throw most of it away. The full mode list lives in build/config.mk rather than the README.

## Where APE breaks: WSL, WINE and old shells

The format's cleverness is also its main failure mode. Some Linux distributions are configured to launch MZ executables under WINE, and others print "run-detectors: unable to find an interpreter" when handed an APE binary. The README documents both. The fix is to register the format with binfmt_misc, which the README presents as two writes into /proc/sys/fs/binfmt_misc/register after placing the ape loader in /usr/bin.

```sh
sudo wget -O /usr/bin/ape https://cosmo.zip/pub/cosmos/bin/ape-$(uname -m).elf
sudo chmod +x /usr/bin/ape
sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
sudo sh -c "echo ':APE-jart:M::jartsr::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
```

WSL is a separate hazard. The README states it is normally unsafe to use APE there because WSL tries to run MZ executables as WIN32 binaries, and gives a one line fix that disables WSLInterop. Shells are a third case: zsh before 5.9, old versions of fish, and Python's subprocess can all fail to launch an APE program, with sh -c ./prog offered as a workaround. None of these are bugs in your program. They are the cost of asking an operating system to accept a file format it was not designed for, and they land on whoever runs your binary, not on you.

## The alternative: static linking with musl

The closest thing to a drop-in alternative is musl libc with fully static linking. Both approaches remove the host libc from the equation, which is the shared insight. The difference is what you get on the other side. A musl static binary is one ELF file that runs on any Linux kernel new enough for the syscalls it uses, and that is where it stops. Cosmopolitan's output is one file that also runs on Windows, macOS, the BSDs and BIOS, which musl does not attempt and cannot do with the same artifact.

The trade runs the other way too. Musl is a libc you can install from your distribution and link against with the compiler you already have, and its behaviour on Linux is well travelled. Cosmopolitan replaces the libc, the compiler wrapper and the executable format, and the README's own platform notes show the seams where host systems disagree about MZ files. If your target is Linux only, musl static linking is the smaller commitment and the one with fewer surprises at the user's end. Cosmopolitan earns its complexity only when the target set genuinely spans operating systems.

## Licence, releases and what upgrades cost you

The repository is licensed ISC, a short permissive licence, and the README points at a tool for inspecting the transitive legalese of a built binary: o//tool/viz/bing -n followed by o//tool/viz/fold prints the closure of licences your artifact carries. That matters here more than in a typical C project, because a statically linked Cosmopolitan binary embeds code from many sources, and the ISC licence on Cosmopolitan itself says nothing about the third_party directory.

Release cadence is visible in the tags: 4.0.0, 4.0.1 and 4.0.2 landed within four days of each other in January 2025. The last push to the default branch was on 2026-07-20, so the tree is moving, but the release tags have not followed at that pace. If you pin a cosmocc version, check whether the fixes you care about are in a tag or only on master. Upgrading means replacing the toolchain zip and rebuilding; the README does not document a rollback path, so keep the previous cosmocc directory until the new one has produced binaries you have run on your oldest supported targets.

## Conclusion

Adopt Cosmopolitan if you ship a C or C++ command line tool to heterogeneous machines and want one artifact instead of a build matrix; the cosmocc zip from cosmo.zip is the whole install. Do not adopt it if your target is a platform outside the support vector, if you need shared library plugins loaded across operating systems, or if your build host is not Linux 2.6+ or WSL, because the Makefile states that is where you must build. Before committing, verify three things yourself: that your binary runs on the oldest Windows and macOS versions you claim to support, that APE registration is in place on the Linux machines you deploy to, and that the ISC licence text in LICENSE satisfies your redistribution terms.

## FAQ

### What is Cosmopolitan Libc?

It is a C library and toolchain that reconfigures stock GCC and Clang to emit a polyglot executable format running natively on Linux, macOS, Windows, FreeBSD, OpenBSD, NetBSD and BIOS. The README describes the goal as making C/C++ build-once run-anywhere without an interpreter or virtual machine.

### How do I install Cosmopolitan and compile a program with it?

Download cosmocc.zip from https://cosmo.zip/pub/cosmocc/ and unzip it, then compile with cosmocc -o hello hello.c as the README shows. The resulting binary is the artifact you distribute across the supported platforms.

### Why does my Cosmopolitan binary fail to start on Linux?

Some distributions launch MZ executables under WINE or report that run-detectors cannot find an interpreter. The README's fix is to place the ape loader in /usr/bin and register the APE format with binfmt_misc.

### Can I use Cosmopolitan binaries under WSL?

The README states it is normally unsafe, because WSL tries to run MZ executables as WIN32 binaries. It gives a command that disables WSLInterop to make Cosmopolitan software safe to use there.

### What build modes does Cosmopolitan provide?

The README names m=tiny for very small binaries, described as small as 12kb, and m=tinylinux, which removes the other operating systems and makes Cosmopolitan behave much more like Musl Libc. Further details are in build/config.mk.

## Sources

- [Issues](https://github.com/jart/cosmopolitan/issues)
- [jart/cosmopolitan on GitHub](https://github.com/jart/cosmopolitan)
- [License: ISC](https://github.com/jart/cosmopolitan/blob/master/LICENSE)
- [README](https://github.com/jart/cosmopolitan/blob/master/README.md)
- [Releases](https://github.com/jart/cosmopolitan/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jart-cosmopolitan
