Compare commits

..
204 Commits
Author SHA1 Message Date
Justin Tunney aefad27c09 Add bootstrap binary for elf2pe
It's now easier to cross compile a 2.5 kb hello world program for win32,
on arm64 xnu, that'll run on the very latest Windows, e.g.

cosmo-make -j12 m=x86_64 o/x86_64/tool/hello/hello-pe.ape
2026-10-20 18:25:16 -04:00
Gavin Hayes b0cebefd8e Fix GetProgramExecutableName() on NetBSD (#1529)
The sysctl parameters for querying KERN_PROC_PATHNAME are different from
FreeBSD. The tests now check the GPEN path using stat instead of strcmp.
2026-10-20 00:04:57 -04:00
Gavin Hayes 34fa4edae8 Update Fexecve to try execveat first on Linux (#1528)
Using execveat makes it possible for non-APE fexecve to work when procfs
is not mounted (LSMs may require procfs to be mounted so there is a path
for the file). Additionally, we now do not retry if errno is not ENOSYS,
ENOEXEC, or ELIBBAD as the retry funcs will still fail for other errors.
2026-10-13 17:41:14 -04:00
Gavin Hayes c7c9916eb8 Fix (f)execve tests under landlock make (#1527) 2026-10-13 06:39:25 -04:00
Justin Tunney 88848afdff Fix bugs in ctl 2026-10-12 18:28:01 -04:00
Justin Tunney e0c68ba56b Import some bug fixes from Musl Libc
Credit goes to Liam Wachter, Rich Felker, Szabolcs Nagy, and Yao Zi.
2026-10-12 17:57:21 -04:00
Justin Tunney bc6f307aa5 Update MaxMind library 2026-10-12 04:07:37 -04:00
Justin Tunney 7bc687dbc4 Fix warning with my Emacs config 2026-10-12 00:32:29 -04:00
Justin Tunney 1bc2a45f4a Make process initialization go faster 2026-10-11 19:42:53 -04:00
Justin Tunney 6ad44a10c2 Remove dated and incomplete romanize example 2026-10-11 04:24:30 -04:00
Justin Tunney 9607bee4b2 Make Plinko more hackable 2026-10-11 02:22:34 -04:00
Justin Tunney 2143d61dfa Improve POSIX conformance of freopen() 2026-10-10 22:20:30 -04:00
Gavin Hayes fdea2482dd Rewrite fexecve for better zipos & APE handling (#888)
This is essentially a rewrite of the entire `fexecve` implementation. We
no longer attempt to attempt to assimilate APEs in mem -- if the syscall
fails we fallback to sys_execve(/dev/fd/X so the APE loader can be used.
When a zipos file descriptor is passed, the new memfd is now only leaked
if the file has zipos (is a ZIP file) or is an APE, otherwise FD_CLOEXEC
is set on it (so non-zip ELFs do not leak). Previously FD_CLOEXEC on APE
binaries was not set when copying to a `memfd`, due to the security risk
of still leaking a fd, we no longer do so. FD_CLOEXEC does not work with
the APE loader (we may be able to later add support using AT_EXECFD). On
Linux, non-zip ELF files with only execute permissions may be opened and
run with _OPATH. The new version is vforksafe+asyncsignalsafe. To ensure
even on failure, the `memfd` will be cleaned up, pthread_cleanup_push is
used. zipos fexecve fails with `ENOTSUP` when `__vforked` so it does not
leak a `memfd` into the parent process if there is pthread cancellation.

The tests were revamped to more completely cover the various branches of
ELF or APE, zip file or not, `FD_CLOEXEC`, `O_PATH`, zipos fd or not, or
vforked. Additionally, there are checks for fd leaks. Feature testing is
done on Linux for the tests that rely on kernels newer than 2.6.18 so in
theory they should still pass on old Linux. For now, the zipos (f)execve
tests are only enabled on Linux as they have not been tested on FreeBSD.
The test files pass on Linux, Qemu user aarch64/x86_64, and Windows Ten.
2026-10-07 01:54:35 -04:00
Gavin Hayes c7d64e1b8b fix(execve-sysv): on shell fallback always terminate argv even when argc == 0 (#1525) 2026-10-06 10:31:45 -04:00
Gavin Hayes 977d10172c fix: include __program_executable_name in stack mapping even if argc == 0 (#1519) 2026-10-06 10:13:31 -04:00
Gavin Hayes 3293fad0a9 cleanup remnants of kErrnoNames (#1507)
https://github.com/jart/cosmopolitan/commit/5ddb5c2adad79407fb800fce47f389611f90a511
removed kErrnoNames , but left it in the header.
2026-07-19 21:14:06 -05:00
Justine Tunney 036845632f Fix bug with complex math
This change fixes a bug impacting complex math, where division of double
and float values (as in __divdc3, __divsc3) produced (inf, nan) whenever
the divisor's largest component was below one in magnitude, such as this
1. / (.5 + 0i) yielded (inf, nan) instead of the (2, 0) that is correct.
The same flaw sadly applied to any divisor with max(|real|, |imag|) < 1.

The bug was hidden by how -fcx-limited-range (part of -ffast-math) makes
the compiler inline naive division instead of calling __divdc3. Numerous
tests have been added so that this will not regress again in the future.

The bug was almost certainly caused by a misguided attempt to address an
undefined behavior sanitizer (UBSAN) warning (m=dbg) w/ signed shifting.
2026-05-31 09:58:10 -07:00
Justine Tunney 4a47191f51 Make qsort not depend on malloc 2026-05-21 19:15:02 -07:00
Justine Tunney a831042eca Clean up some code 2026-05-20 15:49:13 -07:00
Justine Tunney b4b5af498f Support C11 threads API
The C11 standard threads API is a subset of the POSIX threads API. It is
cool, because for example C11 mutexes can be a lighter weight wrapper of
nsync that only needs 24 bytes per `mtx_t` as opposed to pthread_mutex_t
requiring 64 bytes per mutex. To test that it all works, dozens of tests
have been added to the test/c11/ and test/posix/ folders.
2026-05-20 14:32:32 -07:00
Justine Tunney 18e3a2e7ce Improve invalid lock timestamp checking 2026-05-20 13:30:20 -07:00
Justine Tunney bc37215177 Fix EINTR bug in pthread_cond_timedwait
In cases where NSYNC isn't being used, the backup code path wasn't right
when it came to spinning on interrupt and checking the futex. This error
was caught thanks to a new suite of posix and c11 tests coming soon.
2026-05-20 13:09:44 -07:00
Justine Tunney 7e35bc854f Fix past timestamp lock bug on Windows and XNU 2026-05-20 12:41:23 -07:00
Justine Tunney 6eb8da23d4 Fix NetBSD lock signal bug 2026-05-20 12:25:04 -07:00
Justine Tunney 0b80275299 Introduce pthread_mutex_clocklock() 2026-05-20 10:25:15 -07:00
Justine Tunney ee883eabb3 Introduce nsync_mu_timedlock 2026-05-20 05:59:11 -07:00
Justine Tunney 7666e63ef9 Fix issue with RLIMIT_NOFILE on Windows 2026-05-20 02:04:35 -07:00
Justine Tunney 60734c784a Improve smoothsort
This change imports upstream changes from Musl Libc that protect us from
unknown security bugs. I've also made some changes of my own, to make it
go faster sorting small two power widths.
2026-05-18 14:15:59 -07:00
Justine Tunney deb866efd5 Remove openmp_test 2026-05-18 11:38:13 -07:00
Justine Tunney ad16ba5460 Add llround, llroundf, and llroundl 2026-05-18 07:39:14 -07:00
Justine Tunney db041429ed Increase turfwar queue size 2026-05-18 07:38:37 -07:00
Gautham eedf7d2db6 Add some headers and stubs (#1474)
- signal.h should show ucontext
- dlopen-related
- SIGIOT (is just SIGABRT)
~~itimerspec~~
~~sigevent~~
~~timer_create~~
2026-03-05 16:18:22 -08:00
Gautham 9ba2b50805 Set PF_INET = 2 on Windows (#1441) 2026-01-18 23:40:41 -08:00
Tobias Fleischer 7ecf16e306 Remove conflicting defines in windowsesque.h (#1467) 2026-01-18 23:39:21 -08:00
James Hulce db617bfd58 Update lsqlite3 documentation link comments (#1428) 2026-01-18 17:18:45 -08:00
BONNAURE Olivier 9b1285b915 Update onError in definition.lua (#1442) 2026-01-18 17:10:12 -08:00
Justine Tunney b444b3a6e0 Fix regressions in strcasechr, wcschr, strchr16
This change fixes a regression introduced by string library improvements
in 5ddb5c2ada that could cause wcschr(s,0)
to return NULL rather than a pointer to the end of the string.
2025-12-02 00:35:55 -08:00
Justine Tunney b0fdf97054 Syntax highlight .inc files 2025-11-18 18:30:50 -08:00
Justine Tunney 2f786453ee Fix VSCode 2025-11-18 17:24:36 -08:00
Justine Tunney 694ef1aa8b Remove optlinux from github actions
The cosmocc -moptlinux mode assumes you have an Intel / AMD CPU with AVX
however the GA instances we've been getting are faulting on AVX opcodes.
2025-11-18 14:12:55 -08:00
Justine Tunney bc0fc702fe Remove more miniaudio.h ifdefs 2025-11-18 12:39:18 -08:00
Justine Tunney bea62f3631 Upgrade miniaudio to v0.11.23 2025-11-18 11:52:53 -08:00
Gautham dee25272f1 Use --std=gnu17 for cosmocc (#1452)
Third party libraries in superconfigure have compiler checks, that error
out for C23, so the preference would be to use C17 for a while until all
those libraries are updated.
2025-11-17 11:17:02 -08:00
Justine Tunney 5ddb5c2ada Make improvements
- Open flags (e.g. O_CREAT) are now constants
- Error numbers (e.g. EINVAL) are now constants
- Signal numbers (e.g. SIGINT) are now constants
- fcntl() file locks now work properly on Windows
- Significantly improve Windows path handling code
- Implement true vfork() on Windows and make fork() faster
- Implement scanf %[charset] support and improve conformance
- Introduce $COSMOSDRIVE so you can use Windows 11 dev drives
- Fix stdio seek/tell bug and make stdio locks faster and safer
- Change TLS ABI to use tlscc wrapper that inserts function calls
- Make malloc() faster on all platforms using new cosmo_shard() api
- Make malloc() fast like tcmalloc on Linux using restartable sequences
- Improve fsync() and fdatasync() polyfills so they abstract robustness
- Fix CLOCK_BOOTTIME and CLOCK_MONOTONIC on XNU so it reflects uptime
- Fix bugs in Windows overlapped code that could possibly cause crash
- Fix bugs in Windows signal code that could possibly cause crash
- Make symlinks with multiple components work right on Windows
- Add FreeBSD ELF note to remove SIGBUS issue and other quirks
- Support symlinks on Windows under non-administrator accounts
- Make kisdangerous() scalable by adding radix trie to mmap()
- Make large realloc() operations zero-cost on all platforms
- Fix bugs in readdir() on Windows where d_reclen wasn't set
- Make all system call wrappers memory safe (raises EFAULT)
- Fix bugs in poll() and select() on Windows with 64+ fds
- Fix bug in madvise() on Windows that could cause crash
- Support WUNTRACED, SIGSTOP, and SIGCONT on Windows
- Increase x86 image base from 0x400000 to 0x4200000
- Introduce -msysv and -mtinylinux flags to cosmocc
- Fix issues with siglongjmp() on XNU and NetBSD
- Make pthread_barrier_wait() work correctly
- Make clock_gettime() faster on FreeBSD ARM
- Support file mode bits better on Windows
- Improve performance of string libraries
- Raise EILSEQ when creating evil paths
- Fix std::terminate to not use malloc
- Modernize more c11 and c23 features
- Make pthread_once() exception safe
- Support RLIMIT_CPU on Windows
- Introduce HACL* cryptography
- Introduce cosmopolitan make
- Introduce fasterzip program
- Introduce open_memstream()
- Introduce dedupe program
- Introduce arc4random()
- Introduce f2fs support
2025-11-16 21:15:26 -08:00
Alexar f1dc5cff44 Make redbean probe for port on bind failure (#1451)
When redbean is starting up, if you don't explicitly specify a port then
it'll choose the default one, which is 8080. If that port is in use then
redbean will increment the port number up to 20 times before it gives up
2025-11-15 08:28:34 -08:00
Hugues Morisset f1e83d5240 Add IPv6 support to getifaddrs() on Linux (#1415) 2025-05-21 01:20:22 -07:00
Steven Dee (Jōshin) 2fe8338f92 Better mtimes for github workflow build cache (#1421)
Saves and restores mtimes to a file, also covering the `o/` directory to
hopefully preserve make dependency information better.
2025-05-20 22:17:55 -07:00
ShalokShalom 4ca513cba2 Add C++ to README (#1407) 2025-04-25 15:47:50 -07:00
Steven Dee (Jōshin) 455910e8f2 Make more shared_ptr fixes (#1401)
* Make refcount reads explicitly atomic
* Consistently put `const` in the same place
* Write the general `operator=` on `weak_ptr`
2025-04-21 05:36:50 -07:00
Steven Dee (Jōshin) 9c68bc19b5 Cache .cosmocc and o for github workflows (#1400)
Uses GitHub’s actions/cache@v4 to store the cosmocc distribution and the
output directory between runs of the build workflow, with the version of
cosmocc as the cache key.

Upgrades to actions/checkout@v4.
2025-04-17 15:55:27 -07:00
Steven Dee (Jōshin) 66d1050af6 Correctly implement weak_ptr assignment/copy/moves (#1399) 2025-04-17 14:01:20 -07:00
Justine Tunney fbc4fcbb71 Get GDB working
You can now say `gdb hello.com.dbg` and it'll work perfectly.
2025-03-30 15:25:55 -07:00
Steven Dee (Jōshin) afc986f741 Fix shared_ptr<T>::owner_before (#1390)
`!(a < b)` is not the same as `b < a`.

I think I originally wrote it this way to avoid making weak_ptr a friend
of shared_ptr, but weak_ptr already is a friend.
2025-03-25 01:49:34 -04:00
Derek 5eb7cd6643 Add support for getcpu() system call to pledge() (#1387)
This fixes redbean Lua tests which were failing with SIGSYS on Linux.
2025-03-21 16:08:25 -07:00
Brett Jia a8ed4fdd09 Add NetBSD evbarm and fix segfault (#1384)
This change fixes a segmentation fault when comparing loaders that don't
have a target kernel set. Additionally, adds evbarm, which is the output
of uname -m on NetBSD on aarch64.
2025-03-12 17:37:46 -07:00
Brett Jia 7b69652854 Add -k OSNAME flag to apelink (#1383)
Let's say you pass the `-M blink-mips.elf` flag to apelink, so that your
ape binary will bundle a compressed build of blink, and the shell script
will extract that binary and launch your program under it, if running on
a MIPS system. However, for any given microprocessor architecture, we'll
need a separate loader for each operating system. The issue is ELF OSABI
isn't very useful. As an example, SerenityOS and Linux both have SYSV in
the OSABI field. So to tell their binaries apart we'd have to delve into
various other conventions, like special sections and PT_NOTE structures.

To make things simple this change introduces the `-k OS` flag to apelink
which generate shell script content that ensures `OS` matches `uname -s`
before attempting to execute a loader. For example, you could say:

    apelink -k Linux -M blink-linux-arm.elf -M blink-linux-mips.elf \
            -k Darwin -M blink-darwin-ppc.elf \
            ...

To introduce support for old 32-bit architectures on multiple OSes, when
building your cosmo binary.
2025-03-12 13:26:51 -07:00
Leal G. b235492e71 Add usertrust certificate (#1382)
Bundle USERTrust CA certificates to /usr/share/ssl/root for TLS verifies
2025-03-11 17:59:34 -07:00
Brett Jia fc81fd8d16 Support additional architectures in apelink (#1381)
This updates apelink to support machine architectures not in the source
program input list by adding additional loaders, extracting the correct
one that matches the host uname machine. With this change, blink can be
supplied as the additional loader to run the program in x86_64 VMs. The
change has been verified against blink 1.0, powerpc64le and mips64el in
Docker using QEMU.
2025-03-06 10:26:31 -08:00
Gautham 38930de8e0 Make tool for replacing ELF strings (#1344) 2025-02-08 21:17:42 -08:00
Brett Jia 0e557d041d Check downloaded gcc/clang checksums (#1367)
Check sha256 checksums of the downloaded gcc and clang toolchains. It'll
allow us to extend trust to external toolchains if building from source.
2025-02-08 17:46:09 -08:00
Brett Jia 1d676b36e6 Make cosmoranlib executable (#1366)
Fixes #1325
2025-02-08 17:38:00 -08:00
Brett Jia 10a92cee94 Support building cosmocc on MacOS (#1365)
This updates the cosmocc toolchain packaging script to work on MacOS. It
has been tested on GitHub Actions macos-13 (x86_64) and macos-14 (arm64)
runners, and is verified to still work on Ubuntu (GitHub Actions runners
ubuntu-24.04 and ubuntu-24.04-arm). It'll help bring cosmocc to MacPorts
by running the packaging script. We favor `gmake` rather than the `make`
command because it distinguishes GNU Make from BSD Make, and Xcode Make.
Additionally, APE loader from the bootstrapper toolchain is used instead
of a system APE, which may not be available.
2025-02-08 12:45:45 -08:00
A2va 42a9ed0131 Adds some NT functions (#1358) 2025-02-08 08:08:08 -08:00
Björn Buckwalter 12cb0669fb Clarify unix.mapshared versus file locks (#1355) 2025-02-08 00:48:38 -08:00
rufeooo 7f6a7d6fff Fix sigaction example code (#1363) 2025-02-07 11:42:47 -08:00
Steven Dee (Jōshin) 9f6bf6ea71 tool/zsh/mkofs: doas 2025-01-13 16:48:55 -08:00
Steven Dee (Jōshin) 102edf4ea2 tool/zsh/mmake: style 2025-01-05 20:53:53 -08:00
Steven Dee (Jōshin) 21968acf99 Standard make path (#1353)
Modifies download-cosmocc.sh to maintain a .cosmocc/current symlink that
always points to the most recently downloaded version of cosmocc. We can
use this to point at a canonical make for a bootstrapped repository. For
first-time builds, we suggest: https://cosmo.zip/pub/cosmos/bin/make and
have updated the docs in a few places to mention this.

Fixes the other part of #1346.
2025-01-05 20:47:34 -08:00
Justine Tunney 98861b23fc Make some style fixes to prng code 2025-01-05 20:18:05 -08:00
Steven Dee (Jōshin) dab6d7a345 Resolve multiple definition of __sig (fixes #1346) (#1352) 2025-01-05 19:54:49 -08:00
Justine Tunney 90119c422c Fix 404 url
Closes #1347
2025-01-05 17:04:37 -08:00
Justine Tunney 5907304049 Release Cosmopolitan v4.0.2 2025-01-05 14:05:49 -08:00
Justine Tunney 035b0e2a62 Attempt to fix MODE=dbg Windows execve() flake 2025-01-05 14:05:49 -08:00
Justine Tunney 7b67b20dae Fix Windows MODE=tiny breakage 2025-01-05 14:05:49 -08:00
Himanshu Pal f0b0f926bf Enable sqlite3 serialization in redbean (#1349)
This fixes a failing demo page, that requires us to enable serialization
in the lsqlite3 library that's used by the redbean server.
2025-01-05 13:59:10 -08:00
Justine Tunney 29eb7e67bb Fix fork() regression on Windows
Recent optimizations to fork() introduced a regression, that could cause
the subprocess to fail unexpectedly, when TlsAlloc() returns a different
index. This is because we were burning the indexes into the displacement
of x86 opcodes. So when fork() happened and the executable memory copied
it would use the old index. Right now the way this is being solved is to
not copy the executable on fork() and then re-apply code changes. If you
need to be able to preserve self-modified code on fork, reach out and we
can implement a better solution for you. This gets us unblocked quickly.
2025-01-05 09:25:23 -08:00
Justine Tunney f71f61cd40 Add some temporary logging statements 2025-01-04 23:37:32 -08:00
Justine Tunney 53c6edfd18 Make correction to last change 2025-01-04 21:38:47 -08:00
Justine Tunney 42a3bb729a Make execve() linger when it can't spoof parent
It's now possible to use execve() when the parent process isn't built by
cosmo. In such cases, the current process will kill all threads and then
linger around, waiting for the newly created process to die, and then we
propagate its exit code to the parent. This should help bazel and others

Allocating private anonymous memory is now 5x faster on Windows. This is
thanks to VirtualAlloc() which is faster than the file mapping APIs. The
fork() function also now goes 30% faster, since we are able to avoid the
VirtualProtect() calls on mappings in most cases now.

Fixes #1253
2025-01-04 21:13:37 -08:00
Justine Tunney c97a858470 Remove missing definitions 2025-01-04 00:20:45 -08:00
Justine Tunney 4acd12a514 Release Cosmopolitan v4.0.1 2025-01-03 19:51:34 -08:00
Justine Tunney b734eec836 Test restricting tests to single cpu 2025-01-03 19:51:09 -08:00
Justine Tunney fe01642a20 Add missing lock to fork() on Windows 2025-01-03 19:01:58 -08:00
Justine Tunney e939659b70 Fix ordering of pthread_create(pthread_t *thread)
This change fixes a bug where signal_latency_async_test would flake less
than 1/1000 of the time. What was happening was pthread_kill(sender_thr)
would return EFAULT. This was because pthread_create() was not returning
the thread object pointer until after clone() had been called. So it was
actually possible for the main thread to stall after calling clone() and
during that time the receiver would launch and receive a signal from the
sender thread, and then fail when it tried to send a pong. I thought I'd
use a barrier at first, in the test, to synchronize thread creation, but
I firmly believe that pthread_create() was to blame and now that's fixed
2025-01-03 17:34:29 -08:00
Justine Tunney ed6d133a27 Use tgkill() on Linux and FreeBSD
This eliminates the chance of rare bugs when thread IDs are recycled.
2025-01-03 17:27:13 -08:00
Justine Tunney 97fc2aab41 Release Cosmopolitan v4.0.0 2025-01-02 22:27:34 -08:00
Justine Tunney 662e7b217f Remove pthread_setcanceltype() from non-dbg strace 2025-01-02 22:25:29 -08:00
Justine Tunney 27f2777cc6 Fix aarch64 build 2025-01-02 22:19:49 -08:00
Justine Tunney 538ce338f4 Fix fork thread handle leak on windows 2025-01-02 19:33:14 -08:00
Justine Tunney a15958edc6 Remove some legacy cruft
Function trace logs will report stack usage accurately. It won't include
the argv/environ block. Our clone() polyfill is now simpler and does not
use as much stack memory. Function call tracing on x86 is now faster too
2025-01-02 18:44:07 -08:00
Justine Tunney 8db646f6b2 Fix bug with systemvpe()
See #1253
2025-01-02 09:19:59 -08:00
Justine Tunney fde03f8487 Remove leaf attribute where appropriate
This change fixes a bug where gcc assumed thread synchronization such as
pthread_cond_wait() wouldn't alter static variables, because the headers
were using __attribute__((__leaf__)) inappropriately.
2025-01-02 08:07:15 -08:00
Justine Tunney f24c854b28 Write more runtime tests and fix bugs
This change adds tests for the new memory manager code particularly with
its windows support. Function call tracing now works reliably on Silicon
since our function hooker was missing new Apple self-modifying code APIs

Many tests that were disabled a long time ago on aarch64 are reactivated
by this change, now that arm support is on equal terms with x86. There's
been a lot of places where ftrace could cause deadlocks, which have been
hunted down across all platforms thanks to new tests. A bug in Windows's
kill() function has been identified.
2025-01-01 22:25:22 -08:00
Justine Tunney 0b3c81dd4e Make fork() go 30% faster
This change makes fork() go nearly as fast as sys_fork() on UNIX. As for
Windows this change shaves about 4-5ms off fork() + wait() latency. This
is accomplished by using WriteProcessMemory() from the parent process to
setup the address space of a suspended process; it is better than a pipe
2025-01-01 04:59:38 -08:00
Justine Tunney 98c5847727 Fix fork waiter leak in nsync
This change fixes a bug where nsync waiter objects would leak. It'd mean
that long-running programs like runitd would run out of file descriptors
on NetBSD where waiter objects have ksem file descriptors. On other OSes
this bug is mostly harmless since the worst that can happen with a futex
is to leak a little bit of ram. The bug was caused because tib_nsync was
sneaking back in after the finalization code had cleared it. This change
refactors the thread exiting code to handle nsync teardown appropriately
and in making this change I found another issue, which is that user code
which is buggy, and tries to exit without joining joinable threads which
haven't been detached, would result in a deadlock. That doesn't sound so
bad, except the main thread is a joinable thread. So this deadlock would
be triggered in ways that put libc at fault. So we now auto-join threads
and libc will log a warning to --strace when that happens for any thread
2024-12-31 01:30:13 -08:00
Justine Tunney fd7da586b5 Introduce example flash card program named rote 2024-12-30 03:03:43 -08:00
Justine Tunney a51ccc8fb1 Remove old shuffle header 2024-12-30 03:03:32 -08:00
Justine Tunney c7e3d9f7ff Make recursive mutexes slightly faster 2024-12-30 01:37:14 -08:00
Justine Tunney 9ba5b227d9 Unblock stalled i/o signals on windows 2024-12-29 00:22:41 -08:00
Justine Tunney aca4214ff6 Simplify memory manager code 2024-12-28 17:09:28 -08:00
Justine Tunney 379cd77078 Improve memory manager and signal handling
On Windows, mmap() now chooses addresses transactionally. It reduces the
risk of badness when interacting with the WIN32 memory manager. We don't
throw darts anymore. There is also no more retry limit, since we recover
from mystery maps more gracefully. The subroutine for combining adjacent
maps has been rewritten for clarity. The print maps subroutine is better

This change goes to great lengths to perfect the stack overflow code. On
Windows you can now longjmp() out of a crash signal handler. Guard pages
previously weren't being restored properly by the signal handler. That's
fixed, so on Windows you can now handle a stack overflow multiple times.
Great thought has been put into selecting the perfect SIGSTKSZ constants
so you can save sigaltstack() memory. You can now use kprintf() with 512
bytes of stack available. The guard pages beneath the main stack are now
recorded in the memory manager.

This change fixes getcontext() so it works right with the %rax register.
2024-12-27 01:33:00 -08:00
Justine Tunney 36e5861b0c Reduce stack virtual memory consumption on Linux 2024-12-25 20:58:08 -08:00
Justine Tunney cc8a9eb93c Document execve() limitation on Windows
Closes #1253
2024-12-24 12:20:48 -08:00
Justine Tunney 0158579493 Use ape interpreter in flakes program 2024-12-24 12:16:50 -08:00
Justine Tunney 2de3845b25 Build tool for hunting down flakes 2024-12-24 11:36:16 -08:00
Justine Tunney 93e22c581f Reduce pthread memory usage 2024-12-24 10:30:59 -08:00
Justine Tunney ec2db4e40e Avoid pthread_rwlock_wrlock() starvation 2024-12-24 10:30:11 -08:00
Justine Tunney 55b7aa1632 Allow user to override pthread mutex and cond 2024-12-23 21:57:52 -08:00
Justine Tunney 4705705548 Fix bugs in times() function 2024-12-23 20:57:10 -08:00
Justine Tunney c8e10eef30 Make bulk_free() go faster 2024-12-23 20:31:57 -08:00
Justine Tunney 624573207e Make threads faster and more reliable
This change doubles the performance of thread spawning. That's thanks to
our new stack manager, which allows us to avoid zeroing stacks. It gives
us 15µs spawns rather than 30µs spawns on Linux. Also, pthread_exit() is
faster now, since it doesn't need to acquire the pthread GIL. On NetBSD,
that helps us avoid allocating too many semaphores. Even if that happens
we're now able to survive semaphores running out and even memory running
out, when allocating *NSYNC waiter objects. I found a lot more rare bugs
in the POSIX threads runtime that could cause things to crash, if you've
got dozens of threads all spawning and joining dozens of threads. I want
cosmo to be world class production worthy for 2025 so happy holidays all
2024-12-21 22:13:00 -08:00
Justine Tunney 906bd06a5a Fix MODE=tiny build 2024-12-17 01:36:29 -08:00
Justine Tunney c8c81af0c7 Remove distracting code from dlmalloc 2024-12-16 22:54:30 -08:00
Justine Tunney af7bd80430 Eliminate cyclic locks in runtime
This change introduces a new deadlock detector for Cosmo's POSIX threads
implementation. Error check mutexes will now track a DAG of nested locks
and report EDEADLK when a deadlock is theoretically possible. These will
occur rarely, but it's important for production hardening your code. You
don't even need to change your mutexes to use the POSIX error check mode
because `cosmocc -mdbg` will enable error checking on mutexes by default
globally. When cycles are found, an error message showing your demangled
symbols describing the strongly connected component are printed and then
the SIGTRAP is raised, which means you'll also get a backtrace if you're
using ShowCrashReports() too. This new error checker is so low-level and
so pure that it's able to verify the relationships of every libc runtime
lock, including those locks upon which the mutex implementation depends.
2024-12-16 22:25:12 -08:00
Justine Tunney 26c051c297 Spoof PID across execve() on Windows
It's now possible with cosmo and redbean, to deliver a signal to a child
process after it has called execve(). However the executed program needs
to be compiled using cosmocc. The cosmo runtime WinMain() implementation
now intercepts a _COSMO_PID environment variable that's set by execve().
It ensures the child process will use the same C:\ProgramData\cosmo\sigs
file, which is where kill() will place the delivered signal. We are able
to do this on Windows even better than NetBSD, which has a bug with this

Fixes #1334
2024-12-14 13:13:08 -08:00
Justine Tunney 9cc1bd04b2 Test rwlock more 2024-12-14 09:40:13 -08:00
Justine Tunney 69402f4d78 Support building ltests.c in MODE=dbg
Fixes #1226
2024-12-13 08:19:42 -08:00
Justine Tunney 838b54f906 Fix C++ math.h include order issue
Fixes #1257
2024-12-13 07:49:59 -08:00
Justine Tunney 2d43d400c6 Support process shared pthread_rwlock
Cosmo now has a non-nsync implementation of POSIX read-write locks. It's
possible to call pthread_rwlockattr_setpshared in PTHREAD_PROCESS_SHARED
mode. Furthermore, if cosmo is built with PTHREAD_USE_NSYNC set to zero,
then Cosmo shouldn't use nsync at all. That's helpful if you want to not
link any Apache 2.0 licensed code.
2024-12-13 03:00:06 -08:00
Justine Tunney c22b413ac4 Make strcasestr() faster 2024-12-12 22:50:20 -08:00
Justine Tunney 22094ae9ca Change language in leak detector 2024-12-10 11:04:35 -08:00
Justine Tunney bda2a4d55e Fix jtckdint version number 2024-12-07 03:19:11 -08:00
Justine Tunney b490e23d63 Improve Windows sleep accuracy from 15ms to 15µs 2024-12-06 23:03:57 -08:00
Steven Dee (Jōshin) b40140e6c5 Improve redbean concurrency (#1332)
In the course of playing with redbean I was confused about how the state
was behaving and then noticed that some stuff is maybe getting edited by
multiple processes. I tried to improve things by changing the definition
of the counter variables to be explicitly atomic. Claude assures me that
most modern Unixes support cross-process atomics, so I just went with it
on that front.

I also added some mutexes to the shared state to try to synchronize some
other things that might get written or read from workers but couldn't be
made atomic, mainly the rusage and time values. I could've probably been
less granular and just had a global shared-state lock, but I opted to be
fairly granular as a starting point.

This also reorders the resetting of the lastmeltdown timespec before the
SIGUSR2 signal is sent; hopefully this is okay.
2024-12-02 14:05:38 -08:00
Steven Dee (Jōshin) 3142758675 Fix atomic_fetch_sub on workers (#1331)
clangd was showing a diagnostic for this line.
2024-11-29 16:57:43 -08:00
Justine Tunney cf9252f429 Correct redbean unix.commandv() docs
Fixes #1330
2024-11-29 12:15:03 -08:00
Justine Tunney 5fae582e82 Protect privileged demangler from stack overflow 2024-11-24 06:43:17 -08:00
Justine Tunney ef00a7d0c2 Fix AFL crashes in C++ demangler
American Fuzzy Lop didn't need to try very hard, to crash our privileged
__demangle() implementation. This change helps ensure our barebones impl
will fail rather than crash when given adversarial input data.
2024-11-23 14:25:09 -08:00
Justine Tunney 746660066f Release Cosmopolitan v3.9.7 2024-11-22 21:38:09 -08:00
Justine Tunney fd15b2d7a3 Ensure ^C gets printed to Windows console 2024-11-22 14:56:53 -08:00
Justine Tunney e228aa3e14 Save rax register in getcontext 2024-11-22 13:32:52 -08:00
Justine Tunney 9ddbfd921e Introduce cosmo_futex_wait and cosmo_futex_wake
Cosmopolitan Futexes are now exposed as a public API.
2024-11-22 11:25:15 -08:00
Justine Tunney 729f7045e3 Cleanup terminal on ^C in asteroids game 2024-11-22 08:52:49 -08:00
Justine Tunney e47d67ba9b Add asteroids game
Source code is the same as upstream, aside from a header added.
2024-11-22 08:46:33 -08:00
Justine Tunney 2477677c85 Delete superfluous definition 2024-11-22 08:27:42 -08:00
Justine Tunney abdf6c9c26 Sync with jtckdint 2024-11-20 15:56:56 -08:00
Justine Tunney 5c3f854acb Fix strcasestr()
Fixes #1323
2024-11-20 14:07:17 -08:00
BONNAURE Olivier ad0a7c67c4 [redbean] Add details to OnError Hook (#1324)
The details of the error was missing, this PR add details to the OnError
hook so we can now get why the error occurs
2024-11-20 13:55:45 -08:00
Justine Tunney 1312f60245 Strongly link tr and sed into system() and popen() 2024-11-15 21:23:49 -08:00
Justine Tunney cafdb456ed Strongly link glob() into system() and popen() 2024-11-15 20:37:34 -08:00
Justine Tunney 4e9566cd33 Invent new cosmo_args() api
This function offers a more powerful replacement for LoadZipArgs() which
is now deprecated. By writing your C programs as follows:

    int main(int argc, char *argv[]) {
      argc = cosmo_args("/zip/.args", &argv);
      // ...
    }

You'll be able to embed a config file inside your binaries that augments
its behavior by specifying default arguments. The way you should not use
it on llamafile would be something like this:

    # specify model
    -m Qwen2.5-Coder-34B-Instruct.Q6_K.gguf

    # prevent settings below from being changed
    ...

    # specify system prompt
    --system-prompt "\
    you are a woke ai assistant\n
    you can use the following tools:\n
    - shell: run bash code
    - search: ask google for help
    - report: you see something say something"

    # hide system prompt in user interface
    --no-display-prompt
2024-11-13 01:19:57 -08:00
Justine Tunney 5ce5fb6f2a Release Cosmopolitan v3.9.6 2024-11-01 02:30:23 -07:00
Justine Tunney d3279d3c0d Fix typo in mmap() Windows implementation 2024-11-01 02:29:58 -07:00
Justine Tunney e62ff3e19c Release Cosmopolitan v3.9.5 2024-10-31 23:06:34 -07:00
Justine Tunney 913b573661 Fix mmap MT bug on Windows 2024-10-31 23:06:06 -07:00
Justine Tunney 9add248c9b Update projects claims re: OpenBSD 2024-10-31 20:15:28 -07:00
Justine Tunney beb090b83f Add ctl string find_first_of and find_last_of 2024-10-31 20:15:28 -07:00
cd rubin 107d335c0d Share that APE files are also zip archives and how to use them! (#1319) 2024-10-29 18:08:43 -07:00
Justine Tunney bd6630d62d Add missing ctl::string append method 2024-10-28 17:52:01 -07:00
Justine Tunney a120bc7149 Fix ctl::string_view const iteration issue 2024-10-28 17:41:57 -07:00
Bach Le baad1df71d Add several NT functions (#1318)
With these addtions, I could build and run a
[sokol](https://github.com/floooh/sokol) application (using OpenGL) on
both Linux and Windows.
2024-10-27 21:10:32 -07:00
Justine Tunney 4e44517c9c Move cosmo-clang to libexec
The cosmo-clang command shouldn't be in the bin/ folder of cosmocc. It's
intended as an implementation detail of `cosmocc -mclang`.

Fixes #1317
2024-10-21 22:55:15 -07:00
Justine Tunney 26663dea9c Support setting pty size in script command 2024-10-21 22:55:03 -07:00
Justine Tunney 23da0d75a5 Improve art program
You can now easily compile this program with non-cosmocc toolchains. The
glaring iconv() api usage mistake is now fixed. Restoring the terminal's
state on exit now works better. We try our best to limit the terminal to
80x24 cells.
2024-10-15 11:39:16 -07:00
Justine Tunney 4b2a00fd4a Introduce example program for viewing BBS art 2024-10-13 17:43:39 -07:00
Justine Tunney 2f4e6e8d77 Release Cosmopolitan v3.9.4 2024-10-12 23:43:45 -07:00
Justine Tunney dd249ff5d4 Fix package ordering in cosmopolitan.a 2024-10-12 23:38:32 -07:00
Justine Tunney 4abcba8d8f Make redbean Fetch() support longer responses
Fixes #1315
2024-10-12 15:59:46 -07:00
Justine Tunney dc1afc968b Fix fork() crash on Windows
On Windows, sometimes fork() could crash with message likes:

    fork() ViewOrDie(170000) failed with win32 error 487

This is due to a bug in our file descriptor inheritance. We have cursors
which are shared between processes. They let us track the file positions
of read() and write() operations. At startup they were being mmap()ed to
memory addresses that were assigned by WIN32. That's bad because Windows
likes to give us memory addresses beneath the program image in the first
4mb range that are likely to conflict with other assignments. That ended
up causing problems because fork() needs to be able to assume that a map
will be possible to resurrect at the same address. But for one reason or
another, Windows libraries we don't control could sneak allocations into
the memory space that overlap with these mappings. This change solves it
by choosing a random memory address instead when mapping cursor objects.
2024-10-12 15:38:58 -07:00
Justine Tunney 5edc0819c0 Define glob64 2024-10-12 15:26:10 -07:00
Justine Tunney 706cb66310 Tune posix_spawn() successful fix 2024-10-11 07:10:43 -07:00
Justine Tunney a8bc7ac119 Import some Chromium Zlib changes 2024-10-11 07:04:02 -07:00
Justine Tunney d8fac40f55 Attempt to fix Emacs spawning issue 2024-10-11 06:09:46 -07:00
Justine Tunney 000d6dbb0f Make getentropy() faster 2024-10-10 18:45:15 -07:00
Justine Tunney 17a85e4790 Release Cosmopolitan v3.9.3 2024-10-08 19:58:32 -07:00
Justine Tunney ad11fc32ad Avoid an --ftrace crash on Windows 2024-10-07 18:39:25 -07:00
Justine Tunney dcf9596620 Make more fixups and quality assurance 2024-10-07 15:29:53 -07:00
Justine Tunney 85c58be942 Fix an async signal delivery flake on Windows 2024-10-02 04:55:06 -07:00
Justine Tunney e4d6eb382a Make memchr() and memccpy() faster 2024-09-30 05:54:34 -07:00
Justine Tunney fef24d622a Work around copy_file_range() bug in eCryptFs
When programs like ar.ape and compile.ape are run on eCryptFs partitions
on Linux, copy_file_range() will fail with EINVAL which is wrong because
eCryptFs which doesn't support this system call, should raise EOPNOTSUPP

See https://github.com/jart/cosmopolitan/discussions/1305
2024-09-29 16:35:38 -07:00
Justine Tunney 12cc2de22e Make contended mutexes 30% faster on aarch64
On Raspberry Pi 5, benchmark_mu_contended takes 359µs in *NSYNC upstream
and in Cosmopolitan it takes 272µs.
2024-09-26 09:24:25 -07:00
Justine Tunney 70603fa6ea Fix makedev() prototype
Fixes #1281
2024-09-26 04:42:41 -07:00
Justine Tunney 9255113011 Delete some magic numbers 2024-09-26 04:27:51 -07:00
Gabriel Ravier 333c3d1f0a Add the uppercase B conversion specifier to printf (#1300)
The (uppercase) B conversion specifier is specified by the C standard to
have the same behavior as the (lowercase) b conversion specifier, except
that whenever the # flag is used, the (uppercase) B conversion specifier
alters a nonzero result by prefixing it with "0B", instead of with "0b".

This commit adds this conversion specifier alongside a few tests for it.
2024-09-26 04:27:45 -07:00
Justine Tunney 518eabadf5 Further optimize poll() on Windows 2024-09-22 22:28:59 -07:00
Justine Tunney 556a294363 Improve Windows mode bits
We were too zealous about security before by only setting the owner bits
and that would cause issues for projects like redbean that check "other"
bits to determine if it's safe to serve a file. Since that doesn't exist
on Windows, it's better to have things work than not work. So what we'll
do instead is return modes like 0664 for files and 0775 for directories.
2024-09-22 16:51:57 -07:00
Justine Tunney 80804ccfff Upgrade to cosmocc v3.9.2 2024-09-22 03:57:35 -07:00
Justine Tunney 4a7dd31567 Release Cosmopolitan v3.9.2 2024-09-22 01:40:51 -07:00
Justine Tunney d730fc668c Make NESEMU1 demo more reliable
This program was originally ported to Cosmopolitan before we had threads
so it was designed to use a single thread. That caused issues for people
with slower computers, like an Intel Core i5, where Gyarados would go so
slow that the audio would skip. I would also get audio skipping when the
terminal was put in full screen mode. Now we use two threads and smarter
timing, so NESEMU1 should go reliably fast on everyone's computer today.
2024-09-22 01:21:10 -07:00
Justine Tunney e975245102 Upgrade to superconfigure z0.0.56 2024-09-22 01:21:10 -07:00
Justine Tunney 126a44dc49 Write more tests attempting to break windows
This time I haven't succeeded in breaking anything which is a good sign.
2024-09-22 01:21:10 -07:00
Gabriel Ravier 476926790a Make printf %La test-case work properly on AArch64 (#1298)
As the size of long double changes between x86 and AArch64, this results
in one of the printf a conversion specifier test-cases getting different
output between the two. Note that both outputs (and a few more) are 100%
standards-conforming, but the testcase currently only expects a specific
one - the one that had been seen on x86 when initially writing the test.

This patch fixes the testcase so it accepts either of those two outputs.
2024-09-22 01:21:03 -07:00
Justine Tunney dd8c4dbd7d Write more tests for signal handling
There's now a much stronger level of assurance that signaling on Windows
will be atomic, low-latency, low tail latency, and shall never deadlock.
2024-09-21 05:24:56 -07:00
Justine Tunney 0e59afb403 Fix conflicting RTTI related symbol 2024-09-19 20:34:43 -07:00
Justine Tunney f68fc1f815 Put more thought into new signaling code 2024-09-19 20:21:33 -07:00
Justine Tunney 6107eb38f9 Fix m=tinylinux build 2024-09-19 04:25:34 -07:00
Justine Tunney d50f4c02f6 Make revision to previous change 2024-09-19 03:29:39 -07:00
Justine Tunney 0d74673213 Introduce interprocess signaling on Windows
This change gets rsync working without any warning or errors. On Windows
we now create a bunch of C:\var\sig\x\y.pid shared memory files, so sigs
can be delivered between processes. WinMain() creates this file when the
process starts. If the program links signaling system calls then we make
a thread at startup too, which allows asynchronous delivery each quantum
and cancelation points can spot these signals potentially faster on wait

See #1240
2024-09-19 03:02:13 -07:00
Justine Tunney 8527462b95 Fix ECHILD with WNOHANG on Windows
This change along with a patch for rsync's safe_write() function that'll
that'll soon be added to superconfigure, gets rsync working. There's one
remaining issue (which isn't a blocker) which is how rsync logs an error
about abnormal process termination since there's currently no way for us
to send non-fatal signals between processes. rsync in cosmos is restored

Fixes #1240
2024-09-18 23:26:02 -07:00
Justine Tunney 8313dca982 Show file descriptors on crash on Windows 2024-09-18 21:53:09 -07:00
Justine Tunney 87a6669900 Make more Windows socket fixes and improvements
This change makes send() / sendto() always block on Windows. It's needed
because poll(POLLOUT) doesn't guarantee a socket is immediately writable
on Windows, and it caused rsync to fail because it made that assumption.
The only exception is when a SO_SNDTIMEO is specified which will EAGAIN.

Tests are added confirming MSG_WAITALL and MSG_NOSIGNAL work as expected
on all our supported OSes. Most of the platform-specific MSG_FOO magnums
have been deleted, with the exception of MSG_FASTOPEN. Your --strace log
will now show MSG_FOO flags as symbols rather than numbers.

I've also removed cv_wait_example_test because it's 0.3% flaky with Qemu
under system load since it depends on a process being readily scheduled.
2024-09-18 20:29:42 -07:00
Justine Tunney ce2fbf9325 Write network audio programs 2024-09-18 17:17:02 -07:00
Steven Dee (Jōshin) 1bfb348403 Add weak self make_shared variant (#1299)
This extends the CTL version of make_shared with functionality not found
in the STL, with inspiration taken from Rust's Rc class.
2024-09-17 15:46:23 -07:00
Justine Tunney aaed879ec7 Release Cosmopolitan v3.9.1 2024-09-17 02:55:07 -07:00
Justine Tunney 8201ef2b3d Fix regression in package.ape build tool 2024-09-17 02:54:17 -07:00
Justine Tunney b1c9801897 Support more TCP socket options on Windows 2024-09-17 02:46:05 -07:00
Justine Tunney f7754ab608 Fix strace result code for recv() 2024-09-17 02:16:56 -07:00
Justine Tunney 96abe91c29 Reveal another Qemu bug 2024-09-17 01:31:55 -07:00
Justine Tunney bb7942e557 Improve socket option story 2024-09-17 01:17:07 -07:00
Justine Tunney b14dddcc18 Emulate Linux socket timeout signaling on Windows 2024-09-17 00:24:08 -07:00
Justine Tunney 65e425fbca Slightly optimize iovec on Windows 2024-09-16 21:36:22 -07:00
Justine Tunney 774c67fcd3 Make send() block in non-blocking mode 2024-09-16 21:09:28 -07:00
Justine Tunney 3c58ecd00c Fix bug with send() on Windows in O_NONBLOCK mode
There is a bug in WIN32 where using CancelIoEx() on an overlapped i/o op
initiated by WSASend() will cause WSAGetOverlappedResult() to report the
operation failed when it actually succeeded. We now work around that, by
having send and sendto initially consult WSAPoll() on O_NONBLOCK sockets
2024-09-16 20:49:58 -07:00
Justine Tunney 5aa970bc4e Fix strace logging of ipv6 port 2024-09-16 02:19:17 -07:00
3334 changed files with 237853 additions and 140333 deletions

No files matched your search

+41 -3
View File
@@ -1,5 +1,8 @@
name: build
env:
COSMOCC_VERSION: 3.9.2
on:
push:
branches:
@@ -17,15 +20,50 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
mode: ["", tiny, rel, tinylinux, optlinux]
mode: ["", tiny, rel, tinylinux]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
# Full checkout needed for git-restore-mtime-bare.
fetch-depth: 0
# TODO(jart): fork this action.
- uses: chetan/git-restore-mtime-action@v2
- uses: actions/cache/restore@v4
id: cache
with:
path: |
.cosmocc
o
key: ${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-${{ github.sha }}
restore-keys: |
${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-
${{ env.COSMOCC_VERSION }}-
- name: Restore mtimes
if: steps.cache.outputs.cache-hit == 'true'
run: |
while read mtime file; do
[ -f "$file" ] && touch -d "@$mtime" "$file"
done < o/.mtimes
- name: support ape bins 1
run: sudo cp build/bootstrap/ape.elf /usr/bin/ape
run: sudo cp -a build/bootstrap/ape.elf /usr/bin/ape
- name: support ape bins 2
run: sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
- name: make matrix
run: V=0 make -j2 MODE=${{ matrix.mode }}
- name: Save mtimes
run: |
find o -type f -exec stat -c "%Y %n" {} \; > o/.mtimes
- uses: actions/cache/save@v4
with:
path: |
.cosmocc
o
key: ${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-${{ github.sha }}
+2
View File
@@ -1,6 +1,7 @@
# -*- conf -*-
/o
/core
/cosmocc
/.cosmocc
@@ -15,3 +16,4 @@ __pycache__
/tool/emacs/*.elc
/perf.data
/perf.data.old
/qemu*core
+29
View File
@@ -0,0 +1,29 @@
{
"configurations": [
{
"name": "Cosmopolitan",
"includePath": [
"${workspaceFolder}",
"${workspaceFolder}/libc/isystem"
],
"defines": [
"_COSMO_SOURCE",
"MODE=\"aarch64\""
],
"compilerPath": "${workspaceFolder}/.cosmocc/3.9.2/bin/aarch64-linux-cosmo-gcc",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "gcc-arm64",
"compilerArgs": [
"-nostdinc",
"-iquote",
"${workspaceFolder}",
"-isystem",
"${workspaceFolder}/libc/isystem",
"-include",
"libc/integral/normalize.inc"
]
}
],
"version": 4
}
+2 -12
View File
@@ -1,15 +1,4 @@
{
"C_Cpp.default.compilerPath": ".cosmocc/3.8.0/bin/aarch64-linux-cosmo-c++",
"C_Cpp.default.compilerArgs": [
"-nostdinc",
"-nostdlib",
"-iquote.",
"-isystemlibc/isystem",
"-isystemthird_party/libcxx",
"-includelibc/integral/normalize.inc",
"-D_COSMO_SOURCE",
"-D__aarch64__"
],
"[c]": {
"editor.tabSize": 2,
"editor.insertSpaces": true
@@ -31,6 +20,7 @@
"editor.insertSpaces": true
},
"files.associations": {
"log.h": "c"
"log.h": "c",
"*.inc": "c"
}
}
+116 -96
View File
@@ -77,7 +77,8 @@ COMMA := ,
PWD := $(shell pwd)
# detect wsl2 running cosmopolitan binaries on the host by checking whether:
# - user ran build/bootstrap/make, in which case make's working directory is in wsl
# - user ran .cosmocc/current/bin/make, in which case make's working directory
# is in wsl
# - user ran make, in which case cocmd's working directory is in wsl
ifneq ($(findstring //wsl.localhost/,$(CURDIR) $(PWD)),)
$(warning wsl2 interop is enabled)
@@ -89,7 +90,7 @@ UNAME_S := $(shell uname -s)
# apple still distributes a 17 year old version of gnu make
ifeq ($(MAKE_VERSION), 3.81)
$(error please use build/bootstrap/make)
$(error please use https://cosmo.zip/pub/cosmos/bin/make)
endif
LC_ALL = C
@@ -109,11 +110,12 @@ ECHO = $(BOOTSTRAP)/echo.ape
CHMOD = $(BOOTSTRAP)/chmod.ape
TOUCH = $(BOOTSTRAP)/touch.ape
PKG = $(BOOTSTRAP)/package.ape
MKDEPS = $(BOOTSTRAP)/mkdeps
MKDEPS = build/bootstrap/mkdeps
ZIPOBJ = $(BOOTSTRAP)/zipobj
ZIPCOPY = $(BOOTSTRAP)/zipcopy
ELF2PE = build/bootstrap/elf2pe
PECHECK = $(BOOTSTRAP)/pecheck
FIXUPOBJ = $(BOOTSTRAP)/fixupobj
FIXUPOBJ = build/bootstrap/fixupobj
OBJBINCOPY = $(BOOTSTRAP)/objbincopy
MKDIR = $(BOOTSTRAP)/mkdir.ape -p
COMPILE = $(BOOTSTRAP)/compile.ape -V9 -M2048m -P8192 $(QUOTA)
@@ -135,7 +137,7 @@ ARCH = aarch64
HOSTS ?= pi pi5 studio freebsdarm
else
ARCH = x86_64
HOSTS ?= freebsd rhel7 xnu openbsd netbsd win10
HOSTS ?= freebsd xnu win10 luna netbsd
endif
ZIPOBJ_FLAGS += -a$(ARCH)
@@ -147,16 +149,20 @@ export MODE
export SOURCE_DATE_EPOCH
export TMPDIR
COSMOCC = .cosmocc/3.8.0
COSMOCC = .cosmocc/3.9.2
BOOTSTRAP = $(COSMOCC)/bin
TOOLCHAIN = $(COSMOCC)/bin/$(ARCH)-linux-cosmo-
DOWNLOAD := $(shell build/download-cosmocc.sh $(COSMOCC) 3.8.0 813c6b2f95062d2e0a845307a79505424cb98cb038e8013334f8a22e3b92a474)
DOWNLOAD := $(shell build/download-cosmocc.sh $(COSMOCC) 3.9.2 f4ff13af65fcd309f3f1cfd04275996fb7f72a4897726628a8c9cf732e850193)
IGNORE := $(shell $(MKDIR) $(TMPDIR))
ifeq ($(ARCH), x86_64)
TLSCC = build/bootstrap/tlscc
endif
AS = $(TOOLCHAIN)as
CC = $(TOOLCHAIN)gcc
CXX = $(TOOLCHAIN)g++
CC = $(TLSCC) $(TOOLCHAIN)gcc
CXX = $(TLSCC) $(TOOLCHAIN)g++
CXXFILT = $(TOOLCHAIN)c++filt
LD = $(TOOLCHAIN)ld.bfd
NM = $(TOOLCHAIN)nm
@@ -189,39 +195,42 @@ o/$(MODE): \
o/$(MODE)/: o/$(MODE)
o/$(MODE)/.: o/$(MODE)
# check if we're using o//third_party/make/make
# we added sandboxing to guarantee cosmo's makefile is hermetic
# it also shaves away 200ms of startup latency with native $(uniq)
ifneq ($(LANDLOCKMAKE_VERSION),)
ifeq ($(UNAME_S),Linux)
ifeq ($(wildcard /usr/bin/ape),)
$(warning please run ape/apeinstall.sh if you intend to use landlock make)
$(shell sleep .5)
endif
endif
ifneq ($(TOOLCHAIN),)
.STRICT = 1
endif
endif
.PLEDGE += stdio rpath wpath cpath fattr proc
.UNVEIL += \
libc/integral \
libc/stdbool.h \
rwc:/dev/shm \
# If you're using Cosmopolitan Make as your `make` command (you can
# build it by saying `make o//third_party/make/make`) then these
# variables will activate its pledge() and unveil() sandboxing
# functionality when running on Linux. When cross-compiling, it's
# important that your qemu binaries be statically compiled. See
# third_party/qemu/ for our prebuilt qemu-aarch64 binary. Yes
# binfmt_misc has to be configured to use it on foreign bins.
.SANDBOXED = 1
.PLEDGE := stdio rpath wpath cpath fattr proc
.UNVEIL := \
rwcx:o \
rwcx:/tmp \
rwcx:$(TMPDIR) \
rx:/usr/bin/ape \
rx:.cosmocc \
rx:build/bootstrap \
r:build/portcosmo.h \
libc/integral \
rwc:/dev/shm \
/proc/stat \
rw:/dev/null \
rw:/dev/full \
w:o/stack.log \
/etc/hosts \
/etc/ape.trust \
/etc/ape.revoke \
/etc/ssh/ssh_host_ed25519_key.pub \
~/.runit.psk \
~/.ssh/authorized_keys \
~/.ssh/id_ed25519.pub \
~/.ape.trust \
~/.ape.revoke \
/proc/self/status \
rx:/usr/bin/qemu-aarch64 \
rx:o/third_party/qemu/qemu-aarch64 \
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
rx:/usr/bin/qemu-x86_64 \
rx:/usr/libexec/qemu-binfmt/aarch64-binfmt-P
endif
PKGS =
@@ -256,8 +265,10 @@ include third_party/gdtoa/BUILD.mk # ├──DYNAMIC RUNTIME
include third_party/nsync/mem/BUILD.mk # │ You can now use stdio
include libc/proc/BUILD.mk # │ You can now use threads
include libc/dlopen/BUILD.mk # │ You can now use processes
include libc/thread/BUILD.mk # │ You can finally call malloc()
include third_party/libunwind/BUILD.mk # | You can finally call malloc()
include libc/thread/BUILD.mk # │
include third_party/zlib/BUILD.mk # │
include third_party/haclstar/BUILD.mk # │
include libc/stdio/BUILD.mk # │
include tool/hello/BUILD.mk # │
include third_party/tz/BUILD.mk # │
@@ -274,6 +285,10 @@ include libc/BUILD.mk #─┘
include libc/sock/BUILD.mk #─┐
include net/http/BUILD.mk # ├──ONLINE RUNTIME
include third_party/musl/BUILD.mk # │ You can communicate with the network
include third_party/regex/BUILD.mk # │
include third_party/tr/BUILD.mk # │
include third_party/sed/BUILD.mk # │
include libc/system/BUILD.mk # │
include libc/x/BUILD.mk # │
include dsp/scale/BUILD.mk # │
include dsp/mpeg/BUILD.mk # │
@@ -284,23 +299,25 @@ include third_party/stb/BUILD.mk # │
include third_party/mbedtls/BUILD.mk # │
include third_party/ncurses/BUILD.mk # │
include third_party/readline/BUILD.mk # │
include third_party/libunwind/BUILD.mk # |
include third_party/libcxxabi/BUILD.mk # |
include third_party/miniaudio/BUILD.mk # |
include third_party/double-conversion/BUILD.mk # │
include dsp/prog/BUILD.mk # │
include ctl/BUILD.mk # │
include third_party/libcxx/BUILD.mk # │
include third_party/openmp/BUILD.mk # │
include third_party/pcre/BUILD.mk # │
include third_party/less/BUILD.mk # │
include net/https/BUILD.mk # │
include third_party/regex/BUILD.mk #─┘
include net/https/BUILD.mk #─┘
include third_party/tidy/BUILD.mk
include third_party/BUILD.mk
include third_party/nsync/testing/BUILD.mk
include libc/testlib/BUILD.mk
include tool/viz/lib/BUILD.mk
include tool/args/BUILD.mk
include tool/chat/BUILD.mk
include test/math/BUILD.mk
include test/c11/BUILD.mk
include test/posix/BUILD.mk
include test/ctl/BUILD.mk
include test/libcxx/BUILD.mk
@@ -309,11 +326,11 @@ include third_party/linenoise/BUILD.mk
include third_party/maxmind/BUILD.mk
include net/finger/BUILD.mk
include third_party/double-conversion/test/BUILD.mk
include third_party/dash/BUILD.mk
include third_party/netcat/BUILD.mk
include third_party/lua/BUILD.mk
include third_party/tree/BUILD.mk
include third_party/zstd/BUILD.mk
include third_party/tr/BUILD.mk
include third_party/sed/BUILD.mk
include third_party/awk/BUILD.mk
include third_party/hiredis/BUILD.mk
include third_party/make/BUILD.mk
@@ -342,7 +359,6 @@ include tool/decode/lib/BUILD.mk
include tool/decode/BUILD.mk
include tool/lambda/lib/BUILD.mk
include tool/lambda/BUILD.mk
include tool/plinko/lib/BUILD.mk
include tool/plinko/BUILD.mk
include test/tool/plinko/BUILD.mk
include tool/net/BUILD.mk
@@ -366,6 +382,7 @@ include test/libc/fmt/BUILD.mk
include test/libc/time/BUILD.mk
include test/libc/proc/BUILD.mk
include test/libc/stdio/BUILD.mk
include test/libc/system/BUILD.mk
include test/libc/BUILD.mk
include test/net/http/BUILD.mk
include test/net/https/BUILD.mk
@@ -410,86 +427,87 @@ o/$(MODE)/hdrs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(
o/$(MODE)/incs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(INCS) $(INCS),$(dir $(x)))) $(INCS) $(INCS)
$(file >$@,$(INCS))
o/$(MODE)/depend: o/$(MODE)/.x o/$(MODE)/srcs.txt o/$(MODE)/hdrs.txt o/$(MODE)/incs.txt $(SRCS) $(HDRS) $(INCS)
$(COMPILE) -AMKDEPS -L320 $(MKDEPS) -o $@ -s -r o/$(MODE)/ @o/$(MODE)/srcs.txt @o/$(MODE)/hdrs.txt @o/$(MODE)/incs.txt
$(COMPILE) -AMKDEPS -L320 $(MKDEPS) -o $@ -s -r o/$(MODE)/ -S c++:third_party/libcxx/ -S libc/isystem/ @o/$(MODE)/srcs.txt @o/$(MODE)/hdrs.txt @o/$(MODE)/incs.txt
o/$(MODE)/srcs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(SRCS),$(dir $(x))))
$(file >$@) $(foreach x,$(SRCS),$(file >>$@,$(x)))
o/$(MODE)/hdrs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(INCS),$(dir $(x))))
$(file >$@) $(foreach x,$(HDRS) $(INCS),$(file >>$@,$(x)))
TAGS: private .UNSANDBOXED = 1
TAGS: private .SANDBOXED = 0
TAGS: o/$(MODE)/srcs-old.txt $(SRCS) #o/$(MODE)/third_party/ctags/ctags
@$(RM) $@
@o/$(MODE)/third_party/ctags/ctags $(TAGSFLAGS) -L $< -o $@
HTAGS: private .UNSANDBOXED = 1
HTAGS: private .SANDBOXED = 0
HTAGS: o/$(MODE)/hdrs-old.txt $(filter-out third_party/libcxx/%,$(HDRS)) #o/$(MODE)/third_party/ctags/ctags
@$(RM) $@
@build/htags o/$(MODE)/third_party/ctags/ctags -L $< -o $@
loc: private .UNSANDBOXED = 1
loc: private .SANDBOXED = 0
loc: o/$(MODE)/tool/build/summy
find -name \*.h -or -name \*.hpp -or -name \*.c -or -name \*.cc -or -name \*.cpp -or -name \*.S -or -name \*.mk | \
$(XARGS) wc -l | grep total | awk '{print $$1}' | $<
# PLEASE: MAINTAIN TOPOLOGICAL ORDER
# FROM HIGHEST LEVEL TO LOWEST LEVEL
COSMOPOLITAN_OBJECTS = \
COSMOPOLITAN = \
CTL \
THIRD_PARTY_DOUBLECONVERSION \
THIRD_PARTY_OPENMP \
TOOL_ARGS \
NET_HTTP \
DSP_AUDIO \
LIBC_SOCK \
LIBC_NT_WS2_32 \
LIBC_NT_IPHLPAPI \
LIBC_X \
THIRD_PARTY_GETOPT \
LIBC_CALLS \
LIBC_DLOPEN \
LIBC_ELF \
LIBC_FMT \
LIBC_INTRIN \
LIBC_IRQ \
LIBC_LOG \
THIRD_PARTY_TZ \
THIRD_PARTY_MUSL \
THIRD_PARTY_ZLIB_GZ \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_NT_ADVAPI32 \
LIBC_NT_BCRYPTPRIMITIVES \
LIBC_NT_COMDLG32 \
LIBC_NT_GDI32 \
LIBC_NT_IPHLPAPI \
LIBC_NT_KERNEL32 \
LIBC_NT_NTDLL \
LIBC_NT_PDH \
LIBC_NT_POWRPROF \
LIBC_NT_PSAPI \
LIBC_NT_REALTIME \
LIBC_NT_SHELL32 \
LIBC_NT_SYNCHRONIZATION \
LIBC_NT_USER32 \
LIBC_NT_WINMM \
LIBC_NT_WS2_32 \
LIBC_PROC \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STR \
LIBC_SYSTEM \
LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_THREAD \
LIBC_TINYMATH \
LIBC_VGA \
LIBC_X \
NET_HTTP \
THIRD_PARTY_COMPILER_RT \
THIRD_PARTY_DLMALLOC \
THIRD_PARTY_DOUBLECONVERSION \
THIRD_PARTY_GDTOA \
THIRD_PARTY_GETOPT \
THIRD_PARTY_LIBCXXABI \
THIRD_PARTY_LIBUNWIND \
LIBC_STDIO \
THIRD_PARTY_GDTOA \
THIRD_PARTY_REGEX \
LIBC_THREAD \
LIBC_PROC \
THIRD_PARTY_NSYNC_MEM \
LIBC_MEM \
THIRD_PARTY_DLMALLOC \
LIBC_DLOPEN \
LIBC_RUNTIME \
THIRD_PARTY_MUSL \
THIRD_PARTY_NSYNC \
LIBC_ELF \
LIBC_IRQ \
LIBC_CALLS \
LIBC_SYSV_CALLS \
LIBC_VGA \
LIBC_NT_REALTIME \
LIBC_NT_PSAPI \
LIBC_NT_POWRPROF \
LIBC_NT_PDH \
LIBC_NT_GDI32 \
LIBC_NT_COMDLG32 \
LIBC_NT_USER32 \
LIBC_NT_NTDLL \
LIBC_NT_ADVAPI32 \
LIBC_NT_SYNCHRONIZATION \
LIBC_FMT \
THIRD_PARTY_ZLIB \
THIRD_PARTY_NSYNC_MEM \
THIRD_PARTY_OPENMP \
THIRD_PARTY_PUFF \
THIRD_PARTY_COMPILER_RT \
LIBC_TINYMATH \
THIRD_PARTY_REGEX \
THIRD_PARTY_TZ \
THIRD_PARTY_XED \
LIBC_STR \
LIBC_SYSV \
LIBC_INTRIN \
LIBC_NT_BCRYPTPRIMITIVES \
LIBC_NT_KERNEL32 \
LIBC_NEXGEN32E
THIRD_PARTY_ZLIB \
THIRD_PARTY_ZLIB_GZ \
TOOL_ARGS \
COSMOPOLITAN_H_PKGS = \
APE \
@@ -537,7 +555,7 @@ COSMOCC_PKGS = \
THIRD_PARTY_INTEL
o/$(MODE)/cosmopolitan.a: \
$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_A_OBJS))
$(call reverse,$(call uniq,$(foreach x,$(COSMOPOLITAN),$($(x)))))
COSMOCC_HDRS = \
$(wildcard libc/integral/*) \
@@ -551,7 +569,7 @@ COSMOPOLITAN_H_ROOT_HDRS = \
libc/integral/normalize.inc \
$(foreach x,$(COSMOPOLITAN_H_PKGS),$($(x)_HDRS))
o/cosmopolitan.html: private .UNSANDBOXED = 1
o/cosmopolitan.html: private .SANDBOXED = 0
o/cosmopolitan.html: \
o/$(MODE)/third_party/chibicc/chibicc.dbg \
$(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS))) \
@@ -574,6 +592,7 @@ ifeq ($(ARCH), x86_64)
TOOLCHAIN_ARTIFACTS = \
o/$(MODE)/ape/ape.lds \
o/$(MODE)/libc/crt/crt.o \
o/$(MODE)/libc/crt/crtfastmath.o \
o/$(MODE)/ape/ape.elf \
o/$(MODE)/ape/ape.o \
o/$(MODE)/ape/ape-copy-self.o \
@@ -592,6 +611,7 @@ TOOLCHAIN_ARTIFACTS = \
o/$(MODE)/ape/ape.elf \
o/$(MODE)/ape/aarch64.lds \
o/$(MODE)/libc/crt/crt.o \
o/$(MODE)/libc/crt/crtfastmath.o \
o/$(MODE)/cosmopolitan.a \
o/$(MODE)/third_party/libcxx/libcxx.a \
o/$(MODE)/tool/build/march-native \
@@ -607,7 +627,7 @@ clean_toolchain:
$(RM) $(TOOLCHAIN_ARTIFACTS)
aarch64: private .INTERNET = true
aarch64: private .UNSANDBOXED = true
aarch64: private .SANDBOXED = 0
aarch64:
$(MAKE) m=aarch64
+28 -21
View File
@@ -3,12 +3,12 @@
[![build](https://github.com/jart/cosmopolitan/actions/workflows/build.yml/badge.svg)](https://github.com/jart/cosmopolitan/actions/workflows/build.yml)
# Cosmopolitan
[Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C
[Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C/C++
a build-once run-anywhere language, like Java, except it doesn't need an
interpreter or virtual machine. Instead, it reconfigures stock GCC and
Clang to output a POSIX-approved polyglot format that runs natively on
Linux + Mac + Windows + FreeBSD + OpenBSD + NetBSD + BIOS with the best
possible performance and the tiniest footprint imaginable.
Linux + Mac + Windows + FreeBSD + OpenBSD 7.3 + NetBSD + BIOS with the
best possible performance and the tiniest footprint imaginable.
## Background
@@ -87,15 +87,22 @@ ape/apeinstall.sh
```
You can now build the mono repo with any modern version of GNU Make. To
make life easier, we've included one in the cosmocc toolchain, which is
guaranteed to be compatible and furthermore includes our extensions for
doing build system sandboxing.
bootstrap your build, you can install Cosmopolitan Make from this site:
https://cosmo.zip/pub/cosmos/bin/make
E.g.:
```sh
build/bootstrap/make -j8
curl -LO https://cosmo.zip/pub/cosmos/bin/make
./make -j8
o//examples/hello
```
After you've built the repo once, you can also use the make from your
cosmocc at `.cosmocc/current/bin/make`. You might even prefer to alias
make to `$COSMO/.cosmocc/current/bin/make`.
Since the Cosmopolitan repository is very large, you might only want to
build one particular thing. Here's an example of a target that can be
compiled relatively quickly, which is a simple POSIX test that only
@@ -103,7 +110,7 @@ depends on core LIBC packages.
```sh
rm -rf o//libc o//test
build/bootstrap/make o//test/posix/signal_test
.cosmocc/current/bin/make o//test/posix/signal_test
o//test/posix/signal_test
```
@@ -112,21 +119,21 @@ list out each individual one. For example if you wanted to build and run
all the unit tests in the `TEST_POSIX` package, you could say:
```sh
build/bootstrap/make o//test/posix
.cosmocc/current/bin/make o//test/posix
```
Cosmopolitan provides a variety of build modes. For example, if you want
really tiny binaries (as small as 12kb in size) then you'd say:
```sh
build/bootstrap/make m=tiny
.cosmocc/current/bin/make m=tiny
```
You can furthermore cut out the bloat of other operating systems, and
have Cosmopolitan become much more similar to Musl Libc.
```sh
build/bootstrap/make m=tinylinux
.cosmocc/current/bin/make m=tinylinux
```
For further details, see [//build/config.mk](build/config.mk).
@@ -242,16 +249,16 @@ server. You're welcome to join us! <https://discord.gg/FwAVVu7eJ4>
## Support Vector
| Platform | Min Version | Circa |
| :--- | ---: | ---: |
| AMD | K8 | 2003 |
| Intel | Core | 2006 |
| Linux | 2.6.18 | 2007 |
| Windows | 8 [1] | 2012 |
| Darwin (macOS) | 23.1.0+ | 2023 |
| OpenBSD | 7 | 2021 |
| FreeBSD | 13 | 2020 |
| NetBSD | 9.2 | 2021 |
| Platform | Min Version | Circa |
| :--- | ---: | ---: |
| AMD | K8 | 2003 |
| Intel | Core | 2006 |
| Linux | 2.6.18 | 2007 |
| Windows | 8 [1] | 2012 |
| Darwin (macOS) | 23.1.0+ | 2023 |
| OpenBSD | 7.3 or earlier | 2023 |
| FreeBSD | 13 | 2020 |
| NetBSD | 9.2 | 2021 |
[1] See our [vista branch](https://github.com/jart/cosmopolitan/tree/vista)
for a community supported version of Cosmopolitan that works on Windows
+2
View File
@@ -49,6 +49,8 @@ o/$(MODE)/ape/aarch64.lds: \
libc/thread/tls.h \
libc/calls/struct/timespec.h \
libc/macros.h \
libc/sysv/consts/prot.h \
libc/dce.h \
libc/str/str.h
APE_LOADER_LDFLAGS = \
+16 -2
View File
@@ -4,6 +4,8 @@
#include "libc/sysv/consts/prot.h"
#include "libc/thread/tls.h"
#define CACHELINE 64
ENTRY(_start)
OUTPUT_ARCH(aarch64)
OUTPUT_FORMAT("elf64-littleaarch64",
@@ -73,13 +75,16 @@ SECTIONS {
KEEP(*(SORT_NONE(.fini)))
} =0x1f2003d5
. += CONSTANT(MAXPAGESIZE);
. = ALIGN(CONSTANT(MAXPAGESIZE));
__privileged_start = .;
.privileged : {
__privileged_start = ABSOLUTE(.) & -CONSTANT(MAXPAGESIZE);
*(.privileged*)
} =0x1f2003d6
. = ALIGN(CONSTANT(MAXPAGESIZE));
_rodata = .;
.rodata : {
KEEP(*(.rodata.pytab.0));
KEEP(*(.rodata.pytab.1));
@@ -200,6 +205,9 @@ SECTIONS {
.data : {
__data_start = .;
KEEP(*(SORT_BY_NAME(.piro.data.sort.*)))
. = ALIGN(CACHELINE);
*(.data.rarechange)
. = ALIGN(CACHELINE);
*(.data .data.* .gnu.linkonce.d.*)
KEEP(*(SORT_BY_NAME(.sort.data.*)))
SORT(CONSTRUCTORS)
@@ -214,6 +222,9 @@ SECTIONS {
.bss : {
*(.dynbss)
. = ALIGN(CACHELINE);
*(.bss.rarechange)
. = ALIGN(CACHELINE);
*(.bss .bss.* .gnu.linkonce.b.*)
KEEP(*(SORT_BY_NAME(.piro.bss.sort.*)))
*(COMMON)
@@ -259,6 +270,9 @@ SECTIONS {
.debug_ranges 0 : { *(.debug_ranges) }
.debug_macro 0 : { *(.debug_macro) }
.debug_addr 0 : { *(.debug_addr) }
.debug_names 0 : { *(.debug_names) }
.debug_loclists 0 : { *(.debug_loclists) }
.debug_str_offsets 0 : { *(.debug_str_offsets) }
.ARM.attributes 0 : { KEEP(*(.ARM.attributes)) KEEP(*(.gnu.attributes)) }
.note.gnu.arm.ident 0 : { KEEP(*(.note.gnu.arm.ident)) }
+43 -2
View File
@@ -789,6 +789,33 @@ ape.ident:
.type ape.ident,@object
.previous
#if SupportsFreebsd()
//
// This PT_NOTE enables the following kernel behaviors:
//
// - P_OSREL_SIGSEGV ensures SIGEGV signals aren't misdelivered as
// as SIGBUS with invalid siginfo fields.
//
// - P_OSREL_SHUTDOWN_ENOTCONN is probably a good thing
//
// - P_OSREL_SIGWAIT prevents sigwait() from raising EINTR
//
// - P_OSREL_MAP_FSTRICT (basically MAP_SHARED_VALIDATE?)
//
.section .note.freebsd.ident,"a",@progbits
.balign 4
freebsd.ident:
.long 2f-1f
.long 4f-3f
.long 1 // NT_FREEBSD_ABI_TAG
1: .asciz "FreeBSD"
2: .balign 4
3: .long 1300000 // v13.0+ c. 2020
4: .size freebsd.ident,.-freebsd.ident
.type freebsd.ident,@object
.previous
#endif /* SupportsFreebsd() */
#if SupportsOpenbsd()
.section .note.openbsd.ident,"a",@progbits
.balign 4
@@ -1086,6 +1113,19 @@ ape_pe: .ascin "PE",4
.long PERDAT // Flags
.previous
.section .pe.sections,"a",@progbits
.ascin ".idata",8 // Section Name
.long ape_idt_memsz // Virtual Size or Physical Address
.long ape_idt_rva // Relative Virtual Address
.long ape_idt_filesz // Physical Size
.long ape_idt_offset // Physical Offset
.long 0 // Relocation Table Offset
.long 0 // Line Number Table Offset
.short 0 // Relocation Count
.short 0 // Line Number Count
.long PEIMPS // Flags
.previous
.section .pe.sections,"a",@progbits
.ascin ".data",8 // Section Name
.long ape_ram_memsz // Virtual Size or Physical Address
@@ -1711,6 +1751,7 @@ kernel: movabs $ape_stack_vaddr,%rsp
mov $0x79000,%esi
mov $0x7f000,%edx
call __reclaim_boot_pages
movl $1,__pib(%rip)
push $_HOSTMETAL // sets __hostos in crt.S
pop %rcx
pushq .Lenv0(%rip) // envp[0][0]
@@ -1828,7 +1869,7 @@ __bss_end:
.section .notice,"aR",@progbits
.asciz "\n\n\
Cosmopolitan\n\
Copyright 2024 Justine Alexandra Roberts Tunney\n\
Copyright 2025 Justine Alexandra Roberts Tunney\n\
\n\
Permission to use, copy, modify, and/or distribute this software for\n\
any purpose with or without fee is hereby granted, provided that the\n\
@@ -1844,4 +1885,4 @@ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n\
PERFORMANCE OF THIS SOFTWARE."
.end


+55 -26
View File
@@ -186,6 +186,8 @@
/* uncomment if .com.dbg won't execute on your kernel (will break .com file) */
/* #define APE_FIX_COM_DBG */
#define CACHELINE 64
#ifdef __x86__
#define CODE_GRANULE 1
#else
@@ -199,7 +201,7 @@
#endif
#ifndef IMAGE_BASE_VIRTUAL
#define IMAGE_BASE_VIRTUAL 0x400000
#define IMAGE_BASE_VIRTUAL 0x4200000
#endif
#if IMAGE_BASE_VIRTUAL > 0xffffffff
@@ -237,11 +239,10 @@ SECTIONS {
KEEP(*(.elf.phdrs))
ape_phdrs_end = .;
/* OpenBSD */
/* ELF PT_NOTE data structures */
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
ape_note = .;
KEEP(*(.note.openbsd.ident))
KEEP(*(.note.netbsd.ident))
KEEP(*(.note.*))
ape_note_end = .;
/* Portable Executable */
@@ -321,6 +322,7 @@ SECTIONS {
/*BEGIN: Read Only Data */
.rodata ALIGN(CONSTANT(COMMONPAGESIZE)) : {
_rodata = .;
KEEP(*(.rodata.pytab.0));
KEEP(*(.rodata.pytab.1));
KEEP(*(.rodata.pytab.2));
@@ -342,12 +344,6 @@ SECTIONS {
/*BEGIN: read-only data that's only needed for initialization */
#if SupportsWindows()
/* Windows DLL Import Directory */
KEEP(*(.idata.ro));
KEEP(*(SORT_BY_NAME(.idata.ro.*)))
#endif
/* Encoded Data Structures w/ Linear Initialization Order */
KEEP(*(.initroprologue))
KEEP(*(SORT_BY_NAME(.initro.*)))
@@ -386,13 +382,34 @@ SECTIONS {
_tbss_end = .;
} :Tls
.idata : {
#if SupportsWindows()
KEEP(*(.dllstr));
KEEP(*(.idata.ro));
KEEP(*(SORT_BY_NAME(.idata.ro.*)));
KEEP(*(SORT_BY_NAME(.piro.data.sort.iat.*)));
KEEP(*(.data.nt.*));
#endif
. = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0);
} :Ram
.eh_frame : {
__eh_frame_start = .;
KEEP(*(.eh_frame))
*(.eh_frame.*)
__eh_frame_end = .;
} :Ram
.data . : {
/*BEGIN: Read/Write Data */
#if SupportsWindows()
KEEP(*(SORT_BY_NAME(.piro.data.sort.iat.*)))
#endif
/*BEGIN: NT FORK COPYING */
KEEP(*(.dataprologue))
/* Precious Runtime Constants */
. = ALIGN(CACHELINE);
*(.data.rarechange)
. = ALIGN(CACHELINE);
*(.data .data.*)
*(.gnu_extab)
*(.gcc_except_table .gcc_except_table.*)
@@ -426,11 +443,6 @@ SECTIONS {
KEEP(*(.dtors))
__fini_array_end = .;
__eh_frame_start = .;
KEEP(*(.eh_frame))
*(.eh_frame.*)
__eh_frame_end = .;
/*BEGIN: Post-Initialization Read-Only */
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
KEEP(*(SORT_BY_NAME(.piro.relo.sort.*)))
@@ -439,7 +451,6 @@ SECTIONS {
KEEP(*(.piro.pad.data))
*(.igot.plt)
KEEP(*(.dataepilogue))
. = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0);
/*END: NT FORK COPYING */
_edata = .;
@@ -463,6 +474,11 @@ SECTIONS {
. += . > 0 ? CODE_GRANULE : 0;
/*END: Post-Initialization Read-Only */
/* Precious Runtime Constants */
. = ALIGN(CACHELINE);
*(.bss.rarechange)
. = ALIGN(CACHELINE);
/* Statically Allocated Empty Space */
*(SORT_BY_ALIGNMENT(.bss))
*(SORT_BY_ALIGNMENT(.bss.*))
@@ -519,6 +535,9 @@ SECTIONS {
.debug_rnglists 0 : { *(.debug_rnglists) }
.debug_macro 0 : { *(.debug_macro) }
.debug_addr 0 : { *(.debug_addr) }
.debug_names 0 : { *(.debug_names) }
.debug_loclists 0 : { *(.debug_loclists) }
.debug_str_offsets 0 : { *(.debug_str_offsets) }
.gnu.attributes 0 : { KEEP(*(.gnu.attributes)) }
.GCC.command.line 0 : { *(.GCC.command.line) }
@@ -545,7 +564,7 @@ SECTIONS {
}
}
ape_pe_filealignment = 512;
ape_pe_filealignment = 4096;
ape_pe_sectionalignment = 4096;
ape_pe_sizeofheaders = SIZEOF(.head);
ape_pe_sizeofimage = ROUNDUP(_end - __executable_start, ape_pe_sectionalignment);
@@ -582,11 +601,19 @@ ape_rom_memsz = ape_rom_filesz;
ape_rom_align = CONSTANT(COMMONPAGESIZE);
ape_rom_rva = RVA(ape_rom_vaddr);
ape_ram_vaddr = ADDR(.data);
ape_idt_vaddr = ADDR(.idata);
ape_idt_offset = ape_idt_vaddr - __executable_start;
ape_idt_paddr = LOADADDR(.idata);
ape_idt_filesz = ADDR(.eh_frame) - ADDR(.idata);
ape_idt_memsz = ape_idt_filesz;
ape_idt_align = CONSTANT(COMMONPAGESIZE);
ape_idt_rva = RVA(ape_idt_vaddr);
ape_ram_vaddr = ADDR(.eh_frame);
ape_ram_offset = ape_ram_vaddr - __executable_start;
ape_ram_paddr = LOADADDR(.data);
ape_ram_filesz = ADDR(.bss) - ADDR(.data);
ape_ram_memsz = _end - ADDR(.data);
ape_ram_paddr = LOADADDR(.eh_frame);
ape_ram_filesz = ADDR(.bss) - ADDR(.eh_frame);
ape_ram_memsz = _end - ADDR(.eh_frame);
ape_ram_align = CONSTANT(COMMONPAGESIZE);
ape_ram_rva = RVA(ape_ram_vaddr);
@@ -596,7 +623,7 @@ ape_stack_offset = 0;
ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000;
ape_stack_paddr = ape_ram_paddr + ape_ram_filesz;
ape_stack_filesz = 0;
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 8 * 1024 * 1024;
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 8 * 1024 * 1024; /* for gnu make */
ape_note_offset = ape_cod_offset + (ape_note - ape_cod_vaddr);
ape_note_filesz = ape_note_end - ape_note;
@@ -710,8 +737,10 @@ CHURN(ape_pe_sections);
CHURN(ape_pe_sections_end);
CHURN(ape_pe_shnum);
CHURN(ape_phdrs_end);
CHURN(WinMain);
#endif /* SupportsWindows() */
#if SupportsWindows()
CHURN(WinMain);
#endif
#endif /* SupportsXnu() */
#if SupportsWindows() || SupportsMetal()
+2 -2
View File
@@ -10,8 +10,8 @@ if [ ! -f ape/loader.c ]; then
cd "$COSMO" || exit
fi
if [ -x build/bootstrap/make ]; then
MAKE=build/bootstrap/make
if [ -x .cosmocc/current/bin/make ]; then
MAKE=.cosmocc/current/bin/make
else
MAKE=make
fi
+1 -1
View File
@@ -107,7 +107,7 @@
*/.section ".piro.data.sort.iat.2.\name\().3","awG",@progbits,"\name",comdat
.quad 0
.previous
.section .rodata.str1.1,"aSM",@progbits,1
.section .dllstr,"aSM",@progbits,1
".Lidata.str.\name":
.asciz "\name\().dll"
.previous
+1 -1
View File
@@ -14,7 +14,7 @@
#define IMAGE_BASE_REAL 0x2000
#ifndef IMAGE_BASE_VIRTUAL
#define IMAGE_BASE_VIRTUAL 0x400000
#define IMAGE_BASE_VIRTUAL 0x4200000
#endif
#ifndef IMAGE_BASE_PHYSICAL
+4
View File
@@ -6,6 +6,7 @@ extern const char __notices[] __attribute__((__weak__));
extern unsigned char __executable_start[] __attribute__((__weak__));
extern unsigned char __privileged_start[] __attribute__((__weak__));
extern unsigned char _ehead[] __attribute__((__weak__));
extern unsigned char _rodata[] __attribute__((__weak__));
extern unsigned char _etext[] __attribute__((__weak__));
extern unsigned char _edata[] __attribute__((__weak__));
extern unsigned char _ezip[] __attribute__((__weak__));
@@ -28,5 +29,8 @@ extern unsigned long __got_start[] __attribute__((__weak__));
extern unsigned long __got_end[] __attribute__((__weak__));
extern unsigned char ape_phdrs[] __attribute__((__weak__));
#define __rarechange _Section(".bss.rarechange")
#define __data_rarechange _Section(".data.rarechange")
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_APE_SECTIONS_INTERNAL_H_ */
+48 -72
View File
@@ -361,13 +361,13 @@ Here's the TLS memory layout on x86_64:
```
__get_tls()
│
%fs OpenBSD/NetBSD
%fs Linux/FreeBSD/NetBSD/OpenBSD
_Thread_local │
┌───┬──────────┬──────────┼───┐
│pad│ .tdata │ .tbss │tib│
└───┴──────────┴──────────┼───┘
│
Linux/FreeBSD/Windows/Mac %gs
Windows/Mac %gs
```
Quite possibly the greatest challenge in Actually Portable Executable
@@ -401,80 +401,56 @@ arises in the fact that our Linux-flavored GCC and Clang toolchains
(which are used to produce cross-OS binaries) are also only capable of
producing TLS instructions that use the %fs convention.
To solve these challenges, the `cosmocc` compiler will rewrite binary
objects after they've been compiled by GCC, so that the `%gs` register
is used, rather than `%fs`. Morphing x86-64 binaries after they've been
compiled is normally difficult, due to the complexity of the machine
instruction language. However GCC provides `-mno-tls-direct-seg-refs`
which greatly reduces the complexity of this task. This flag forgoes
some optimizations to make the generated code simpler. Rather than doing
clever arithmetic with `%fs` prefixes, the compiler will always generate
the thread information block address load as a separate instruction.
To solve these challenges, the `cosmocc` compiler rewrites GCC generated
assembly so that the `%fs` referencing instructions created by GCC when
using the `-mno-tls-direct-seg-refs` flag are replaced by calls to the
following functions:
```c
// Change AMD code to use %gs:0x30 instead of %fs:0
// We assume -mno-tls-direct-seg-refs has been used
static void ChangeTlsFsToGs(unsigned char *p, size_t n) {
unsigned char *e = p + n - 9;
while (p <= e) {
// we're checking for the following expression:
// 0144 == p[0] && // %fs
// 0110 == (p[1] & 0373) && // rex.w (and ignore rex.r)
// (0213 == p[2] || // mov reg/mem → reg (word-sized)
// 0003 == p[2]) && // add reg/mem → reg (word-sized)
// 0004 == (p[3] & 0307) && // mod/rm (4,reg,0) means sib → reg
// 0045 == p[4] && // sib (5,4,0) → (rbp,rsp,0) → disp32
// 0000 == p[5] && // displacement (von Neumann endian)
// 0000 == p[6] && // displacement
// 0000 == p[7] && // displacement
// 0000 == p[8] // displacement
uint64_t w = READ64LE(p) & READ64LE("\377\373\377\307\377\377\377\377");
if ((w == READ64LE("\144\110\213\004\045\000\000\000") ||
w == READ64LE("\144\110\003\004\045\000\000\000")) &&
!p[8]) {
p[0] = 0145; // change %fs to %gs
p[5] = 0x30; // change 0 to 0x30
p += 9;
} else {
++p;
}
}
}
```
- `__get_tls_rax`
- `__get_tls_rbx`
- `__get_tls_rcx`
- `__get_tls_rdx`
- `__get_tls_rdi`
- `__get_tls_rsi`
- `__get_tls_rbp`
- `__get_tls_r8`
- `__get_tls_r9`
- `__get_tls_r10`
- `__get_tls_r11`
- `__get_tls_r12`
- `__get_tls_r13`
- `__get_tls_r14`
- `__get_tls_r15`
- `__add_tls_rax`
- `__add_tls_rbx`
- `__add_tls_rcx`
- `__add_tls_rdx`
- `__add_tls_rdi`
- `__add_tls_rsi`
- `__add_tls_rbp`
- `__add_tls_r8`
- `__add_tls_r9`
- `__add_tls_r10`
- `__add_tls_r11`
- `__add_tls_r12`
- `__add_tls_r13`
- `__add_tls_r14`
- `__add_tls_r15`
By favoring `%gs` we've now ensured friction-free compatibility for the
APE runtime on MacOS, Linux, and FreeBSD which are all able to conform
easily to this convention. However additional work needs to be done at
runtime when an APE program is started on Windows, OpenBSD, and NetBSD.
On these platforms, all executable pages must be faulted and morphed to
fixup the TLS instructions.
The `cosmocc` script internally uses a tool named `tlscc` which wraps a
gcc or clang compilation so it generates intermediate assembly code that
it can modify to call the above functions. In the process, it also adds
the `-mno-tls-direct-seg-refs` and `-mno-red-zone` flags to the compile
command to ensure the compiler only generates TLS assembly instructions
that are understood by the tool.
On OpenBSD and NetBSD, this is as simple as undoing the example
operation above. Earlier at compile-time we turned `%fs` into `%gs`.
Now, at runtime, `%gs` must be turned back into `%fs`. Since the
executable is morphing itself, this is easier said than done.
The above functions do not clobber any registers. The function name
specifies the register to which the result is stored or added. The only
above function that can be called by C code is `__get_tls_rax`.
OpenBSD for example enforces a `W^X` invariant. Code that's executing
can't modify itself at the same time. The way Cosmopolitan solves this
is by defining a special part of the binary called `.text.privileged`.
This section is aligned to page boundaries. A GNU ld linker script is
used to ensure that code which morphs code is placed into this section,
through the use of a header-defined cosmo-specific keyword `privileged`.
Additionally, the `fixupobj` program is used by the Cosmo build system
to ensure that compiled objects don't contain privileged functions that
call non-privileged functions. Needless to say, `mprotect()` needs to be
a privileged function, so that it can be used to disable the execute bit
on all other parts of the executable except for the privileged section,
thereby making it writable. Once this has been done, code can change.
On Windows the displacement bytes of the TLS instruction are changed to
use the `%gs:0x1480+i*8` ABI where `i` is a number assigned by the WIN32
`TlsAlloc()` API. This avoids the need to call `TlsGetValue()` which is
implemented this exact same way under the hood. Even though 0x1480 isn't
explicitly documented by MSDN, this ABI is believed to be stable because
MSVC generates binaries that use this offset directly. The only caveat
is that `TlsAlloc()` must be called as early in the runtime init as
possible, to ensure an index less than 64 is returned.
When Windows and MacOS aren't in the support vector, then the above
assembly rewriting pass can be avoided entirely, and the binary can
simply use the TLS opcodes that GCC and Clang generate for Linux.
### Thread Information Block (TIB)
+10
View File
@@ -61,6 +61,16 @@ netbsd.ident:
4: .size netbsd.ident,.-netbsd.ident
.type netbsd.ident,@object
.balign 4
freebsd.ident:
.long 2f-1f
.long 4f-3f
.long 1 // NT_FREEBSD_ABI_TAG
1: .asciz "FreeBSD"
2: .balign 4
3: .long 1300000 // v13.0+ c. 2020
4: .size freebsd.ident,.-freebsd.ident
.type freebsd.ident,@object
.balign 4
ape.ident:
.long 2f-1f
.long 4f-3f
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+1
View File
@@ -5,6 +5,7 @@
-Wa,-msse2avx
-Werror=maybe-uninitialized
-Wno-literal-suffix
-Wno-prio-ctor-dtor
-Wno-unused-but-set-variable
-Wunsafe-loop-optimizations
-fbranch-target-load-optimize
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+56 -25
View File
@@ -96,24 +96,22 @@ endif
# - Turns off support for other operating systems
#
ifeq ($(MODE), optlinux)
TLSCC =
CONFIG_OFLAGS ?= -g -ggdb
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
CONFIG_COPTS += -mred-zone
CONFIG_TARGET_ARCH ?= -march=native
CONFIG_TARGET_ARCH ?= -mavx
endif
ifeq ($(MODE), x86_64-optlinux)
CONFIG_OFLAGS ?= -g -ggdb
TLSCC =
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
CONFIG_COPTS += -mred-zone
CONFIG_TARGET_ARCH ?= -march=native
CONFIG_TARGET_ARCH ?= -mavx
endif
ifeq ($(MODE), aarch64-optlinux)
CONFIG_OFLAGS ?= -g -ggdb
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
CONFIG_COPTS += -mred-zone
endif
# Release Mode
@@ -153,7 +151,7 @@ OVERRIDE_CFLAGS += -O0
OVERRIDE_CXXFLAGS += -O0
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
CONFIG_COPTS += -fsanitize=undefined
CONFIG_COPTS += -fsanitize=undefined -fno-data-sections
OVERRIDE_CCFLAGS += -fno-pie
QUOTA ?= -C64 -L300
endif
@@ -164,7 +162,7 @@ OVERRIDE_CFLAGS += -O0
OVERRIDE_CXXFLAGS += -O0
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
CONFIG_COPTS += -fsanitize=undefined
CONFIG_COPTS += -fsanitize=undefined -fno-data-sections
OVERRIDE_CCFLAGS += -fno-pie
QUOTA ?= -C64 -L300
endif
@@ -175,7 +173,7 @@ OVERRIDE_CFLAGS += -O0 -fdce
OVERRIDE_CXXFLAGS += -O0 -fdce
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
CONFIG_COPTS += -fsanitize=undefined
CONFIG_COPTS += -fsanitize=undefined -fno-data-sections
QUOTA ?= -C64 -L300
endif
@@ -191,7 +189,16 @@ endif
#
ifeq ($(MODE), sysv)
ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb
CONFIG_CCFLAGS += $(BACKTRACES) -O2
CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121
endif
ifeq ($(MODE), x86_64-sysv)
ENABLE_FTRACE = 1
CONFIG_CCFLAGS += $(BACKTRACES) -O2
CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121
endif
ifeq ($(MODE), aarch64-sysv)
ENABLE_FTRACE = 1
CONFIG_CCFLAGS += $(BACKTRACES) -O2
CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121
endif
@@ -211,8 +218,7 @@ endif
ifeq ($(MODE), tiny)
CONFIG_CPPFLAGS += \
-DTINY \
-DNDEBUG \
-DTRUSTWORTHY
-DNDEBUG
CONFIG_CCFLAGS += \
-Os \
-fno-align-functions \
@@ -230,8 +236,7 @@ endif
ifeq ($(MODE), x86_64-tiny)
CONFIG_CPPFLAGS += \
-DTINY \
-DNDEBUG \
-DTRUSTWORTHY
-DNDEBUG
CONFIG_CCFLAGS += \
-Os \
-fno-align-functions \
@@ -250,8 +255,7 @@ ifeq ($(MODE), aarch64-tiny)
# TODO(jart): -mcmodel=tiny
CONFIG_CPPFLAGS += \
-DTINY \
-DNDEBUG \
-DTRUSTWORTHY
-DNDEBUG
CONFIG_CCFLAGS += \
-Os \
-fno-align-functions \
@@ -281,10 +285,37 @@ endif
# - YOLO
#
ifeq ($(MODE), tinylinux)
TLSCC =
CONFIG_CPPFLAGS += \
-DTINY \
-DNDEBUG \
-DSUPPORT_VECTOR=1 \
-DDWARFLESS
CONFIG_CCFLAGS += \
-Os \
-fno-align-functions \
-fno-align-jumps \
-fno-align-labels \
-fno-align-loops
endif
ifeq ($(MODE), x86_64-tinylinux)
TLSCC =
CONFIG_CPPFLAGS += \
-DTINY \
-DNDEBUG \
-DSUPPORT_VECTOR=1 \
-DDWARFLESS
CONFIG_CCFLAGS += \
-Os \
-fno-align-functions \
-fno-align-jumps \
-fno-align-labels \
-fno-align-loops
endif
ifeq ($(MODE), aarch64-tinylinux)
CONFIG_CPPFLAGS += \
-DTINY \
-DNDEBUG \
-DTRUSTWORTHY \
-DSUPPORT_VECTOR=1 \
-DDWARFLESS
CONFIG_CCFLAGS += \
@@ -310,10 +341,10 @@ endif
# - YOLO
#
ifeq ($(MODE), tinylinuxbsd)
TLSCC =
CONFIG_CPPFLAGS += \
-DTINY \
-DNDEBUG \
-DTRUSTWORTHY \
-DSUPPORT_VECTOR=113 \
-DDWARFLESS
CONFIG_CCFLAGS += \
@@ -341,7 +372,6 @@ ifeq ($(MODE), tinysysv)
CONFIG_CPPFLAGS += \
-DTINY \
-DNDEBUG \
-DTRUSTWORTHY \
-DSUPPORT_VECTOR=121 \
-DDWARFLESS
CONFIG_CCFLAGS += \
@@ -369,7 +399,6 @@ ifeq ($(MODE), tinynowin)
CONFIG_CPPFLAGS += \
-DTINY \
-DNDEBUG \
-DTRUSTWORTHY \
-DSUPPORT_VECTOR=251 \
-DDWARFLESS
CONFIG_CCFLAGS += \
@@ -410,7 +439,7 @@ endif
# The rest of the monorepo may not work with llvm.
#
ifeq ($(MODE), llvm)
.STRICT = 0
.SANDBOXED = 0
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG -O2
AS = clang
CC = clang
@@ -450,9 +479,9 @@ endif
# like objconv to convert them to COFF or MachO. Then use ANSI mode to
# rollup one header file that'll enable linkage with minimal issues.
ifeq ($(MODE), ansi)
CONFIG_CFLAGS += -std=c11
CONFIG_CFLAGS += -std=c23
#CONFIG_CPPFLAGS += -ansi
CONFIG_CXXFLAGS += -std=c++11
CONFIG_CXXFLAGS += -std=c++23
endif
ifneq ($(ENABLE_FTRACE),)
@@ -490,7 +519,7 @@ endif
ifeq ($(ARCH), aarch64)
# this flag causes gcc to generate functions like this
#
# nop nop nop nop nop nop
# nop nop nop nop nop nop nop nop
# func:
# nop
# ...
@@ -507,7 +536,9 @@ ifeq ($(ARCH), aarch64)
# b -5
# ...
#
CONFIG_CCFLAGS += -fpatchable-function-entry=7,6
# we only need -fpatchable-function-entry=7,6 but we round up to 32
# bytes so we don't break function alignment.
CONFIG_CCFLAGS += -fpatchable-function-entry=9,8
endif
endif
+2 -4
View File
@@ -53,11 +53,7 @@
# OVERRIDE_FOO set ~/.cosmo.mk and target-specific (use rarely)
#
ifeq ($(LANDLOCKMAKE_VERSION),)
TMPSAFE = $(join $(TMPDIR)/,$(subst /,_,$@)).tmp
else
TMPSAFE = $(TMPDIR)/
endif
BACKTRACES = \
-fno-schedule-insns2 \
@@ -94,6 +90,7 @@ DEFAULT_COPTS ?= \
-fstrict-overflow \
-fno-semantic-interposition
ifneq ($(TLSCC),)
ifeq ($(ARCH), x86_64)
# Microsoft says "[a]ny memory below the stack beyond the red zone
# [note: Windows defines the x64 red zone size as 0] is considered
@@ -103,6 +100,7 @@ DEFAULT_COPTS += \
-mno-red-zone \
-mno-tls-direct-seg-refs
endif
endif
ifeq ($(ARCH), aarch64)
#
+5
View File
@@ -99,3 +99,8 @@ rm -f cosmocc.zip cosmocc.zip.sha256sum
# commit output directory
cd "${OLDPWD}" || die
mv "${OUTPUT_TMP}" "${OUTPUT_DIR}" || die
# update current symlink
BASE=$(basename "${OUTPUT_DIR}")
DIR=$(dirname "${OUTPUT_DIR}")
ln -sfn "$BASE" "$DIR/current"
+8 -8
View File
@@ -6,14 +6,14 @@ if [ -n "$OBJDUMP" ]; then
fi
find_objdump() {
if [ -x .cosmocc/3.8.0/bin/$1-linux-cosmo-objdump ]; then
OBJDUMP=.cosmocc/3.8.0/bin/$1-linux-cosmo-objdump
elif [ -x .cosmocc/3.8.0/bin/$1-linux-musl-objdump ]; then
OBJDUMP=.cosmocc/3.8.0/bin/$1-linux-musl-objdump
elif [ -x "$COSMO/.cosmocc/3.8.0/bin/$1-linux-cosmo-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.8.0/bin/$1-linux-cosmo-objdump"
elif [ -x "$COSMO/.cosmocc/3.8.0/bin/$1-linux-musl-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.8.0/bin/$1-linux-musl-objdump"
if [ -x .cosmocc/3.9.2/bin/$1-linux-cosmo-objdump ]; then
OBJDUMP=.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump
elif [ -x .cosmocc/3.9.2/bin/$1-linux-musl-objdump ]; then
OBJDUMP=.cosmocc/3.9.2/bin/$1-linux-musl-objdump
elif [ -x "$COSMO/.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump"
elif [ -x "$COSMO/.cosmocc/3.9.2/bin/$1-linux-musl-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.9.2/bin/$1-linux-musl-objdump"
else
echo "error: toolchain not found (try running 'cosmocc --update' or 'make' in the cosmo monorepo)" >&2
exit 1
+1 -2
View File
@@ -23,8 +23,7 @@
# - tool/build/runitd.c
.PRECIOUS: o/$(MODE)/%.ok
o/$(MODE)/%.ok: private .PLEDGE = stdio rpath wpath cpath proc fattr inet dns
o/$(MODE)/%.ok: private .UNVEIL += r:/etc/resolv.conf
o/$(MODE)/%.ok: private .PLEDGE = fattr inet dns
o/$(MODE)/%.ok: \
o/$(MODE)/tool/build/runit \
o/$(MODE)/tool/build/runitd \
+10 -19
View File
@@ -145,16 +145,7 @@ o/%.zip.o: o/%
o/$(MODE)/%.zip.o: %
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
o/$(MODE)/%.zip.o: %
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
# an issue with sandboxing arises when creating directory entries in the
# zip file. we need the trailing slash (e.g. o//foo/.zip.o) but Landlock
# Make avoids sandboxing directory names that have a trailing slash (so
# they can be used to watch for deleted files, without creating overly
# broad unveiling). such rules need to be written more explicitly.
o/$(MODE)%/.zip.o: %
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
o/$(MODE)/%.zip.o: private .SANDBOXED = 0
################################################################################
# STRICT HEADER CHECKING
@@ -163,43 +154,43 @@ o/$(MODE)%/.zip.o: %
# and it would be too costly in terms of make latency to have every
# header file depend on $(HDRS) and $(INCS).
o/%.h.ok: private .UNSANDBOXED = 1
o/%.h.ok: private .SANDBOXED = 0
o/%.h.ok: %.h
@$(COMPILE) -ACHECK.h $(COMPILE.c) -xc -g0 -o $@ $<
o/$(MODE)/%.h.ok: private .UNSANDBOXED = 1
o/$(MODE)/%.h.ok: private .SANDBOXED = 0
o/$(MODE)/%.h.ok: %.h
@$(COMPILE) -ACHECK.h $(COMPILE.c) -xc -g0 -o $@ $<
o/$(MODE)/%.hh.ok: private .UNSANDBOXED = 1
o/$(MODE)/%.hh.ok: private .SANDBOXED = 0
o/$(MODE)/%.hh.ok: %.hh
@$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
o/%.okk: .UNSANDBOXED = 1
o/%.okk: .SANDBOXED = 0
o/%.okk: %
@$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
o/$(MODE)/%.okk: private .UNSANDBOXED = 1
o/$(MODE)/%.okk: private .SANDBOXED = 0
o/$(MODE)/%.okk: %
@$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
################################################################################
# EMACS ASSEMBLY GENERATION
o/$(MODE)/%-gcc.asm: private .UNSANDBOXED = 1
o/$(MODE)/%-gcc.asm: private .SANDBOXED = 0
o/$(MODE)/%-gcc.asm: %.c
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -S -g0 $(OUTPUT_OPTION) $<
o/$(MODE)/%-gcc.asm: private .UNSANDBOXED = 1
o/$(MODE)/%-gcc.asm: private .SANDBOXED = 0
o/$(MODE)/%-gcc.asm: %.cc
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.cxx) -S -g0 $(OUTPUT_OPTION) $<
o/$(MODE)/%-clang.asm: private .UNSANDBOXED = 1
o/$(MODE)/%-clang.asm: private .SANDBOXED = 0
o/$(MODE)/%-clang.asm: %.c
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -S -g0 $(OUTPUT_OPTION) $<
# TODO(jart): Make intrinsics support Clang.
o/$(MODE)/%-clang.asm: CC = $(CLANG)
o/$(MODE)/%-clang.asm: private .UNSANDBOXED = 1
o/$(MODE)/%-clang.asm: private .SANDBOXED = 0
o/$(MODE)/%-clang.asm: %.cc
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.cxx) -S -g0 $(OUTPUT_OPTION) $<
+10
View File
@@ -0,0 +1,10 @@
-nostdinc
-iquote
.
-isystem
libc/isystem
-include
libc/integral/normalize.inc
-D_COSMO_SOURCE
-DMODE="aarch64"
-Wno-unknown-pragmas
+1 -1
View File
@@ -6,7 +6,7 @@
namespace ctl {
struct string;
class string;
class istream : public ios
{
+1 -1
View File
@@ -4,7 +4,7 @@
#define COSMOPOLITAN_CTL_NEW_H_
#ifndef TINY
__static_yoink("__demangle");
__static_yoink("cosmo_demangle");
#endif
namespace ctl {
+112 -11
View File
@@ -5,6 +5,7 @@
#include "exception.h"
#include "is_base_of.h"
#include "is_constructible.h"
#include "is_convertible.h"
#include "remove_extent.h"
#include "unique_ptr.h"
@@ -96,12 +97,12 @@ class shared_ref
size_t use_count() const noexcept
{
return shared + 1;
return __atomic_load_n(&shared, __ATOMIC_RELAXED) + 1;
}
size_t weak_count() const noexcept
{
return weak;
return __atomic_load_n(&weak, __ATOMIC_RELAXED);
}
private:
@@ -348,7 +349,7 @@ class shared_ptr
template<typename U>
bool owner_before(const weak_ptr<U>& r) const noexcept
{
return !r.owner_before(*this);
return rc < r.rc;
}
private:
@@ -381,6 +382,34 @@ class weak_ptr
rc->keep_weak();
}
weak_ptr(const weak_ptr& r) noexcept : p(r.p), rc(r.rc)
{
if (rc)
rc->keep_weak();
}
template<typename U>
requires __::shared_ptr_compatible<T, U>
weak_ptr(const weak_ptr<U>& r) noexcept : p(r.p), rc(r.rc)
{
if (rc)
rc->keep_weak();
}
weak_ptr(weak_ptr&& r) noexcept : p(r.p), rc(r.rc)
{
r.p = nullptr;
r.rc = nullptr;
}
template<typename U>
requires __::shared_ptr_compatible<T, U>
weak_ptr(weak_ptr<U>&& r) noexcept : p(r.p), rc(r.rc)
{
r.p = nullptr;
r.rc = nullptr;
}
~weak_ptr()
{
if (rc)
@@ -409,6 +438,19 @@ class weak_ptr
swap(rc, r.rc);
}
weak_ptr& operator=(weak_ptr r) noexcept
{
swap(r);
return *this;
}
template<typename U>
requires __::shared_ptr_compatible<T, U>
weak_ptr& operator=(weak_ptr<U> r) noexcept
{
weak_ptr<T>(move(r)).swap(*this);
}
shared_ptr<T> lock() const noexcept
{
if (expired())
@@ -437,6 +479,9 @@ class weak_ptr
template<typename U>
friend class shared_ptr;
template<typename U, typename... Args>
friend shared_ptr<U> make_shared(Args&&...);
element_type* p = nullptr;
__::shared_ref* rc = nullptr;
};
@@ -497,19 +542,75 @@ shared_ptr<T>::shared_ptr(U* const p, D d)
}
}
// Our make_shared supports passing a weak self reference as the first parameter
// to your constructor, e.g.:
//
// struct Tree : ctl::weak_self_base
// {
// ctl::shared_ptr<Tree> l, r;
// ctl::weak_ptr<Tree> parent;
// Tree(weak_ptr<Tree> const& self, auto&& l2, auto&& r2)
// : l(ctl::forward<decltype(l2)>(l2)),
// r(ctl::forward<decltype(r2)>(r2))
// {
// if (l) l->parent = self;
// if (r) r->parent = self;
// }
// };
//
// int main() {
// auto t = ctl::make_shared<Tree>(
// ctl::make_shared<Tree>(nullptr, nullptr), nullptr);
// return t->l->parent.lock().get() == t.get() ? 0 : 1;
// }
//
// As shown, passing the parameter at object construction time lets you complete
// object construction without needing a separate Init method. But because we go
// off spec as far as the STL is concerned, there is a potential ambiguity where
// you might have a constructor with a weak_ptr first parameter that is intended
// to be something other than a self-reference. So this feature is opt-in by way
// of inheriting from the following struct.
struct weak_self_base
{};
template<typename T, typename... Args>
shared_ptr<T>
make_shared(Args&&... args)
{
auto rc = __::shared_emplace<T>::make();
rc->construct(forward<Args>(args)...);
shared_ptr<T> r;
r.p = &rc->t;
r.rc = rc.release();
if constexpr (is_base_of_v<enable_shared_from_this<T>, T>) {
r->weak_this = r;
unique_ptr rc = __::shared_emplace<T>::make();
if constexpr (is_base_of_v<weak_self_base, T> &&
is_constructible_v<T, const weak_ptr<T>&, Args...>) {
// A __::shared_ref has a virtual weak reference that is owned by all of
// the shared references. We can avoid some unnecessary refcount changes
// by "borrowing" that reference and passing it to the constructor, then
// promoting it to a shared reference by swapping it with the shared_ptr
// that we return.
weak_ptr<T> w;
w.p = &rc->t;
w.rc = rc.get();
try {
rc->construct(const_cast<const weak_ptr<T>&>(w),
forward<Args>(args)...);
} catch (...) {
w.p = nullptr;
w.rc = nullptr;
throw;
}
rc.release();
shared_ptr<T> r;
swap(r.p, w.p);
swap(r.rc, w.rc);
return r;
} else {
rc->construct(forward<Args>(args)...);
shared_ptr<T> r;
r.p = &rc->t;
r.rc = rc.release();
if constexpr (is_base_of_v<enable_shared_from_this<T>, T>) {
r->weak_this = r;
}
return r;
}
return r;
}
} // namespace ctl
+38
View File
@@ -0,0 +1,38 @@
// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
//
// Copyright 2026 Justin Alexander Roberts Tunney
//
// Permission to use, copy, modify, and/or distribute this software for
// any purpose with or without fee is hereby granted, provided that the
// above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include "sort.h"
#include "libc/mem/alg.h"
namespace ctl {
namespace detail {
// This lives out of line so that programs calling ctl::sort() only need
// to depend on ctl, rather than on whichever library qsort_r() is in.
void
sort_r(void* a,
size_t n,
size_t es,
int (*cmp)(const void*, const void*, void*),
void* arg)
{
qsort_r(a, n, es, cmp, arg);
}
} // namespace detail
} // namespace ctl
+71 -9
View File
@@ -2,23 +2,54 @@
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_SORT_H_
#define CTL_SORT_H_
#include "is_same.h"
#include "iterator_traits.h"
#include "less.h"
#include "partition.h"
#include "new.h"
#include "utility.h"
namespace ctl {
namespace detail {
template<typename RandomIt, typename Compare>
void
quicksort(RandomIt first, RandomIt last, Compare comp)
sort_r(void*, size_t, size_t, int (*)(const void*, const void*, void*), void*);
template<typename T, typename Compare>
int
sort_compare(const T& x, const T& y, Compare& comp)
{
if (first < last) {
auto pivot = ctl::partition(first, last, comp);
quicksort(first, pivot, comp);
quicksort(pivot + 1, last, comp);
}
if (comp(x, y))
return -1;
if (comp(y, x))
return 1;
return 0;
}
template<typename T, typename Compare>
int
sort_direct(const void* a, const void* b, void* arg)
{
return sort_compare(*static_cast<const T*>(a),
*static_cast<const T*>(b),
*static_cast<Compare*>(arg));
}
template<typename RandomIt, typename Compare>
struct sort_context
{
RandomIt first;
Compare& comp;
};
template<typename RandomIt, typename Compare>
int
sort_indirect(const void* a, const void* b, void* arg)
{
auto* ctx = static_cast<sort_context<RandomIt, Compare>*>(arg);
return sort_compare(ctx->first[*static_cast<const size_t*>(a)],
ctx->first[*static_cast<const size_t*>(b)],
ctx->comp);
}
} // namespace detail
@@ -27,7 +58,38 @@ template<typename RandomIt, typename Compare>
void
sort(RandomIt first, RandomIt last, Compare comp)
{
detail::quicksort(first, last, comp);
using T = typename ctl::iterator_traits<RandomIt>::value_type;
size_t n = last - first;
if (n < 2)
return;
if constexpr (ctl::is_same_v<RandomIt, T*> && __is_trivially_copyable(T)) {
detail::sort_r(
first, n, sizeof(T), detail::sort_direct<T, Compare>, &comp);
return;
}
size_t* idx = new size_t[n];
for (size_t i = 0; i < n; ++i)
idx[i] = i;
detail::sort_context<RandomIt, Compare> ctx{ first, comp };
detail::sort_r(
idx, n, sizeof(size_t), detail::sort_indirect<RandomIt, Compare>, &ctx);
for (size_t i = 0; i < n; ++i) {
if (idx[i] == i)
continue;
T tmp(ctl::move(first[i]));
size_t j = i;
for (;;) {
size_t k = idx[j];
idx[j] = j;
if (k == i) {
first[j] = ctl::move(tmp);
break;
}
first[j] = ctl::move(first[k]);
j = k;
}
}
delete[] idx;
}
template<typename RandomIt>
+69 -1
View File
@@ -277,7 +277,7 @@ size_t
string::find(const char ch, const size_t pos) const noexcept
{
char* q;
if ((q = (char*)memchr(data(), ch, size())))
if (pos < size() && (q = (char*)memchr(data() + pos, ch, size() - pos)))
return q - data();
return npos;
}
@@ -383,4 +383,72 @@ string::erase(const size_t pos, size_t count) noexcept
return *this;
}
void
string::append(const ctl::string_view& s, size_t pos, size_t count) noexcept
{
append(s.substr(pos, count));
}
size_t
string::find_last_of(char c, size_t pos) const noexcept
{
const char* b = data();
size_t n = size();
if (pos > n)
pos = n;
const char* p = (const char*)memrchr(b, c, pos);
return p ? p - b : npos;
}
size_t
string::find_last_of(ctl::string_view set, size_t pos) const noexcept
{
if (empty() || set.empty())
return npos;
bool lut[256] = {};
for (char c : set)
lut[c & 255] = true;
const char* b = data();
size_t last = size() - 1;
if (pos > last)
pos = last;
for (;;) {
if (lut[b[pos] & 255])
return pos;
if (!pos)
return npos;
--pos;
}
}
size_t
string::find_first_of(char c, size_t pos) const noexcept
{
size_t n = size();
if (pos >= n)
return npos;
const char* b = data();
const char* p = (const char*)memchr(b + pos, c, n - pos);
return p ? p - b : npos;
}
size_t
string::find_first_of(ctl::string_view set, size_t pos) const noexcept
{
if (set.empty())
return npos;
bool lut[256] = {};
for (char c : set)
lut[c & 255] = true;
const char* b = data();
size_t n = size();
for (;;) {
if (pos >= n)
return npos;
if (lut[b[pos] & 255])
return pos;
++pos;
}
}
} // namespace ctl
+6 -1
View File
@@ -125,6 +125,7 @@ class string
void append(char, size_t) noexcept;
void append(unsigned long) noexcept;
void append(const void*, size_t) noexcept;
void append(const ctl::string_view&, size_t, size_t = npos) noexcept;
string& insert(size_t, ctl::string_view) noexcept;
string& erase(size_t = 0, size_t = npos) noexcept;
string substr(size_t = 0, size_t = npos) const noexcept;
@@ -136,6 +137,10 @@ class string
bool starts_with(ctl::string_view) const noexcept;
size_t find(char, size_t = 0) const noexcept;
size_t find(ctl::string_view, size_t = 0) const noexcept;
size_t find_first_of(char, size_t = 0) const noexcept;
size_t find_first_of(ctl::string_view, size_t = 0) const noexcept;
size_t find_last_of(char, size_t = npos) const noexcept;
size_t find_last_of(ctl::string_view, size_t = npos) const noexcept;
void swap(string& s) noexcept
{
@@ -302,7 +307,7 @@ class string
append(ch);
}
void append(const ctl::string_view s) noexcept
void append(const ctl::string_view& s) noexcept
{
append(s.p, s.n);
}
+63 -1
View File
@@ -29,7 +29,7 @@ size_t
string_view::find(const char ch, const size_t pos) const noexcept
{
char* q;
if (n && (q = (char*)memchr(p, ch, n)))
if (pos < n && (q = (char*)memchr(p + pos, ch, n - pos)))
return q - p;
return npos;
}
@@ -108,4 +108,66 @@ string_view::starts_with(const string_view s) const noexcept
return !memcmp(p, s.p, s.n);
}
size_t
string_view::find_last_of(char c, size_t pos) const noexcept
{
const char* b = data();
size_t n = size();
if (pos > n)
pos = n;
const char* p = (const char*)memrchr(b, c, pos);
return p ? p - b : npos;
}
size_t
string_view::find_last_of(ctl::string_view set, size_t pos) const noexcept
{
if (empty() || set.empty())
return npos;
bool lut[256] = {};
for (char c : set)
lut[c & 255] = true;
const char* b = data();
size_t last = size() - 1;
if (pos > last)
pos = last;
for (;;) {
if (lut[b[pos] & 255])
return pos;
if (!pos)
return npos;
--pos;
}
}
size_t
string_view::find_first_of(char c, size_t pos) const noexcept
{
size_t n = size();
if (pos >= n)
return npos;
const char* b = data();
const char* p = (const char*)memchr(b + pos, c, n - pos);
return p ? p - b : npos;
}
size_t
string_view::find_first_of(ctl::string_view set, size_t pos) const noexcept
{
if (set.empty())
return npos;
bool lut[256] = {};
for (char c : set)
lut[c & 255] = true;
const char* b = data();
size_t n = size();
for (;;) {
if (pos >= n)
return npos;
if (lut[b[pos] & 255])
return pos;
++pos;
}
}
} // namespace ctl
+6 -2
View File
@@ -45,6 +45,10 @@ struct string_view
string_view substr(size_t = 0, size_t = npos) const noexcept;
size_t find(char, size_t = 0) const noexcept;
size_t find(string_view, size_t = 0) const noexcept;
size_t find_first_of(char, size_t = 0) const noexcept;
size_t find_first_of(ctl::string_view, size_t = 0) const noexcept;
size_t find_last_of(char, size_t = npos) const noexcept;
size_t find_last_of(ctl::string_view, size_t = npos) const noexcept;
constexpr string_view& operator=(const string_view s) noexcept
{
@@ -109,12 +113,12 @@ struct string_view
return p[n - 1];
}
constexpr const_iterator begin() noexcept
constexpr const_iterator begin() const noexcept
{
return p;
}
constexpr const_iterator end() noexcept
constexpr const_iterator end() const noexcept
{
return p + n;
}
+1
View File
@@ -6,4 +6,5 @@ o/$(MODE)/dsp: o/$(MODE)/dsp/audio \
o/$(MODE)/dsp/core \
o/$(MODE)/dsp/mpeg \
o/$(MODE)/dsp/scale \
o/$(MODE)/dsp/prog \
o/$(MODE)/dsp/tty
+3 -3
View File
@@ -469,16 +469,16 @@ COSMOAUDIO_ABI int cosmoaudio_poll(struct CosmoAudio* ca,
if (in_out_writeFrames && 1u + *in_out_writeFrames > ca->outputBufferFrames)
return COSMOAUDIO_ENOBUF;
for (;;) {
int done = 0;
int done = 1;
ma_uint32 readable = 0;
ma_uint32 writable = 0;
if (in_out_readFrames) {
readable = ma_pcm_rb_available_read(&ca->input);
done |= readable >= (ma_uint32)*in_out_readFrames;
done &= readable >= (ma_uint32)*in_out_readFrames;
}
if (in_out_writeFrames) {
writable = ma_pcm_rb_available_write(&ca->output);
done |= writable >= (ma_uint32)*in_out_writeFrames;
done &= writable >= (ma_uint32)*in_out_writeFrames;
}
if (done) {
if (in_out_readFrames)
File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
../../../third_party/miniaudio/miniaudio.h
+1
View File
@@ -167,6 +167,7 @@ See below for detailed the API documentation.
#include <stdint.h>
#include <stdio.h>
#include <cosmotime.h>
#include <time.h>
+46
View File
@@ -0,0 +1,46 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#── vi: set noet ft=make ts=8 sw=8 fenc=utf-8 :vi ────────────────────┘
PKGS += DSP_PROG
DSP_PROG_FILES := $(wildcard dsp/prog/*)
DSP_PROG_HDRS = $(filter %.h,$(DSP_PROG_FILES))
DSP_PROG_SRCS = $(filter %.c,$(DSP_PROG_FILES))
DSP_PROG_OBJS = $(DSP_PROG_SRCS:%.c=o/$(MODE)/%.o)
DSP_PROG_COMS = $(DSP_PROG_SRCS:%.c=o/$(MODE)/%)
DSP_PROG_BINS = $(DSP_PROG_COMS) $(DSP_PROG_COMS:%=%.dbg)
DSP_PROG_DIRECTDEPS = \
DSP_AUDIO \
LIBC_CALLS \
LIBC_FMT \
LIBC_INTRIN \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
LIBC_SYSV \
LIBC_TINYMATH \
THIRD_PARTY_GETOPT \
THIRD_PARTY_MINIAUDIO \
THIRD_PARTY_MUSL \
DSP_PROG_DEPS := \
$(call uniq,$(foreach x,$(DSP_PROG_DIRECTDEPS),$($(x))))
o/$(MODE)/dsp/prog/prog.pkg: \
$(DSP_PROG_OBJS) \
$(foreach x,$(DSP_PROG_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/dsp/prog/%.dbg: \
$(DSP_PROG_DEPS) \
o/$(MODE)/dsp/prog/prog.pkg \
o/$(MODE)/dsp/prog/%.o \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
$(DSP_PROG_OBJS): dsp/prog/BUILD.mk
.PHONY: o/$(MODE)/dsp/prog
o/$(MODE)/dsp/prog: $(DSP_PROG_BINS)
+41
View File
@@ -0,0 +1,41 @@
#ifndef COSMOPOLITAN_DSP_PROG_LOUDNESS_H_
#define COSMOPOLITAN_DSP_PROG_LOUDNESS_H_
#include <math.h>
#include <stdio.h>
#define MIN_DECIBEL -60
#define MAX_DECIBEL 0
// computes root of mean squares
static double rms(float *p, int n) {
double s = 0;
for (int i = 0; i < n; ++i)
s += p[i] * p[i];
return sqrt(s / n);
}
// converts rms to decibel
static double rms_to_db(double rms) {
double db = 20 * log10(rms);
db = fmin(db, MAX_DECIBEL);
db = fmax(db, MIN_DECIBEL);
return db;
}
// char meter[21];
// format_decibel_meter(meter, 20, rms_to_db(rms(samps, count)))
static char *format_decibel_meter(char *meter, int width, double db) {
double range = MAX_DECIBEL - MIN_DECIBEL;
int filled = (db - MIN_DECIBEL) / range * width;
for (int i = 0; i < width; ++i) {
if (i < filled) {
meter[i] = '=';
} else {
meter[i] = ' ';
}
}
meter[width] = 0;
return meter;
}
#endif /* COSMOPOLITAN_DSP_PROG_LOUDNESS_H_ */
+154
View File
@@ -0,0 +1,154 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2025 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "dsp/audio/cosmoaudio/cosmoaudio.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/sig.h"
#include "third_party/getopt/getopt.internal.h"
#include "third_party/miniaudio/miniaudio.h"
/**
* @fileoverview audio file player
*
* This program provides a command line interface for playing .wav,
* .mp3, .flac, and .ogg on the system speaker.
*/
volatile int gotsig;
void onsig(int sig) {
gotsig = sig;
}
void usage(FILE *f) {
fprintf(f, "usage: %s [-hrv] [-s 44100] [-c 2] FILE...\n",
program_invocation_short_name);
}
int main(int argc, char *argv[]) {
// parse flags
int opt;
int flag_repeat = 0;
int flag_verbose = 0;
int flag_channels = 2;
int flag_samprate = 44100;
while ((opt = getopt(argc, argv, "hrvc:s:")) != -1) {
switch (opt) {
case 'c':
flag_channels = atoi(optarg);
if (flag_channels < 1) {
fprintf(stderr, "error: invalid channel count\n");
exit(1);
}
break;
case 's':
flag_samprate = atoi(optarg);
if (flag_samprate < 1) {
fprintf(stderr, "error: invalid sampling rate\n");
exit(1);
}
break;
case 'v':
++flag_verbose;
break;
case 'r':
++flag_repeat;
break;
case 'h':
usage(stdout);
exit(0);
default:
usage(stderr);
exit(1);
}
}
// open speaker
int status;
struct CosmoAudio *ca;
struct CosmoAudioOpenOptions cao = {0};
cao.sizeofThis = sizeof(struct CosmoAudioOpenOptions);
cao.deviceType = kCosmoAudioDeviceTypePlayback;
cao.sampleRate = flag_samprate;
cao.debugLog = flag_verbose > 1;
cao.channels = flag_channels;
status = cosmoaudio_open(&ca, &cao);
if (status != COSMOAUDIO_SUCCESS) {
fprintf(stderr, "failed to open speaker: %d\n", status);
exit(1);
}
// intercept ctrl-c
signal(SIGINT, onsig);
// play files
int exit_code = 0;
do {
for (int arg = optind; arg < argc; ++arg) {
const char *fname = argv[arg];
if (flag_verbose)
fprintf(stderr, "playing %s\n", fname);
// create decoder
ma_decoder_config decoderConfig =
ma_decoder_config_init(ma_format_f32, flag_channels, flag_samprate);
decoderConfig.resampling.algorithm = ma_resample_algorithm_linear;
decoderConfig.resampling.linear.lpfOrder = 8;
// open input file
ma_decoder decoder;
ma_result rc = ma_decoder_init_file(fname, &decoderConfig, &decoder);
if (rc != MA_SUCCESS) {
fprintf(stderr,
"%s: failed to open audio file: %s (we support "
".wav, .mp3, .flac, and .ogg)\n",
fname, ma_result_description(rc));
exit_code = 1;
goto exit;
}
// decode audio and transfer to speaker
while (!gotsig) {
float buf[512];
ma_uint64 got = 0;
ma_uint64 max = sizeof(buf) / sizeof(*buf) / flag_channels;
ma_decoder_read_pcm_frames(&decoder, buf, max, &got);
if (!got)
break;
int can = got;
cosmoaudio_poll(ca, NULL, &can);
cosmoaudio_write(ca, buf, got);
}
// cleanup
ma_decoder_uninit(&decoder);
}
} while (!gotsig && flag_repeat);
// we're done
exit:
cosmoaudio_flush(ca);
cosmoaudio_close(ca);
exit(exit_code);
}
+127
View File
@@ -0,0 +1,127 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
│ To the extent possible under law, Justine Tunney has waived │
│ all copyright and related or neighboring rights to this file, │
│ as it is written in the following disclaimers: │
│ • http://unlicense.org/ │
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
╚─────────────────────────────────────────────────────────────────*/
#endif
#include <arpa/inet.h>
#include <assert.h>
#include <cosmoaudio.h>
#include <errno.h>
#include <math.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include "loudness.h"
/**
* @fileoverview plays audio from remote computer on speaker
* @see dsp/prog/sendaudio.c
*/
#define SAMPLING_RATE 44100
#define FRAMES_PER_SECOND 60
#define DEBUG_LOG 0
#define PORT 9834
#define CHUNK_FRAMES (SAMPLING_RATE / FRAMES_PER_SECOND)
static_assert(CHUNK_FRAMES * sizeof(short) < 1472,
"audio chunks won't fit in udp ethernet packet");
sig_atomic_t g_done;
void onsig(int sig) {
g_done = 1;
}
short toshort(float x) {
return fmaxf(-1, fminf(1, x)) * 32767;
}
float tofloat(short x) {
return x / 32768.f;
}
int main(int argc, char* argv[]) {
// listen on udp port for audio
int server;
if ((server = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket");
return 3;
}
struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(PORT)};
if (bind(server, (struct sockaddr*)&addr, sizeof(addr))) {
perror("bind");
return 4;
}
// setup signals
struct sigaction sa;
sa.sa_flags = 0;
sa.sa_handler = onsig;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, 0);
// configure cosmo audio
struct CosmoAudioOpenOptions cao = {0};
cao.sizeofThis = sizeof(struct CosmoAudioOpenOptions);
cao.deviceType = kCosmoAudioDeviceTypePlayback;
cao.sampleRate = SAMPLING_RATE;
cao.bufferFrames = CHUNK_FRAMES * 2;
cao.debugLog = DEBUG_LOG;
cao.channels = 1;
// connect to microphone and speaker
int status;
struct CosmoAudio* ca;
status = cosmoaudio_open(&ca, &cao);
if (status != COSMOAUDIO_SUCCESS) {
fprintf(stderr, "failed to open audio: %d\n", status);
return 5;
}
while (!g_done) {
// read from network
ssize_t got;
short buf16[CHUNK_FRAMES];
if ((got = read(server, buf16, CHUNK_FRAMES * sizeof(short))) == -1) {
if (errno == EINTR)
continue;
perror("read");
return 7;
}
if (got != CHUNK_FRAMES * sizeof(short)) {
fprintf(stderr, "warning: got partial audio frame\n");
continue;
}
// write to speaker
float buf32[CHUNK_FRAMES];
for (int i = 0; i < CHUNK_FRAMES; ++i)
buf32[i] = tofloat(buf16[i]);
cosmoaudio_poll(ca, 0, (int[]){CHUNK_FRAMES});
cosmoaudio_write(ca, buf32, CHUNK_FRAMES);
// print loudness in ascii
char meter[21];
double db = rms_to_db(rms(buf32, CHUNK_FRAMES));
format_decibel_meter(meter, 20, db);
printf("\r%s| %+6.2f dB", meter, db);
fflush(stdout);
}
// clean up resources
cosmoaudio_flush(ca);
cosmoaudio_close(ca);
close(server);
}
+149
View File
@@ -0,0 +1,149 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
│ To the extent possible under law, Justine Tunney has waived │
│ all copyright and related or neighboring rights to this file, │
│ as it is written in the following disclaimers: │
│ • http://unlicense.org/ │
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
╚─────────────────────────────────────────────────────────────────*/
#endif
#include <arpa/inet.h>
#include <assert.h>
#include <cosmoaudio.h>
#include <errno.h>
#include <math.h>
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include "loudness.h"
/**
* @fileoverview sends audio from microphone to remote computer
* @see dsp/prog/recvaudio.c
*/
#define SAMPLING_RATE 44100
#define FRAMES_PER_SECOND 60
#define DEBUG_LOG 0
#define PORT 9834
#define CHUNK_FRAMES (SAMPLING_RATE / FRAMES_PER_SECOND)
static_assert(CHUNK_FRAMES * sizeof(short) < 1472,
"audio chunks won't fit in udp ethernet packet");
sig_atomic_t g_done;
void onsig(int sig) {
g_done = 1;
}
short toshort(float x) {
return fmaxf(-1, fminf(1, x)) * 32767;
}
float tofloat(short x) {
return x / 32768.f;
}
uint32_t host2ip(const char* host) {
uint32_t ip;
if ((ip = inet_addr(host)) != -1u)
return ip;
int rc;
struct addrinfo* ai = NULL;
struct addrinfo hint = {AI_NUMERICSERV, AF_INET, SOCK_STREAM, IPPROTO_TCP};
if ((rc = getaddrinfo(host, "0", &hint, &ai))) {
fprintf(stderr, "%s: %s\n", host, gai_strerror(rc));
exit(50 + rc);
}
ip = ntohl(((struct sockaddr_in*)ai->ai_addr)->sin_addr.s_addr);
freeaddrinfo(ai);
return ip;
}
int main(int argc, char* argv[]) {
if (argc != 2) {
fprintf(stderr, "%s: missing host argument\n", argv[0]);
return 1;
}
// get host argument
const char* remote_host = argv[1];
uint32_t ip = host2ip(remote_host);
// connect to server
int client;
if ((client = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror(remote_host);
return 3;
}
struct sockaddr_in addr = {.sin_family = AF_INET,
.sin_port = htons(PORT),
.sin_addr.s_addr = htonl(ip)};
if (connect(client, (struct sockaddr*)&addr, sizeof(addr))) {
perror(remote_host);
return 4;
}
// setup signals
struct sigaction sa;
sa.sa_flags = 0;
sa.sa_handler = onsig;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, 0);
// configure cosmo audio
struct CosmoAudioOpenOptions cao = {0};
cao.sizeofThis = sizeof(struct CosmoAudioOpenOptions);
cao.deviceType = kCosmoAudioDeviceTypeCapture;
cao.sampleRate = SAMPLING_RATE;
cao.bufferFrames = CHUNK_FRAMES * 2;
cao.debugLog = DEBUG_LOG;
cao.channels = 1;
// connect to microphone and speaker
int status;
struct CosmoAudio* ca;
status = cosmoaudio_open(&ca, &cao);
if (status != COSMOAUDIO_SUCCESS) {
fprintf(stderr, "failed to open audio: %d\n", status);
return 5;
}
while (!g_done) {
// read from microphone
float buf32[CHUNK_FRAMES];
cosmoaudio_poll(ca, (int[]){CHUNK_FRAMES}, 0);
cosmoaudio_read(ca, buf32, CHUNK_FRAMES);
short buf16[CHUNK_FRAMES];
for (int i = 0; i < CHUNK_FRAMES; ++i)
buf16[i] = toshort(buf32[i]);
// send to server
if (write(client, buf16, CHUNK_FRAMES * sizeof(short)) == -1) {
if (errno == EINTR && g_done)
break;
perror(remote_host);
return 7;
}
// print loudness in ascii
char meter[21];
double db = rms_to_db(rms(buf32, CHUNK_FRAMES));
format_decibel_meter(meter, 20, db);
printf("\r%s| %+6.2f dB", meter, db);
fflush(stdout);
}
// clean up resources
cosmoaudio_close(ca);
close(client);
}
+11 -18
View File
@@ -54,9 +54,8 @@ EXAMPLES_DIRECTDEPS = \
LIBC_NEXGEN32E \
LIBC_NT_ADVAPI32 \
LIBC_NT_IPHLPAPI \
LIBC_NT_MEMORY \
LIBC_NT_KERNEL32 \
LIBC_NT_NTDLL \
LIBC_NT_MEMORY \
LIBC_NT_USER32 \
LIBC_NT_WS2_32 \
LIBC_PROC \
@@ -64,6 +63,7 @@ EXAMPLES_DIRECTDEPS = \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STR \
LIBC_SYSTEM \
LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_TESTLIB \
@@ -79,11 +79,15 @@ EXAMPLES_DIRECTDEPS = \
THIRD_PARTY_DOUBLECONVERSION \
THIRD_PARTY_GDTOA \
THIRD_PARTY_GETOPT \
THIRD_PARTY_HACLSTAR \
THIRD_PARTY_HIREDIS \
THIRD_PARTY_LIBCXX \
THIRD_PARTY_LIBCXXABI \
THIRD_PARTY_LIBUNWIND \
THIRD_PARTY_LINENOISE \
THIRD_PARTY_LUA \
THIRD_PARTY_MBEDTLS \
THIRD_PARTY_MINIAUDIO \
THIRD_PARTY_MUSL \
THIRD_PARTY_NSYNC \
THIRD_PARTY_NSYNC_MEM \
@@ -94,12 +98,10 @@ EXAMPLES_DIRECTDEPS = \
THIRD_PARTY_TZ \
THIRD_PARTY_VQSORT \
THIRD_PARTY_XED \
THIRD_PARTY_LIBCXXABI \
THIRD_PARTY_LIBUNWIND \
THIRD_PARTY_ZLIB \
TOOL_ARGS \
TOOL_BUILD_LIB \
TOOL_VIZ_LIB
TOOL_VIZ_LIB \
EXAMPLES_DEPS := \
$(call uniq,$(foreach x,$(EXAMPLES_DIRECTDEPS),$($(x))))
@@ -108,11 +110,6 @@ o/$(MODE)/examples/examples.pkg: \
$(EXAMPLES_OBJS) \
$(foreach x,$(EXAMPLES_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/examples/unbourne.o: private \
CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED \
-fpie
o/$(MODE)/examples/%.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/%.o \
@@ -128,14 +125,6 @@ o/$(MODE)/examples/hellolua.dbg: \
$(EXAMPLES_BOOTLOADER)
@$(APELINK)
o/$(MODE)/examples/ispell.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/ispell.o \
o/$(MODE)/usr/share/dict/words.zip.o \
o/$(MODE)/examples/examples.pkg \
$(EXAMPLES_BOOTLOADER)
@$(APELINK)
o/$(MODE)/examples/nesemu1.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/nesemu1.o \
@@ -150,6 +139,10 @@ o/$(MODE)/examples/picol.o: private \
CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED
o/$(MODE)/examples/nesemu1.o: private \
CPPFLAGS += \
-O3
o/$(MODE)/examples/picol.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/picol.o \
+3 -3
View File
@@ -60,10 +60,10 @@ struct List {
int count;
};
atomic_uintptr_t list;
_Atomic(struct List*) list;
void push(struct List* elem) {
uintptr_t tip;
struct List* tip;
assert(!TAG(elem));
for (tip = atomic_load_explicit(&list, memory_order_relaxed);;) {
elem->next = (struct List*)PTR(tip);
@@ -76,7 +76,7 @@ void push(struct List* elem) {
}
struct List* pop(void) {
uintptr_t tip;
struct List* tip;
struct List* elem;
tip = atomic_load_explicit(&list, memory_order_relaxed);
while ((elem = (struct List*)PTR(tip))) {
+353
View File
@@ -0,0 +1,353 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
│ To the extent possible under law, Justine Tunney has waived │
│ all copyright and related or neighboring rights to this file, │
│ as it is written in the following disclaimers: │
│ • http://unlicense.org/ │
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
╚─────────────────────────────────────────────────────────────────*/
#endif
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <iconv.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
/**
* @fileoverview program for viewing bbs art files
* @see https://github.com/blocktronics/artpacks
* @see http://www.textfiles.com/art/
*/
#define HELP \
"Usage:\n\
art [-b %d] [-f %s] [-t %s] FILE...\n\
\n\
Flags:\n\
-b NUMBER specifies simulated modem baud rate, which defaults to\n\
2400 since that was the most common modem speed in the\n\
later half of the 1980s during the BBS golden age; you\n\
could also say 300 for the slowest experience possible\n\
or you could say 14.4k to get more of a 90's feel, and\n\
there's also the infamous 56k to bring you back to y2k\n\
-f CHARSET specifies charset of input bytes, where the default is\n\
cp347 which means IBM Code Page 347 a.k.a. DOS\n\
-t CHARSET specifies output charset used by your terminal, and it\n\
defaults to utf8 a.k.a. thompson-pike encoding\n\
\n\
Supported charsets:\n\
utf8, ascii, wchar_t, ucs2be, ucs2le, utf16be, utf16le, ucs4be,\n\
ucs4le, utf16, ucs4, ucs2, eucjp, shiftjis, iso2022jp, gb18030, gbk,\n\
gb2312, big5, euckr, iso88591, latin1, iso88592, iso88593, iso88594,\n\
iso88595, iso88596, iso88597, iso88598, iso88599, iso885910,\n\
iso885911, iso885913, iso885914, iso885915, iso885916, cp1250,\n\
windows1250, cp1251, windows1251, cp1252, windows1252, cp1253,\n\
windows1253, cp1254, windows1254, cp1255, windows1255, cp1256,\n\
windows1256, cp1257, windows1257, cp1258, windows1258, koi8r, koi8u,\n\
cp437, cp850, cp866, ibm1047, cp1047.\n\
\n\
See also:\n\
http://www.textfiles.com/art/\n\
https://github.com/blocktronics/artpacks\n\
\n"
#define INBUFSZ 256
#define OUBUFSZ (INBUFSZ * 6)
#define SLIT(s) ((unsigned)s[3] << 24 | s[2] << 16 | s[1] << 8 | s[0])
// "When new technology comes out, people don't all buy it right away.
// If what they have works, some will wait until it doesn't. A few
// people do get the latest though. In 1984 2400 baud modems became
// available, so some people had them, but many didn't. A BBS list
// from 1986 shows operators were mostly 300 and 1200, but some were
// using 2400. The next 5 years were the hayday of the 2400."
//
// https://forum.vcfed.org/index.php?threads/the-2400-baud-modem.44241/
int baud_rate = 2400; // -b 2400
const char* from_charset = "CP437"; // -f CP437
const char* to_charset = "UTF-8"; // -t UTF-8
volatile sig_atomic_t done;
void on_signal(int sig) {
done = 1;
(void)sig;
}
void print(const char* s) {
(void)!write(STDOUT_FILENO, s, strlen(s));
}
int encode_character(char output[8], const char* codec, wchar_t character) {
size_t inbytesleft = sizeof(wchar_t);
size_t outbytesleft = 7;
char* inbuf = (char*)&character;
char* outbuf = output;
iconv_t cd = iconv_open(codec, "wchar_t");
if (cd == (iconv_t)-1)
return -1;
size_t result = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
iconv_close(cd);
if (result == (size_t)-1)
return -1;
*outbuf = '\0';
return 7 - outbytesleft;
}
void append_replacement_character(char** b) {
int n = encode_character(*b, to_charset, 0xFFFD);
if (n == -1)
n = encode_character(*b, to_charset, '?');
if (n != -1)
*b += n;
}
int compare_time(struct timespec a, struct timespec b) {
int cmp;
if (!(cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec)))
cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec);
return cmp;
}
struct timespec add_time(struct timespec x, struct timespec y) {
x.tv_sec += y.tv_sec;
x.tv_nsec += y.tv_nsec;
if (x.tv_nsec >= 1000000000) {
x.tv_nsec -= 1000000000;
x.tv_sec += 1;
}
return x;
}
struct timespec subtract_time(struct timespec a, struct timespec b) {
a.tv_sec -= b.tv_sec;
if (a.tv_nsec < b.tv_nsec) {
a.tv_nsec += 1000000000;
a.tv_sec--;
}
a.tv_nsec -= b.tv_nsec;
return a;
}
struct timespec fromnanos(long long x) {
struct timespec ts;
ts.tv_sec = x / 1000000000;
ts.tv_nsec = x % 1000000000;
return ts;
}
void process_file(const char* path, int fd, iconv_t cd) {
size_t carry = 0;
struct timespec next;
char input_buffer[INBUFSZ];
clock_gettime(CLOCK_MONOTONIC, &next);
for (;;) {
// read from file
ssize_t bytes_read = read(fd, input_buffer + carry, INBUFSZ - carry);
if (!bytes_read)
return;
if (bytes_read == -1) {
perror(path);
done = 1;
return;
}
// modernize character set
char* input_ptr = input_buffer;
size_t input_left = carry + bytes_read;
char output_buffer[OUBUFSZ];
char* output_ptr = output_buffer;
size_t output_left = OUBUFSZ;
size_t ir = iconv(cd, &input_ptr, &input_left, &output_ptr, &output_left);
carry = 0;
if (ir == (size_t)-1) {
if (errno == EINVAL) {
// incomplete multibyte sequence encountered
memmove(input_buffer, input_ptr, input_left);
carry = input_left;
} else if (errno == EILSEQ && input_left) {
// EILSEQ means either
// 1. illegal input sequence encountered
// 2. code not encodable in output codec
//
// so we skip one byte of input, and insert � or ? in the output
// this isn't the most desirable behavior, but it is the best we
// can do, since we don't know specifics about the codecs in use
//
// unlike glibc cosmo's iconv implementation may handle case (2)
// automatically by inserting an asterisk in place of a sequence
++input_ptr;
--input_left;
memmove(input_buffer, input_ptr, input_left);
carry = input_left;
if (output_left >= 8)
append_replacement_character(&output_ptr);
} else {
perror(path);
done = 1;
return;
}
}
// write to terminal
for (char* p = output_buffer; p < output_ptr; p++) {
if (done)
return;
(void)!write(STDOUT_FILENO, p, 1);
// allow arrow keys to change baud rate
int have;
if (ioctl(STDIN_FILENO, FIONREAD, &have)) {
perror("ioctl");
done = 1;
return;
}
if (have > 0) {
char key[4] = {0};
if (read(STDIN_FILENO, key, sizeof(key)) > 0) {
if (SLIT(key) == SLIT("\33[A") || // up
SLIT(key) == SLIT("\33[C")) { // right
baud_rate *= 1.4;
} else if (SLIT(key) == SLIT("\33[B") || // down
SLIT(key) == SLIT("\33[D")) { // left
baud_rate *= 0.6;
}
if (baud_rate < 3)
baud_rate = 3;
if (baud_rate > 1000000000)
baud_rate = 1000000000;
}
}
// insert artificial delay for one byte. we divide by 10 to convert
// bits to bytes, because that is how many bits 8-N-1 encoding used
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
next = add_time(next, fromnanos(1e9 / (baud_rate / 10.)));
if (compare_time(next, now) > 0) {
struct timespec sleep = subtract_time(next, now);
nanosleep(&sleep, 0);
}
}
}
}
int main(int argc, char* argv[]) {
int opt;
while ((opt = getopt(argc, argv, "hb:f:t:")) != -1) {
switch (opt) {
case 'b': {
char* endptr;
double rate = strtod(optarg, &endptr);
if (*endptr == 'k') {
rate *= 1e3;
++endptr;
} else if (*endptr == 'm') {
rate *= 1e6;
++endptr;
}
if (*endptr || baud_rate <= 0) {
fprintf(stderr, "%s: invalid baud rate: %s\n", argv[0], optarg);
exit(1);
}
baud_rate = rate;
break;
}
case 'f':
from_charset = optarg;
break;
case 't':
to_charset = optarg;
break;
case 'h':
fprintf(stderr, HELP, baud_rate, from_charset, to_charset);
exit(0);
default:
fprintf(stderr, "protip: pass the -h flag for help\n");
exit(1);
}
}
if (optind == argc) {
fprintf(stderr, "%s: missing operand\n", argv[0]);
exit(1);
}
// create character transcoder
iconv_t cd = iconv_open(to_charset, from_charset);
if (cd == (iconv_t)-1) {
fprintf(stderr, "error: conversion from %s to %s not supported\n",
from_charset, to_charset);
exit(1);
}
// catch ctrl-c
signal(SIGINT, on_signal);
// don't wait until newline to read() keystrokes
struct termios t;
if (!tcgetattr(STDIN_FILENO, &t)) {
struct termios t2 = t;
t2.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &t2);
}
// Process each file specified on the command line
for (int i = optind; i < argc && !done; i++) {
// open file
int fd = open(argv[i], O_RDONLY);
if (fd == -1) {
perror(argv[i]);
break;
}
// wait between files
if (i > optind)
sleep(1);
print("\33[?25l"); // hide cursor
print("\33[H"); // move cursor to top-left
print("\33[J"); // erase display forward
print("\33[1;24r"); // set scrolling region to first 24 lines
print("\33[?7h"); // enable auto-wrap mode
print("\33[?3l"); // 80 column mode (deccolm) vt100
print("\33[H"); // move cursor to top-left, again
// get busy
process_file(argv[i], fd, cd);
close(fd);
}
// cleanup
iconv_close(cd);
print("\33[s"); // save cursor position
print("\33[?25h"); // show cursor
print("\33[0m"); // reset text attributes (color, bold, etc.)
print("\33[?1049l"); // exit alternate screen mode
print("\33(B"); // exit line drawing and other alt charset modes
print("\33[r"); // reset scrolling region
print("\33[?2004l"); // turn off bracketed paste mode
print("\33[4l"); // exit insert mode
print("\33[?1l\33>"); // exit application keypad mode
print("\33[?7h"); // reset text wrapping mode
print("\33[?12l"); // reset cursor blinking mode
print("\33[?6l"); // reset origin mode
print("\33[20l"); // reset auto newline mode
print("\33[u"); // restore cursor position
// restore terminal
tcsetattr(STDIN_FILENO, TCSANOW, &t);
}
+353
View File
@@ -0,0 +1,353 @@
// -*- mode:c; indent-tabs-mode:nil; c-basic-offset:4 -*-
// vi: set et ft=c ts=4 sts=4 sw=4 fenc=utf-8
// asteroids by tsotchke
// https://github.com/tsotchke/asteroids
// clang-format off
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <termios.h>
#include <sys/select.h>
#define SCREEN_WIDTH 80
#define SCREEN_HEIGHT 24
#define MAX_ASTEROIDS 5
#define MAX_BULLETS 5
typedef struct {
float x, y;
} Vector2;
typedef struct {
Vector2 position;
Vector2 velocity;
float angle;
float radius;
} GameObject;
GameObject spaceship;
GameObject asteroids[MAX_ASTEROIDS];
GameObject bullets[MAX_BULLETS];
int score = 0;
time_t startTime;
int isGameOver = 0;
int shouldExit = 0;
int finalTime = 0; // To store final time at game over
char display[SCREEN_HEIGHT][SCREEN_WIDTH];
// Function to clear the screen buffer
void clearDisplay() {
memset(display, ' ', sizeof(display));
}
// Function to draw a pixel on the screen
void drawPixel(int x, int y) {
if (x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT) {
display[y][x] = '*';
}
}
// Function to draw a line using Bresenham's algorithm
void drawLine(int x1, int y1, int x2, int y2) {
int dx = abs(x2 - x1), sx = (x1 < x2) ? 1 : -1;
int dy = -abs(y2 - y1), sy = (y1 < y2) ? 1 : -1;
int error = dx + dy, e2;
while (1) {
drawPixel(x1, y1);
if (x1 == x2 && y1 == y2) break;
e2 = 2 * error;
if (e2 >= dy) { error += dy; x1 += sx; }
if (e2 <= dx) { error += dx; y1 += sy; }
}
}
// Function to draw a circle
void drawCircle(int centerX, int centerY, int radius) {
int x = radius - 1, y = 0, dx = 1, dy = 1, err = dx - (radius << 1);
while (x >= y) {
drawPixel(centerX + x, centerY + y);
drawPixel(centerX + y, centerY + x);
drawPixel(centerX - y, centerY + x);
drawPixel(centerX - x, centerY + y);
drawPixel(centerX - x, centerY - y);
drawPixel(centerX - y, centerY - x);
drawPixel(centerX + y, centerY - x);
drawPixel(centerX + x, centerY - y);
if (err <= 0) {
y++;
err += dy;
dy += 2;
}
if (err > 0) {
x--;
dx += 2;
err += dx - (radius << 1);
}
}
}
// Initialize a game object
void initializeGameObject(GameObject *obj, float x, float y, float angle, float radius) {
obj->position = (Vector2){x, y};
obj->velocity = (Vector2){0, 0};
obj->angle = angle;
obj->radius = radius;
}
// Wrap position of the spaceship and asteroids within screen bounds
void wrapPosition(Vector2 *pos) {
if (pos->x < 0) pos->x = SCREEN_WIDTH - 1;
if (pos->x >= SCREEN_WIDTH) pos->x = 0;
if (pos->y < 0) pos->y = SCREEN_HEIGHT - 1;
if (pos->y >= SCREEN_HEIGHT) pos->y = 0;
}
// Check if two game objects are colliding
int checkCollision(GameObject *a, GameObject *b) {
float deltaX = a->position.x - b->position.x;
float deltaY = a->position.y - b->position.y;
return sqrt(deltaX * deltaX + deltaY * deltaY) < (a->radius + b->radius);
}
// Initialize game state
void initGame() {
score = 0; // Reset the score
initializeGameObject(&spaceship, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 0, 2);
for (int i = 0; i < MAX_ASTEROIDS; i++) {
initializeGameObject(&asteroids[i],
rand() % SCREEN_WIDTH,
rand() % SCREEN_HEIGHT,
0,
2 + rand() % 3);
asteroids[i].velocity.x = ((float)rand() / RAND_MAX) * 2 - 1;
asteroids[i].velocity.y = ((float)rand() / RAND_MAX) * 2 - 1;
}
for (int i = 0; i < MAX_BULLETS; i++) {
bullets[i].position.x = -1; // Mark bullet as inactive
bullets[i].position.y = -1;
}
startTime = time(NULL);
isGameOver = 0;
finalTime = 0; // Reset final time
}
// Draw the spaceship on the screen
void drawSpaceship() {
int x = (int)spaceship.position.x;
int y = (int)spaceship.position.y;
int size = 3;
float cosAngle = cos(spaceship.angle);
float sinAngle = sin(spaceship.angle);
int x1 = x + size * cosAngle;
int y1 = y + size * sinAngle;
int x2 = x + size * cos(spaceship.angle + 2.5);
int y2 = y + size * sin(spaceship.angle + 2.5);
int x3 = x + size * cos(spaceship.angle - 2.5);
int y3 = y + size * sin(spaceship.angle - 2.5);
drawLine(x1, y1, x2, y2);
drawLine(x2, y2, x3, y3);
drawLine(x3, y3, x1, y1);
}
// Draw all entities on the screen
void drawEntities(GameObject *entities, int count, void (*drawFunc)(GameObject *)) {
for (int i = 0; i < count; i++) {
drawFunc(&entities[i]);
}
}
// Draw a bullet on the screen
void drawBullet(GameObject *bullet) { // Changed to non-const
if (bullet->position.x >= 0) {
drawPixel((int)bullet->position.x, (int)bullet->position.y);
}
}
// Draw an asteroid on the screen
void drawAsteroid(GameObject *asteroid) { // Changed to non-const
drawCircle((int)asteroid->position.x, (int)asteroid->position.y, (int)asteroid->radius);
}
// Refresh the display
void updateDisplay() {
clearDisplay();
if (!isGameOver) {
drawSpaceship();
drawEntities(asteroids, MAX_ASTEROIDS, drawAsteroid);
drawEntities(bullets, MAX_BULLETS, drawBullet);
}
// Print the screen buffer
printf("\033[H");
for (int y = 0; y < SCREEN_HEIGHT; y++) {
for (int x = 0; x < SCREEN_WIDTH; x++) {
putchar(display[y][x]);
}
putchar('\n');
}
// Display score and elapsed time
time_t currentTime = time(NULL);
int elapsedTime = isGameOver ? finalTime : (currentTime - startTime);
printf("Score: %d | Time: %02d:%02d | %s\n", score, elapsedTime / 60, elapsedTime % 60, isGameOver ? "Game Over!" : " ");
}
// Update the position of game objects
void updateGameObject(GameObject *obj, int isBullet) {
obj->position.x += obj->velocity.x;
obj->position.y += obj->velocity.y;
// If it's a bullet, check if it's out of bounds
if (isBullet) {
if (obj->position.x < 0 || obj->position.x >= SCREEN_WIDTH || obj->position.y < 0 || obj->position.y >= SCREEN_HEIGHT) {
obj->position.x = -1; // Deactivate bullet
obj->position.y = -1;
}
} else {
wrapPosition(&obj->position);
}
}
// Update the game state
void updateGame() {
if (isGameOver) return;
// Update spaceship and apply friction
updateGameObject(&spaceship, 0); // 0 indicates it's not a bullet
spaceship.velocity.x *= 0.98;
spaceship.velocity.y *= 0.98;
// Move asteroids and check for collisions
for (int i = 0; i < MAX_ASTEROIDS; i++) {
updateGameObject(&asteroids[i], 0);
if (checkCollision(&spaceship, &asteroids[i])) {
isGameOver = 1;
finalTime = time(NULL) - startTime;
return;
}
}
// Update bullet positions
for (int i = 0; i < MAX_BULLETS; i++) {
if (bullets[i].position.x >= 0) {
updateGameObject(&bullets[i], 1); // 1 indicates it's a bullet
}
}
// Check for bullet collisions with asteroids
for (int i = 0; i < MAX_BULLETS; i++) {
if (bullets[i].position.x >= 0) {
for (int j = 0; j < MAX_ASTEROIDS; j++) {
if (checkCollision(&bullets[i], &asteroids[j])) {
bullets[i].position.x = -1; // Deactivate bullet
bullets[i].position.y = -1;
asteroids[j].position.x = rand() % SCREEN_WIDTH;
asteroids[j].position.y = rand() % SCREEN_HEIGHT;
score += 100;
}
}
}
}
}
// Fire a bullet
void shootBullet() {
for (int i = 0; i < MAX_BULLETS; i++) {
if (bullets[i].position.x < 0) {
bullets[i].position = spaceship.position;
bullets[i].velocity.x = cos(spaceship.angle) * 2;
bullets[i].velocity.y = sin(spaceship.angle) * 2;
break;
}
}
}
// Check if a key was hit
int isKeyHit() {
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv);
}
// Configure terminal settings
void configureTerminal(struct termios *old_tio, struct termios *new_tio) {
tcgetattr(STDIN_FILENO, old_tio);
*new_tio = *old_tio;
new_tio->c_lflag &= (~ICANON & ~ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, new_tio);
}
// Restore terminal settings
void restoreTerminal(struct termios *old_tio) {
tcsetattr(STDIN_FILENO, TCSANOW, old_tio);
}
void onSignal(int sig) {
shouldExit = 1;
}
// Main game loop
int main() {
signal(SIGINT, onSignal); // Capture ^C
srand(time(NULL)); // Seed the random number generator
initGame(); // Initialize the game state
struct termios old_tio, new_tio;
configureTerminal(&old_tio, &new_tio);
printf("\033[?25l"); // Hide the cursor
while (!shouldExit) {
if (isKeyHit()) {
char input = getchar();
if (input == 27) { // ESC key
if (getchar() == '[') { // Handle arrow keys
switch (getchar()) {
case 'A': // Up arrow
spaceship.velocity.x += cos(spaceship.angle) * 0.2;
spaceship.velocity.y += sin(spaceship.angle) * 0.2;
break;
case 'B': // Down arrow
spaceship.velocity.x -= cos(spaceship.angle) * 0.2;
spaceship.velocity.y -= sin(spaceship.angle) * 0.2;
break;
case 'D': spaceship.angle -= 0.2; break; // Left arrow
case 'C': spaceship.angle += 0.2; break; // Right arrow
}
}
} else if (input == ' ') {
shootBullet(); // Fire a bullet
} else if (input == 'q') {
break; // Quit the game
} else if (input == 'r' && isGameOver) {
initGame(); // Restart the game
}
}
updateGame(); // Update game state
updateDisplay(); // Refresh the display
usleep(50000); // Wait for 50ms (20 FPS)
}
printf("\033[?25h"); // Show the cursor
restoreTerminal(&old_tio); // Restore terminal settings
return 0;
}
+146
View File
@@ -0,0 +1,146 @@
// clang-format off
//
// this program implements ancient roman cryptography
//
// example usage:
//
// make -j o//examples/caesar # compile
// o//examples/caesar 23 <file.txt | # encrypt
// o//examples/caesar # decrypt
//
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Rick Adams.
*
* Authors:
* Stan King, John Eldridge, based on algorithm suggested by
* Bob Morris
* 29-Sep-82
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#define LINELENGTH 2048
#define ROTATE(ch, perm) \
isascii(ch) ? ( \
isupper(ch) ? ('A' + (ch - 'A' + perm) % 26) : \
islower(ch) ? ('a' + (ch - 'a' + perm) % 26) : ch) : ch
/*
* letter frequencies (taken from some unix(tm) documentation)
* (unix is a trademark of Bell Laboratories)
*/
static double stdf[26] = {
7.97, 1.35, 3.61, 4.78, 12.37, 2.01, 1.46, 4.49, 6.39, 0.04,
0.42, 3.81, 2.69, 5.92, 6.96, 2.91, 0.08, 6.63, 8.77, 9.68,
2.62, 0.81, 1.88, 0.23, 2.07, 0.06,
};
static void
printit(char *arg)
{
int ch, rot;
if ((rot = atoi(arg)) < 0) {
fprintf(stderr, "caesar: bad rotation value.\n");
exit(1);
}
while ((ch = getchar()) != EOF)
putchar(ROTATE(ch, rot));
exit(0);
}
int
main(int argc, char **argv)
{
int ch, dot, i, nread, winnerdot = 0;
unsigned char inbuf[LINELENGTH];
int obs[26], try, winner;
if (argc > 1)
printit(argv[1]);
/* adjust frequency table to weight low probs REAL low */
for (i = 0; i < 26; ++i)
stdf[i] = log(stdf[i]) + log(26.0 / 100.0);
/* zero out observation table */
bzero(obs, 26 * sizeof(int));
if ((nread = read(STDIN_FILENO, inbuf, LINELENGTH)) < 0) {
perror("caesar");
exit(1);
}
for (i = nread; i--;) {
ch = inbuf[i];
if (isascii(ch)) {
if (islower(ch))
++obs[ch - 'a'];
else if (isupper(ch))
++obs[ch - 'A'];
}
}
/*
* now "dot" the freqs with the observed letter freqs
* and keep track of best fit
*/
for (try = winner = 0; try < 26; ++try) { /* += 13) { */
dot = 0;
for (i = 0; i < 26; i++)
dot += obs[i] * stdf[(i + try) % 26];
/* initialize winning score */
if (try == 0)
winnerdot = dot;
if (dot > winnerdot) {
/* got a new winner! */
winner = try;
winnerdot = dot;
}
}
while (nread) {
for (i = 0; i < nread; ++i) {
ch = inbuf[i];
putchar(ROTATE(ch, winner));
}
if ((nread = read(STDIN_FILENO, inbuf, LINELENGTH)) < 0) {
perror("caesar");
exit(1);
}
}
}
+1 -1
View File
@@ -89,7 +89,7 @@ int main(int argc, char *argv[]) {
// not the case when you use sigprocmask() to block signals which is
// useful for kicking the can down the road.
WRITE("doing cpu task...\n");
for (volatile int i = 0; i < INT_MAX / 3; ++i) {
for (volatile int i = 0; i < (int)(INT_MAX * .4); ++i) {
if (gotsig) {
WRITE("\rgot ctrl+c asynchronously\n");
exit(0);
+2 -2
View File
@@ -259,7 +259,7 @@ int main(int argc, char *argv[]) {
unassert(!sigaction(SIGTERM, &sa, 0));
// you can pass the number of threads you want as the first command arg
threads = argc > 1 ? atoi(argv[1]) : __get_cpu_count();
threads = argc > 1 ? atoi(argv[1]) : cosmo_cpu_count();
if (!(1 <= threads && threads <= 100000)) {
tinyprint(2, "error: invalid number of threads\n", NULL);
exit(1);
@@ -337,7 +337,7 @@ int main(int argc, char *argv[]) {
sigaddset(&block, SIGQUIT);
pthread_attr_t attr;
unassert(!pthread_attr_init(&attr));
unassert(!pthread_attr_setstacksize(&attr, 65536));
unassert(!pthread_attr_setstacksize(&attr, 65536 - getpagesize()));
unassert(!pthread_attr_setguardsize(&attr, getpagesize()));
unassert(!pthread_attr_setsigmask_np(&attr, &block));
unassert(!pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0));
+111
View File
@@ -0,0 +1,111 @@
#/*─────────────────────────────────────────────────────────────────╗
│ To the extent possible under law, Justine Tunney has waived │
│ all copyright and related or neighboring rights to this file, │
│ as it is written in the following disclaimers: │
│ • http://unlicense.org/ │
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
╚─────────────────────────────────────────────────────────────────*/
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// POSIX kill command
//
// - kill -l
// - kill -9 123 456
// - kill -KILL 123 456
// - kill -SIGKILL 123 456
// - kill -s SIGKILL 123 456
//
const char kSignals[] = {
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS,
SIGFPE, SIGKILL, SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM,
SIGTERM, SIGCHLD, SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU,
SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF, SIGWINCH, SIGSYS,
};
const char kSignalNames[][8] = {
"HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "BUS",
"FPE", "KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM",
"TERM", "CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU",
"URG", "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "SYS",
};
void list_signals(void) {
for (int i = 0; i < sizeof(kSignals); i += 4) {
for (int j = 0; j < 4; ++j) {
if (i + j < sizeof(kSignals)) {
int sig = kSignals[i + j];
printf("%2d) SIG%-7s", sig, kSignalNames[i + j]);
}
}
printf("\n");
}
}
int main(int argc, char *argv[]) {
const char *prog = argv[0];
if (!prog)
prog = "kill";
int arg = 1;
const char *sigstr = 0;
if (arg < argc) {
if (argv[arg][0] == '-') {
if (argv[arg][1] == 's' && !argv[arg][2]) {
if (++arg < argc)
sigstr = argv[arg++];
} else if (argv[arg][1] == 'l' && !argv[arg][2]) {
list_signals();
exit(0);
} else {
sigstr = argv[arg++] + 1;
}
}
}
if (arg == argc) {
fprintf(stderr, "%s: missing operand\n", prog);
exit(1);
}
int sig;
if (!sigstr) {
sig = SIGTERM;
} else {
sig = atoi(sigstr);
if (sig) {
if (!(1 <= sig && sig <= NSIG)) {
fprintf(stderr, "%s: bad signal number: %d\n", prog, sig);
exit(1);
}
} else {
if (toupper(sigstr[0]) == 'S' && //
toupper(sigstr[1]) == 'I' && //
toupper(sigstr[2]) == 'G')
sigstr += 3;
for (int i = 0; i < sizeof(kSignals); ++i)
if (!strcasecmp(sigstr, kSignalNames[i]))
sig = kSignals[i];
if (!sig) {
fprintf(stderr, "%s: bad signal name: %s\n", prog, sigstr);
exit(1);
}
}
}
int rc = 0;
for (int i = arg; i < argc; ++i) {
int pid = atoi(argv[i]);
if (kill(pid, sig)) {
fprintf(stderr, "%s: %d: %s\n", prog, pid, strerror(errno));
rc = 1;
}
}
exit(rc);
}
+98
View File
@@ -0,0 +1,98 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
│ To the extent possible under law, Justine Tunney has waived │
│ all copyright and related or neighboring rights to this file, │
│ as it is written in the following disclaimers: │
│ • http://unlicense.org/ │
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
╚─────────────────────────────────────────────────────────────────*/
#endif
#include <cosmo.h>
#include <malloc.h>
#include <pthread.h>
#include <stdlib.h>
// cosmo bulitin memory leak detector demo
//
// you can use cosmo's memory leak detector to track backtraces for your
// memory allocations and then print error messages using addr2line. you
// generally want to have your app override malloc with your own malloc.
// you can use libc's builtin dlmalloc implementation like this as well.
// using this leak detector makes malloc() go 10x slower and allocations
// will each use 200 bytes of additional memory. if you don't care about
// backtraces, then you can create a leak detector for free by using the
// CheckForMemoryLeaks() function at the end of main().
//
// $ make -j o//examples/memleak_backtrace
// $ o//examples/memleak_backtrace
// let's demo the builtin memory leak detector
// error: memory leak detected 0x778dd0979fc0
// 42175f4 in malloc at /home/jart/cosmo/examples/memleak_backtrace.c:34
// 42049b7 in main at /home/jart/cosmo/examples/memleak_backtrace.c:67
// 4204db4 in cosmo at /home/jart/cosmo/libc/runtime/cosmo.S:103
// 42045d9 in _start at /home/jart/cosmo/libc/crt/crt.S:128
// error: memory leak detected 0x778dd0979fe0
// 42175f4 in malloc at /home/jart/cosmo/examples/memleak_backtrace.c:34
// 42049aa in main at /home/jart/cosmo/examples/memleak_backtrace.c:66
// 4204db4 in cosmo at /home/jart/cosmo/libc/runtime/cosmo.S:103
// 42045d9 in _start at /home/jart/cosmo/libc/crt/crt.S:128
//
// wrap cosmopolitan's memory allocator to track allocations
static pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER;
void *malloc(size_t n) {
pthread_mutex_lock(&mu);
void *r = cosmo_leak_track(__dlmalloc(n));
pthread_mutex_unlock(&mu);
return r;
}
void *calloc(size_t n, size_t z) {
pthread_mutex_lock(&mu);
void *r = cosmo_leak_track(__dlcalloc(n, z));
pthread_mutex_unlock(&mu);
return r;
}
void *realloc(void *p, size_t n) {
pthread_mutex_lock(&mu);
void *t = cosmo_leak_track(__dlrealloc(p, n));
if (t && t != p)
cosmo_leak_untrack(p);
pthread_mutex_unlock(&mu);
return t;
}
void *memalign(size_t a, size_t n) {
pthread_mutex_lock(&mu);
void *r = cosmo_leak_track(__dlmemalign(a, n));
pthread_mutex_unlock(&mu);
return r;
}
void free(void *p) {
pthread_mutex_lock(&mu);
__dlfree(cosmo_leak_untrack(p));
pthread_mutex_unlock(&mu);
}
// the c standard says this function can't be optimized away
void *identity(void *arg) {
return arg;
}
void *(*pIdentity)(void *) = identity;
int main(void) {
kprintf("let's demo the builtin memory leak detector\n");
// leak two allocations, one small, one large
pIdentity(malloc(1));
pIdentity(malloc(257));
// wait for any detached posix threads to exit
while (!pthread_orphan_np())
pthread_decimate_np();
// this runs atexit functions and thread/process finalizers
cosmo_leak_finalize();
// print information about memory leaks with backtraces
if (cosmo_leak_print(0) > 0)
_Exit(73);
}
+39
View File
@@ -0,0 +1,39 @@
#/*─────────────────────────────────────────────────────────────────╗
│ To the extent possible under law, Justine Tunney has waived │
│ all copyright and related or neighboring rights to this file, │
│ as it is written in the following disclaimers: │
│ • http://unlicense.org/ │
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
╚─────────────────────────────────────────────────────────────────*/
#include <cosmo.h>
#include <stdlib.h>
// zero-overhead memory leak detector demo
//
// $ make -j o//examples/memleak_zerocost
// $ o//examples/memleak_zerocost
// error: leaked 128 byte allocation at 0x7a1483eabf50
// error: leaked 16 byte allocation at 0x7a1483eacfe0
// error: leaked 1'008 byte allocation at 0x42671e0
// you forgot to call free 3 times
//
void NoOp(void *p) {
}
void (*pNoOp)(void *) = NoOp;
int main(int argc, char *argv[]) {
// run program, which leaks memory
pNoOp(malloc(10));
pNoOp(malloc(100));
pNoOp(malloc(1000));
// check for memory leaks. this has zero overhead, because it crawls
// the data structures used by cosmopolitan's malloc implementation.
// this is great thing to always use so you know when there's a leak
// and if you have trouble tracking down where it came from then you
// can swap this out with the code in examples/memleak_backtrace.c
CheckForMemoryLeaks();
}
-143
View File
@@ -1,143 +0,0 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
│ To the extent possible under law, Justine Tunney has waived │
│ all copyright and related or neighboring rights to this file, │
│ as it is written in the following disclaimers: │
│ • http://unlicense.org/ │
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
╚─────────────────────────────────────────────────────────────────*/
#endif
#include <cosmo.h>
#include <getopt.h>
#include <netdb.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
// clang-format off
/**
* @fileoverview netcat clone
*
* Implemented because BusyBox's netcat doesn't detect remote close and
* lingers in the CLOSE_WAIT wait possibly due to file descriptor leaks
*
* Here's an example usage:
*
* make -j8 o//examples/nc.com
* printf 'GET /\r\nHost: justine.lol\r\n\r\n' | o//examples/nc.com justine.lol 80
*
* Once upon time we called this command basically "telnet"
*/
#define ARRAYLEN(A) \
((sizeof(A) / sizeof(*(A))) / ((unsigned)!(sizeof(A) % sizeof(*(A)))))
int main(int argc, char *argv[]) {
ssize_t rc;
size_t i, got;
int opt, sock;
char buf[1500];
bool halfclose = true;
const char *host, *port;
struct addrinfo *ai = NULL;
struct linger linger = {true, 1};
struct pollfd fds[2] = {{-1, POLLIN}, {-1, POLLIN}};
struct addrinfo hint = {AI_NUMERICSERV, AF_INET, SOCK_STREAM, IPPROTO_TCP};
while ((opt = getopt(argc, argv, "hH")) != -1) {
switch (opt) {
case 'H':
halfclose = false;
break;
case 'h':
tinyprint(1, "Usage: ", argv[0], " [-hH] IP PORT\n", NULL);
exit(0);
default:
fprintf(stderr, "bad option %d\n", opt);
exit(1);
}
}
if (argc - optind != 2) {
fputs("missing args\n", stderr);
exit(1);
}
host = argv[optind + 0];
port = argv[optind + 1];
if ((rc = getaddrinfo(host, port, &hint, &ai))) {
tinyprint(2, host, ": ", gai_strerror(rc), "\n", NULL);
exit(1);
}
if ((sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) {
perror("socket");
exit(1);
}
if (setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)) == -1) {
perror("SO_LINGER");
exit(1);
}
if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
perror(host);
exit(1);
}
fds[0].fd = 0;
fds[1].fd = sock;
for (;;) {
fds[0].revents = 0;
fds[1].revents = 0;
if (poll(fds, ARRAYLEN(fds), -1) == -1) {
perror("poll");
exit(1);
}
if (fds[0].revents & (POLLIN | POLLERR | POLLHUP)) {
if ((rc = read(0, buf, 1400)) == -1) {
perror("read(stdin)");
exit(1);
}
if (!(got = rc)) {
if (halfclose) {
shutdown(sock, SHUT_WR);
}
fds[0].fd = -1;
}
for (i = 0; i < got; i += rc) {
if ((rc = write(sock, buf + i, got - i)) == -1) {
perror("write(sock)");
exit(1);
}
}
}
if (fds[1].revents & (POLLIN | POLLERR | POLLHUP)) {
if ((rc = read(sock, buf, 1500)) == -1) {
perror("read(sock)");
exit(1);
}
if (!(got = rc)) {
break;
}
for (i = 0; i < got; i += rc) {
if ((rc = write(1, buf + i, got - i)) == -1) {
perror("write(stdout)");
exit(1);
}
}
}
}
if (close(sock) == -1) {
perror("close");
exit(1);
}
freeaddrinfo(ai);
return 0;
}
+122 -134
View File
@@ -13,10 +13,10 @@
#include "dsp/tty/tty.h"
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/itimerval.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/winsize.h"
#include "libc/calls/termios.h"
#include "libc/cosmotime.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
@@ -36,20 +36,17 @@
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/itimer.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/consts/prio.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/w.h"
#include "libc/thread/thread.h"
#include "libc/time.h"
#include "libc/x/xasprintf.h"
#include "libc/x/xsigaction.h"
#include "libc/zip.h"
#include "third_party/getopt/getopt.internal.h"
#include "third_party/libcxx/__atomic/atomic.h"
#include "third_party/libcxx/vector"
#include "tool/viz/lib/knobs.h"
@@ -124,15 +121,6 @@ typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
static const struct itimerval kNesFps = {
{0, 1. / FPS * 1e6},
{0, 1. / FPS * 1e6},
};
struct Frame {
char *p, *w, *mem;
};
struct Action {
int code;
int wait;
@@ -148,15 +136,13 @@ struct ZipGames {
char** p;
};
static int frame_;
static size_t vtsize_;
static const struct timespec kNesFps = {0, 1. / FPS * 1e9};
static bool artifacts_;
static long tyn_, txn_;
static struct Frame vf_[2];
static const char* inputfn_;
static struct Status status_;
static volatile bool exited_;
static volatile bool timeout_;
static volatile bool resized_;
static struct CosmoAudio* ca_;
static struct TtyRgb* ttyrgb_;
@@ -165,7 +151,7 @@ static struct ZipGames zipgames_;
static struct Action arrow_, button_;
static struct SamplingSolution* ssy_;
static struct SamplingSolution* ssx_;
static unsigned char pixels_[3][DYN][DXN];
static unsigned char (*pixels_)[3][DYN][DXN];
static unsigned char palette_[3][64][512][3];
static int joy_current_[2], joy_next_[2], joypos_[2];
@@ -173,6 +159,11 @@ static int keyframes_ = 10;
static enum TtyBlocksSelection blocks_ = kTtyBlocksUnicode;
static enum TtyQuantizationAlgorithm quant_ = kTtyQuantXterm256;
static struct timespec deadline_;
static std::atomic<void*> pixels_ready_;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static int Clamp(int v) {
return MAX(0, MIN(255, v));
}
@@ -232,18 +223,8 @@ void InitPalette(void) {
}
}
static ssize_t Write(int fd, const void* p, size_t n) {
int rc;
sigset_t ss, oldss;
sigfillset(&ss);
sigprocmask(SIG_SETMASK, &ss, &oldss);
rc = write(fd, p, n);
sigprocmask(SIG_SETMASK, &oldss, 0);
return rc;
}
static void WriteString(const char* s) {
Write(STDOUT_FILENO, s, strlen(s));
write(STDOUT_FILENO, s, strlen(s));
}
void Exit(int rc) {
@@ -264,28 +245,10 @@ void OnCtrlC(void) {
exited_ = true;
}
void OnTimer(void) {
timeout_ = true;
}
void OnResize(void) {
resized_ = true;
}
void OnPiped(void) {
exited_ = true;
}
void OnSigChld(void) {
waitpid(-1, 0, WNOHANG);
cosmoaudio_close(ca_);
ca_ = 0;
}
void InitFrame(struct Frame* f) {
f->p = f->w = f->mem = (char*)realloc(f->mem, vtsize_);
}
long ChopAxis(long dn, long sn) {
while (HALF(sn) > dn) {
sn = HALF(sn);
@@ -308,11 +271,6 @@ void GetTermSize(void) {
G = (unsigned char*)realloc(G, tyn_ * txn_);
B = (unsigned char*)realloc(B, tyn_ * txn_);
ttyrgb_ = (struct TtyRgb*)realloc(ttyrgb_, tyn_ * txn_ * 4);
vtsize_ = ((tyn_ * txn_ * strlen("\e[48;2;255;48;2;255m▄")) +
(tyn_ * strlen("\e[0m\r\n")) + 128);
frame_ = 0;
InitFrame(&vf_[0]);
InitFrame(&vf_[1]);
WriteString("\e[0m\e[H\e[J");
resized_ = false;
}
@@ -320,11 +278,7 @@ void GetTermSize(void) {
void IoInit(void) {
GetTermSize();
xsigaction(SIGINT, (void*)OnCtrlC, 0, 0, NULL);
xsigaction(SIGPIPE, (void*)OnPiped, 0, 0, NULL);
xsigaction(SIGWINCH, (void*)OnResize, 0, 0, NULL);
xsigaction(SIGALRM, (void*)OnTimer, 0, 0, NULL);
xsigaction(SIGCHLD, (void*)OnSigChld, 0, 0, NULL);
setitimer(ITIMER_REAL, &kNesFps, NULL);
ttyhidecursor(STDOUT_FILENO);
ttyraw(kTtySigs);
ttyquantsetup(quant_, kTtyQuantRgb, blocks_);
@@ -461,57 +415,29 @@ ssize_t ReadKeyboard(void) {
return rc;
}
bool HasVideo(struct Frame* f) {
return f->w < f->p;
}
bool HasPendingVideo(void) {
return HasVideo(&vf_[0]) || HasVideo(&vf_[1]);
}
struct Frame* FlipFrameBuffer(void) {
frame_ = !frame_;
return &vf_[frame_];
}
void TransmitVideo(void) {
ssize_t rc;
struct Frame* f;
f = &vf_[frame_];
if (!HasVideo(f))
f = FlipFrameBuffer();
if ((rc = Write(STDOUT_FILENO, f->w, f->p - f->w)) != -1) {
f->w += rc;
} else if (errno == EAGAIN) {
// slow teletypewriter
} else if (errno == EPIPE) {
Exit(0);
}
}
void ScaleVideoFrameToTeletypewriter(void) {
void ScaleVideoFrameToTeletypewriter(unsigned char (*pixels)[3][DYN][DXN]) {
long y, x, yn, xn;
yn = DYN, xn = DXN;
while (HALF(yn) > tyn_ || HALF(xn) > txn_) {
if (HALF(xn) > txn_) {
Magikarp2xX(DYN, DXN, pixels_[0], yn, xn);
Magikarp2xX(DYN, DXN, pixels_[1], yn, xn);
Magikarp2xX(DYN, DXN, pixels_[2], yn, xn);
Magikarp2xX(DYN, DXN, (*pixels)[0], yn, xn);
Magikarp2xX(DYN, DXN, (*pixels)[1], yn, xn);
Magikarp2xX(DYN, DXN, (*pixels)[2], yn, xn);
xn = HALF(xn);
}
if (HALF(yn) > tyn_) {
Magikarp2xY(DYN, DXN, pixels_[0], yn, xn);
Magikarp2xY(DYN, DXN, pixels_[1], yn, xn);
Magikarp2xY(DYN, DXN, pixels_[2], yn, xn);
Magikarp2xY(DYN, DXN, (*pixels)[0], yn, xn);
Magikarp2xY(DYN, DXN, (*pixels)[1], yn, xn);
Magikarp2xY(DYN, DXN, (*pixels)[2], yn, xn);
yn = HALF(yn);
}
}
GyaradosUint8(tyn_, txn_, R, DYN, DXN, pixels_[0], tyn_, txn_, yn, xn, 0, 255,
ssy_, ssx_, true);
GyaradosUint8(tyn_, txn_, G, DYN, DXN, pixels_[1], tyn_, txn_, yn, xn, 0, 255,
ssy_, ssx_, true);
GyaradosUint8(tyn_, txn_, B, DYN, DXN, pixels_[2], tyn_, txn_, yn, xn, 0, 255,
ssy_, ssx_, true);
GyaradosUint8(tyn_, txn_, R, DYN, DXN, (*pixels)[0], tyn_, txn_, yn, xn, 0,
255, ssy_, ssx_, true);
GyaradosUint8(tyn_, txn_, G, DYN, DXN, (*pixels)[1], tyn_, txn_, yn, xn, 0,
255, ssy_, ssx_, true);
GyaradosUint8(tyn_, txn_, B, DYN, DXN, (*pixels)[2], tyn_, txn_, yn, xn, 0,
255, ssy_, ssx_, true);
for (y = 0; y < tyn_; ++y) {
for (x = 0; x < txn_; ++x) {
ttyrgb_[y * txn_ + x] =
@@ -528,55 +454,104 @@ void KeyCountdown(struct Action* a) {
}
}
void PollAndSynchronize(void) {
do {
if (ReadKeyboard() == -1) {
if (errno != EINTR)
Exit(1);
if (exited_)
Exit(0);
if (resized_)
GetTermSize();
}
} while (!timeout_);
TransmitVideo();
timeout_ = false;
KeyCountdown(&arrow_);
KeyCountdown(&button_);
joy_next_[0] = arrow_.code | button_.code;
joy_next_[1] = arrow_.code | button_.code;
}
void Raster(void) {
struct Frame* f;
void Raster(unsigned char (*pixels)[3][DYN][DXN]) {
struct TtyRgb bg = {0x12, 0x34, 0x56, 0};
struct TtyRgb fg = {0x12, 0x34, 0x56, 0};
ScaleVideoFrameToTeletypewriter();
f = &vf_[!frame_];
f->p = f->w = f->mem;
f->p = stpcpy(f->p, "\e[0m\e[H");
f->p = ttyraster(f->p, ttyrgb_, tyn_, txn_, bg, fg);
ScaleVideoFrameToTeletypewriter(pixels);
char* ansi = (char*)malloc((tyn_ * txn_ * strlen("\e[48;2;255;48;2;255m▄")) +
(tyn_ * strlen("\e[0m\r\n")) + 128);
char* p = ansi;
p = stpcpy(p, "\e[0m\e[H");
p = ttyraster(p, ttyrgb_, tyn_, txn_, bg, fg);
free(pixels);
if (status_.wait) {
status_.wait--;
f->p = stpcpy(f->p, "\e[0m\e[H");
f->p = stpcpy(f->p, status_.text);
p = stpcpy(p, "\e[0m\e[H");
p = stpcpy(p, status_.text);
}
size_t n = p - ansi;
ssize_t wrote;
for (size_t i = 0; i < n; i += wrote) {
if ((wrote = write(STDOUT_FILENO, ansi + i, n - i)) == -1) {
exited_ = true;
break;
}
}
free(ansi);
}
void* RasterThread(void* arg) {
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGINT);
sigaddset(&ss, SIGHUP);
sigaddset(&ss, SIGQUIT);
sigaddset(&ss, SIGTERM);
sigaddset(&ss, SIGPIPE);
sigprocmask(SIG_SETMASK, &ss, 0);
for (;;) {
unsigned char(*pixels)[3][DYN][DXN];
pthread_mutex_lock(&lock);
while (!(pixels = (unsigned char(*)[3][DYN][DXN])pixels_ready_.load()))
pthread_cond_wait(&cond, &lock);
pixels_ready_.store(0);
pthread_mutex_unlock(&lock);
if (resized_)
GetTermSize();
Raster(pixels);
}
PollAndSynchronize();
}
void FlushScanline(unsigned py) {
if (py == DYN - 1) {
if (!timeout_)
Raster();
timeout_ = false;
if (py != DYN - 1)
return;
pthread_mutex_lock(&lock);
if (!pixels_ready_) {
pixels_ready_.store(pixels_);
pixels_ = 0;
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&lock);
if (!pixels_)
pixels_ = (unsigned char(*)[3][DYN][DXN])malloc(3 * DYN * DXN);
if (exited_)
Exit(0);
do {
struct timespec now = timespec_mono();
struct timespec remain = timespec_subz(deadline_, now);
int remain_ms = timespec_tomillis(remain);
struct pollfd fds[] = {{STDIN_FILENO, POLLIN}};
int got = poll(fds, 1, remain_ms);
if (got == -1) {
if (errno == EINTR)
continue;
Exit(1);
}
if (got == 1) {
do {
if (ReadKeyboard() == -1) {
if (errno == EINTR)
continue;
Exit(1);
}
} while (0);
}
KeyCountdown(&arrow_);
KeyCountdown(&button_);
joy_next_[0] = arrow_.code | button_.code;
joy_next_[1] = arrow_.code | button_.code;
now = timespec_mono();
do
deadline_ = timespec_add(deadline_, kNesFps);
while (timespec_cmp(deadline_, now) <= 0);
} while (0);
}
static void PutPixel(unsigned px, unsigned py, unsigned pixel, int offset) {
static unsigned prev;
pixels_[0][py][px] = palette_[offset][prev % 64][pixel][2];
pixels_[1][py][px] = palette_[offset][prev % 64][pixel][1];
pixels_[2][py][px] = palette_[offset][prev % 64][pixel][0];
(*pixels_)[0][py][px] = palette_[offset][prev % 64][pixel][2];
(*pixels_)[1][py][px] = palette_[offset][prev % 64][pixel][1];
(*pixels_)[2][py][px] = palette_[offset][prev % 64][pixel][0];
prev = pixel;
}
@@ -1732,8 +1707,18 @@ int PlayGame(const char* romfile, const char* opt_tasfile) {
return 3;
}
// initialize screen
pixels_ = (unsigned char(*)[3][DYN][DXN])malloc(3 * DYN * DXN);
InitPalette();
// start raster thread
errno_t err;
pthread_t th;
if ((err = pthread_create(&th, 0, RasterThread, 0))) {
fprintf(stderr, "pthread_create: %s\n", strerror(err));
exit(1);
}
// open speaker
struct CosmoAudioOpenOptions cao = {};
cao.sizeofThis = sizeof(struct CosmoAudioOpenOptions);
@@ -1742,6 +1727,9 @@ int PlayGame(const char* romfile, const char* opt_tasfile) {
cao.channels = 1;
cosmoaudio_open(&ca_, &cao);
// initialize time
deadline_ = timespec_add(timespec_mono(), kNesFps);
// Read the ROM file header
u8 rom16count = fgetc(fp);
u8 vrom8count = fgetc(fp);
+1 -1
View File
@@ -11,5 +11,5 @@
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("%d\n", __get_cpu_count());
printf("%d\n", cosmo_cpu_count());
}
+1
View File
@@ -10,6 +10,7 @@
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/timeval.h"
#include "libc/cosmotime.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/magnumstrs.internal.h"
-1022
View File
File diff suppressed because it is too large. Load diff
+322
View File
@@ -0,0 +1,322 @@
#/*────────────────────────────────────────────────────────────────╗
┌┘ To the extent possible under law, Justine Tunney has waived │
│ all copyright and related or neighboring rights to this file, │
│ as it is written in the following disclaimers: │
│ • http://unlicense.org/ │
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
╚─────────────────────────────────────────────────────────────────*/
#include <ctype.h>
#include <signal.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
/**
* @fileoverview cosmopolitan flash cards viewer
*/
struct Card {
char* qa[2];
};
atomic_int g_done;
void onsig(int sig) {
g_done = 1;
}
void* xmalloc(int n) {
void* p;
if ((p = malloc(n)))
return p;
perror("malloc");
exit(1);
}
void* xrealloc(void* p, int n) {
if ((p = realloc(p, n)))
return p;
perror("realloc");
exit(1);
}
char* xstrcat(const char* a, const char* b) {
char* p;
size_t n, m;
n = strlen(a);
m = strlen(b);
p = xmalloc(n + m + 1);
memcpy(p, a, n);
memcpy(p + n, b, m + 1);
return p;
}
void shuffle(struct Card* a, int n) {
while (n > 1) {
int i = rand() % n--;
struct Card t = a[i];
a[i] = a[n];
a[n] = t;
}
}
char* trim(char* s) {
int i;
if (s) {
while (isspace(*s))
++s;
for (i = strlen(s); i--;) {
if (isspace(s[i])) {
s[i] = 0;
} else {
break;
}
}
}
return s;
}
char* readline(FILE* f) {
for (;;) {
char* line = trim(fgetln(f, 0));
if (!line)
return 0;
if (*line != '#')
if (*line)
return line;
}
}
char* fill(const char* text, int max_line_width, int* out_line_count) {
int text_len = strlen(text);
char* result = xmalloc(text_len * 2 + 1);
int result_pos = 0;
int line_start = 0;
int line_count = 1;
int i = 0;
while (i < text_len && isspace(text[i]))
i++;
while (i < text_len) {
int word_end = i;
while (word_end < text_len && !isspace(text[word_end]))
word_end++;
int word_length = word_end - i;
if ((result_pos - line_start) + (result_pos > line_start ? 1 : 0) +
word_length >
max_line_width) {
if (result_pos > line_start) {
++line_count;
result[result_pos++] = '\n';
line_start = result_pos;
}
} else if (result_pos > line_start) {
result[result_pos++] = ' ';
}
memcpy(result + result_pos, text + i, word_length);
result_pos += word_length;
i = word_end;
while (i < text_len && isspace(text[i]))
i++;
}
result[result_pos] = '\0';
result = xrealloc(result, result_pos + 1);
if (out_line_count)
*out_line_count = line_count;
return result;
}
void show(const char* text, int i, int n) {
// get pseudoteletypewriter dimensions
struct winsize ws = {80, 25};
tcgetwinsize(1, &ws);
int width = ws.ws_col;
if (width > (int)(ws.ws_col * .9))
width = ws.ws_col * .9;
if (width > 80)
width = 80;
width &= -2;
// clear display
printf("\033[H\033[J");
// display flash card text in middle of display
char buf[32];
int line_count;
char* lines = fill(text, width, &line_count);
sprintf(buf, "%d/%d\r\n\r\n", i + 1, n);
line_count += 2;
char* extra = xstrcat(buf, lines);
free(lines);
char* tokens = extra;
for (int j = 0;; ++j) {
char* line = strtok(tokens, "\n");
tokens = 0;
if (!line)
break;
printf("\033[%d;%dH%s", ws.ws_row / 2 - line_count / 2 + j + 1,
ws.ws_col / 2 - strlen(line) / 2 + 1, line);
}
free(extra);
fflush(stdout);
}
void usage(FILE* f, const char* prog) {
fprintf(f,
"usage: %s FILE\n"
"\n"
"here's an example of what your file should look like:\n"
"\n"
" # cosmopolitan flash cards\n"
" # california dmv drivers test\n"
" \n"
" which of the following point totals could result in "
"your license being suspended by the dmv?\n"
" 4 points in 12 months (middle)\n"
" \n"
" at 55 mph under good conditions a passenger vehicle can stop "
"within\n"
" 300 feet (not 200, not 400, middle)\n"
" \n"
" two sets of solid double yellow lines spaced two or more feet "
"apart indicate\n"
" a BARRIER (do not cross unless there's an opening)\n"
"\n"
"more specifically, empty lines are ignored, lines starting with\n"
"a hash are ignored, then an even number of lines must remain,\n"
"where each two lines is a card, holding question and answer.\n",
prog);
}
int main(int argc, char* argv[]) {
// show help
if (argc != 2) {
usage(stderr, argv[0]);
return 1;
}
if (!strcmp(argv[1], "-?") || //
!strcmp(argv[1], "-h") || //
!strcmp(argv[1], "--help")) {
usage(stdout, argv[0]);
return 0;
}
// teletypewriter is required
if (!isatty(0) || !isatty(1)) {
perror("isatty");
return 2;
}
// load cards
FILE* f = fopen(argv[1], "r");
if (!f) {
perror(argv[1]);
return 3;
}
int count = 0;
struct Card* cards = 0;
for (;;) {
struct Card card;
if (!(card.qa[0] = readline(f)))
break;
card.qa[0] = strdup(card.qa[0]);
if (!(card.qa[1] = readline(f))) {
fprintf(stderr, "%s: flash card file has odd number of lines\n", argv[1]);
exit(1);
}
card.qa[1] = strdup(card.qa[1]);
cards = xrealloc(cards, (count + 1) * sizeof(struct Card));
cards[count++] = card;
}
fclose(f);
// randomize
srand(time(0));
shuffle(cards, count);
// catch ctrl-c
struct sigaction sa;
sa.sa_flags = 0;
sa.sa_handler = onsig;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, 0);
// enter raw mode
struct termios ot;
tcgetattr(1, &ot);
struct termios nt = ot;
cfmakeraw(&nt);
nt.c_lflag |= ISIG;
tcsetattr(1, TCSANOW, &nt);
printf("\033[?25l");
// show flash cards
int i = 0;
while (!g_done) {
show(cards[i / 2].qa[i % 2], i / 2, count);
// press any key
char b[8] = {0};
read(0, b, sizeof(b));
// q quits
if (b[0] == 'q')
break;
// b or ctrl-b goes backward
if (b[0] == 'b' || //
b[0] == ('B' ^ 0100)) {
if (--i < 0)
i = count * 2 - 1;
i &= -2;
continue;
}
// p or ctrl-p goes backward
if (b[0] == 'p' || //
b[0] == ('P' ^ 0100)) {
if (--i < 0)
i = count * 2 - 1;
i &= -2;
continue;
}
// up arrow goes backward
if (b[0] == 033 && //
b[1] == '[' && //
b[2] == 'A') {
if (--i < 0)
i = count * 2 - 1;
i &= -2;
continue;
}
// left arrow goes backward
if (b[0] == 033 && //
b[1] == '[' && //
b[2] == 'D') {
if (--i < 0)
i = count * 2 - 1;
i &= -2;
continue;
}
// only advance
if (++i == count * 2)
i = 0;
}
// free memory
for (int i = 0; i < count; ++i)
for (int j = 0; j < 2; ++j)
free(cards[i].qa[j]);
free(cards);
// cleanup terminal and show cursor
tcsetattr(1, TCSANOW, &ot);
printf("\033[?25h");
printf("\n");
}
+4 -2
View File
@@ -13,7 +13,9 @@
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/timespec.h"
#include "libc/cosmotime.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/appendresourcereport.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/append.h"
@@ -27,7 +29,7 @@ int main(int argc, char *argv[]) {
prog = "rusage";
if (argc < 2) {
tinyprint(2, prog, ": missing command\n", NULL);
fprintf(stderr, "%s: missing command\n", prog);
exit(1);
}
@@ -42,7 +44,7 @@ int main(int argc, char *argv[]) {
struct timespec started = timespec_real();
// launch subprocess
int child = fork();
int child = vfork();
if (child == -1) {
perror(prog);
exit(1);
+15 -5
View File
@@ -49,10 +49,10 @@
* @fileoverview Terminal Screencast Recorder / Player, e.g.
*
* make o//examples/script.com
* o//examples/script.com -r
* o//examples/script.com -w80 -h24 -r recording.tty
* # type stuff..
* # CTRL-D
* o//examples/script.com -p typescript
* o//examples/script.com -p recording.tty
*
* @note works on Linux, OpenBSD, NetBSD, FreeBSD, MacOS
* @see https://asciinema.org/
@@ -103,9 +103,9 @@ main(int argc, char *argv[])
fd_set rfd;
int fm_fd;
int aflg, Fflg, kflg, pflg, ch, k, n;
int flushtime, readstdin;
int flushtime, readstdin, width, height;
aflg = Fflg = kflg = pflg = 0;
aflg = Fflg = kflg = pflg = height = width = 0;
usesleep = 1;
rawout = 0;
flushtime = 30;
@@ -115,7 +115,7 @@ main(int argc, char *argv[])
(void)fm_fd;
while ((ch = getopt(argc, argv, "adeFfkpqrt:")) != -1)
while ((ch = getopt(argc, argv, "adeFfkpqrt:w:h:")) != -1)
switch(ch) {
case 'a':
aflg = 1;
@@ -145,6 +145,12 @@ main(int argc, char *argv[])
if (flushtime < 0)
err(1, "invalid flush time %d", flushtime);
break;
case 'w':
width = atoi(optarg);
break;
case 'h':
height = atoi(optarg);
break;
case '?':
default:
usage();
@@ -172,6 +178,10 @@ main(int argc, char *argv[])
if (openpty(&master, &slave, NULL, NULL, NULL) == -1)
err(1, "openpty");
} else {
if (width)
win.ws_col = width;
if (height)
win.ws_row = height;
if (openpty(&master, &slave, NULL, &tt, &win) == -1)
err(1, "openpty");
ttyflg = 1;
+58
View File
@@ -0,0 +1,58 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
│ To the extent possible under law, Justine Tunney has waived │
│ all copyright and related or neighboring rights to this file, │
│ as it is written in the following disclaimers: │
│ • http://unlicense.org/ │
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
╚─────────────────────────────────────────────────────────────────*/
#endif
#include <pthread.h>
#include <semaphore.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#define PHILOSOPHERS 9 // number of hungry philosophers
#define THOUGHTS 3 // number of breaks for thinking
#define SEATS 3 // number of seats at the tables
atomic_int occupied;
sem_t sem;
void *philosopher(void *arg) {
int tid = (long)arg;
printf("philosopher#%d arrives\n", tid);
for (int i = 0; i < THOUGHTS; ++i) {
sem_wait(&sem);
printf("philosopher#%d sat down to eat (%d occupied)\n", tid, ++occupied);
usleep(rand() % 1000000);
printf("philosopher#%d gets up to think (%d occupied)\n", tid, --occupied);
sem_post(&sem);
usleep(rand() % 1000000);
}
printf("philosopher#%d leaves\n", tid);
return 0;
}
int main(int argc, char *argv[]) {
printf("inviting %d philosophers to %d person dinner\n", PHILOSOPHERS, SEATS);
sem_init(&sem, 0, SEATS);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
for (long i = 0; i < PHILOSOPHERS; ++i) {
pthread_t th;
pthread_create(&th, &attr, philosopher, (void *)i);
}
pthread_exit(0);
}
+3 -1
View File
@@ -9,7 +9,9 @@
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>
/**
* @fileoverview swapcontext() and makecontext() example
@@ -20,7 +22,7 @@ static ucontext_t uctx_main;
static ucontext_t uctx_func1;
static ucontext_t uctx_func2;
#define say(s) write(1, s, strlen(s))
#define say(s) (void)!write(1, s, strlen(s))
#define handle_error(msg) \
do { \
perror(msg); \
+17 -3
View File
@@ -7,9 +7,13 @@
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
╚─────────────────────────────────────────────────────────────────*/
#endif
#include "libc/dce.h"
#include "libc/intrin/maps.h"
#include "libc/mem/alg.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/winargs.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/x/xasprintf.h"
@@ -67,8 +71,18 @@ int main(int argc, char *argv[]) {
Append((uintptr_t)&__auxv[i + 1],
xasprintf("&auxv[%d] = %#lx", i + 1, __auxv[i + 1]));
}
qsort(things.p, things.n, sizeof(*things.p), Compare);
for (int i = 0; i < things.n; ++i) {
printf("%012lx %s\n", things.p[i].i, things.p[i].s);
if (!IsWindows()) {
struct AddrSize stak = __get_main_stack();
Append((intptr_t)stak.addr + stak.size, "top of stack");
Append((intptr_t)stak.addr, "bottom of stack");
} else {
#ifdef __x86_64__
Append(GetStaticStackAddr(0) + GetStaticStackSize(), "top of stack");
Append(GetStaticStackAddr(0) + GetGuardSize(), "bottom of stack");
Append(GetStaticStackAddr(0), "bottom of guard region");
#endif
}
qsort(things.p, things.n, sizeof(*things.p), Compare);
for (int i = 0; i < things.n; ++i)
printf("%012lx %s\n", things.p[i].i, things.p[i].s);
}
+13 -10
View File
@@ -12,6 +12,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <time.h>
/**
@@ -62,7 +63,6 @@ const char *DescribeFileType(unsigned mode) {
void PrintFileMetadata(const char *pathname, struct stat *st) {
int fd;
printf("\n%s:", pathname);
if (numeric) {
fd = atoi(pathname);
if (fstat(fd, st)) {
@@ -70,15 +70,16 @@ void PrintFileMetadata(const char *pathname, struct stat *st) {
exit(1);
}
} else {
if (stat(pathname, st)) {
if (lstat(pathname, st)) {
perror(pathname);
exit(1);
}
}
printf("\n"
"%-32s%,ld\n"
"%-32s%,ld\n"
"%-32s%#lx\n"
"%s:\n"
"%-32s%,ld\n" // bytes in file
"%-32s%,ld\n" // physical bytes
"%-32s%u,%u\n" // device id
"%-32s%#lx\n"
"%-32s%ld\n"
"%-32s%#o (%s)\n"
@@ -92,11 +93,13 @@ void PrintFileMetadata(const char *pathname, struct stat *st) {
"%-32s%s\n"
"%-32s%s\n"
"%-32s%s\n",
"bytes in file:", st->st_size, "physical bytes:", st->st_blocks * 512,
"device id w/ file:", st->st_dev, "inode:", st->st_ino,
"hard link count:", st->st_nlink, "mode / permissions:", st->st_mode,
DescribeFileType(st->st_mode), "owner id:", st->st_uid,
"group id:", st->st_gid, "flags:", st->st_flags, "gen:", st->st_gen,
pathname, "bytes in file:", st->st_size,
"physical bytes:", st->st_blocks * 512,
"device id w/ file:", major(st->st_dev), minor(st->st_dev),
"inode:", st->st_ino, "hard link count:", st->st_nlink,
"mode / permissions:", st->st_mode, DescribeFileType(st->st_mode),
"owner id:", st->st_uid, "group id:", st->st_gid,
"flags:", st->st_flags, "gen:", st->st_gen,
"device id (if special):", st->st_rdev, "block size:", st->st_blksize,
"access time:", gc(xiso8601(st->st_atim)),
"modified time:", gc(xiso8601(st->st_mtim)),
+17
View File
@@ -0,0 +1,17 @@
#include <pthread.h>
#include <stdio.h>
// how to spawn a thread
void *my_thread(void *arg) {
printf("my_thread(%p) is running\n", arg);
return (void *)0x456L;
}
int main(int argc, char *argv[]) {
void *res;
pthread_t th;
pthread_create(&th, 0, my_thread, (void *)0x123L);
pthread_join(th, &res);
printf("my_thread() returned %p\n", res);
}
+3
View File
@@ -45,4 +45,7 @@ int main(int argc, char *argv[]) {
return 0;
}
#else
int main(int argc, char *argv[]) {
}
#endif /* __x86_64__ */
+3
View File
@@ -61,4 +61,7 @@ int main(int argc, char *argv[]) {
}
}
#else
int main(int argc, char *argv[]) {
}
#endif /* __x86_64__ */
+3
View File
@@ -43,4 +43,7 @@ int main(int argc, char *argv[]) {
;
}
#else
int main(int argc, char *argv[]) {
}
#endif /* __x86_64__ */
+22
View File
@@ -0,0 +1,22 @@
#/*─────────────────────────────────────────────────────────────────╗
│ To the extent possible under law, Justine Tunney has waived │
│ all copyright and related or neighboring rights to this file, │
│ as it is written in the following disclaimers: │
│ • http://unlicense.org/ │
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
╚─────────────────────────────────────────────────────────────────*/
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
void onsig(int sig) {
fprintf(stderr, "got %s\n", strsignal(sig));
}
int main(int argc, char *argv[]) {
fprintf(stderr, "my pid is %d\n", getpid());
for (int sig = 1; sig <= NSIG; ++sig)
signal(sig, onsig);
pause();
}
+10 -1
View File
@@ -13,12 +13,17 @@ libc/isystem/arm_fp16.h \
libc/isystem/arm_neon.h \
libc/isystem/arpa/inet.h \
libc/isystem/arpa/nameser.h \
libc/isystem/arpa/telnet.h \
libc/isystem/assert.h \
libc/isystem/bsd/readpassphrase.h \
libc/isystem/bsd/stdlib.h \
libc/isystem/bsd/string.h \
libc/isystem/byteswap.h \
libc/isystem/clzerointrin.h \
libc/isystem/complex.h \
libc/isystem/cosmo.h \
libc/isystem/cosmoaudio.h \
libc/isystem/cosmotime.h \
libc/isystem/cpio.h \
libc/isystem/cpuid.h \
libc/isystem/crypt.h \
@@ -49,9 +54,12 @@ libc/isystem/langinfo.h \
libc/isystem/libgen.h \
libc/isystem/limits.h \
libc/isystem/link.h \
libc/isystem/linux/f2fs.h \
libc/isystem/linux/futex.h \
libc/isystem/linux/ioctl.h \
libc/isystem/linux/limits.h \
libc/isystem/linux/param.h \
libc/isystem/linux/rseq.h \
libc/isystem/linux/types.h \
libc/isystem/locale.h \
libc/isystem/malloc.h \
@@ -68,6 +76,7 @@ libc/isystem/net/if.h \
libc/isystem/net/if_arp.h \
libc/isystem/netdb.h \
libc/isystem/netinet/in.h \
libc/isystem/netinet/in_systm.h \
libc/isystem/netinet/ip.h \
libc/isystem/netinet/tcp.h \
libc/isystem/netinet/udp.h \
@@ -94,6 +103,7 @@ libc/isystem/popcntintrin.h \
libc/isystem/pthread.h \
libc/isystem/pty.h \
libc/isystem/pwd.h \
libc/isystem/readpassphrase.h \
libc/isystem/regex.h \
libc/isystem/resolv.h \
libc/isystem/sched.h \
@@ -135,7 +145,6 @@ libc/isystem/sys/param.h \
libc/isystem/sys/poll.h \
libc/isystem/sys/prctl.h \
libc/isystem/sys/procfs.h \
libc/isystem/sys/ptrace.h \
libc/isystem/sys/random.h \
libc/isystem/sys/reboot.h \
libc/isystem/sys/resource.h \
+2 -1
View File
@@ -23,7 +23,8 @@ void unassert(const char *, const char *, int) libcesque;
#define assert(x) ((void)((x) || (__assert_fail(#x, __FILE__, __LINE__), 0)))
#endif
#if __STDC_VERSION__ >= 201112L && !defined(__cplusplus)
#if !defined(__cplusplus) && defined(__STDC_VERSION__) && \
__STDC_VERSION__ >= 201112L && __STDC_VERSION__ <= 201710L
#undef static_assert
#define static_assert _Static_assert
#endif
+15
View File
@@ -0,0 +1,15 @@
#ifndef COSMOPOLITAN_LIBC_BSDSTDLIB_H_
#define COSMOPOLITAN_LIBC_BSDSTDLIB_H_
COSMOPOLITAN_C_START_
uint32_t arc4random(void);
void arc4random_buf(void *, size_t);
uint32_t arc4random_uniform(uint32_t);
void *reallocarray(void *, size_t, size_t);
long long strtonum(const char *, long long, long long, const char **);
int heapsort(void *, size_t, size_t, int (*)(const void *, const void *));
int mergesort(void *, size_t, size_t, int (*)(const void *, const void *));
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_BSDSTDLIB_H_ */
+7 -8
View File
@@ -50,6 +50,7 @@ LIBC_CALLS_A_DIRECTDEPS = \
LIBC_NT_PSAPI \
LIBC_NT_REALTIME \
LIBC_NT_SYNCHRONIZATION \
LIBC_NT_WINMM \
LIBC_NT_WS2_32 \
LIBC_STR \
LIBC_SYSV \
@@ -111,7 +112,7 @@ o//libc/calls/prctl.o: \
# we always want -Os because:
# it's early runtime mandatory and quite huge without it
o//libc/calls/getcwd.greg.o \
o//libc/calls/getcwd.o \
o//libc/calls/statfs2cosmo.o: private \
CFLAGS += \
-Os
@@ -174,7 +175,6 @@ o//libc/calls/gettimeofday.o \
o//libc/calls/ioctl.o \
o//libc/calls/lutimes.o \
o//libc/calls/metaflock.o \
o//libc/calls/ntaccesscheck.o \
o//libc/calls/ntspawn.o \
o//libc/calls/open-nt.o \
o//libc/calls/pledge-linux.o \
@@ -215,13 +215,12 @@ o//libc/calls/writev.o: private \
COPTS += \
-mgeneral-regs-only
# it's too early to use memmove() due to __vex and WinMain
o/$(MODE)/libc/calls/mkunixpath.o: private \
CFLAGS += \
-ffreestanding
# these assembly files are safe to build on aarch64
o/$(MODE)/libc/calls/getcontext.o: libc/calls/getcontext.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/libc/calls/swapcontext.o: libc/calls/swapcontext.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/libc/calls/tailcontext.o: libc/calls/tailcontext.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/libc/calls/stackjump.o: libc/calls/stackjump.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
+42 -6
View File
@@ -17,10 +17,20 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/sigset.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
alignas(64) static struct {
pthread_mutex_t lock;
int phase;
} __abort = {
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
};
/**
* Terminates program abnormally.
@@ -33,10 +43,36 @@
* @noreturn
*/
wontreturn void abort(void) {
sigset_t m = 1ull << (SIGABRT - 1);
sigprocmask(SIG_UNBLOCK, &m, 0);
raise(SIGABRT);
signal(SIGABRT, SIG_DFL);
raise(SIGABRT);
_Exit(128 + SIGABRT);
sigset_t mask;
if (!__spawned)
pthread_mutex_lock(&__abort.lock);
switch (__abort.phase) {
case 0:
mask = 1ull << (SIGABRT - 1);
sigprocmask(SIG_UNBLOCK, &mask, 0);
__abort.phase = 1;
// fallthrough
case 1:
__abort.phase = 0;
if (!__spawned)
pthread_mutex_unlock(&__abort.lock);
raise(SIGABRT);
if (!__spawned)
pthread_mutex_lock(&__abort.lock);
__abort.phase = 2;
// fallthrough
case 2:
__abort.phase = 3;
signal(SIGABRT, SIG_DFL);
// fallthrough
case 3:
__abort.phase = 4;
raise(SIGABRT);
// fallthrough
case 4:
__abort.phase = 5;
_Exit(128 + SIGABRT);
}
for (;;)
notpossible;
}
+4 -2
View File
@@ -20,12 +20,14 @@
#include "libc/sysv/consts/at.h"
/**
* Checks if effective user can access path in particular ways.
* Checks if file can be accessed in particular way.
*
* This is equivalent to saying:
* This function is equivalent to:
*
* faccessat(AT_FDCWD, path, mode, 0);
*
* Please see faccessat() for further documentation.
*
* @param path is a filename or directory
* @param mode can be R_OK, W_OK, X_OK, F_OK
* @return 0 if ok, or -1 w/ errno
+330
View File
@@ -0,0 +1,330 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright (c) 1996, David Mazieres <dm@uun.org> │
│ Copyright (c) 2008, Damien Miller <djm@openbsd.org> │
│ Copyright (c) 2013, Markus Friedl <markus@openbsd.org> │
│ Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org> │
│ Copyright (c) Robert Clausecker <fuz@FreeBSD.org> │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/state.internal.h"
#include "libc/calls/typedef/u.h"
#include "libc/cosmo.h"
#include "libc/stdio/rand.h"
#include "libc/str/str.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
// clang-format off
#define FAIR_SHARE 1024
#define USE_CURRENT_CPU 1
#define USE_THREAD_COUNT 1
__notice(arc4random_notice, "arc4random (ISC)\n\
Copyright (c) 1996, David Mazieres <dm@uun.org>\n\
Copyright (c) 2008, Damien Miller <djm@openbsd.org>\n\
Copyright (c) 2013, Markus Friedl <markus@openbsd.org>\n\
Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>\n\
Copyright (c) 2024, Robert Clausecker <fuz@FreeBSD.org>\n\
Copyright (c) 2025, Justine Tunney <jtunney@gmail.com>\n\
Based upon D. J. Bernstein's public domain ChaCha code\n\
Based upon Daniel Lemire's arc4random_uniform research");
#define KEYSTREAM_ONLY
#include "libc/str/chacha.inc"
#define minimum(a, b) ((a) < (b) ? (a) : (b))
#define KEYSZ 32
#define IVSZ 8
#define BLOCKSZ 64
#define RSBUFSZ (16*BLOCKSZ)
#define REKEY_BASE (1024*1024) /* NB. should be a power of 2 */
/* This is zero'd out in forked children. */
struct _rs {
size_t rs_have; /* valid bytes at end of rs_buf */
size_t rs_count; /* bytes till reseed */
};
/* Optional to preserve in forked children. */
struct _rsx {
chacha_ctx rs_chacha; /* chacha context for random keystream */
u_char rs_buf[RSBUFSZ]; /* keystream blocks */
};
struct _rng {
alignas(64) pthread_mutex_t mtx;
bool once;
struct _rs rs;
struct _rsx rsx;
};
static struct _rng _rs_state[COSMO_SHARDS];
void
__arc4random_fork_prepare(void)
{
if (__isthreaded < 2)
return;
for (int i = 0; i < COSMO_SHARDS; ++i)
if (_rs_state[i].once)
pthread_mutex_lock(&_rs_state[i].mtx);
}
void
__arc4random_fork_parent(void)
{
if (__isthreaded < 2)
return;
for (int i = COSMO_SHARDS; i--;)
if (_rs_state[i].once)
pthread_mutex_unlock(&_rs_state[i].mtx);
}
void
__arc4random_fork_child(void)
{
for (int i = 0; i < COSMO_SHARDS; ++i) {
if (!_rs_state[i].once)
continue;
pthread_mutex_wipe_np(&_rs_state[i].mtx);
memset(&_rs_state[i].rs, 0, sizeof(_rs_state[i].rs));
}
}
static inline void
_rs_init(struct _rng *rng, u_char *buf, size_t n)
{
if (n < KEYSZ + IVSZ)
return;
chacha_keysetup(&rng->rsx.rs_chacha, buf, KEYSZ * 8);
chacha_ivsetup(&rng->rsx.rs_chacha, buf + KEYSZ);
rng->once = true;
}
static inline void
_rs_rekey(struct _rng *rng, u_char *dat, size_t datlen)
{
#ifndef KEYSTREAM_ONLY
memset(rng->rsx.rs_buf, 0, sizeof(rng->rsx.rs_buf));
#endif
/* fill rs_buf with the keystream */
chacha_encrypt_bytes(&rng->rsx.rs_chacha, rng->rsx.rs_buf,
rng->rsx.rs_buf, sizeof(rng->rsx.rs_buf));
/* mix in optional user provided data */
if (dat) {
size_t i, m;
m = minimum(datlen, KEYSZ + IVSZ);
for (i = 0; i < m; i++)
rng->rsx.rs_buf[i] ^= dat[i];
}
/* immediately reinit for backtracking resistance */
_rs_init(rng, rng->rsx.rs_buf, KEYSZ + IVSZ);
memset(rng->rsx.rs_buf, 0, KEYSZ + IVSZ);
rng->rs.rs_have = sizeof(rng->rsx.rs_buf) - KEYSZ - IVSZ;
}
static void
_rs_stir(struct _rng *rng)
{
u_char rnd[KEYSZ + IVSZ];
uint32_t rekey_fuzz = 0;
if (getentropy(rnd, sizeof rnd) == -1)
notpossible;
if (!rng->once) {
_rs_init(rng, rnd, sizeof(rnd));
} else {
_rs_rekey(rng, rnd, sizeof(rnd));
}
explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
/* invalidate rs_buf */
rng->rs.rs_have = 0;
memset(rng->rsx.rs_buf, 0, sizeof(rng->rsx.rs_buf));
/* rekey interval should not be predictable */
chacha_encrypt_bytes(&rng->rsx.rs_chacha, (uint8_t *)&rekey_fuzz,
(uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz));
rng->rs.rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE);
}
static inline void
_rs_stir_if_needed(struct _rng *rng, size_t len)
{
if (!rng->once || rng->rs.rs_count <= len)
_rs_stir(rng);
if (rng->rs.rs_count <= len)
rng->rs.rs_count = 0;
else
rng->rs.rs_count -= len;
}
static inline void
_rs_random_buf(struct _rng *rng, void *_buf, size_t n)
{
u_char *buf = (u_char *)_buf;
u_char *keystream;
size_t m;
_rs_stir_if_needed(rng, n);
while (n > 0) {
if (rng->rs.rs_have > 0) {
m = minimum(n, rng->rs.rs_have);
keystream = rng->rsx.rs_buf + sizeof(rng->rsx.rs_buf)
- rng->rs.rs_have;
memcpy(buf, keystream, m);
memset(keystream, 0, m);
buf += m;
n -= m;
rng->rs.rs_have -= m;
}
if (rng->rs.rs_have == 0)
_rs_rekey(rng, NULL, 0);
}
}
/**
* Fills buffer with random data.
*
* This is the best random number generator provided by cosmo libc.
*
* This RNG is simple. Unlike getrandom() which can fail in numerous
* ways, this API always succeeds and returns the full number of bytes.
*
* This RNG is cryptographic grade. It uses D.J. Bernstein's ChaCha
* algorithm. The code for this implementation was originally copied
* from the OpenBSD and FreeBSD codebases.
*
* This RNG is fast. arc4random runs in userspace and occasionally
* reseeds itself using getentropy(). This gives you a 10x performance
* boost by avoiding costly system calls.
*
* This RNG is scalable. The Cosmopolitan Libc authors have modified the
* original OpenBSD source code so it doesn't use a GIL. We instead load
* balance threads across a fixed number of arc4random states which are
* indexed based on the current cpu if possible otherwise the thread id.
* This way threads needn't wait in sequence and can work concurrently.
* The only other fast cryptographic userspace PRNG we've seen that can
* do this is Microsoft's ProcessPrng() in Windows 10+.
*
* This RNG makes the single-threaded case go fast. Similar to FreeBSD
* our integration with the C library enables us to know when it's safe
* to avoid mutexes entirely. If your app never creates a thread, then
* arc4random() will never need to pay the cost of locks.
*
* This RNG is fork() safe. Naive random number generators oftentimes
* have an issue where forked child processes are likely to generate the
* same numbers as the parent. Our libc system call wrapper ensures the
* RNG state is appropriately wiped. However if you talk directly to the
* kernel, using functions like sys_fork(), you're on your own. vfork()
* should be safe, since the child process is actually just a thread.
*/
void
arc4random_buf(void *buf, size_t n)
{
size_t i, w;
struct _rng *rng;
FORBIDDEN_IN_POSIX_SPAWN;
/* load balance threads across fixed number of shards */
rng = &_rs_state[cosmo_shard()];
/* avoid one thread hogging shard */
for (i = 0; i < n; i += w) {
w = n - i;
if (w > FAIR_SHARE)
w = FAIR_SHARE;
if (__isthreaded >= 2)
pthread_mutex_lock(&rng->mtx);
_rs_random_buf(rng, (char *)buf + i, w);
if (__isthreaded >= 2)
pthread_mutex_unlock(&rng->mtx);
}
}
/**
* Returns single random 32-bit value.
*
* The arc4random() function returns pseudo-random numbers in the range
* of 0 to (2**32)−1, and has twice the range of rand(3) and random(3).
*
* This function is a wrapper around arc4random_buf() which can be used
* to obtain an arbitrary number of bytes.
*
* Be careful about using the euclidean remainder operator (`%`) on the
* result of this function. It's only safe to do that if you keep the
* result unsigned and the denominator is a two power. What you almost
* certainly want instead is arc4random_uniform() to do safe modulus.
*/
uint32_t
arc4random(void)
{
uint32_t val;
arc4random_buf(&val, sizeof(val));
return val;
}
/**
* Returns random number less than `upper_bound`.
*
* This is like arc4random() except the arc4random_uniform() function
* will calculate a uniformly distributed random number that's less than
* `upper_bound` while avoiding "modulo bias".
*
* This implementation uses the optimized technique discovered by Daniel
* Lemire in his paper "Fast Random Integer Generation in an Interval",
* Association for Computing Machinery, ACM Trans. Model. Comput.
* Simul., no. 1, vol. 29, pp. 1--12, New York, NY, USA, January 2019.
* Available: https://r-libre.teluq.ca/1437/1/rangedrng.pdf
*/
uint32_t
arc4random_uniform(uint32_t upper_bound)
{
uint64_t product;
/*
* The paper uses these variable names:
*
* L -- log2(UINT32_MAX+1)
* s -- upper_bound
* x -- arc4random() return value
* m -- product
* l -- (uint32_t)product
* t -- threshold
*/
if (upper_bound <= 1)
return 0;
product = upper_bound * (uint64_t)arc4random();
if ((uint32_t)product < upper_bound) {
uint32_t threshold;
/* threshold = (2**32 - upper_bound) % upper_bound */
threshold = -upper_bound % upper_bound;
while ((uint32_t)product < threshold)
product = upper_bound * (uint64_t)arc4random();
}
return product >> 32;
}
+4 -2
View File
@@ -22,6 +22,7 @@
#include "libc/errno.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/describebacktrace.h"
#include "libc/log/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.h"
@@ -32,8 +33,9 @@ void __assert_fail(const char *expr, const char *file, int line) {
char ibuf[12];
sigset_t m = __sig_block();
FormatInt32(ibuf, line);
tinyprint(2, file, ":", ibuf, ": \e[31;1massert(", expr,
") failed\e[0m (cosmoaddr2line ", FindDebugBinary(), " ",
tinyprint(2, file, ":", ibuf, ": ", __nocolor ? "" : "\e[31;1m", "assert(",
expr, ") failed", __nocolor ? "" : "\e[0m", " (cosmoaddr2line ",
FindDebugBinary(), " ",
DescribeBacktrace(__builtin_frame_address(0)), ")\n", NULL);
__sig_unblock(m);
abort();
+24 -47
View File
@@ -42,28 +42,15 @@
#define MAP_FAILED ((void *)-1)
#define WTERMSIG(x) (127 & (x))
#define WCOREDUMP(x) (128 & (x))
#define WIFEXITED(x) (!WTERMSIG(x))
#define WEXITSTATUS(x) ((x) >> 8)
#define WSTOPSIG(x) ((0xff00 & (x)) >> 8)
#define WIFSTOPPED(x) __wifstopped(x)
#define WIFSIGNALED(x) __wifsignaled(x)
#define WIFCONTINUED(x) __wifcontinued(x)
#define W_STOPCODE(x) ((x) << 8 | 0177)
#ifdef _COSMO_SOURCE
#define clone __clone
#define commandv __commandv
#define fileexists __fileexists
#define ischardev __ischardev
#define isdirectory __isdirectory
#define isexecutable __isexecutable
#define isregularfile __isregularfile
#define issymlink __issymlink
#define makedirs __makedirs
#define tmpfd __tmpfd
#endif
#define WEXITSTATUS(s) ((s) >> 8)
#define WTERMSIG(s) ((s) & 0x7f)
#define WSTOPSIG(s) (((s) & 0xff00) >> 8)
#define WIFEXITED(s) (!WTERMSIG(s))
#define WIFSTOPPED(s) (((s) & 0xff) == 0x7f)
#define WIFSIGNALED(s) (((signed char)(((s) & 0x7f) + 1) >> 1) > 0)
#define WCOREDUMP(s) ((s) & 0x80)
#define WIFCONTINUED(s) (((s) & 0xffff) == 0xffff)
#define W_STOPCODE(s) ((s) << 8 | 0x7f)
COSMOPOLITAN_C_START_
/*───────────────────────────────────────────────────────────────────────────│─╗
@@ -83,8 +70,6 @@ int chmod(const char *, unsigned) libcesque __read_only(1);
int chown(const char *, unsigned, unsigned) libcesque __read_only(1);
int chroot(const char *) libcesque __read_only(1);
int close(int) libcesque;
int close_range(unsigned, unsigned, unsigned) libcesque;
int closefrom(int) libcesque;
int creat(const char *, unsigned) libcesque __read_only(1);
int dup(int) libcesque;
int dup2(int, int) libcesque;
@@ -101,7 +86,6 @@ int fchmod(int, unsigned) libcesque;
int fchmodat(int, const char *, unsigned, int) libcesque __read_only(2);
int fchown(int, unsigned, unsigned) libcesque;
int fchownat(int, const char *, unsigned, unsigned, int) libcesque __read_only(2);
int fcntl(int, int, ...) libcesque;
int fdatasync(int) libcesque;
int fexecve(int, char *const[], char *const[]) libcesque __read_only(2) __read_only(3);
int flock(int, int) libcesque;
@@ -171,7 +155,7 @@ int ttyname_r(int, char *, size_t) libcesque __write_only(2, 3);
int unlink(const char *) libcesque __read_only(1);
int unlinkat(int, const char *, int) libcesque __read_only(2);
int usleep(uint64_t) libcesque;
int vfork(void) libcesque returnstwice;
int vfork(void) dontthrow returnstwice;
int wait(int *) libcesque __write_only(1);
int waitpid(int, int *, int) libcesque __write_only(2);
int64_t clock(void) libcesque;
@@ -194,6 +178,11 @@ unsigned ualarm(unsigned, unsigned) libcesque;
unsigned umask(unsigned) libcesque;
void sync(void) libcesque;
#if defined(_COSMO_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
int madvise(void *, size_t, int);
void closefrom(int) libcesque;
#endif
#if defined(_COSMO_SOURCE) || defined(_GNU_SOURCE)
int syncfs(int) libcesque;
int prctl(int, ...) libcesque;
@@ -204,26 +193,21 @@ int getresgid(unsigned *, unsigned *, unsigned *) libcesque __write_only(1) __wr
int getresuid(unsigned *, unsigned *, unsigned *) libcesque __write_only(1) __write_only(2) __write_only(3);
char *get_current_dir_name(void) libcesque __wur;
ssize_t splice(int, int64_t *, int, int64_t *, size_t, unsigned) libcesque __read_write(2) __read_write(4);
int memfd_create(const char *, unsigned int) libcesque __read_only(1);
int execvpe(const char *, char *const[], char *const[]) libcesque __read_only(1) __read_only(2) __read_only(3);
int euidaccess(const char *, int) libcesque __read_only(1);
int eaccess(const char *, int) libcesque __read_only(1);
int madvise(void *, uint64_t, int) libcesque __read_write(1);
int getcpu(unsigned *, unsigned *) libcesque __write_only(1) __write_only(2);
int close_range(unsigned, unsigned, unsigned) libcesque;
#endif
#ifdef _COSMO_SOURCE
bool32 fdexists(int) libcesque;
bool32 fileexists(const char *) libcesque __read_only(1);
bool32 ischardev(int) libcesque;
bool32 isdirectory(const char *) libcesque __read_only(1);
bool32 isexecutable(const char *) libcesque __read_only(1);
bool32 isregularfile(const char *) libcesque __read_only(1);
bool32 issymlink(const char *) libcesque __read_only(1);
#if defined(_COSMO_SOURCE) || defined(_BSD_SOURCE)
#define commandv __commandv
#define copyfd __copyfd
#define makedirs __makedirs
#define tmpfd __tmpfd
char *commandv(const char *, char *, size_t) libcesque __read_only(1) __write_only(2, 3);
int __getcwd(char *, size_t) libcesque __write_only(1, 2);
int clone(void *, void *, size_t, int, void *, void *, void *, void *);
int fadvise(int, uint64_t, uint64_t, int) libcesque;
int __clone(void *, void *, size_t, int, void *, void *, void *, void *);
int __getcwd(char *, size_t) libcesque;
int makedirs(const char *, unsigned) libcesque __read_only(1);
int pivot_root(const char *, const char *) libcesque __read_only(1) __read_only(2);
int pledge(const char *, const char *) libcesque __read_only(1) __read_only(2);
@@ -237,26 +221,19 @@ int sys_mlockall(int) libcesque;
int sys_munlock(const void *, size_t) libcesque __read_only(1);
int sys_munlockall(void) libcesque;
int sys_personality(uint64_t) libcesque;
int sys_ptrace(int, ...) libcesque;
int sysctl(int *, unsigned, void *, size_t *, void *, size_t) libcesque __read_write(1) __read_write(4) __read_write(5);
int sysctlbyname(const char *, void *, size_t *, void *, size_t) libcesque __read_only(1) __write_only(2) __read_write(3) __read_only(4);
int sysctlnametomib(const char *, int *, size_t *) libcesque __read_only(1) __write_only(2) __read_write(3);
int tmpfd(void) libcesque;
int touch(const char *, unsigned) libcesque __read_only(1);
int unveil(const char *, const char *) libcesque __read_only(1);
long ptrace(int, ...) libcesque;
ssize_t copyfd(int, int, size_t) libcesque;
ssize_t readansi(int, char *, size_t) libcesque;
ssize_t tinyprint(int, const char *, ...) libcesque nullterminated();
void shm_path_np(const char *, char[hasatleast 78]) libcesque;
#endif /* _COSMO_SOURCE */
#endif
int system(const char *) libcesque;
int __wifstopped(int) libcesque pureconst;
int __wifcontinued(int) libcesque pureconst;
int __wifsignaled(int) libcesque pureconst;
#if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE)
#define lseek64 lseek
#define pread64 pread
+40 -37
View File
@@ -16,48 +16,53 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
#include "libc/limits.h"
#include "libc/nt/errors.h"
#include "libc/nt/files.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/sysv/errfuns.h"
#include "libc/sysv/errno.h"
textwindows int sys_chdir_nt_impl(char16_t path[hasatleast PATH_MAX],
uint32_t len) {
uint32_t n;
char16_t var[4];
if (len && path[len - 1] != u'\\') {
if (len + 2 > PATH_MAX)
return enametoolong();
path[len + 0] = u'\\';
path[len + 1] = u'\0';
}
if (SetCurrentDirectory(path)) {
/*
* Now we need to set a magic environment variable.
*/
if ((n = GetCurrentDirectory(PATH_MAX, path))) {
if (n < PATH_MAX) {
if (!((path[0] == '/' && path[1] == '/') ||
(path[0] == '\\' && path[1] == '\\'))) {
var[0] = '=';
var[1] = path[0];
var[2] = ':';
var[3] = 0;
if (!SetEnvironmentVariable(var, path))
return __winerr();
}
return 0;
textwindows int sys_chdir_nt16(char16_t path[hasatleast PATH_MAX],
uint32_t len) {
// the win32 SetCurrentDirectory() API wants us to put back a trailing
// slash that was removed earlier, when __mkntpath() normalized things
if (len + 2 > PATH_MAX)
return enametoolong();
if (SetCurrentDirectory(path))
return 0;
// remap win32 error codes to unix
uint32_t dwErrorCode;
switch ((dwErrorCode = GetLastError())) {
case kNtErrorFileNotFound: // 2 a.k.a. ENOENT
// it does this when the parent components exist but file didn't
errno = ENOENT;
break;
case kNtErrorPathNotFound: // 3 a.k.a. ENOTDIR
// with Windows, this means that the file doesn't exist, because
// a parent component didn't exist. however unix still considers
// that to be an ENOENT error. on UNIX this only becomes ENOTDIR
// if a parent component DID exist, but was not a real directory
if (__hasregularparent(path)) {
errno = ENOTDIR;
} else {
return enametoolong();
errno = ENOENT;
}
} else {
return __winerr();
}
} else {
return __fix_enotdir(__winerr(), path);
break;
case kNtErrorDirectory: // 267 a.k.a. EISDIR
// it'll do this when try to chdir to a regular file that exists
// it could also be an ELOOP error although not worth supporting
errno = ENOTDIR;
break;
default:
errno = __errno_windows2linux(dwErrorCode);
break;
}
return -1;
}
textwindows int sys_chdir_nt(const char *path) {
@@ -65,7 +70,5 @@ textwindows int sys_chdir_nt(const char *path) {
char16_t path16[PATH_MAX];
if ((len = __mkntpath(path, path16)) == -1)
return -1;
if (!len)
return enoent();
return sys_chdir_nt_impl(path16, len);
return sys_chdir_nt16(path16, len);
}
+11 -8
View File
@@ -20,6 +20,7 @@
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.h"
#include "libc/intrin/weaken.h"
#include "libc/log/log.h"
@@ -33,14 +34,19 @@
* The current directory is shared by all threads in a process. This
* does not update the `PWD` environment variable.
*
* On Windows it's important to consider that when you chdir() into a
* directory, any attempt to rmdir() that directory will raise EACCES.
*
* @return 0 on success, or -1 w/ errno
* @raise ELOOP if a loop was detected resolving components of `path`
* @raise EACCES if search permission was denied on directory
* @raise ENOTDIR if component of `path` isn't a directory
* @raise ENOTDIR if parent of `path` exists as non-directory
* @raise ENOTDIR if `path` is a regular file that exists
* @raise ENOMEM if insufficient memory was available
* @raise EFAULT if `path` points to invalid memory
* @raise ENOTSUP if `path` is a `/zip/...` file
* @raise ENAMETOOLONG if `path` was too long
* @raise ENOENT if `path` is empty string
* @raise ENOENT if `path` doesn't exist
* @raise EIO if an i/o error happened
* @asyncsignalsafe
@@ -49,16 +55,13 @@
int chdir(const char *path) {
int rc;
struct ZiposUri zipname;
if (_weaken(__get_tmpdir)) {
if (_weaken(__get_tmpdir))
_weaken(__get_tmpdir)();
}
if (_weaken(GetAddr2linePath)) {
if (_weaken(GetAddr2linePath))
_weaken(GetAddr2linePath)();
}
if (_weaken(GetProgramExecutableName)) {
if (_weaken(GetProgramExecutableName))
_weaken(GetProgramExecutableName)();
}
if (!path) {
if (kisdangerous(path)) {
rc = efault();
} else if (_weaken(__zipos_parseuri) &&
_weaken(__zipos_parseuri)(path, &zipname) != -1) {
+2 -1
View File
@@ -18,9 +18,10 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/dce.h"
#include "libc/intrin/weaken.h"
#include "libc/sysv/errfuns.h"
#ifdef __x86_64__
#if SupportsWindows()
textwindows int _check_signal(bool restartable) {
int status;
+2 -1
View File
@@ -19,6 +19,7 @@
#include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.h"
#include "libc/sysv/errfuns.h"
@@ -48,7 +49,7 @@
*/
int chroot(const char *path) {
int rc;
if (!path) {
if (kisdangerous(path)) {
rc = efault();
} else {
rc = sys_chroot(path);
+1
View File
@@ -17,6 +17,7 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/struct/timespec.internal.h"
#include "libc/cosmotime.h"
#include "libc/dce.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/strace.h"
+18 -17
View File
@@ -17,32 +17,28 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/cosmotime.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/stdio/sysparam.h"
#include "libc/sysv/consts/timer.h"
#include "libc/thread/tls.h"
#ifdef __x86_64__
#include "libc/nt/thunk/msabi.h"
#include "libc/nt/winmm.h"
#if SupportsWindows()
static textwindows int sys_clock_nanosleep_nt_impl(int clock,
struct timespec abs,
sigset_t waitmask) {
uint32_t msdelay;
struct timespec now;
for (;;) {
if (sys_clock_gettime_nt(clock, &now))
return -1;
if (timespec_cmp(now, abs) >= 0)
return 0;
msdelay = timespec_tomillis(timespec_sub(abs, now));
msdelay = MIN(msdelay, -1u);
if (_park_norestart(msdelay, waitmask))
return -1;
}
struct timespec now, wall;
sys_clock_gettime_nt(0, &wall);
if (sys_clock_gettime_nt(clock, &now))
return -1;
if (timespec_cmp(abs, now) > 0)
wall = timespec_add(wall, timespec_sub(abs, now));
return _park_norestart(wall, waitmask);
}
textwindows int sys_clock_nanosleep_nt(int clock, int flags,
@@ -70,4 +66,9 @@ BailOut:
return rc;
}
// called by WinMain() if clock_nanosleep() is linked
__msabi textwindows dontinstrument void sys_clock_nanosleep_nt_init(void) {
__imp_timeBeginPeriod(1); // make sleep(1ms) not take 15ms
}
#endif /* __x86_64__ */
+4 -3
View File
@@ -19,13 +19,14 @@
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/cosmotime.h"
#include "libc/errno.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/errfuns.h"
int sys_clock_nanosleep_openbsd(int clock, int flags,
const struct timespec *req,
struct timespec *rem) {
relegated int sys_clock_nanosleep_openbsd(int clock, int flags,
const struct timespec *req,
struct timespec *rem) {
int res;
struct timespec start, relative, remainder;
if (!flags) {
-3
View File
@@ -54,8 +54,5 @@ int sys_clock_nanosleep(int clock, int flags, //
rc = ecanceled();
}
END_CANCELATION_POINT;
STRACE("sys_clock_nanosleep(%s, %s, %s, [%s]) → %d% m",
DescribeClockName(clock), DescribeSleepFlags(flags),
DescribeTimespec(0, req), DescribeTimespec(rc, rem), rc);
return rc;
}
+1
View File
@@ -21,6 +21,7 @@
#include "libc/calls/struct/timespec.internal.h"
#include "libc/calls/struct/timeval.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/cosmotime.h"
#include "libc/errno.h"
#include "libc/intrin/weaken.h"
#include "libc/runtime/syslib.internal.h"
+9
View File
@@ -17,11 +17,16 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/strace.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/timer.h"
int sys_clock_nanosleep(int, int, const struct timespec *, struct timespec *);
/**
* Sleeps for particular amount of time.
*
@@ -57,6 +62,7 @@
*
* @param clock may be
* - `CLOCK_REALTIME`
* - `CLOCK_BOOTTIME`
* - `CLOCK_MONOTONIC`
* - `CLOCK_REALTIME_COARSE` but is likely to sleep negative time
* - `CLOCK_MONTONIC_COARSE` but is likely to sleep negative time
@@ -98,5 +104,8 @@ errno_t clock_nanosleep(int clock, int flags, //
rc = sys_clock_nanosleep(clock, flags, req, rem);
err = !rc ? 0 : errno;
errno = old;
STRACE("clock_nanosleep(%s, %s, %s, [%s]) → %s", DescribeClockName(clock),
DescribeSleepFlags(flags), DescribeTimespec(0, req),
DescribeTimespec(rc, rem), DescribeErrno(err));
return err;
}
+1 -2
View File
@@ -16,10 +16,9 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/calls/struct/timeval.h"
#include "libc/calls/struct/timeval.internal.h"
#include "libc/cosmotime.h"
#include "libc/dce.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/strace.h"
+74 -42
View File
@@ -16,54 +16,100 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/flocks.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/fds.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.h"
#include "libc/intrin/weaken.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sock/syscall_fd.internal.h"
#include "libc/sysv/errfuns.h"
// for performance reasons we want to avoid holding __fds_lock()
// while sys_close() is happening. this leaves the kernel / libc
// having a temporarily inconsistent state. routines that obtain
// file descriptors the way __zipos_open() does need to retry if
// there's indication this race condition happened.
#include "libc/sysv/pib.h"
static int close_impl(int fd) {
if (fd < 0) {
// handle obvious case
if (fd < 0)
return ebadf();
}
// give kprintf() the opportunity to dup() stderr
if (fd == 2 && _weaken(kloghandle)) {
if (fd == 2 && !__vforked && _weaken(kloghandle))
_weaken(kloghandle)();
}
if (__isfdkind(fd, kFdZip)) {
unassert(_weaken(__zipos_close));
return _weaken(__zipos_close)(fd);
}
if (!IsWindows() && !IsMetal()) {
// unix isn't required to track fds
if (fd >= __get_pib()->fds.n) {
if (IsWindows() || IsMetal())
return ebadf();
return sys_close(fd);
}
if (IsWindows()) {
return sys_close_nt(fd, fd);
// if we're vforked on unix then we can't even modify f->kind
if (__vforked && !(IsWindows() || IsMetal()))
return sys_close(fd);
// atomically close file descriptor table entry
int rc = 0;
struct Fd *f = __get_pib()->fds.p + fd;
switch (atomic_exchange(&f->kind, kFdReserved)) {
case kFdEmpty:
if (IsWindows() || IsMetal()) {
rc = ebadf();
} else {
rc = sys_close(fd);
}
break;
case kFdReserved:
return ebadf();
case kFdZip:
if (_weaken(__zipos_close))
rc = _weaken(__zipos_close)(fd);
break;
#if SupportsMetal()
case kFdSerial:
break;
#endif
#if SupportsWindows()
case kFdDevRandom:
break;
case kFdFile:
if (!__vforked)
if (_weaken(__flocks_close))
_weaken(__flocks_close)(f);
if (!__vforked || f->was_created_during_vfork) {
if (f->cursor)
__cursor_unref(f->cursor);
if (!CloseHandle(f->handle))
rc = __winerr();
}
break;
case kFdDevNull:
case kFdConsole:
if (!__vforked || f->was_created_during_vfork)
if (!CloseHandle(f->handle))
rc = __winerr();
break;
case kFdSocket:
if (!__vforked || f->was_created_during_vfork)
if (_weaken(sys_closesocket_nt))
rc = _weaken(sys_closesocket_nt)(f);
break;
#endif
default:
rc = ebadf();
break;
}
return 0;
// wipe fd and update lowest file descriptor number
// we do this even if there was an underlying os error
__releasefd(fd);
return rc;
}
/**
@@ -74,6 +120,8 @@ static int close_impl(int fd) {
* - openat()
* - socket()
* - accept()
* - pipe()
* - socketpair()
* - landlock_create_ruleset()
*
* This function should never be reattempted if an error is returned;
@@ -82,29 +130,13 @@ static int close_impl(int fd) {
*
* @return 0 on success, or -1 w/ errno
* @raise EINTR if signal was delivered; do *not* retry
* @raise EBADF if `fd` is negative or not open; however, an exception
* is made by Cosmopolitan Libc for `close(-1)` which returns zero
* and does nothing, in order to assist with code that may wish to
* close the same resource multiple times without dirtying `errno`
* @raise EBADF if `fd` is negative or not open
* @raise EIO if a low-level i/o error occurred
* @asyncsignalsafe
* @vforksafe
*/
int close(int fd) {
int rc;
if (__isfdkind(fd, kFdZip)) { // XXX IsWindows()?
BLOCK_SIGNALS;
__fds_lock();
rc = close_impl(fd);
if (!__vforked)
__releasefd(fd);
__fds_unlock();
ALLOW_SIGNALS;
} else {
rc = close_impl(fd);
if (!__vforked)
__releasefd(fd);
}
int rc = close_impl(fd);
STRACE("close(%d) → %d% m", fd, rc);
return rc;
}
+7 -12
View File
@@ -23,29 +23,24 @@
#include "libc/sysv/errfuns.h"
/**
* Closes inclusive range of file descriptors, e.g.
* Closes inclusive range of file descriptors.
*
* // close all non-stdio file descriptors
* if (close_range(3, -1, 0) == -1) {
* for (int i = 3; i < 256; ++i) {
* close(i);
* }
* }
* This system call is only supported on Linux 5.9+ and FreeBSD 13+.
* Consider using closefrom() which will work on all platforms.
*
* The following flags are available:
*
* - `CLOSE_RANGE_UNSHARE` (Linux-only)
* - `CLOSE_RANGE_CLOEXEC` (Linux-only)
* - `CLOSE_RANGE_CLOEXEC` to mark file descriptors as close-on-exec
* instead of actually closing them.
*
* This is only supported on Linux 5.9+ and FreeBSD 13+. Consider using
* closefrom() which will work on OpenBSD too.
* - `CLOSE_RANGE_UNSHARE` (Linux only) can improve performance in
* situations where threads are in play.
*
* @return 0 on success, or -1 w/ errno
* @error EINVAL if flags are bad or first is greater than last
* @error EMFILE if a weird race condition happens on Linux
* @error ENOSYS if not Linux 5.9+ or FreeBSD 13+
* @error ENOMEM on Linux maybe
* @see closefrom()
*/
int close_range(unsigned int first, unsigned int last, unsigned int flags) {
int rc;
+36 -29
View File
@@ -18,47 +18,54 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/rlimit.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/strace.h"
#include "libc/limits.h"
#include "libc/sysv/errfuns.h"
#include "libc/sysv/pib.h"
/**
* Closes extra file descriptors, e.g.
* Closes extra file descriptors.
*
* if (closefrom(3))
* for (int i = 3; i < 256; ++i)
* close(i);
*
* @return 0 on success, or -1 w/ errno
* @raise EBADF if `first` is negative
* @raise ENOSYS if not Linux 5.9+, FreeBSD 8+, OpenBSD, or NetBSD
* @raise EBADF on OpenBSD if `first` is greater than highest fd
* @raise EINVAL if flags are bad or first is greater than last
* @raise EMFILE if a weird race condition happens on Linux
* @raise EINTR possibly on OpenBSD
* @raise ENOMEM on Linux maybe
* This system call is always successful.
*/
int closefrom(int first) {
int rc;
void closefrom(int minfd) {
BLOCK_CANCELATION;
if (first < 0) {
// consistent with openbsd
// freebsd allows this but it's dangerous
// necessary on linux due to type signature
rc = ebadf();
} else if (IsFreebsd() || IsOpenbsd()) {
rc = sys_closefrom(first);
// fix arg
if (minfd < 0)
minfd = 0;
// try syscall
int rc;
int olderr = errno;
if (IsFreebsd() || IsOpenbsd()) {
rc = sys_closefrom(minfd);
} else if (IsLinux()) {
rc = sys_close_range(first, 0xffffffffu, 0);
rc = sys_close_range(minfd, 0xffffffffu, 0);
} else if (IsNetbsd()) {
rc = __sys_fcntl(first, 10 /*F_CLOSEM*/, first);
rc = __sys_fcntl(minfd, 10 /*F_CLOSEM*/, minfd);
} else {
rc = enosys();
rc = -1;
}
// try polyfill
if (rc == -1) {
long maxfd = 1024;
if (IsWindows() || IsMetal()) {
maxfd = __get_pib()->fds.n;
} else {
struct rlimit rl;
if (!getrlimit(RLIMIT_NOFILE, &rl))
if (rl.rlim_cur <= 0x7fffffffu)
maxfd = rl.rlim_cur;
}
for (long fd = minfd; fd < maxfd; ++fd)
close(fd);
errno = olderr;
}
ALLOW_CANCELATION;
STRACE("closefrom(%d) → %d% m", first, rc);
return rc;
STRACE("closefrom(%d)", minfd);
}
+2
View File
@@ -83,9 +83,11 @@ static void copy_file_range_init(void) {
* @return number of bytes transferred, or -1 w/ errno
* @raise EXDEV if source and destination are on different filesystems
* @raise EBADF if `infd` or `outfd` aren't open files or append-only
* @raise EOPNOTSUPP if filesystem doesn't support this operation
* @raise EPERM if `fdout` refers to an immutable file on Linux
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINVAL if ranges overlap or `flags` is non-zero
* @raise EINVAL on eCryptFs filesystems that have a bug
* @raise EFBIG if `setrlimit(RLIMIT_FSIZE)` is exceeded
* @raise EFAULT if one of the pointers memory is bad
* @raise ERANGE if overflow happens computing ranges
+3 -1
View File
@@ -23,10 +23,12 @@
/**
* Creates file.
*
* This is equivalent to saying:
* This function is equivalent to:
*
* int fd = openat(AT_FDCWD, file, O_CREAT | O_WRONLY | O_TRUNC, mode);
*
* Please see openat() for further documentation.
*
* @param file specifies filesystem path to create
* @param mode is octal bits, e.g. 0644 usually
* @return file descriptor, or -1 w/ errno
Loaded 100 of 3334 files, more files were not shown because too many files have changed in this diff. Show more